- pypy
- pypy3
install:
-- pip install --upgrade --force-reinstall "setuptools; python_version != '3.2' and python_version != '3.3'" "setuptools < 30; python_version == '3.2'" "setuptools < 40; python_version == '3.3'"
+- pip install --upgrade --force-reinstall "setuptools; python_version != '3.3'" "setuptools < 40; python_version == '3.3'"
- pip uninstall --yes six || true
- pip install --upgrade --force-reinstall --ignore-installed -e .
-- pip install pytest==2.9.2 typing
+- pip install "pytest==4.6.9; python_version != '3.3'" "pytest==2.9.2; python_version == '3.3'" typing
- &py_pkg_list pip list --format=columns || pip list
script:
- py.test
jobs:
fast_finish: true
include:
- - python: 2.6
- dist: trusty
- - python: 3.2
- dist: trusty
- python: 3.3
dist: trusty
- stage: upload new version of python package to PYPI (only for tagged commits)
This file lists the changes in each six version.
+1.14.0
+------
+
+- Issue #288, pull request #289: Add `six.assertNotRegex`.
+
+- Issue #317: `six.moves._dummy_thread` now points to the `_thread` module on
+ Python 3.9+. Python 3.7 and later requires threading and deprecated the
+ `_dummy_thread` module.
+
+- Issue #308, pull request #314: Remove support for Python 2.6 and Python 3.2.
+
+- Issue #250, issue #165, pull request #251: `six.wraps` now ignores missing
+ attributes. This follows the Python 3.2+ standard library behavior.
+
1.13.0
------
otherwise worked to improve six:
Marc Abramowitz
+immerrr again
Alexander Artemenko
Aymeric Augustin
Lee Ball
Max Grender-Jones
Joshua Harlow
Toshiki Kataoka
+Hugo van Kemenade
Anselm Kruis
Ivan Levkivskyi
Alexander Lukanin
Miroslav Shubernetskiy
Eli Schwartz
Anthony Sottile
+Jonathan Vanasco
Lucas Wiman
Jingxin Zhu
-Copyright (c) 2010-2019 Benjamin Peterson
+Copyright (c) 2010-2020 Benjamin Peterson
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
writing Python code that is compatible on both Python versions. See the
documentation for more information on what is provided.
-Six supports every Python version since 2.6. It is contained in only one Python
+Six supports Python 2.7 and 3.3+. It is contained in only one Python
file, so it can be easily copied into your project. (The copyright and license
notice must be retained.)
Bugs can be reported to https://github.com/benjaminp/six. The code can also
be found there.
-
-For questions about six or porting in general, email the python-porting mailing
-list: https://mail.python.org/mailman/listinfo/python-porting
# General information about the project.
project = u"six"
-copyright = u"2010-2019, Benjamin Peterson"
+copyright = u"2010-2020, Benjamin Peterson"
sys.path.append(os.path.abspath(os.path.join(".", "..")))
from six import __version__ as six_version
.. decorator:: wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, updated=functools.WRAPPER_UPDATES)
- This is exactly the :func:`py3:functools.wraps` decorator, but it sets the
- ``__wrapped__`` attribute on what it decorates as :func:`py3:functools.wraps`
- does on Python versions after 3.2.
+ This is Python 3.2's :func:`py3:functools.wraps` decorator. It sets the
+ ``__wrapped__`` attribute on what it decorates. It doesn't raise an error if
+ any of the attributes mentioned in ``assigned`` and ``updated`` are missing
+ on ``wrapped`` object.
Syntax compatibility
Alias for :meth:`~py3:unittest.TestCase.assertRegex` on Python 3 and
:meth:`~py2:unittest.TestCase.assertRegexpMatches` on Python 2.
+.. function:: assertNotRegex()
+
+ Alias for :meth:`~py3:unittest.TestCase.assertNotRegex` on Python 3 and
+ :meth:`~py2:unittest.TestCase.assertNotRegexpMatches` on Python 2.
+
Renamed modules and attributes compatibility
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+------------------------------+-------------------------------------+---------------------------------------+
| ``dbm_ndbm`` | :mod:`py2:dbm` | :mod:`py3:dbm.ndbm` |
+------------------------------+-------------------------------------+---------------------------------------+
-| ``_dummy_thread`` | :mod:`py2:dummy_thread` | :mod:`py3:_dummy_thread` |
+| ``_dummy_thread`` | :mod:`py2:dummy_thread` | :mod:`py3:_dummy_thread` (< 3.9) |
+| | | :mod:`py3:_thread` (3.9+) |
+------------------------------+-------------------------------------+---------------------------------------+
| ``email_mime_base`` | :mod:`py2:email.MIMEBase` | :mod:`py3:email.mime.base` |
+------------------------------+-------------------------------------+---------------------------------------+
-# Copyright (c) 2010-2019 Benjamin Peterson
+# Copyright (c) 2010-2020 Benjamin Peterson
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
long_description=six_long_description,
license="MIT",
classifiers=six_classifiers,
- python_requires=">=2.6, !=3.0.*, !=3.1.*",
+ python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*",
)
-# Copyright (c) 2010-2019 Benjamin Peterson
+# Copyright (c) 2010-2020 Benjamin Peterson
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
import types
__author__ = "Benjamin Peterson <benjamin@python.org>"
-__version__ = "1.13.0"
+__version__ = "1.14.0"
# Useful for very coarse version differentiation.
MovedModule("copyreg", "copy_reg"),
MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"),
- MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
+ MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"),
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
MovedModule("http_cookies", "Cookie", "http.cookies"),
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
if sys.version_info[1] <= 1:
_assertRaisesRegex = "assertRaisesRegexp"
_assertRegex = "assertRegexpMatches"
+ _assertNotRegex = "assertNotRegexpMatches"
else:
_assertRaisesRegex = "assertRaisesRegex"
_assertRegex = "assertRegex"
+ _assertNotRegex = "assertNotRegex"
else:
def b(s):
return s
_assertCountEqual = "assertItemsEqual"
_assertRaisesRegex = "assertRaisesRegexp"
_assertRegex = "assertRegexpMatches"
+ _assertNotRegex = "assertNotRegexpMatches"
_add_doc(b, """Byte literal""")
_add_doc(u, """Text literal""")
return getattr(self, _assertRegex)(*args, **kwargs)
+def assertNotRegex(self, *args, **kwargs):
+ return getattr(self, _assertNotRegex)(*args, **kwargs)
+
+
if PY3:
exec_ = getattr(moves.builtins, "exec")
""")
-if sys.version_info[:2] == (3, 2):
- exec_("""def raise_from(value, from_value):
- try:
- if from_value is None:
- raise value
- raise value from from_value
- finally:
- value = None
-""")
-elif sys.version_info[:2] > (3, 2):
+if sys.version_info[:2] > (3,):
exec_("""def raise_from(value, from_value):
try:
raise value from from_value
_add_doc(reraise, """Reraise an exception.""")
if sys.version_info[0:2] < (3, 4):
+ # This does exactly the same what the :func:`py3:functools.update_wrapper`
+ # function does on Python versions after 3.2. It sets the ``__wrapped__``
+ # attribute on ``wrapper`` object and it doesn't raise an error if any of
+ # the attributes mentioned in ``assigned`` and ``updated`` are missing on
+ # ``wrapped`` object.
+ def _update_wrapper(wrapper, wrapped,
+ assigned=functools.WRAPPER_ASSIGNMENTS,
+ updated=functools.WRAPPER_UPDATES):
+ for attr in assigned:
+ try:
+ value = getattr(wrapped, attr)
+ except AttributeError:
+ continue
+ else:
+ setattr(wrapper, attr, value)
+ for attr in updated:
+ getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
+ wrapper.__wrapped__ = wrapped
+ return wrapper
+ _update_wrapper.__doc__ = functools.update_wrapper.__doc__
+
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
updated=functools.WRAPPER_UPDATES):
- def wrapper(f):
- f = functools.wraps(wrapped, assigned, updated)(f)
- f.__wrapped__ = wrapped
- return f
- return wrapper
+ return functools.partial(_update_wrapper, wrapped=wrapped,
+ assigned=assigned, updated=updated)
+ wraps.__doc__ = functools.wraps.__doc__
+
else:
wraps = functools.wraps
raise TypeError("not expecting type '%s'" % type(s))
-
def python_2_unicode_compatible(klass):
"""
- A decorator that defines __unicode__ and __str__ methods under Python 2.
+ A class decorator that defines __unicode__ and __str__ methods under Python 2.
Under Python 3 it does nothing.
To support Python 2 and 3 with a single code base, define a __str__ method
-# Copyright (c) 2010-2019 Benjamin Peterson
+# Copyright (c) 2010-2020 Benjamin Peterson
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
item = getattr(six.moves, item_name)
if isinstance(item, types.ModuleType):
__import__("six.moves." + item_name)
- except AttributeError:
- if item_name == "zip_longest" and sys.version_info < (2, 6):
- pytest.skip("zip_longest only available on 2.6+")
except ImportError:
if item_name == "winreg" and not sys.platform.startswith("win"):
pytest.skip("Windows only module")
if item_name.startswith("tkinter"):
if not have_tkinter:
pytest.skip("requires tkinter")
- if item_name == "tkinter_ttk" and sys.version_info[:2] <= (2, 6):
- pytest.skip("ttk only available on 2.7+")
if item_name.startswith("dbm_gnu") and not have_gdbm:
pytest.skip("requires gdbm")
raise
- if sys.version_info[:2] >= (2, 6):
- assert item_name in dir(six.moves)
+ assert item_name in dir(six.moves)
@pytest.mark.parametrize("item_name",
[item.name for item in six._urllib_parse_moved_attributes])
def test_move_items_urllib_parse(item_name):
"""Ensure that everything loads correctly."""
- if item_name == "ParseResult" and sys.version_info < (2, 5):
- pytest.skip("ParseResult is only found on 2.5+")
- if item_name in ("parse_qs", "parse_qsl") and sys.version_info < (2, 6):
- pytest.skip("parse_qs[l] is new in 2.6")
- if sys.version_info[:2] >= (2, 6):
- assert item_name in dir(six.moves.urllib.parse)
+ assert item_name in dir(six.moves.urllib.parse)
getattr(six.moves.urllib.parse, item_name)
[item.name for item in six._urllib_error_moved_attributes])
def test_move_items_urllib_error(item_name):
"""Ensure that everything loads correctly."""
- if sys.version_info[:2] >= (2, 6):
- assert item_name in dir(six.moves.urllib.error)
+ assert item_name in dir(six.moves.urllib.error)
getattr(six.moves.urllib.error, item_name)
[item.name for item in six._urllib_request_moved_attributes])
def test_move_items_urllib_request(item_name):
"""Ensure that everything loads correctly."""
- if sys.version_info[:2] >= (2, 6):
- assert item_name in dir(six.moves.urllib.request)
+ assert item_name in dir(six.moves.urllib.request)
getattr(six.moves.urllib.request, item_name)
[item.name for item in six._urllib_response_moved_attributes])
def test_move_items_urllib_response(item_name):
"""Ensure that everything loads correctly."""
- if sys.version_info[:2] >= (2, 6):
- assert item_name in dir(six.moves.urllib.response)
+ assert item_name in dir(six.moves.urllib.response)
getattr(six.moves.urllib.response, item_name)
[item.name for item in six._urllib_robotparser_moved_attributes])
def test_move_items_urllib_robotparser(item_name):
"""Ensure that everything loads correctly."""
- if sys.version_info[:2] >= (2, 6):
- assert item_name in dir(six.moves.urllib.robotparser)
+ assert item_name in dir(six.moves.urllib.robotparser)
getattr(six.moves.urllib.robotparser, item_name)
assert six.advance_iterator(zip(range(2), range(2))) == (0, 0)
-@pytest.mark.skipif("sys.version_info < (2, 6)")
def test_zip_longest():
from six.moves import zip_longest
it = zip_longest(range(2), range(1))
monkeypatch.undo()
-@pytest.mark.skipif("sys.version_info[:2] < (2, 7)",
- reason="view methods on dictionaries only available on 2.7+")
def test_dictionary_views():
- def stock_method_name(viewwhat):
- """Given a method suffix like "keys" or "values", return the name
- of the dict method that delivers those on the version of Python
- we're running in."""
- if six.PY3:
- return viewwhat
- return 'view' + viewwhat
-
d = dict(zip(range(10), (range(11, 20))))
for name in "keys", "values", "items":
meth = getattr(six, "view" + name)
# We should have done a raise f from None equivalent.
assert val.__cause__ is None
assert val.__context__ is ctx
- if sys.version_info[:2] >= (3, 3):
# And that should suppress the context on the exception.
assert val.__suppress_context__
# For all versions the outer exception should have raised successfully.
assert out.flushed
-@pytest.mark.skipif("sys.version_info[:2] >= (2, 6)")
-def test_print_encoding(monkeypatch):
- # Fool the type checking in print_.
- monkeypatch.setattr(six, "file", six.BytesIO, raising=False)
- out = six.BytesIO()
- out.encoding = "utf-8"
- out.errors = None
- six.print_(six.u("\u053c"), end="", file=out)
- assert out.getvalue() == six.b("\xd4\xbc")
- out = six.BytesIO()
- out.encoding = "ascii"
- out.errors = "strict"
- pytest.raises(UnicodeEncodeError, six.print_, six.u("\u053c"), file=out)
- out.errors = "backslashreplace"
- six.print_(six.u("\u053c"), end="", file=out)
- assert out.getvalue() == six.b("\\u053c")
-
-
def test_print_exceptions():
pytest.raises(TypeError, six.print_, x=3)
pytest.raises(TypeError, six.print_, end=3)
assert Y.__mro__ == (Y, X, object)
-@pytest.mark.skipif("sys.version_info[:2] < (2, 7)")
def test_with_metaclass_typing():
try:
import typing
def f(g, assign, update):
def w():
return 42
- w.glue = {"foo" : "bar"}
+ w.glue = {"foo": "bar"}
+ w.xyzzy = {"qux": "quux"}
return six.wraps(g, assign, update)(w)
- k.glue = {"melon" : "egg"}
+ k.glue = {"melon": "egg"}
k.turnip = 43
- k = f(k, ["turnip"], ["glue"])
+ k = f(k, ["turnip", "baz"], ["glue", "xyzzy"])
assert k.__name__ == "w"
assert k.turnip == 43
- assert k.glue == {"melon" : "egg", "foo" : "bar"}
+ assert not hasattr(k, "baz")
+ assert k.glue == {"melon": "egg", "foo": "bar"}
+ assert k.xyzzy == {"qux": "quux"}
+
+
+def test_wraps_raises_on_missing_updated_field_on_wrapper():
+ """Ensure six.wraps doesn't ignore missing attrs wrapper.
+
+ Because that's what happens in Py3's functools.update_wrapper.
+ """
+ def wrapped():
+ pass
+
+ def wrapper():
+ pass
+
+ with pytest.raises(AttributeError, match='has no attribute.*xyzzy'):
+ six.wraps(wrapped, [], ['xyzzy'])(wrapper)
+
def test_add_metaclass():
assert A.B.__qualname__ == expected
-@pytest.mark.skipif("sys.version_info[:2] < (2, 7) or sys.version_info[:2] in ((3, 0), (3, 1))")
def test_assertCountEqual():
class TestAssertCountEqual(unittest.TestCase):
def test(self):
TestAssertCountEqual('test').test()
-@pytest.mark.skipif("sys.version_info[:2] < (2, 7)")
def test_assertRegex():
class TestAssertRegex(unittest.TestCase):
def test(self):
TestAssertRegex('test').test()
-@pytest.mark.skipif("sys.version_info[:2] < (2, 7)")
+def test_assertNotRegex():
+ class TestAssertNotRegex(unittest.TestCase):
+ def test(self):
+ with self.assertRaises(AssertionError):
+ six.assertNotRegex(self, 'test', r'^t')
+
+ six.assertNotRegex(self, 'test', r'^a')
+
+ TestAssertNotRegex('test').test()
+
+
def test_assertRaisesRegex():
class TestAssertRaisesRegex(unittest.TestCase):
def test(self):
[tox]
-envlist=py26,py27,py32,py33,py34,py35,py36,py37,py38,pypy,flake8
+envlist=py27,py33,py34,py35,py36,py37,py38,pypy,flake8
[testenv]
deps= pytest