Imported Upstream version 1.14.0 upstream/1.14.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 4 Jan 2021 06:35:52 +0000 (15:35 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 4 Jan 2021 06:35:52 +0000 (15:35 +0900)
.travis.yml
CHANGES
CONTRIBUTORS
LICENSE
README.rst
documentation/conf.py
documentation/index.rst
setup.py
six.py
test_six.py
tox.ini

index 94231049a7f1db3be2b2470a7e4f7f9518334294..772aeff16aad05520929f951e17d2ef3a2b63f26 100644 (file)
@@ -12,10 +12,10 @@ python:
 - 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
@@ -26,10 +26,6 @@ script:
 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)
diff --git a/CHANGES b/CHANGES
index ffa702601b6509b25fbd6787d1e7682e5ac39356..63eb48ea321ec79221c580a9894353ce9b555497 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -3,6 +3,20 @@ Changelog for six
 
 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
 ------
 
index 26180decfb7f56ea7fd8d3762dcc4d3809e347ee..4480c34b1f867371f6f84138c8f4e661afc0ca4e 100644 (file)
@@ -3,6 +3,7 @@ acknowledge the following people who submitted bug reports, pull requests, and
 otherwise worked to improve six:
 
 Marc Abramowitz
+immerrr again
 Alexander Artemenko
 Aymeric Augustin
 Lee Ball
@@ -20,6 +21,7 @@ Thomas Grainger
 Max Grender-Jones
 Joshua Harlow
 Toshiki Kataoka
+Hugo van Kemenade
 Anselm Kruis
 Ivan Levkivskyi
 Alexander Lukanin
@@ -33,6 +35,7 @@ Peter Ruibal
 Miroslav Shubernetskiy
 Eli Schwartz
 Anthony Sottile
+Jonathan Vanasco
 Lucas Wiman
 Jingxin Zhu
 
diff --git a/LICENSE b/LICENSE
index 4b05a545261c0f9b31abe3edec710587587a659a..de6633112c1f9951fd688e1fb43457a1ec11d6d8 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-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
index a99e6f5585a918b8a68ceabace2fb6e34e92183b..6339ba5d932c796edf6bd5c1301a0d7cb2dd0ae7 100644 (file)
@@ -19,7 +19,7 @@ for smoothing over the differences between the Python versions with the goal of
 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.)
 
@@ -27,6 +27,3 @@ Online documentation is at https://six.readthedocs.io/.
 
 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
index b3d1328adc2a42afde71915193060433504ce208..2f0f3238ae8530dc95cea70d663fd21f891123a7 100644 (file)
@@ -33,7 +33,7 @@ master_doc = "index"
 
 # 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
index b7ec2754ecf83d5a291c13988738fef62045c986..e2e82e3df8d073b2fff8625e4a4c340cb168b79c 100644 (file)
@@ -257,9 +257,10 @@ functions and methods is the stdlib :mod:`py3:inspect` module.
 
 .. 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
@@ -509,6 +510,11 @@ Note these functions are only available on Python 2.7 or later.
    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
 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -590,7 +596,8 @@ Supported renames:
 +------------------------------+-------------------------------------+---------------------------------------+
 | ``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`            |
 +------------------------------+-------------------------------------+---------------------------------------+
index 97c685b5a50bd9769804861f4ad7a8396215d239..d90958b69d399aeda2c298b89843cbb760d4e164 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,4 @@
-# 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
@@ -54,5 +54,5 @@ setup(name="six",
       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.*",
       )
diff --git a/six.py b/six.py
index 357e624abc6c9b3ffdaec126b7009abb5ef4275d..5fe9f8e141ee13756fec536415adc446bfc109d7 100644 (file)
--- a/six.py
+++ b/six.py
@@ -1,4 +1,4 @@
-# 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
@@ -29,7 +29,7 @@ import sys
 import types
 
 __author__ = "Benjamin Peterson <benjamin@python.org>"
-__version__ = "1.13.0"
+__version__ = "1.14.0"
 
 
 # Useful for very coarse version differentiation.
@@ -259,7 +259,7 @@ _moved_attributes = [
     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"),
@@ -644,9 +644,11 @@ if PY3:
     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
@@ -668,6 +670,7 @@ else:
     _assertCountEqual = "assertItemsEqual"
     _assertRaisesRegex = "assertRaisesRegexp"
     _assertRegex = "assertRegexpMatches"
+    _assertNotRegex = "assertNotRegexpMatches"
 _add_doc(b, """Byte literal""")
 _add_doc(u, """Text literal""")
 
@@ -684,6 +687,10 @@ def assertRegex(self, *args, **kwargs):
     return getattr(self, _assertRegex)(*args, **kwargs)
 
 
+def assertNotRegex(self, *args, **kwargs):
+    return getattr(self, _assertNotRegex)(*args, **kwargs)
+
+
 if PY3:
     exec_ = getattr(moves.builtins, "exec")
 
@@ -719,16 +726,7 @@ else:
 """)
 
 
-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
@@ -808,13 +806,33 @@ if sys.version_info[:2] < (3, 3):
 _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
 
@@ -919,10 +937,9 @@ def ensure_text(s, encoding='utf-8', errors='strict'):
         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
index 0b72067416320373a9a7a2c321ac9a4c99963796..7b8b03b5e61a77532a9395b697e11aa85a095bea 100644 (file)
@@ -1,4 +1,4 @@
-# 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
@@ -121,34 +121,23 @@ def test_move_items(item_name):
         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)
 
 
@@ -156,8 +145,7 @@ def test_move_items_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)
 
 
@@ -165,8 +153,7 @@ def test_move_items_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)
 
 
@@ -174,8 +161,7 @@ def test_move_items_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)
 
 
@@ -183,8 +169,7 @@ def test_move_items_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)
 
 
@@ -244,7 +229,6 @@ def test_zip():
     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))
@@ -417,17 +401,7 @@ def test_dictionary_iterators(monkeypatch):
         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)
@@ -644,7 +618,6 @@ def test_raise_from():
         # 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.
@@ -690,24 +663,6 @@ def test_print_():
     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)
@@ -745,7 +700,6 @@ def test_with_metaclass():
     assert Y.__mro__ == (Y, X, object)
 
 
-@pytest.mark.skipif("sys.version_info[:2] < (2, 7)")
 def test_with_metaclass_typing():
     try:
         import typing
@@ -840,14 +794,33 @@ def test_wraps():
     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():
@@ -943,7 +916,6 @@ def test_add_metaclass_nested():
     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):
@@ -955,7 +927,6 @@ def test_assertCountEqual():
     TestAssertCountEqual('test').test()
 
 
-@pytest.mark.skipif("sys.version_info[:2] < (2, 7)")
 def test_assertRegex():
     class TestAssertRegex(unittest.TestCase):
         def test(self):
@@ -967,7 +938,17 @@ def test_assertRegex():
     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):
diff --git a/tox.ini b/tox.ini
index 292aee807d9f47395c7b08dda85aedf21f649a21..a1e34676267a1377bb5ad4b43a3f36a73d685962 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [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