From 1b8ef510652eec238f835ba8d2b28bfef677833a Mon Sep 17 00:00:00 2001
From: Fabian Neundorf <CommodoreFabianus@gmx.de>
Date: Sat, 20 Jun 2015 00:57:38 +0200
Subject: [PATCH 001/183] Add count from 2.7 to 2.6

The future.types.newrange already has a _count method which is now moved to the
backports to allow make this public.
---
 src/future/backports/misc.py    | 10 ++++++
 src/future/types/newrange.py    | 10 ++----
 tests/test_future/test_count.py | 57 +++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+), 8 deletions(-)
 create mode 100644 tests/test_future/test_count.py

diff --git a/src/future/backports/misc.py b/src/future/backports/misc.py
index 03c68c13..71acad27 100644
--- a/src/future/backports/misc.py
+++ b/src/future/backports/misc.py
@@ -22,6 +22,16 @@ def ceil(x):
     return int(oldceil(x))
 
 
+if PY26:
+    # itertools.count in Py 2.6 doesn't accept a step parameter
+    def count(start=0, step=1):
+        while True:
+            yield start
+            start += step
+else:
+    from itertools import count
+
+
 # OrderedDict Shim from  Raymond Hettinger, python core dev
 # http://code.activestate.com/recipes/576693-ordered-dictionary-for-py24/
 # here to support version 2.6.
diff --git a/src/future/types/newrange.py b/src/future/types/newrange.py
index 432f11a1..17b6736b 100644
--- a/src/future/types/newrange.py
+++ b/src/future/types/newrange.py
@@ -21,6 +21,7 @@
 from collections import Sequence, Iterator
 from itertools import islice
 
+from future.backports.misc import count
 
 class newrange(Sequence):
     """
@@ -141,7 +142,7 @@ class range_iterator(Iterator):
     """An iterator for a :class:`range`.
     """
     def __init__(self, range_):
-        self._stepper = islice(_count(range_.start, range_.step), len(range_))
+        self._stepper = islice(count(range_.start, range_.step), len(range_))
 
     def __iter__(self):
         return self
@@ -150,11 +151,4 @@ def next(self):
         return next(self._stepper)
 
 
-# itertools.count in Py 2.6 doesn't accept a step parameter
-def _count(start=0, step=1):
-    while True:
-        yield start
-        start += step
-
-
 __all__ = ['newrange']
diff --git a/tests/test_future/test_count.py b/tests/test_future/test_count.py
new file mode 100644
index 00000000..cc849bd5
--- /dev/null
+++ b/tests/test_future/test_count.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+"""
+Tests for the backported class:`range` class.
+"""
+from itertools import count as it_count
+
+from future.backports.misc import count
+from future.tests.base import unittest, skip26
+
+
+class CountTest(unittest.TestCase):
+
+    """Test the count function."""
+
+    def _test_count_func(self, func):
+        self.assertEqual(next(func(1)), 1)
+        self.assertEqual(next(func(start=1)), 1)
+
+        c = func()
+        self.assertEqual(next(c), 0)
+        self.assertEqual(next(c), 1)
+        self.assertEqual(next(c), 2)
+        c = func(1, 1)
+        self.assertEqual(next(c), 1)
+        self.assertEqual(next(c), 2)
+        c = func(step=1)
+        self.assertEqual(next(c), 0)
+        self.assertEqual(next(c), 1)
+        c = func(start=1, step=1)
+        self.assertEqual(next(c), 1)
+        self.assertEqual(next(c), 2)
+
+        c = func(-1)
+        self.assertEqual(next(c), -1)
+        self.assertEqual(next(c), 0)
+        self.assertEqual(next(c), 1)
+        c = func(1, -1)
+        self.assertEqual(next(c), 1)
+        self.assertEqual(next(c), 0)
+        self.assertEqual(next(c), -1)
+        c = func(-1, -1)
+        self.assertEqual(next(c), -1)
+        self.assertEqual(next(c), -2)
+        self.assertEqual(next(c), -3)
+
+    def test_count(self):
+        """Test the count function."""
+        self._test_count_func(count)
+
+    @skip26
+    def test_own_count(self):
+        """Test own count implementation."""
+        self._test_count_func(it_count)
+
+
+if __name__ == '__main__':
+    unittest.main()

From 5b25b778b63353df0f97e538ea28e44b3dce53e2 Mon Sep 17 00:00:00 2001
From: Fergus Symon <fergofrog@fergofrog.com>
Date: Tue, 16 Aug 2016 13:40:41 +1000
Subject: [PATCH 002/183] Add IntelliJ (PyCharm) folder to gitignore

---
 .gitignore | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.gitignore b/.gitignore
index e211af6a..3cdec3ca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,3 +42,5 @@ nosetests.xml
 .project
 .pydevproject
 
+# IntelliJ
+.idea

From 88b621fe4ff6cd1f977df0fa79b74c560498cfd2 Mon Sep 17 00:00:00 2001
From: Fergus Symon <fergofrog@fergofrog.com>
Date: Tue, 16 Aug 2016 15:12:45 +1000
Subject: [PATCH 003/183] Add multiprocessing's SimpleQueue to those fixed by
 future

---
 src/future/moves/multiprocessing.py        | 7 +++++++
 src/future/standard_library/__init__.py    | 3 +++
 src/libpasteurize/fixes/fix_imports.py     | 1 +
 tests/test_future/test_standard_library.py | 9 +++++++++
 4 files changed, 20 insertions(+)
 create mode 100644 src/future/moves/multiprocessing.py

diff --git a/src/future/moves/multiprocessing.py b/src/future/moves/multiprocessing.py
new file mode 100644
index 00000000..a871b676
--- /dev/null
+++ b/src/future/moves/multiprocessing.py
@@ -0,0 +1,7 @@
+from __future__ import absolute_import
+from future.utils import PY3
+
+from multiprocessing import *
+if not PY3:
+    __future_module__ = True
+    from multiprocessing.queues import SimpleQueue
diff --git a/src/future/standard_library/__init__.py b/src/future/standard_library/__init__.py
index 9e22c59d..9aa654bd 100644
--- a/src/future/standard_library/__init__.py
+++ b/src/future/standard_library/__init__.py
@@ -34,6 +34,7 @@
     from collections import OrderedDict, Counter     # even on Py2.6
     from subprocess import getoutput, getstatusoutput
     from subprocess import check_output              # even on Py2.6
+    from multiprocessing import SimpleQueue
 
 (The renamed modules and functions are still available under their old
 names on Python 2.)
@@ -109,6 +110,7 @@
            'future.moves.socketserver': 'socketserver',
            'ConfigParser': 'configparser',
            'repr': 'reprlib',
+           'multiprocessing.queues': 'multiprocessing',
            # 'FileDialog': 'tkinter.filedialog',
            # 'tkFileDialog': 'tkinter.filedialog',
            # 'SimpleDialog': 'tkinter.simpledialog',
@@ -184,6 +186,7 @@
          ('itertools', 'filterfalse','itertools', 'ifilterfalse'),
          ('itertools', 'zip_longest','itertools', 'izip_longest'),
          ('sys', 'intern','__builtin__', 'intern'),
+         ('multiprocessing', 'SimpleQueue', 'multiprocessing.queues', 'SimpleQueue'),
          # The re module has no ASCII flag in Py2, but this is the default.
          # Set re.ASCII to a zero constant. stat.ST_MODE just happens to be one
          # (and it exists on Py2.6+).
diff --git a/src/libpasteurize/fixes/fix_imports.py b/src/libpasteurize/fixes/fix_imports.py
index 4db0d548..d5b21009 100644
--- a/src/libpasteurize/fixes/fix_imports.py
+++ b/src/libpasteurize/fixes/fix_imports.py
@@ -16,6 +16,7 @@
            u"winreg": u"_winreg",
            u"configparser": u"ConfigParser",
            u"copyreg": u"copy_reg",
+           u"multiprocessing.SimpleQueue": u"multiprocessing.queues.SimpleQueue",
            u"queue": u"Queue",
            u"socketserver": u"SocketServer",
            u"_markupbase": u"markupbase",
diff --git a/tests/test_future/test_standard_library.py b/tests/test_future/test_standard_library.py
index 399d587f..d7a10294 100644
--- a/tests/test_future/test_standard_library.py
+++ b/tests/test_future/test_standard_library.py
@@ -295,6 +295,15 @@ def test_bytesio(self):
         for method in ['tell', 'read', 'seek', 'close', 'flush', 'getvalue']:
             self.assertTrue(hasattr(s, method))
 
+    def test_SimpleQueue(self):
+        from multiprocessing import SimpleQueue
+        sq = SimpleQueue()
+        self.assertTrue(sq.empty())
+        sq.put('thing')
+        self.assertFalse(sq.empty())
+        self.assertEqual(sq.get(), 'thing')
+        self.assertTrue(sq.empty())
+
     def test_queue(self):
         import queue
         q = queue.Queue()

From 99030ec81309164fefc0cd6b5d090eeb08b4a2f7 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital.vaja@googlemail.com>
Date: Mon, 31 Jul 2017 20:18:15 +0100
Subject: [PATCH 004/183] Fixed newdict checking version every time

---
 src/future/types/newdict.py | 63 +++++++++----------------------------
 1 file changed, 14 insertions(+), 49 deletions(-)

diff --git a/src/future/types/newdict.py b/src/future/types/newdict.py
index 5dbcc4b7..7e9cdbc1 100644
--- a/src/future/types/newdict.py
+++ b/src/future/types/newdict.py
@@ -23,7 +23,7 @@
 
 
 _builtin_dict = dict
-ver = sys.version_info[:2]
+ver = sys.version_info
 
 
 class BaseNewDict(type):
@@ -38,47 +38,18 @@ class newdict(with_metaclass(BaseNewDict, _builtin_dict)):
     """
     A backport of the Python 3 dict object to Py2
     """
-    def items(self):
-        """
-        On Python 2.7+:
-            D.items() -> a set-like object providing a view on D's items
-        On Python 2.6:
-            D.items() -> an iterator over D's items
-        """
-        if ver == (2, 7):
-            return self.viewitems()
-        elif ver == (2, 6):
-            return self.iteritems()
-        elif ver >= (3, 0):
-            return self.items()
-
-    def keys(self):
-        """
-        On Python 2.7+:
-            D.keys() -> a set-like object providing a view on D's keys
-        On Python 2.6:
-            D.keys() -> an iterator over D's keys
-        """
-        if ver == (2, 7):
-            return self.viewkeys()
-        elif ver == (2, 6):
-            return self.iterkeys()
-        elif ver >= (3, 0):
-            return self.keys()
-
-    def values(self):
-        """
-        On Python 2.7+:
-            D.values() -> a set-like object providing a view on D's values
-        On Python 2.6:
-            D.values() -> an iterator over D's values
-        """
-        if ver == (2, 7):
-            return self.viewvalues()
-        elif ver == (2, 6):
-            return self.itervalues()
-        elif ver >= (3, 0):
-            return self.values()
+
+    if ver >= (3,):
+        # Inherit items, keys and values from `dict` in 3.x
+        pass
+    elif ver >= (2, 7):
+        items = dict.viewitems
+        keys = dict.viewkeys
+        values = dict.viewvalues
+    else:
+        items = dict.iteritems
+        keys = dict.iterkeys
+        values = dict.itervalues
 
     def __new__(cls, *args, **kwargs):
         """
@@ -93,13 +64,7 @@ def __new__(cls, *args, **kwargs):
             in the keyword argument list.  For example:  dict(one=1, two=2)
         """
 
-        if len(args) == 0:
-            return super(newdict, cls).__new__(cls)
-        elif type(args[0]) == newdict:
-            value = args[0]
-        else:
-            value = args[0]
-        return super(newdict, cls).__new__(cls, value)
+        return super(newdict, cls).__new__(cls, *args)
         
     def __native__(self):
         """

From d87713e5165b6907895ebbd21d98a8a8a739e5c3 Mon Sep 17 00:00:00 2001
From: Austin Marshall <amarshall@23andme.com>
Date: Tue, 10 Oct 2017 10:14:50 -0700
Subject: [PATCH 005/183] Fixup broken link to external django documentation
 re: porting to Python 3 and unicode_literals

---
 docs/unicode_literals.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/unicode_literals.rst b/docs/unicode_literals.rst
index 7af644c8..d17c8a4f 100644
--- a/docs/unicode_literals.rst
+++ b/docs/unicode_literals.rst
@@ -145,7 +145,7 @@ Others' perspectives
 In favour of ``unicode_literals``
 *********************************
 
-Django recommends importing ``unicode_literals`` as its top `porting tip <https://docs.djangoproject.com/en/dev/topics/python3/#unicode-literals>`_ for
+Django recommends importing ``unicode_literals`` as its top `porting tip <https://docs.djangoproject.com/en/1.11/topics/python3/#unicode-literals>`_ for
 migrating Django extension modules to Python 3.  The following `quote
 <https://groups.google.com/forum/#!topic/django-developers/2ddIWdicbNY>`_ is
 from Aymeric Augustin on 23 August 2012 regarding why he chose

From 04dd9ba0678dd475b4de9f0c35474164d0b4acad Mon Sep 17 00:00:00 2001
From: santi <sdvillal@gmail.com>
Date: Fri, 8 Dec 2017 10:27:43 +0100
Subject: [PATCH 006/183] Export and document types in future.utils

---
 src/future/utils/__init__.py | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index cb4ade35..53d07276 100644
--- a/src/future/utils/__init__.py
+++ b/src/future/utils/__init__.py
@@ -18,8 +18,10 @@
     * types:
 
         * text_type: unicode in Python 2, str in Python 3
-        * binary_type: str in Python 2, bythes in Python 3
         * string_types: basestring in Python 2, str in Python 3
+        * binary_type: str in Python 2, bytes in Python 3
+        * integer_types: (int, long) in Python 2, int in Python 3
+        * class_types: (type, types.ClassType) in Python 2, type in Python 3
 
     * bchr(c):
         Take an integer and make a 1-character byte string
@@ -724,16 +726,16 @@ def ensure_new_type(obj):
 
 
 __all__ = ['PY2', 'PY26', 'PY3', 'PYPY',
-           'as_native_str', 'bind_method', 'bord', 'bstr',
-           'bytes_to_native_str', 'encode_filename', 'ensure_new_type',
-           'exec_', 'get_next', 'getexception', 'implements_iterator',
-           'is_new_style', 'isbytes', 'isidentifier', 'isint',
-           'isnewbytes', 'istext', 'iteritems', 'iterkeys', 'itervalues',
-           'lfilter', 'listitems', 'listvalues', 'lmap', 'lrange',
-           'lzip', 'native', 'native_bytes', 'native_str',
+           'as_native_str', 'binary_type', 'bind_method', 'bord', 'bstr',
+           'bytes_to_native_str', 'class_types', 'encode_filename',
+           'ensure_new_type', 'exec_', 'get_next', 'getexception',
+           'implements_iterator', 'integer_types', 'is_new_style', 'isbytes',
+           'isidentifier', 'isint', 'isnewbytes', 'istext', 'iteritems',
+           'iterkeys', 'itervalues', 'lfilter', 'listitems', 'listvalues',
+           'lmap', 'lrange', 'lzip', 'native', 'native_bytes', 'native_str',
            'native_str_to_bytes', 'old_div',
            'python_2_unicode_compatible', 'raise_',
-           'raise_with_traceback', 'reraise', 'text_to_native_str',
-           'tobytes', 'viewitems', 'viewkeys', 'viewvalues',
-           'with_metaclass'
-          ]
+           'raise_with_traceback', 'reraise', 'string_types',
+           'text_to_native_str', 'text_type', 'tobytes', 'viewitems',
+           'viewkeys', 'viewvalues', 'with_metaclass'
+           ]

From 1ba02ca419b6d9061bdca0650902df7915278cbd Mon Sep 17 00:00:00 2001
From: Markus Gerstel <markus.gerstel@diamond.ac.uk>
Date: Fri, 27 Apr 2018 09:43:28 +0100
Subject: [PATCH 007/183] Fix direct attribute assignment to newobject

and add test. Also resolves #336.
---
 src/future/types/newobject.py    |  1 +
 tests/test_future/test_object.py | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/src/future/types/newobject.py b/src/future/types/newobject.py
index 1ec09ff5..6331a8e9 100644
--- a/src/future/types/newobject.py
+++ b/src/future/types/newobject.py
@@ -130,5 +130,6 @@ def __native__(self):
         """
         return object(self)
 
+    __slots__ = []
 
 __all__ = ['newobject']
diff --git a/tests/test_future/test_object.py b/tests/test_future/test_object.py
index 07715029..fe14f8d5 100644
--- a/tests/test_future/test_object.py
+++ b/tests/test_future/test_object.py
@@ -271,6 +271,16 @@ def __len__(self):
 
         self.assertFalse(bool(FalseThing()))
 
+    def test_cannot_assign_new_attributes_to_object(self):
+        """
+        New attributes cannot be assigned to object() instances in Python.
+        The same should apply to newobject.
+        """
+        from builtins import object
+
+        with self.assertRaises(AttributeError):
+          object().arbitrary_attribute_name = True
+
 
 if __name__ == '__main__':
     unittest.main()

From cce97e2fa6fb4096b7599f455f0e00dc2c4dff74 Mon Sep 17 00:00:00 2001
From: Nate Bogdanowicz <natezb@gmail.com>
Date: Thu, 30 Jun 2016 18:14:49 -0700
Subject: [PATCH 008/183] Add more robust support for --nofix

---
 src/libfuturize/main.py | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/src/libfuturize/main.py b/src/libfuturize/main.py
index 18f33ec0..02808786 100644
--- a/src/libfuturize/main.py
+++ b/src/libfuturize/main.py
@@ -205,7 +205,27 @@ def main(args=None):
         print("Use --help to show usage.", file=sys.stderr)
         return 2
 
-    unwanted_fixes = set(fixer_pkg + ".fix_" + fix for fix in options.nofix)
+    unwanted_fixes = set()
+    for fix in options.nofix:
+        if ".fix_" in fix:
+            unwanted_fixes.add(fix)
+        else:
+            # Infer the full module name for the fixer.
+            # First ensure that no names clash (e.g.
+            # lib2to3.fixes.fix_blah and libfuturize.fixes.fix_blah):
+            found = [f for f in avail_fixes
+                     if f.endswith('fix_{0}'.format(fix))]
+            if len(found) > 1:
+                print("Ambiguous fixer name. Choose a fully qualified "
+                      "module name instead from these:\n" +
+                      "\n".join("  " + myf for myf in found),
+                      file=sys.stderr)
+                return 2
+            elif len(found) == 0:
+                print("Unknown fixer. Use --list-fixes or -l for a list.",
+                      file=sys.stderr)
+                return 2
+            unwanted_fixes.add(found[0])
 
     extra_fixes = set()
     if options.all_imports:

From 66ca231e897bb06656c314aa0c4c9ffb4a1f41d3 Mon Sep 17 00:00:00 2001
From: Lucas Cimon <lucas.cimon@gmail.com>
Date: Tue, 12 Jun 2018 10:50:14 +0200
Subject: [PATCH 009/183] Setting the __traceback__ attribute in
 future.utils.raise_from - close #340

---
 TESTING.txt                     |  8 +++++
 src/future/utils/__init__.py    |  2 ++
 tests/test_future/test_utils.py | 57 ++++++++++++++++++++++++++++++++-
 3 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/TESTING.txt b/TESTING.txt
index 13aeca83..6e31d22f 100644
--- a/TESTING.txt
+++ b/TESTING.txt
@@ -7,3 +7,11 @@ The test suite can be run either with:
 which uses the unittest module's test discovery mechanism, or with:
 
     $ py.test
+
+To execute a single test:
+
+    $ python setup.py test -s tests.test_future.test_utils.TestCause.test_chained_exceptions_stacktrace
+
+or with:
+
+    $ pytest -k test_chained_exceptions_stacktrace
diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index cb4ade35..3f55df5c 100644
--- a/src/future/utils/__init__.py
+++ b/src/future/utils/__init__.py
@@ -441,12 +441,14 @@ def raise_from(exc, cause):
         e.__suppress_context__ = False
         if isinstance(cause, type) and issubclass(cause, Exception):
             e.__cause__ = cause()
+            e.__cause__.__traceback__ = sys.exc_info()[2]
             e.__suppress_context__ = True
         elif cause is None:
             e.__cause__ = None
             e.__suppress_context__ = True
         elif isinstance(cause, BaseException):
             e.__cause__ = cause
+            e.__cause__.__traceback__ = sys.exc_info()[2]
             e.__suppress_context__ = True
         else:
             raise TypeError("exception causes must derive from BaseException")
diff --git a/tests/test_future/test_utils.py b/tests/test_future/test_utils.py
index 19dc11d3..b80e8c17 100644
--- a/tests/test_future/test_utils.py
+++ b/tests/test_future/test_utils.py
@@ -4,7 +4,7 @@
 """
 
 from __future__ import absolute_import, unicode_literals, print_function
-import sys
+import sys, traceback
 from future.builtins import *
 from future.utils import (old_div, istext, isbytes, native, PY2, PY3,
                          native_str, raise_, as_native_str, ensure_new_type,
@@ -314,6 +314,61 @@ def __init__(self):
         else:
             self.fail("No exception raised")
 
+    def test_single_exception_stacktrace(self):
+        expected = '''Traceback (most recent call last):
+  File "/opt/python-future/tests/test_future/test_utils.py", line 328, in test_single_exception_stacktrace
+    raise CustomException('ERROR')
+'''
+        if PY2:
+            expected += 'CustomException: ERROR\n'
+        else:
+            expected += 'tests.test_future.test_utils.CustomException: ERROR\n'
+
+        try:
+            raise CustomException('ERROR')
+        except:
+            self.assertEqual(expected, traceback.format_exc())
+        else:
+            self.fail('No exception raised')
+
+    if PY2:
+        def test_chained_exceptions_stacktrace(self):
+            expected = '''Traceback (most recent call last):
+  File "/opt/python-future/tests/test_future/test_utils.py", line 354, in test_chained_exceptions_stacktrace
+    raise_from(CustomException('ERROR'), val_err)
+  File "/opt/python-future/src/future/utils/__init__.py", line 456, in raise_from
+    raise e
+CustomException: ERROR
+
+The above exception was the direct cause of the following exception:
+
+  File "/opt/python-future/tests/test_future/test_utils.py", line 352, in test_chained_exceptions_stacktrace
+    raise ValueError('Wooops')
+ValueError: Wooops
+'''
+
+            try:
+                try:
+                    raise ValueError('Wooops')
+                except ValueError as val_err:
+                    raise_from(CustomException('ERROR'), val_err)
+            except Exception as err:
+                self.assertEqual(expected.splitlines(), traceback.format_exc().splitlines())
+            else:
+                self.fail('No exception raised')
+
+
+class CustomException(Exception):
+    if PY2:
+        def __str__(self):
+            out = Exception.__str__(self)
+            if hasattr(self, '__cause__') and self.__cause__ and hasattr(self.__cause__, '__traceback__') and self.__cause__.__traceback__:
+                out += '\n\nThe above exception was the direct cause of the following exception:\n\n'
+                out += ''.join(traceback.format_tb(self.__cause__.__traceback__) + ['{}: {}'.format(self.__cause__.__class__.__name__, self.__cause__)])
+            return out
+    else:
+        pass
+
 
 if __name__ == '__main__':
     unittest.main()

From fb786929748b1fe80f57bead0b783a27c2a26cda Mon Sep 17 00:00:00 2001
From: tst <tst@contact-software.com>
Date: Mon, 16 Jul 2018 13:20:30 +0200
Subject: [PATCH 010/183] fixed undefined variable children_hooks + imports
 cleanup

---
 src/libfuturize/fixer_util.py | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/src/libfuturize/fixer_util.py b/src/libfuturize/fixer_util.py
index ce1e9753..afc22510 100644
--- a/src/libfuturize/fixer_util.py
+++ b/src/libfuturize/fixer_util.py
@@ -9,11 +9,11 @@
 """
 
 from lib2to3.fixer_util import (FromImport, Newline, is_import,
-                                find_root, does_tree_import, Comma)
+                                find_root, does_tree_import,
+                                Call, Name, Comma)
 from lib2to3.pytree import Leaf, Node
-from lib2to3.pygram import python_symbols as syms, python_grammar
+from lib2to3.pygram import python_symbols as syms
 from lib2to3.pygram import token
-from lib2to3.fixer_util import (Node, Call, Name, syms, Comma, Number)
 import re
 
 
@@ -62,7 +62,7 @@ def Minus(prefix=None):
 
 def commatize(leafs):
     """
-    Accepts/turns: (Name, Name, ..., Name, Name) 
+    Accepts/turns: (Name, Name, ..., Name, Name)
     Returns/into: (Name, Comma, Name, Comma, ..., Name, Comma, Name)
     """
     new_leafs = []
@@ -272,7 +272,7 @@ def future_import2(feature, node):
     An alternative to future_import() which might not work ...
     """
     root = find_root(node)
-    
+
     if does_tree_import(u"__future__", feature, node):
         return
 
@@ -304,7 +304,7 @@ def parse_args(arglist, scheme):
     Parse a list of arguments into a dict
     """
     arglist = [i for i in arglist if i.type != token.COMMA]
-    
+
     ret_mapping = dict([(k, None) for k in scheme])
 
     for i, arg in enumerate(arglist):
@@ -338,7 +338,7 @@ def touch_import_top(package, name_to_import, node):
     Based on lib2to3.fixer_util.touch_import()
 
     Calling this multiple times adds the imports in reverse order.
-        
+
     Also adds "standard_library.install_aliases()" after "from future import
     standard_library".  This should probably be factored into another function.
     """
@@ -390,6 +390,7 @@ def touch_import_top(package, name_to_import, node):
                 break
         insert_pos = idx
 
+    children_hooks = []
     if package is None:
         import_ = Node(syms.import_name, [
             Leaf(token.NAME, u"import"),
@@ -413,9 +414,7 @@ def touch_import_top(package, name_to_import, node):
                                  ]
                                 )
             children_hooks = [install_hooks, Newline()]
-        else:
-            children_hooks = []
-        
+
         # FromImport(package, [Leaf(token.NAME, name_to_import, prefix=u" ")])
 
     children_import = [import_, Newline()]
@@ -445,7 +444,6 @@ def check_future_import(node):
         return set()
     node = node.children[3]
     # now node is the import_as_name[s]
-    # print(python_grammar.number2symbol[node.type])  # breaks sometimes
     if node.type == syms.import_as_names:
         result = set()
         for n in node.children:

From 0881f37e6ac3eac73aac4e231a740566dcab99c0 Mon Sep 17 00:00:00 2001
From: jenssss <37403847+jenssss@users.noreply.github.com>
Date: Tue, 31 Jul 2018 14:23:15 +0900
Subject: [PATCH 011/183] Fixed import statements

Changed `import itertools.imap as map` to `from itertools import imap as map` in two places
---
 docs/notebooks/Writing Python 2-3 compatible code.ipynb | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/notebooks/Writing Python 2-3 compatible code.ipynb b/docs/notebooks/Writing Python 2-3 compatible code.ipynb
index f33a9204..c1af15e7 100644
--- a/docs/notebooks/Writing Python 2-3 compatible code.ipynb	
+++ b/docs/notebooks/Writing Python 2-3 compatible code.ipynb	
@@ -1747,7 +1747,7 @@
    "source": [
     "# Python 2 and 3: option 3\n",
     "try:\n",
-    "    import itertools.imap as map\n",
+    "    from itertools import imap as map\n",
     "except ImportError:\n",
     "    pass\n",
     "\n",
@@ -1845,7 +1845,7 @@
    "source": [
     "# Python 2 and 3: option 2\n",
     "try:\n",
-    "    import itertools.imap as map\n",
+    "    from itertools import imap as map\n",
     "except ImportError:\n",
     "    pass\n",
     "\n",

From c35e6e55eda1a504a1d13f1f7344aa56cf6a618a Mon Sep 17 00:00:00 2001
From: Ryan Barrett <git@ryanb.org>
Date: Tue, 16 Jan 2018 09:21:26 -0800
Subject: [PATCH 012/183] app engine compatibility: handle missing
 httplib._CS_* constants

fixes #320
---
 src/http/client.py | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/http/client.py b/src/http/client.py
index 7566fe4d..a6a31006 100644
--- a/src/http/client.py
+++ b/src/http/client.py
@@ -11,10 +11,6 @@
 from httplib import (HTTP_PORT,
                      HTTPS_PORT,
 
-                     _CS_IDLE,
-                     _CS_REQ_STARTED,
-                     _CS_REQ_SENT,
-
                      CONTINUE,
                      SWITCHING_PROTOCOLS,
                      PROCESSING,
@@ -81,6 +77,9 @@
 # These may not be available on all versions of Python 2.6.x or 2.7.x
 try:
     from httplib import (
+                         _CS_IDLE,
+                         _CS_REQ_STARTED,
+                         _CS_REQ_SENT,
                          _MAXLINE,
                          _MAXHEADERS,
                          _is_legal_header_name,

From 62b4c35d995aa5503855ffcc5c8fb2262e02c2a2 Mon Sep 17 00:00:00 2001
From: Ryan Barrett <git@ryanb.org>
Date: Wed, 21 Mar 2018 07:19:09 -0700
Subject: [PATCH 013/183] allow mixed string type inputs to misc urllib.parse.*
 functions

...eg urlparse, urlunparse, urlsplit, urlunsplit, urljoin, urldefrag, and parse_qsl.

fixes #273.

the original bug result in this kind of exception:

```
Traceback (most recent call last):
  ...
  File ".../future/backports/urllib/parse.py", line 387, in urlunparse
    _coerce_args(*components))
  File ".../future/backports/urllib/parse.py", line 115, in _coerce_args
    raise TypeError("Cannot mix str and non-str arguments")
TypeError: Cannot mix str and non-str arguments
```
---
 src/future/backports/urllib/parse.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/future/backports/urllib/parse.py b/src/future/backports/urllib/parse.py
index 04e52d49..2c26846d 100644
--- a/src/future/backports/urllib/parse.py
+++ b/src/future/backports/urllib/parse.py
@@ -107,11 +107,11 @@ def _coerce_args(*args):
     # an appropriate result coercion function
     #   - noop for str inputs
     #   - encoding function otherwise
-    str_input = isinstance(args[0], str)
+    str_input = isinstance(args[0], basestring)
     for arg in args[1:]:
         # We special-case the empty string to support the
         # "scheme=''" default argument to some functions
-        if arg and isinstance(arg, str) != str_input:
+        if arg and isinstance(arg, basestring) != str_input:
             raise TypeError("Cannot mix str and non-str arguments")
     if str_input:
         return args + (_noop,)

From 69d486107ffd6691568ae698a1e1fe9e989e0094 Mon Sep 17 00:00:00 2001
From: Grant Bakker <grant@bakker.pw>
Date: Tue, 16 Oct 2018 10:25:49 -0400
Subject: [PATCH 014/183] Remove unused tmp files from past translation

---
 src/past/translation/__init__.py | 16 ----------------
 1 file changed, 16 deletions(-)

diff --git a/src/past/translation/__init__.py b/src/past/translation/__init__.py
index c7ae2b7a..bf8998e6 100644
--- a/src/past/translation/__init__.py
+++ b/src/past/translation/__init__.py
@@ -219,22 +219,9 @@ def detect_python2(source, pathname):
     if source != str(tree)[:-1]:   # remove added newline
         # The above fixers made changes, so we conclude it's Python 2 code
         logger.debug('Detected Python 2 code: {0}'.format(pathname))
-        with open('/tmp/original_code.py', 'w') as f:
-            f.write('### Original code (detected as py2): %s\n%s' %
-                    (pathname, source))
-        with open('/tmp/py2_detection_code.py', 'w') as f:
-            f.write('### Code after running py3 detection (from %s)\n%s' %
-                    (pathname, str(tree)[:-1]))
         return True
     else:
         logger.debug('Detected Python 3 code: {0}'.format(pathname))
-        with open('/tmp/original_code.py', 'w') as f:
-            f.write('### Original code (detected as py3): %s\n%s' %
-                    (pathname, source))
-        try:
-            os.remove('/tmp/futurize_code.py')
-        except OSError:
-            pass
         return False
 
 
@@ -395,9 +382,6 @@ def load_module(self, fullname):
 
                         if detect_python2(source, self.pathname):
                             source = self.transform(source)
-                            with open('/tmp/futurized_code.py', 'w') as f:
-                                f.write('### Futurized code (from %s)\n%s' %
-                                        (self.pathname, source))
 
                         code = compile(source, self.pathname, 'exec')
 

From 7ff6a5ce803ef34a8b56d0c08ee3b5e687eac647 Mon Sep 17 00:00:00 2001
From: cclauss <cclauss@bluewin.ch>
Date: Wed, 31 Oct 2018 12:34:16 +0100
Subject: [PATCH 015/183] credits.rst: Remove my misspelled GitHub ID and add
 my name

---
 docs/credits.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/credits.rst b/docs/credits.rst
index 3cf1c9c8..23e00f3b 100644
--- a/docs/credits.rst
+++ b/docs/credits.rst
@@ -50,6 +50,7 @@ Python-Future is largely written by Ed Schofield <ed@pythoncharmers.com> with th
 - Grant Bakker
 - Jacob Beck
 - Nate Bogdanowicz
+- Christian Clauss
 - Denis Cornehl
 - Nicolas Delaby
 - Jon Dufresne
@@ -79,7 +80,6 @@ Python-Future is largely written by Ed Schofield <ed@pythoncharmers.com> with th
 - Jeff Tratner
 - Tim Tröndle
 - Brad Walker
-- cclaus (GiHub user)
 - lsm (GiHub user)
 - Mystic-Mirage (GitHub user)
 - str4d (GitHub user)

From 1233756c8fa2f5a76c410badb54a5162a6b825a6 Mon Sep 17 00:00:00 2001
From: Timothy Hopper <tdhopper@users.noreply.github.com>
Date: Thu, 1 Nov 2018 08:57:19 -0400
Subject: [PATCH 016/183] typo

---
 docs/futurize_overview.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/futurize_overview.rst b/docs/futurize_overview.rst
index 2beef1ac..769b65c7 100644
--- a/docs/futurize_overview.rst
+++ b/docs/futurize_overview.rst
@@ -51,5 +51,5 @@ use the ``-w`` flag.
 
 For complex projects, it is probably best to divide the porting into two stages.
 Stage 1 is for "safe" changes that modernize the code but do not break Python
-2.7 compatibility or introduce a depdendency on the ``future`` package. Stage 2
+2.7 compatibility or introduce a dependency on the ``future`` package. Stage 2
 is to complete the process.

From e9415516d51e922a6444426e3ef1198f228ba4d8 Mon Sep 17 00:00:00 2001
From: Jon Dufresne <jon.dufresne@gmail.com>
Date: Fri, 2 Nov 2018 18:46:02 -0700
Subject: [PATCH 017/183] Remove empty and unused past.tests module

---
 setup.py                          | 1 -
 src/past/builtins/noniterators.py | 2 +-
 src/past/tests/__init__.py        | 0
 3 files changed, 1 insertion(+), 2 deletions(-)
 delete mode 100644 src/past/tests/__init__.py

diff --git a/setup.py b/setup.py
index 2f7b36a3..11d694c2 100755
--- a/setup.py
+++ b/setup.py
@@ -46,7 +46,6 @@
             "past.builtins",
             "past.types",
             "past.utils",
-            # "past.tests",
             "past.translation",
             "libfuturize",
             "libfuturize.fixes",
diff --git a/src/past/builtins/noniterators.py b/src/past/builtins/noniterators.py
index 5826b97c..183ffffd 100644
--- a/src/past/builtins/noniterators.py
+++ b/src/past/builtins/noniterators.py
@@ -72,7 +72,7 @@ def oldmap(func, *iterables):
         >>> oldmap(None, range(4))
         [0, 1, 2, 3]
 
-        More test cases are in past.tests.test_builtins.
+        More test cases are in test_past.test_builtins.
         """
         zipped = itertools.zip_longest(*iterables)
         l = list(zipped)
diff --git a/src/past/tests/__init__.py b/src/past/tests/__init__.py
deleted file mode 100644
index e69de29b..00000000

From cfc494e86d7f942fcaf71452be8f2d491591a487 Mon Sep 17 00:00:00 2001
From: Joe Gordon <jogo@pinterest.com>
Date: Wed, 7 Nov 2018 16:34:50 -0800
Subject: [PATCH 018/183] Update doc/futurize to reflect what gets run

Compare rc/libfuturize/fixes/__init__.py and doc/futurize.rst to make
sure they align.
---
 docs/futurize.rst | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/docs/futurize.rst b/docs/futurize.rst
index fdbf3026..e7e593c0 100644
--- a/docs/futurize.rst
+++ b/docs/futurize.rst
@@ -81,6 +81,7 @@ The complete set of fixers applied by ``futurize --stage1`` is:
 
     lib2to3.fixes.fix_apply
     lib2to3.fixes.fix_except
+    lib2to3.fixes.fix_exec
     lib2to3.fixes.fix_exitfunc
     lib2to3.fixes.fix_funcattrs
     lib2to3.fixes.fix_has_key
@@ -224,9 +225,7 @@ becomes::
 
 The complete list of fixers applied in Stage 2 is::
 
-    lib2to3.fixes.fix_basestring
     lib2to3.fixes.fix_dict
-    lib2to3.fixes.fix_exec
     lib2to3.fixes.fix_getcwdu
     lib2to3.fixes.fix_input
     lib2to3.fixes.fix_itertools
@@ -239,8 +238,9 @@ The complete list of fixers applied in Stage 2 is::
     lib2to3.fixes.fix_raw_input
     lib2to3.fixes.fix_zip
 
+    libfuturize.fixes.fix_basestring
     libfuturize.fixes.fix_cmp
-    libfuturize.fixes.fix_division
+    libfuturize.fixes.fix_division_safe
     libfuturize.fixes.fix_execfile
     libfuturize.fixes.fix_future_builtins
     libfuturize.fixes.fix_future_standard_library
@@ -269,11 +269,6 @@ Not applied::
     lib2to3.fixes.fix_xrange    # Custom one because of a bug with Py3.3's lib2to3
 
 
-Fixes applied with the ``futurize --conservative`` option::
-
-    libfuturize.fixes.fix_division_safe    # instead of libfuturize.fixes.fix_division.
-
-
 
 .. Ideally the output of this stage should not be a ``SyntaxError`` on either
 .. Python 3 or Python 2.

From 02fdb329d782a1bbf6c08700d63e95a5de01fd86 Mon Sep 17 00:00:00 2001
From: Kyle Altendorf <kyle.altendorf@epcpower.com>
Date: Mon, 19 Nov 2018 17:44:27 -0500
Subject: [PATCH 019/183] Fix several warnings

---
 src/future/backports/misc.py              |  8 ++++++--
 src/future/backports/urllib/request.py    | 10 ++++++++--
 src/past/builtins/misc.py                 | 10 +++++++---
 src/past/types/oldstr.py                  |  6 +++++-
 tests/test_future/test_backports.py       |  8 ++++++--
 tests/test_future/test_bytes.py           |  7 +++++--
 tests/test_future/test_dict.py            |  7 +++++--
 tests/test_future/test_int.py             |  6 +++---
 tests/test_future/test_list.py            |  7 +++++--
 tests/test_future/test_object.py          |  7 +++++--
 tests/test_future/test_range.py           | 10 ++++++++--
 tests/test_future/test_str.py             |  7 +++++--
 tests/test_future/test_urllib_response.py |  4 ++--
 13 files changed, 70 insertions(+), 27 deletions(-)

diff --git a/src/future/backports/misc.py b/src/future/backports/misc.py
index ef752078..098a0667 100644
--- a/src/future/backports/misc.py
+++ b/src/future/backports/misc.py
@@ -16,7 +16,6 @@
 
 import subprocess
 from math import ceil as oldceil
-from collections import Mapping, MutableMapping
 
 from operator import itemgetter as _itemgetter, eq as _eq
 import sys
@@ -25,7 +24,12 @@
 from itertools import repeat as _repeat, chain as _chain, starmap as _starmap
 from socket import getaddrinfo, SOCK_STREAM, error, socket
 
-from future.utils import iteritems, itervalues, PY26, PY3
+from future.utils import iteritems, itervalues, PY2, PY26, PY3
+
+if PY2:
+    from collections import Mapping, MutableMapping
+else:
+    from collections.abc import Mapping, MutableMapping
 
 
 def ceil(x):
diff --git a/src/future/backports/urllib/request.py b/src/future/backports/urllib/request.py
index b1545ca0..baee5401 100644
--- a/src/future/backports/urllib/request.py
+++ b/src/future/backports/urllib/request.py
@@ -109,11 +109,17 @@
 import socket
 import sys
 import time
-import collections
 import tempfile
 import contextlib
 import warnings
 
+from future.utils import PY2
+
+if PY2:
+    from collections import Iterable
+else:
+    from collections.abc import Iterable
+
 # check for SSL
 try:
     import ssl
@@ -1221,7 +1227,7 @@ def do_request_(self, request):
                         mv = memoryview(data)
                         size = len(mv) * mv.itemsize
                 except TypeError:
-                    if isinstance(data, collections.Iterable):
+                    if isinstance(data, Iterable):
                         raise ValueError("Content-Length should be specified "
                                 "for iterable data of type %r %r" % (type(data),
                                 data))
diff --git a/src/past/builtins/misc.py b/src/past/builtins/misc.py
index 06fbb92d..ed5800a7 100644
--- a/src/past/builtins/misc.py
+++ b/src/past/builtins/misc.py
@@ -1,9 +1,13 @@
 from __future__ import unicode_literals
 import sys
 import inspect
-from collections import Mapping
 
-from future.utils import PY3, exec_
+from future.utils import PY2, PY3, exec_
+
+if PY2:
+    from collections import Mapping
+else:
+    from collections.abc import Mapping
 
 
 if PY3:
@@ -76,7 +80,7 @@ def execfile(filename, myglobals=None, mylocals=None):
             raise TypeError('globals must be a mapping')
         if not isinstance(mylocals, Mapping):
             raise TypeError('locals must be a mapping')
-        with open(filename, "rbU") as fin:
+        with open(filename, "rb") as fin:
              source = fin.read()
         code = compile(source, filename, "exec")
         exec_(code, myglobals, mylocals)
diff --git a/src/past/types/oldstr.py b/src/past/types/oldstr.py
index 7768d328..0237d239 100644
--- a/src/past/types/oldstr.py
+++ b/src/past/types/oldstr.py
@@ -2,11 +2,15 @@
 Pure-Python implementation of a Python 2-like str object for Python 3.
 """
 
-from collections import Iterable
 from numbers import Integral
 
 from past.utils import PY2, with_metaclass
 
+if PY2:
+    from collections import Iterable
+else:
+    from collections.abc import Iterable
+
 
 _builtin_bytes = bytes
 
diff --git a/tests/test_future/test_backports.py b/tests/test_future/test_backports.py
index 21ebb202..9eeb741b 100644
--- a/tests/test_future/test_backports.py
+++ b/tests/test_future/test_backports.py
@@ -10,7 +10,6 @@
 import inspect
 import pickle
 from random import randrange, shuffle
-from collections import Mapping, MutableMapping
 
 from future.backports.misc import (count,
                                    _count,
@@ -18,9 +17,14 @@
                                    Counter,
                                    ChainMap,
                                    _count_elements)
-from future.utils import PY26
+from future.utils import PY2, PY26
 from future.tests.base import unittest, skip26, expectedFailurePY27
 
+if PY2:
+    from collections import Mapping, MutableMapping
+else:
+    from collections.abc import Mapping, MutableMapping
+
 
 class CountTest(unittest.TestCase):
     """Test the count function."""
diff --git a/tests/test_future/test_bytes.py b/tests/test_future/test_bytes.py
index bb90a71c..b9b157d8 100644
--- a/tests/test_future/test_bytes.py
+++ b/tests/test_future/test_bytes.py
@@ -701,12 +701,15 @@ def test_multiple_inheritance(self):
         """
         Issue #96 (for newbytes instead of newobject)
         """
-        import collections
+        if utils.PY2:
+            from collections import Container
+        else:
+            from collections.abc import Container
 
         class Base(bytes):
             pass
 
-        class Foo(Base, collections.Container):
+        class Foo(Base, Container):
             def __contains__(self, item):
                 return False
 
diff --git a/tests/test_future/test_dict.py b/tests/test_future/test_dict.py
index 68856828..ff9dd4ab 100644
--- a/tests/test_future/test_dict.py
+++ b/tests/test_future/test_dict.py
@@ -111,12 +111,15 @@ def test_multiple_inheritance(self):
         """
         Issue #96 (for newdict instead of newobject)
         """
-        import collections
+        if utils.PY2:
+            from collections import Container
+        else:
+            from collections.abc import Container
 
         class Base(dict):
             pass
 
-        class Foo(Base, collections.Container):
+        class Foo(Base, Container):
             def __contains__(self, item):
                 return False
 
diff --git a/tests/test_future/test_int.py b/tests/test_future/test_int.py
index 9acbd23a..573a0d53 100644
--- a/tests/test_future/test_int.py
+++ b/tests/test_future/test_int.py
@@ -344,7 +344,7 @@ def __int__(self):
 
         class Foo3(int):
             def __int__(self):
-                return self
+                return self.real
 
         class Foo4(int):
             def __int__(self):
@@ -1069,12 +1069,12 @@ def test_multiple_inheritance(self):
         """
         Issue #96 (for newint instead of newobject)
         """
-        import collections
+        import collections.abc
 
         class Base(int):
             pass
 
-        class Foo(Base, collections.Container):
+        class Foo(Base, collections.abc.Container):
             def __add__(self, other):
                 return 0
 
diff --git a/tests/test_future/test_list.py b/tests/test_future/test_list.py
index 6134c74f..16fb84c5 100644
--- a/tests/test_future/test_list.py
+++ b/tests/test_future/test_list.py
@@ -162,12 +162,15 @@ def test_multiple_inheritance(self):
         """
         Issue #96 (for newdict instead of newobject)
         """
-        import collections
+        if utils.PY2:
+            from collections import Container
+        else:
+            from collections.abc import Container
 
         class Base(list):
             pass
 
-        class Foo(Base, collections.Container):
+        class Foo(Base, Container):
             def __contains__(self, item):
                 return False
 
diff --git a/tests/test_future/test_object.py b/tests/test_future/test_object.py
index 8352f3a3..eb876471 100644
--- a/tests/test_future/test_object.py
+++ b/tests/test_future/test_object.py
@@ -209,12 +209,15 @@ def test_multiple_inheritance(self):
         """
         Issue #96
         """
-        import collections
+        if utils.PY2:
+            from collections import Container
+        else:
+            from collections.abc import Container
 
         class Base(object):
             pass
 
-        class Foo(Base, collections.Container):
+        class Foo(Base, Container):
             def __contains__(self, item):
                 return False
 
diff --git a/tests/test_future/test_range.py b/tests/test_future/test_range.py
index 2e9471ae..dba15228 100644
--- a/tests/test_future/test_range.py
+++ b/tests/test_future/test_range.py
@@ -6,9 +6,15 @@
 from future.builtins import range
 from future.tests.base import unittest
 
-from collections import Iterator, Sequence
 from operator import attrgetter
 
+from future.utils import PY2
+
+if PY2:
+    from collections import Iterator, Sequence
+else:
+    from collections.abc import Iterator, Sequence
+
 
 class RangeTests(unittest.TestCase):
     def test_range(self):
@@ -192,7 +198,7 @@ def test_rev_stepped_slice_rev_stepped_range(self):
 
     def test_slice_zero_step(self):
         msg = '^slice step cannot be zero$'
-        with self.assertRaisesRegexp(ValueError, msg):
+        with self.assertRaisesRegex(ValueError, msg):
             range(8)[::0]
 
     def test_properties(self):
diff --git a/tests/test_future/test_str.py b/tests/test_future/test_str.py
index dcc15628..98e71c41 100644
--- a/tests/test_future/test_str.py
+++ b/tests/test_future/test_str.py
@@ -525,12 +525,15 @@ def test_multiple_inheritance(self):
         """
         Issue #96 (for newstr instead of newobject)
         """
-        import collections
+        if utils.PY2:
+            from collections import Container
+        else:
+            from collections.abc import Container
 
         class Base(str):
             pass
 
-        class Foo(Base, collections.Container):
+        class Foo(Base, Container):
             def __contains__(self, item):
                 return False
 
diff --git a/tests/test_future/test_urllib_response.py b/tests/test_future/test_urllib_response.py
index 27da4a31..e8f4b4f1 100644
--- a/tests/test_future/test_urllib_response.py
+++ b/tests/test_future/test_urllib_response.py
@@ -8,7 +8,7 @@
 from future.tests.base import unittest
 
 
-class TestFile(object):
+class File(object):
 
     def __init__(self):
         self.closed = False
@@ -28,7 +28,7 @@ class Testaddbase(unittest.TestCase):
     # TODO(jhylton): Write tests for other functionality of addbase()
 
     def setUp(self):
-        self.fp = TestFile()
+        self.fp = File()
         self.addbase = urllib_response.addbase(self.fp)
 
     def test_with(self):

From 89c8030dffa8a22aca7db44fffdb807ea2a67642 Mon Sep 17 00:00:00 2001
From: Tomer Chachamu <tomer.chachamu@gmail.com>
Date: Fri, 30 Nov 2018 10:10:11 +0000
Subject: [PATCH 020/183] Corrected lists of fixers

---
 docs/futurize.rst | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/docs/futurize.rst b/docs/futurize.rst
index fdbf3026..2d2f9c28 100644
--- a/docs/futurize.rst
+++ b/docs/futurize.rst
@@ -81,6 +81,7 @@ The complete set of fixers applied by ``futurize --stage1`` is:
 
     lib2to3.fixes.fix_apply
     lib2to3.fixes.fix_except
+    lib2to3.fixes.fix_exec
     lib2to3.fixes.fix_exitfunc
     lib2to3.fixes.fix_funcattrs
     lib2to3.fixes.fix_has_key
@@ -106,7 +107,6 @@ The complete set of fixers applied by ``futurize --stage1`` is:
     libfuturize.fixes.fix_print_with_import
     libfuturize.fixes.fix_raise
 
-
 The following fixers from ``lib2to3`` are not applied:
 
 .. code-block:: python
@@ -224,23 +224,23 @@ becomes::
 
 The complete list of fixers applied in Stage 2 is::
 
-    lib2to3.fixes.fix_basestring
     lib2to3.fixes.fix_dict
-    lib2to3.fixes.fix_exec
+    lib2to3.fixes.fix_filter
     lib2to3.fixes.fix_getcwdu
     lib2to3.fixes.fix_input
     lib2to3.fixes.fix_itertools
     lib2to3.fixes.fix_itertools_imports
-    lib2to3.fixes.fix_filter
     lib2to3.fixes.fix_long
     lib2to3.fixes.fix_map
+    lib2to3.fixes.fix_next
     lib2to3.fixes.fix_nonzero
     lib2to3.fixes.fix_operator
     lib2to3.fixes.fix_raw_input
     lib2to3.fixes.fix_zip
-
+    
+    libfuturize.fixes.fix_basestring
     libfuturize.fixes.fix_cmp
-    libfuturize.fixes.fix_division
+    libfuturize.fixes.fix_division_safe
     libfuturize.fixes.fix_execfile
     libfuturize.fixes.fix_future_builtins
     libfuturize.fixes.fix_future_standard_library
@@ -269,12 +269,6 @@ Not applied::
     lib2to3.fixes.fix_xrange    # Custom one because of a bug with Py3.3's lib2to3
 
 
-Fixes applied with the ``futurize --conservative`` option::
-
-    libfuturize.fixes.fix_division_safe    # instead of libfuturize.fixes.fix_division.
-
-
-
 .. Ideally the output of this stage should not be a ``SyntaxError`` on either
 .. Python 3 or Python 2.
 

From 13611f355c86bf2ed97b6922429102c6acc0fc6a Mon Sep 17 00:00:00 2001
From: Dan Yeaw <dan@yeaw.me>
Date: Fri, 7 Dec 2018 21:34:29 -0500
Subject: [PATCH 021/183] Fix deprecation warnings for abc module in Python 3.7

Signed-off-by: Dan Yeaw <dan@yeaw.me>
---
 src/past/builtins/misc.py | 6 +++---
 src/past/types/oldstr.py  | 4 +---
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/src/past/builtins/misc.py b/src/past/builtins/misc.py
index 06fbb92d..a5393104 100644
--- a/src/past/builtins/misc.py
+++ b/src/past/builtins/misc.py
@@ -1,13 +1,12 @@
 from __future__ import unicode_literals
-import sys
+
 import inspect
-from collections import Mapping
 
 from future.utils import PY3, exec_
 
-
 if PY3:
     import builtins
+    from collections.abc import Mapping
 
     def apply(f, *args, **kw):
         return f(*args, **kw)
@@ -44,6 +43,7 @@ def oct(number):
     xrange = range
 else:
     import __builtin__
+    from collections import Mapping
     apply = __builtin__.apply
     chr = __builtin__.chr
     cmp = __builtin__.cmp
diff --git a/src/past/types/oldstr.py b/src/past/types/oldstr.py
index 7768d328..163276d6 100644
--- a/src/past/types/oldstr.py
+++ b/src/past/types/oldstr.py
@@ -2,11 +2,9 @@
 Pure-Python implementation of a Python 2-like str object for Python 3.
 """
 
-from collections import Iterable
 from numbers import Integral
 
-from past.utils import PY2, with_metaclass
-
+from past.utils import with_metaclass
 
 _builtin_bytes = bytes
 

From d977505cab6d213bf6d492622d1980c365e3bd6c Mon Sep 17 00:00:00 2001
From: Kjell Wooding <kjell@wooding.org>
Date: Fri, 4 Jan 2019 13:16:26 -0500
Subject: [PATCH 022/183] on python 2, socket.fp doesn't have a readinto()
 method. Work around this like limitation like is done elsewhere in this
 module

---
 src/future/backports/http/client.py | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/future/backports/http/client.py b/src/future/backports/http/client.py
index 5dd983d8..1a43f013 100644
--- a/src/future/backports/http/client.py
+++ b/src/future/backports/http/client.py
@@ -696,9 +696,19 @@ def _safe_readinto(self, b):
         while total_bytes < len(b):
             if MAXAMOUNT < len(mvb):
                 temp_mvb = mvb[0:MAXAMOUNT]
-                n = self.fp.readinto(temp_mvb)
+                if PY2:
+                    data = self.fp.read(len(temp_mvb))
+                    n = len(data)
+                    temp_mvb[:n] = data
+                else:
+                    n = self.fp.readinto(temp_mvb)
             else:
-                n = self.fp.readinto(mvb)
+                if PY2:
+                    data = self.fp.read(len(mvb))
+                    n = len(data)
+                    mvb[:n] = data
+                else:
+                    n = self.fp.readinto(mvb)
             if not n:
                 raise IncompleteRead(bytes(mvb[0:total_bytes]), len(b))
             mvb = mvb[n:]

From 961aa0f6baf7d6cb88c9fa51c6d6312e3b4635a9 Mon Sep 17 00:00:00 2001
From: Sesh Sadasivam <seshrs@umich.edu>
Date: Fri, 18 Jan 2019 22:48:24 -0500
Subject: [PATCH 023/183] Fixed bug in email Message.set_boundary() Also added
 tests that would expose the bug.

---
 src/future/backports/email/message.py     |  6 ++---
 tests/test_future/test_email_multipart.py | 31 +++++++++++++++++++++++
 2 files changed, 34 insertions(+), 3 deletions(-)
 create mode 100644 tests/test_future/test_email_multipart.py

diff --git a/src/future/backports/email/message.py b/src/future/backports/email/message.py
index 99715fcc..d8d9615d 100644
--- a/src/future/backports/email/message.py
+++ b/src/future/backports/email/message.py
@@ -800,7 +800,7 @@ def set_boundary(self, boundary):
             # There was no Content-Type header, and we don't know what type
             # to set it to, so raise an exception.
             raise errors.HeaderParseError('No Content-Type header found')
-        newparams = []
+        newparams = list()
         foundp = False
         for pk, pv in params:
             if pk.lower() == 'boundary':
@@ -814,10 +814,10 @@ def set_boundary(self, boundary):
             # instead???
             newparams.append(('boundary', '"%s"' % boundary))
         # Replace the existing Content-Type header with the new value
-        newheaders = []
+        newheaders = list()
         for h, v in self._headers:
             if h.lower() == 'content-type':
-                parts = []
+                parts = list()
                 for k, v in newparams:
                     if v == '':
                         parts.append(k)
diff --git a/tests/test_future/test_email_multipart.py b/tests/test_future/test_email_multipart.py
new file mode 100644
index 00000000..cbd93b89
--- /dev/null
+++ b/tests/test_future/test_email_multipart.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+"""Tests for multipart emails."""
+
+from future.tests.base import unittest
+import future.backports.email as email
+import future.backports.email.mime.multipart
+from future.builtins import list
+
+class EmailMultiPartTests(unittest.TestCase):
+    """Tests for handling multipart email Messages."""
+
+    def test_multipart_serialize_without_boundary(self):
+        """Tests that serializing an empty multipart email does not fail."""
+        multipart_message = email.mime.multipart.MIMEMultipart()
+        self.assertIsNot(multipart_message.as_string(), None)
+
+    def test_multipart_set_boundary_does_not_change_header_type(self):
+        """
+        Tests that Message.set_boundary() does not cause Python2 errors.
+        
+        In particular, tests that set_boundary does not cause the type of the
+        message headers list to be changed from the future built-in list.
+        """
+        multipart_message = email.mime.multipart.MIMEMultipart()
+        headers_type = type(multipart_message._headers)
+        self.assertEqual(headers_type, type(list()))
+
+        boundary = '===============6387699881409002085=='
+        multipart_message.set_boundary(boundary)
+        headers_type = type(multipart_message._headers)
+        self.assertEqual(headers_type, type(list()))

From 2c410ded2a424b9b57f770e5d6084524ac37d440 Mon Sep 17 00:00:00 2001
From: Andrew Wason <rectalogic@rectalogic.com>
Date: Wed, 20 Feb 2019 11:33:08 -0500
Subject: [PATCH 024/183] Do not globally destroy re.ASCII in PY3

---
 src/future/backports/http/cookiejar.py | 5 +++--
 src/future/backports/http/cookies.py   | 3 ++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/future/backports/http/cookiejar.py b/src/future/backports/http/cookiejar.py
index cad72f9b..af3ef415 100644
--- a/src/future/backports/http/cookiejar.py
+++ b/src/future/backports/http/cookiejar.py
@@ -33,7 +33,7 @@
 from __future__ import division
 from __future__ import absolute_import
 from future.builtins import filter, int, map, open, str
-from future.utils import as_native_str
+from future.utils import as_native_str, PY2
 
 __all__ = ['Cookie', 'CookieJar', 'CookiePolicy', 'DefaultCookiePolicy',
            'FileCookieJar', 'LWPCookieJar', 'LoadError', 'MozillaCookieJar']
@@ -41,7 +41,8 @@
 import copy
 import datetime
 import re
-re.ASCII = 0
+if PY2:
+    re.ASCII = 0
 import time
 from future.backports.urllib.parse import urlparse, urlsplit, quote
 from future.backports.http.client import HTTP_PORT
diff --git a/src/future/backports/http/cookies.py b/src/future/backports/http/cookies.py
index ae32ed7e..8bb61e22 100644
--- a/src/future/backports/http/cookies.py
+++ b/src/future/backports/http/cookies.py
@@ -138,7 +138,8 @@
 # Import our required modules
 #
 import re
-re.ASCII = 0    # for py2 compatibility
+if PY2:
+    re.ASCII = 0    # for py2 compatibility
 import string
 
 __all__ = ["CookieError", "BaseCookie", "SimpleCookie"]

From 49495ee850573259e4d18fecc57ec71d16d87ddc Mon Sep 17 00:00:00 2001
From: "Fumihiro Bessho (Ben)" <fumihiro.bessho@gmail.com>
Date: Sun, 17 Mar 2019 08:05:23 +0900
Subject: [PATCH 025/183] Fixed bug in FixDivisionSafe()

---
 src/libfuturize/fixes/fix_division_safe.py | 55 ++++++++++------------
 tests/test_future/test_futurize.py         | 10 +++-
 2 files changed, 34 insertions(+), 31 deletions(-)

diff --git a/src/libfuturize/fixes/fix_division_safe.py b/src/libfuturize/fixes/fix_division_safe.py
index 7b0f3cbd..3d5909cc 100644
--- a/src/libfuturize/fixes/fix_division_safe.py
+++ b/src/libfuturize/fixes/fix_division_safe.py
@@ -14,10 +14,8 @@
 """
 
 import re
-import lib2to3.pytree as pytree
 from lib2to3.fixer_util import Leaf, Node, Comma
 from lib2to3 import fixer_base
-from lib2to3.fixer_util import syms, does_tree_import
 from libfuturize.fixer_util import (token, future_import, touch_import_top,
                                     wrap_in_fn_call)
 
@@ -33,8 +31,8 @@ def match_division(node):
 
 const_re = re.compile('^[0-9]*[.][0-9]*$')
 
-def is_floaty(node, div_idx):
-    return _is_floaty(node.children[0:div_idx]) or _is_floaty(node.children[div_idx+1:])
+def is_floaty(node):
+    return _is_floaty(node.prev_sibling) or _is_floaty(node.next_sibling)
 
 
 def _is_floaty(expr):
@@ -50,24 +48,6 @@ def _is_floaty(expr):
             return expr.children[0].value == u'float'
     return False
 
-def find_division(node):
-    for i, child in enumerate(node.children):
-        if match_division(child):
-            return i
-    return False
-
-def clone_div_operands(node, div_idx):
-    children = []
-    for i, child in enumerate(node.children):
-        if i == div_idx:
-            children.append(Comma())
-        else:
-            children.append(child.clone())
-
-    # Strip any leading space for the first number:
-    children[0].prefix = u''
-
-    return children
 
 class FixDivisionSafe(fixer_base.BaseFix):
     # BM_compatible = True
@@ -92,13 +72,28 @@ def match(self, node):
         matches, we can start discarding matches after the first.
         """
         if node.type == self.syms.term:
-            div_idx = find_division(node)
-            if div_idx is not False:
-                # if expr1 or expr2 are obviously floats, we don't need to wrap in
-                # old_div, as the behavior of division between any number and a float
-                # should be the same in 2 or 3
-                if not is_floaty(node, div_idx):
-                    return clone_div_operands(node, div_idx)
+            matched = False
+            skip = False
+            children = []
+            for child in node.children:
+                if skip:
+                    skip = False
+                    continue
+                if match_division(child) and not is_floaty(child):
+                    matched = True
+
+                    # Strip any leading space for the first number:
+                    children[0].prefix = u''
+
+                    children = [wrap_in_fn_call("old_div",
+                                                children + [Comma(), child.next_sibling.clone()],
+                                                prefix=node.prefix)]
+                    skip = True
+                else:
+                    children.append(child.clone())
+            if matched:
+                return Node(node.type, children, fixers_applied=node.fixers_applied)
+
         return False
 
     def transform(self, node, results):
@@ -106,4 +101,4 @@ def transform(self, node, results):
             return
         future_import(u"division", node)
         touch_import_top(u'past.utils', u'old_div', node)
-        return wrap_in_fn_call("old_div", results, prefix=node.prefix)
+        return results
diff --git a/tests/test_future/test_futurize.py b/tests/test_future/test_futurize.py
index 5549a010..82ba6563 100644
--- a/tests/test_future/test_futurize.py
+++ b/tests/test_future/test_futurize.py
@@ -1208,6 +1208,10 @@ def total_count(self):
         val = float(obj.numer) / obj.denom * 1e-9
         obj.numer * obj.denom / val
         obj.total_count() * val / 100
+        obj.numer / obj.denom * 1e-9
+        obj.numer / (obj.denom * 1e-9)
+        obj.numer / obj.denom / 1e-9
+        obj.numer / (obj.denom / 1e-9)
         original_numer = 1
         original_denom = 50
         100 * abs(obj.numer - original_numer) / float(max(obj.denom, original_denom))
@@ -1237,13 +1241,17 @@ def total_count(self):
         b = 1 + foo[old_div(len(foo) * 3, 4)]
         assert a == 51
         assert b == 76
-        r = old_div(random.randint(0, 1000) * 1.0, 1000)
+        r = random.randint(0, 1000) * 1.0 / 1000
         output = { "SUCCESS": 5, "TOTAL": 10 }
         old_div(output["SUCCESS"] * 100, output["TOTAL"])
         obj = fraction(1, 50)
         val = float(obj.numer) / obj.denom * 1e-9
         old_div(obj.numer * obj.denom, val)
         old_div(obj.total_count() * val, 100)
+        old_div(obj.numer, obj.denom) * 1e-9
+        old_div(obj.numer, (obj.denom * 1e-9))
+        old_div(old_div(obj.numer, obj.denom), 1e-9)
+        old_div(obj.numer, (old_div(obj.denom, 1e-9)))
         original_numer = 1
         original_denom = 50
         100 * abs(obj.numer - original_numer) / float(max(obj.denom, original_denom))

From a39f2dabd6108867ba7f3a4d820b55546b095208 Mon Sep 17 00:00:00 2001
From: Aiden Scandella <git@sca.ndella.com>
Date: Wed, 3 Apr 2019 10:36:19 -0700
Subject: [PATCH 026/183] Fix future import ordering

Fixes #445
---
 src/future/tests/base.py                              |  6 +++++-
 ...ix_add__future__imports_except_unicode_literals.py |  4 ++--
 .../fixes/fix_add_all__future__imports.py             |  6 +++---
 tests/test_future/test_futurize.py                    | 11 ++++++-----
 4 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/src/future/tests/base.py b/src/future/tests/base.py
index 9f4607b6..a7cc8ec1 100644
--- a/src/future/tests/base.py
+++ b/src/future/tests/base.py
@@ -272,7 +272,11 @@ def convert_check(self, before, expected, stages=(1, 2), all_imports=False,
         else:
             headers = ''
 
-        self.compare(output, headers + reformat_code(expected),
+        reformatted = reformat_code(expected)
+        if headers in reformatted:
+            headers = ''
+
+        self.compare(output, headers + reformatted,
                      ignore_imports=ignore_imports)
 
     def unchanged(self, code, **kwargs):
diff --git a/src/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py b/src/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py
index 1d419a1c..37d7feec 100644
--- a/src/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py
+++ b/src/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py
@@ -21,6 +21,6 @@ class FixAddFutureImportsExceptUnicodeLiterals(fixer_base.BaseFix):
 
     def transform(self, node, results):
         # Reverse order:
-        future_import(u"print_function", node)
-        future_import(u"division", node)
         future_import(u"absolute_import", node)
+        future_import(u"division", node)
+        future_import(u"print_function", node)
diff --git a/src/libpasteurize/fixes/fix_add_all__future__imports.py b/src/libpasteurize/fixes/fix_add_all__future__imports.py
index 37897946..a151f9f1 100644
--- a/src/libpasteurize/fixes/fix_add_all__future__imports.py
+++ b/src/libpasteurize/fixes/fix_add_all__future__imports.py
@@ -18,7 +18,7 @@ class FixAddAllFutureImports(fixer_base.BaseFix):
     run_order = 1
 
     def transform(self, node, results):
-        future_import(u"unicode_literals", node)
-        future_import(u"print_function", node)
-        future_import(u"division", node)
         future_import(u"absolute_import", node)
+        future_import(u"division", node)
+        future_import(u"print_function", node)
+        future_import(u"unicode_literals", node)
diff --git a/tests/test_future/test_futurize.py b/tests/test_future/test_futurize.py
index 5549a010..633eb648 100644
--- a/tests/test_future/test_futurize.py
+++ b/tests/test_future/test_futurize.py
@@ -1361,6 +1361,7 @@ def test_open(self):
         """
         self.convert_check(before, after, conservative=True)
 
+
 class TestFuturizeAllImports(CodeHandler):
     """
     Tests "futurize --all-imports".
@@ -1378,14 +1379,14 @@ def test_all_imports(self):
         print('Hello')
         """
         after = """
-        from __future__ import unicode_literals
-        from __future__ import print_function
-        from __future__ import division
         from __future__ import absolute_import
+        from __future__ import division
+        from __future__ import print_function
+        from __future__ import unicode_literals
         from future import standard_library
         standard_library.install_aliases()
-        from builtins import range
         from builtins import *
+        from builtins import range
         import math
         import os
         l = list(range(10))
@@ -1395,7 +1396,7 @@ def test_all_imports(self):
             pass
         print('Hello')
         """
-        self.convert_check(before, after, all_imports=True)
+        self.convert_check(before, after, all_imports=True, ignore_imports=False)
 
 
 if __name__ == '__main__':

From 4b1ddb49e8e7d4c812c50ac38d2c11db64ac9c1e Mon Sep 17 00:00:00 2001
From: Chih-Hsuan Yen <yan12125@gmail.com>
Date: Fri, 28 Sep 2018 14:39:32 +0800
Subject: [PATCH 027/183] Fix urllib.request imports for Python 3.8
 compatibility

Fixes https://github.com/PythonCharmers/python-future/issues/447
---
 src/future/moves/urllib/request.py | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/src/future/moves/urllib/request.py b/src/future/moves/urllib/request.py
index 60e440a7..972aa4ab 100644
--- a/src/future/moves/urllib/request.py
+++ b/src/future/moves/urllib/request.py
@@ -11,19 +11,8 @@
                                 proxy_bypass,
                                 quote,
                                 request_host,
-                                splitattr,
-                                splithost,
-                                splitpasswd,
-                                splitport,
-                                splitquery,
-                                splittag,
-                                splittype,
-                                splituser,
-                                splitvalue,
                                 thishost,
-                                to_bytes,
                                 unquote,
-                                unwrap,
                                 url2pathname,
                                 urlcleanup,
                                 urljoin,
@@ -32,6 +21,18 @@
                                 urlretrieve,
                                 urlsplit,
                                 urlunparse)
+
+    from urllib.parse import (splitattr,
+                              splithost,
+                              splitpasswd,
+                              splitport,
+                              splitquery,
+                              splittag,
+                              splittype,
+                              splituser,
+                              splitvalue,
+                              to_bytes,
+                              unwrap)
 else:
     __future_module__ = True
     with suspend_hooks():

From 202ca193955eb1bfc4bc46cc830006eb169f4d2e Mon Sep 17 00:00:00 2001
From: Jeff Widman <jeff@jeffwidman.com>
Date: Tue, 23 Apr 2019 17:13:11 -0700
Subject: [PATCH 028/183] Fix awkward wording

> you may like to use X instead

reads a bit awkward / non-native English to my ears. Afraid I can't point to a specific rule why though.

Anyway, just thought I'd help fix it up. Thank you for the super helpful package!
---
 docs/compatible_idioms.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/compatible_idioms.rst b/docs/compatible_idioms.rst
index 52a6a8b0..9ba94280 100644
--- a/docs/compatible_idioms.rst
+++ b/docs/compatible_idioms.rst
@@ -1270,7 +1270,7 @@ urllib module
 ~~~~~~~~~~~~~
 
 ``urllib`` is the hardest module to use from Python 2/3 compatible code.
-You may like to use Requests (http://python-requests.org) instead.
+You might want to switch to Requests (http://python-requests.org) instead.
 
 .. code:: python
 

From 23989c4d61a5e3b2308b107efc1402bc727e8fe6 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Mon, 6 May 2019 10:56:37 -0400
Subject: [PATCH 029/183] Future-proof for Py4.0

---
 src/future/backports/__init__.py | 2 +-
 src/future/moves/__init__.py     | 2 +-
 src/future/utils/__init__.py     | 2 +-
 src/html/parser.py               | 2 +-
 src/past/translation/__init__.py | 2 +-
 src/past/utils/__init__.py       | 2 +-
 6 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/future/backports/__init__.py b/src/future/backports/__init__.py
index 68291141..c71e0653 100644
--- a/src/future/backports/__init__.py
+++ b/src/future/backports/__init__.py
@@ -10,7 +10,7 @@
 from future.standard_library import import_top_level_modules
 
 
-if sys.version_info[0] == 3:
+if sys.version_info[0] >= 3:
     import_top_level_modules()
 
 
diff --git a/src/future/moves/__init__.py b/src/future/moves/__init__.py
index 040fdcf0..0cd60d3d 100644
--- a/src/future/moves/__init__.py
+++ b/src/future/moves/__init__.py
@@ -4,5 +4,5 @@
 __future_module__ = True
 from future.standard_library import import_top_level_modules
 
-if sys.version_info[0] == 3:
+if sys.version_info[0] >= 3:
     import_top_level_modules()
diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index 906f1e46..090a2bbe 100644
--- a/src/future/utils/__init__.py
+++ b/src/future/utils/__init__.py
@@ -55,7 +55,7 @@
 import inspect
 
 
-PY3 = sys.version_info[0] == 3
+PY3 = sys.version_info[0] >= 3
 PY35_PLUS = sys.version_info[0:2] >= (3, 5)
 PY36_PLUS = sys.version_info[0:2] >= (3, 6)
 PY2 = sys.version_info[0] == 2
diff --git a/src/html/parser.py b/src/html/parser.py
index 541def39..e3948879 100644
--- a/src/html/parser.py
+++ b/src/html/parser.py
@@ -2,7 +2,7 @@
 import sys
 __future_module__ = True
 
-if sys.version_info[0] == 3:
+if sys.version_info[0] >= 3:
     raise ImportError('Cannot import module from python-future source folder')
 else:
     from future.moves.html.parser import *
diff --git a/src/past/translation/__init__.py b/src/past/translation/__init__.py
index c7ae2b7a..5ac57068 100644
--- a/src/past/translation/__init__.py
+++ b/src/past/translation/__init__.py
@@ -432,7 +432,7 @@ def install_hooks(include_paths=(), exclude_paths=()):
     _hook.include(include_paths)
     _hook.exclude(exclude_paths)
     # _hook.debug = debug
-    enable = sys.version_info[0] >= 3   # enabled for all 3.x
+    enable = sys.version_info[0] >= 3   # enabled for all 3.x+
     if enable and _hook not in sys.meta_path:
         sys.meta_path.insert(0, _hook)  # insert at beginning. This could be made a parameter
 
diff --git a/src/past/utils/__init__.py b/src/past/utils/__init__.py
index c6606d0b..f6b2642d 100644
--- a/src/past/utils/__init__.py
+++ b/src/past/utils/__init__.py
@@ -16,7 +16,7 @@
 import sys
 import numbers
 
-PY3 = sys.version_info[0] == 3
+PY3 = sys.version_info[0] >= 3
 PY2 = sys.version_info[0] == 2
 PYPY = hasattr(sys, 'pypy_translation_info')
 

From 671a63e7f7b693b26df6285f3cdaf4a2fb598194 Mon Sep 17 00:00:00 2001
From: Ghanshyam Lele <ghanshyam.lele@gmail.com>
Date: Mon, 6 May 2019 15:15:51 -0400
Subject: [PATCH 030/183] Bug fix #454: Implement __hash__() in newstr

---
 src/future/types/newstr.py | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/future/types/newstr.py b/src/future/types/newstr.py
index e6272fb9..76374656 100644
--- a/src/future/types/newstr.py
+++ b/src/future/types/newstr.py
@@ -105,6 +105,7 @@ def __repr__(self):
         """
         Without the u prefix
         """
+
         value = super(newstr, self).__repr__()
         # assert value[0] == u'u'
         return value[1:]
@@ -292,6 +293,13 @@ def __eq__(self, other):
         else:
             return False
 
+    def __hash__(self):
+        if (isinstance(self, unicode) or
+            isinstance(self, bytes) and not isnewbytes(self)):
+            return super(newstr, self).__hash__()
+        else:
+            raise NotImplementedError()
+
     def __ne__(self, other):
         if (isinstance(other, unicode) or
             isinstance(other, bytes) and not isnewbytes(other)):

From 064a12ae5b1e70e9be1faaee74259a3d6cf4b2f2 Mon Sep 17 00:00:00 2001
From: Ghanshyam Lele <ghanshyam.lele@gmail.com>
Date: Mon, 6 May 2019 15:42:11 -0400
Subject: [PATCH 031/183] add test for newstr.__hash__()

---
 tests/test_future/test_str.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tests/test_future/test_str.py b/tests/test_future/test_str.py
index dcc15628..debbd849 100644
--- a/tests/test_future/test_str.py
+++ b/tests/test_future/test_str.py
@@ -363,6 +363,10 @@ def test_eq(self):
             self.assertFalse(b'ABCD' == s)
         self.assertFalse(bytes(b'ABCD') == s)
 
+    def test_hash(self):
+        s = str('ABCD')
+        self.assertIsInstance(hash(s),int)
+
     def test_ne(self):
         s = str('ABCD')
         self.assertNotEqual('A', s)

From f141395ce73acc3d1461ff21a29813c1e9e15baf Mon Sep 17 00:00:00 2001
From: Sameera <sameerasomisetty@gmail.com>
Date: Tue, 7 May 2019 11:50:40 -0400
Subject: [PATCH 032/183] for issue 334

---
 src/future/builtins/__init__.py    |  4 +--
 src/future/builtins/misc.py        |  9 ++++++-
 src/future/builtins/new_min_max.py | 43 ++++++++++++++++++++++++++++++
 3 files changed, 53 insertions(+), 3 deletions(-)
 create mode 100644 src/future/builtins/new_min_max.py

diff --git a/src/future/builtins/__init__.py b/src/future/builtins/__init__.py
index 216465a1..8bc1649d 100644
--- a/src/future/builtins/__init__.py
+++ b/src/future/builtins/__init__.py
@@ -11,7 +11,7 @@
 # The isinstance import is no longer needed. We provide it only for
 # backward-compatibility with future v0.8.2. It will be removed in future v1.0.
 from future.builtins.misc import (ascii, chr, hex, input, isinstance, next,
-                                  oct, open, pow, round, super)
+                                  oct, open, pow, round, super, max, min)
 from future.utils import PY3
 
 if PY3:
@@ -43,7 +43,7 @@
     __all__ = ['filter', 'map', 'zip',
                'ascii', 'chr', 'hex', 'input', 'next', 'oct', 'open', 'pow',
                'round', 'super',
-               'bytes', 'dict', 'int', 'list', 'object', 'range', 'str',
+               'bytes', 'dict', 'int', 'list', 'object', 'range', 'str', 'max', 'min'
               ]
 
 else:
diff --git a/src/future/builtins/misc.py b/src/future/builtins/misc.py
index 90dc384a..b0b67066 100644
--- a/src/future/builtins/misc.py
+++ b/src/future/builtins/misc.py
@@ -13,6 +13,8 @@
 - ``open`` (equivalent to io.open on Py2)
 - ``super`` (backport of Py3's magic zero-argument super() function
 - ``round`` (new "Banker's Rounding" behaviour from Py3)
+- ``max`` (new max behaviour from Py3)
+- ``min`` (new min behaviour from Py3)
 
 ``isinstance`` is also currently exported for backwards compatibility
 with v0.8.2, although this has been deprecated since v0.9.
@@ -59,6 +61,8 @@
     from future.builtins.newnext import newnext as next
     from future.builtins.newround import newround as round
     from future.builtins.newsuper import newsuper as super
+    from future.builtins.new_min_max import newmax as max
+    from future.builtins.new_min_max import newmin as min
     from future.types.newint import newint
 
     _SENTINEL = object()
@@ -89,11 +93,12 @@ def pow(x, y, z=_SENTINEL):
             else:
                 return _builtin_pow(x+0j, y, z)
 
+
     # ``future`` doesn't support Py3.0/3.1. If we ever did, we'd add this:
     #     callable = __builtin__.callable
 
     __all__ = ['ascii', 'chr', 'hex', 'input', 'isinstance', 'next', 'oct',
-               'open', 'pow', 'round', 'super']
+               'open', 'pow', 'round', 'super', 'max', 'min']
 
 else:
     import builtins
@@ -109,6 +114,8 @@ def pow(x, y, z=_SENTINEL):
     pow = builtins.pow
     round = builtins.round
     super = builtins.super
+    max = builtins.max
+    min = builtins.min
 
     __all__ = []
 
diff --git a/src/future/builtins/new_min_max.py b/src/future/builtins/new_min_max.py
new file mode 100644
index 00000000..128a7f16
--- /dev/null
+++ b/src/future/builtins/new_min_max.py
@@ -0,0 +1,43 @@
+from __builtin__ import max as _builtin_max, min as _builtin_min
+
+
+def newmin(*args, **kwargs):
+    return new_min_max(_builtin_min, *args, **kwargs)
+
+
+def newmax(*args, **kwargs):
+    return new_min_max(_builtin_max, *args, **kwargs)
+
+
+def new_min_max(_builtin_func, *args, **kwargs):
+    """
+    To support the argument "default" introduced in python 3.4 for min and max
+    :param _builtin_func: builtin min or builtin max
+    :param args:
+    :param kwargs:
+    :return: returns the min or max based on the arguments passed
+    """
+
+    for key, _ in kwargs.items():
+        if key not in set(['key', 'default']):
+            raise TypeError('Illegal argument %s', key)
+
+    if len(args) != 1 and kwargs.get('default') is not None:
+        raise TypeError
+
+    if len(args) == 1:
+        if len(args[0]) == 0:
+            if kwargs.get('default') is not None:
+                return kwargs.get('default')
+            else:
+                raise ValueError('iterable is an empty sequence')
+        if kwargs.get('key') is not None:
+            return _builtin_func(args[0], key=kwargs.get('key'))
+        else:
+            return _builtin_func(args[0])
+
+    if len(args) > 1:
+        if kwargs.get('key') is not None:
+            return _builtin_func(args, key=kwargs.get('key'))
+        else:
+            return _builtin_func(args)

From 98e8ae154e90434be0a32205c5f78b22c201afb3 Mon Sep 17 00:00:00 2001
From: Sameera <sameerasomisetty@gmail.com>
Date: Tue, 7 May 2019 13:56:35 -0400
Subject: [PATCH 033/183] adding tests and fixing functions

---
 src/future/builtins/new_min_max.py |  9 +++++++--
 tests/test_future/test_builtins.py | 16 +++++++++++++++-
 2 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/src/future/builtins/new_min_max.py b/src/future/builtins/new_min_max.py
index 128a7f16..1adbd491 100644
--- a/src/future/builtins/new_min_max.py
+++ b/src/future/builtins/new_min_max.py
@@ -19,14 +19,19 @@ def new_min_max(_builtin_func, *args, **kwargs):
     """
 
     for key, _ in kwargs.items():
-        if key not in set(['key', 'default']):
+        if key not in {'key', 'default'}:
             raise TypeError('Illegal argument %s', key)
 
+    if len(args) == 0:
+        raise TypeError
+
     if len(args) != 1 and kwargs.get('default') is not None:
         raise TypeError
 
     if len(args) == 1:
-        if len(args[0]) == 0:
+        try:
+            next(iter(args[0]))
+        except StopIteration:
             if kwargs.get('default') is not None:
                 return kwargs.get('default')
             else:
diff --git a/tests/test_future/test_builtins.py b/tests/test_future/test_builtins.py
index 05d597a5..a514d697 100644
--- a/tests/test_future/test_builtins.py
+++ b/tests/test_future/test_builtins.py
@@ -6,7 +6,7 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
 from future.builtins import (bytes, dict, int, range, round, str, super,
                              ascii, chr, hex, input, next, oct, open, pow,
-                             filter, map, zip)
+                             filter, map, zip, min, max)
 
 from future.utils import PY3, exec_, native_str, implements_iterator
 from future.tests.base import (unittest, skip26, expectedFailurePY2,
@@ -138,6 +138,7 @@ def test_round(self):
         self.assertEqual(round(0.125000001, 2), 0.13)
         self.assertEqual(round(123.5, 0), 124.0)
         self.assertEqual(round(123.5), 124)
+        self.assertEqual(round(123.5), 124)
         self.assertEqual(round(12.35, 2), 12.35)
         self.assertEqual(round(12.35, 1), 12.3)
         self.assertEqual(round(12.35, 0), 12.0)
@@ -1099,6 +1100,13 @@ def test_max(self):
         self.assertEqual(max(data, key=f),
                          sorted(reversed(data), key=f)[-1])
 
+        self.assertEqual(max([], default=5), 5)
+        with self.assertRaises(TypeError):
+            max(None, default=5)
+        with self.assertRaises(TypeError):
+            max(1, 2, default=0)
+        self.assertEqual(max([], default=0), 0)
+
     def test_min(self):
         self.assertEqual(min('123123'), '1')
         self.assertEqual(min(1, 2, 3), 1)
@@ -1140,6 +1148,12 @@ def __getitem__(self, index):
         f = keys.__getitem__
         self.assertEqual(min(data, key=f),
                          sorted(data, key=f)[0])
+        self.assertEqual(min([], default=5), 5)
+        self.assertEqual(min([], default=0), 0)
+        with self.assertRaises(TypeError):
+            max(None, default=5)
+        with self.assertRaises(TypeError):
+            max(1, 2, default=0)
 
     def test_next(self):
         it = iter(range(2))

From 5c3f9949614b92bf75132c0f122d099363b957d7 Mon Sep 17 00:00:00 2001
From: Sameera <sameerasomisetty@gmail.com>
Date: Tue, 7 May 2019 14:17:03 -0400
Subject: [PATCH 034/183] minor changes

---
 src/future/builtins/misc.py        | 4 ++--
 tests/test_future/test_builtins.py | 1 -
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/future/builtins/misc.py b/src/future/builtins/misc.py
index b0b67066..52d19926 100644
--- a/src/future/builtins/misc.py
+++ b/src/future/builtins/misc.py
@@ -13,8 +13,8 @@
 - ``open`` (equivalent to io.open on Py2)
 - ``super`` (backport of Py3's magic zero-argument super() function
 - ``round`` (new "Banker's Rounding" behaviour from Py3)
-- ``max`` (new max behaviour from Py3)
-- ``min`` (new min behaviour from Py3)
+- ``max`` (new default option from Py3)
+- ``min`` (new default option from Py3)
 
 ``isinstance`` is also currently exported for backwards compatibility
 with v0.8.2, although this has been deprecated since v0.9.
diff --git a/tests/test_future/test_builtins.py b/tests/test_future/test_builtins.py
index a514d697..d983f9d6 100644
--- a/tests/test_future/test_builtins.py
+++ b/tests/test_future/test_builtins.py
@@ -138,7 +138,6 @@ def test_round(self):
         self.assertEqual(round(0.125000001, 2), 0.13)
         self.assertEqual(round(123.5, 0), 124.0)
         self.assertEqual(round(123.5), 124)
-        self.assertEqual(round(123.5), 124)
         self.assertEqual(round(12.35, 2), 12.35)
         self.assertEqual(round(12.35, 1), 12.3)
         self.assertEqual(round(12.35, 0), 12.0)

From fbea2790e6ab207681cd7049cea2b79e52548af9 Mon Sep 17 00:00:00 2001
From: Sameera <sameerasomisetty@gmail.com>
Date: Tue, 7 May 2019 15:04:04 -0400
Subject: [PATCH 035/183] set represenation for python 2.6

---
 src/future/builtins/new_min_max.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/future/builtins/new_min_max.py b/src/future/builtins/new_min_max.py
index 1adbd491..2858e541 100644
--- a/src/future/builtins/new_min_max.py
+++ b/src/future/builtins/new_min_max.py
@@ -19,7 +19,7 @@ def new_min_max(_builtin_func, *args, **kwargs):
     """
 
     for key, _ in kwargs.items():
-        if key not in {'key', 'default'}:
+        if key not in set(['key', 'default']):
             raise TypeError('Illegal argument %s', key)
 
     if len(args) == 0:

From ee0d6dc217be6ee051c1fe058d426a21d73100c3 Mon Sep 17 00:00:00 2001
From: Sameera <sameerasomisetty@gmail.com>
Date: Tue, 7 May 2019 15:31:17 -0400
Subject: [PATCH 036/183] fix for python 3.3 and below

---
 src/future/builtins/misc.py  | 9 +++++++--
 src/future/utils/__init__.py | 1 +
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/future/builtins/misc.py b/src/future/builtins/misc.py
index 52d19926..0691dc3a 100644
--- a/src/future/builtins/misc.py
+++ b/src/future/builtins/misc.py
@@ -13,8 +13,8 @@
 - ``open`` (equivalent to io.open on Py2)
 - ``super`` (backport of Py3's magic zero-argument super() function
 - ``round`` (new "Banker's Rounding" behaviour from Py3)
-- ``max`` (new default option from Py3)
-- ``min`` (new default option from Py3)
+- ``max`` (new default option from Py3.4)
+- ``min`` (new default option from Py3.4)
 
 ``isinstance`` is also currently exported for backwards compatibility
 with v0.8.2, although this has been deprecated since v0.9.
@@ -100,6 +100,11 @@ def pow(x, y, z=_SENTINEL):
     __all__ = ['ascii', 'chr', 'hex', 'input', 'isinstance', 'next', 'oct',
                'open', 'pow', 'round', 'super', 'max', 'min']
 
+elif not utils.PY34_PLUS:
+    from future.builtins.new_min_max import newmax as max
+    from future.builtins.new_min_max import newmin as min
+    __all__ = ['min', 'max']
+
 else:
     import builtins
     ascii = builtins.ascii
diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index 090a2bbe..9ce2ec08 100644
--- a/src/future/utils/__init__.py
+++ b/src/future/utils/__init__.py
@@ -56,6 +56,7 @@
 
 
 PY3 = sys.version_info[0] >= 3
+PY34_PLUS = sys.version_info[0:2] >= (3, 4)
 PY35_PLUS = sys.version_info[0:2] >= (3, 5)
 PY36_PLUS = sys.version_info[0:2] >= (3, 6)
 PY2 = sys.version_info[0] == 2

From 4102c9a14d5208914902858da2912806c1ae5e1e Mon Sep 17 00:00:00 2001
From: Sameera <sameerasomisetty@gmail.com>
Date: Tue, 7 May 2019 16:00:27 -0400
Subject: [PATCH 037/183] correction for python 3.3 and below

---
 src/future/builtins/misc.py | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/src/future/builtins/misc.py b/src/future/builtins/misc.py
index 0691dc3a..f86ce5f3 100644
--- a/src/future/builtins/misc.py
+++ b/src/future/builtins/misc.py
@@ -100,11 +100,6 @@ def pow(x, y, z=_SENTINEL):
     __all__ = ['ascii', 'chr', 'hex', 'input', 'isinstance', 'next', 'oct',
                'open', 'pow', 'round', 'super', 'max', 'min']
 
-elif not utils.PY34_PLUS:
-    from future.builtins.new_min_max import newmax as max
-    from future.builtins.new_min_max import newmin as min
-    __all__ = ['min', 'max']
-
 else:
     import builtins
     ascii = builtins.ascii
@@ -119,10 +114,14 @@ def pow(x, y, z=_SENTINEL):
     pow = builtins.pow
     round = builtins.round
     super = builtins.super
-    max = builtins.max
-    min = builtins.min
-
-    __all__ = []
+    if utils.PY34_PLUS:
+        max = builtins.max
+        min = builtins.min
+        __all__ = []
+    else:
+        from future.builtins.new_min_max import newmax as max
+        from future.builtins.new_min_max import newmin as min
+        __all__ = ['min', 'max']
 
     # The callable() function was removed from Py3.0 and 3.1 and
     # reintroduced into Py3.2+. ``future`` doesn't support Py3.0/3.1. If we ever

From 99f2a8a6d3883d3fc72e1fc17605f50b83bc1679 Mon Sep 17 00:00:00 2001
From: shivapbhusal <civabhusal@gmail.com>
Date: Tue, 7 May 2019 16:08:38 -0400
Subject: [PATCH 038/183] add unit test to prove the behaviour for string
 equality

---
 tests/test_future/test_str.py | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/tests/test_future/test_str.py b/tests/test_future/test_str.py
index 7135ed1d..5563abf3 100644
--- a/tests/test_future/test_str.py
+++ b/tests/test_future/test_str.py
@@ -363,6 +363,19 @@ def test_eq(self):
             self.assertFalse(b'ABCD' == s)
         self.assertFalse(bytes(b'ABCD') == s)
 
+        class OurCustomString(object):
+            def __init__(self, string):
+                self.string = string
+
+            def __str__(self):
+                return self.string
+
+        our_str = OurCustomString("foobar")
+        new_str = str("foobar")
+
+        self.assertFalse(our_str == new_str)
+        self.assertFalse(new_str == our_str)
+
     def test_hash(self):
         s = str('ABCD')
         self.assertIsInstance(hash(s),int)

From 61c9aec4aa4113aee3e37edbca97658a00632819 Mon Sep 17 00:00:00 2001
From: Sameera <sameerasomisetty@gmail.com>
Date: Tue, 7 May 2019 16:13:01 -0400
Subject: [PATCH 039/183] fixing builtin library for python 3.3 and below in
 min, max

---
 src/future/builtins/new_min_max.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/future/builtins/new_min_max.py b/src/future/builtins/new_min_max.py
index 2858e541..8fd63fdf 100644
--- a/src/future/builtins/new_min_max.py
+++ b/src/future/builtins/new_min_max.py
@@ -1,4 +1,8 @@
-from __builtin__ import max as _builtin_max, min as _builtin_min
+from future import utils
+if utils.PY2:
+    from __builtin__ import max as _builtin_max, min as _builtin_min
+else:
+    from builtins import max as _builtin_max, min as _builtin_min
 
 
 def newmin(*args, **kwargs):

From df3a9dcc5328d15efdedcd96e25674b3db4ceaf3 Mon Sep 17 00:00:00 2001
From: Mark Huang <mark@tignis.com>
Date: Tue, 7 May 2019 21:51:20 -0700
Subject: [PATCH 040/183] Translate "raise E, V" to "future.utils.raise_(E, V)"

1. Handle the 3-argument version of "raise E, V, T".

2. "raise E, V, T" cannot be statically translated safely in general. If V is
not a tuple or a (number, string, None) literal, then translate "raise E, V, T"
to "from future.utils import raise_; raise_(E, V, T)" and let raise_() handle
determining whether V is an instance of E at runtime.

Fixes #387 and #455.
---
 src/future/utils/__init__.py       | 32 ++++++++++--
 src/libfuturize/fixes/fix_raise.py | 82 +++++++++++++++++++++---------
 2 files changed, 85 insertions(+), 29 deletions(-)

diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index 628b8f97..59920077 100644
--- a/src/future/utils/__init__.py
+++ b/src/future/utils/__init__.py
@@ -406,12 +406,34 @@ def raise_(tp, value=None, tb=None):
         allows re-raising exceptions with the cls value and traceback on
         Python 2 and 3.
         """
-        if value is not None and isinstance(tp, Exception):
-            raise TypeError("instance exception may not have a separate value")
-        if value is not None:
-            exc = tp(value)
-        else:
+        if isinstance(tp, Exception):
+            # If the first object is an instance, the type of the exception
+            # is the class of the instance, the instance itself is the value,
+            # and the second object must be None.
+            if value is not None:
+                raise TypeError("instance exception may not have a separate value")
             exc = tp
+        elif not issubclass(tp, Exception):
+            # If the first object is a class, it becomes the type of the
+            # exception.
+            raise TypeError("class must derive from Exception")
+        else:
+            # The second object is used to determine the exception value: If it
+            # is an instance of the class, the instance becomes the exception
+            # value. If the second object is a tuple, it is used as the argument
+            # list for the class constructor; if it is None, an empty argument
+            # list is used, and any other object is treated as a single argument
+            # to the constructor. The instance so created by calling the
+            # constructor is used as the exception value.
+            if isinstance(value, tp):
+                exc = value
+            elif isinstance(value, tuple):
+                exc = tp(*value)
+            elif value is None:
+                exc = tp()
+            else:
+                exc = tp(value)
+
         if exc.__traceback__ is not tb:
             raise exc.with_traceback(tb)
         raise exc
diff --git a/src/libfuturize/fixes/fix_raise.py b/src/libfuturize/fixes/fix_raise.py
index 3e8323de..f7518416 100644
--- a/src/libfuturize/fixes/fix_raise.py
+++ b/src/libfuturize/fixes/fix_raise.py
@@ -4,33 +4,39 @@
 
 raise         -> raise
 raise E       -> raise E
-raise E, V    -> raise E(V)
+raise E, 5    -> raise E(5)
+raise E, 5, T -> raise E(5).with_traceback(T)
+raise E, None, T -> raise E.with_traceback(T)
 
-raise (((E, E'), E''), E'''), V -> raise E(V)
+raise (((E, E'), E''), E'''), 5 -> raise E(5)
+raise "foo", V, T               -> warns about string exceptions
 
+raise E, (V1, V2) -> raise E(V1, V2)
+raise E, (V1, V2), T -> raise E(V1, V2).with_traceback(T)
 
-CAVEATS:
-1) "raise E, V" will be incorrectly translated if V is an exception
-   instance. The correct Python 3 idiom is
 
-        raise E from V
+CAVEATS:
+1) "raise E, V, T" cannot be translated safely in general. If V
+   is not a tuple or a (number, string, None) literal, then:
 
-   but since we can't detect instance-hood by syntax alone and since
-   any client code would have to be changed as well, we don't automate
-   this.
+   raise E, V, T -> from future.utils import raise_
+                    raise_(E, V, T)
 """
-# Author: Collin Winter, Armin Ronacher
+# Author: Collin Winter, Armin Ronacher, Mark Huang
 
 # Local imports
 from lib2to3 import pytree, fixer_base
 from lib2to3.pgen2 import token
-from lib2to3.fixer_util import Name, Call, is_tuple
+from lib2to3.fixer_util import Name, Call, is_tuple, Comma, Attr, ArgList
+
+from libfuturize.fixer_util import touch_import_top
+
 
 class FixRaise(fixer_base.BaseFix):
 
     BM_compatible = True
     PATTERN = """
-    raise_stmt< 'raise' exc=any [',' val=any] >
+    raise_stmt< 'raise' exc=any [',' val=any [',' tb=any]] >
     """
 
     def transform(self, node, results):
@@ -55,19 +61,47 @@ def transform(self, node, results):
                 exc = exc.children[1].children[0].clone()
             exc.prefix = u" "
 
-        if "val" not in results:
-            # One-argument raise
-            new = pytree.Node(syms.raise_stmt, [Name(u"raise"), exc])
-            new.prefix = node.prefix
-            return new
-
-        val = results["val"].clone()
-        if is_tuple(val):
-            args = [c.clone() for c in val.children[1:-1]]
+        if "tb" in results:
+            tb = results["tb"].clone()
+        else:
+            tb = None
+
+        if "val" in results:
+            val = results["val"].clone()
+            if is_tuple(val):
+                # Assume that exc is a subclass of Exception and call exc(*val).
+                args = [c.clone() for c in val.children[1:-1]]
+                exc = Call(exc, args)
+            elif val.type in (token.NUMBER, token.STRING):
+                # Handle numeric and string literals specially, e.g.
+                # "raise Exception, 5" -> "raise Exception(5)".
+                val.prefix = u""
+                exc = Call(exc, [val])
+            elif val.type == token.NAME and val.value == u"None":
+                # Handle None specially, e.g.
+                # "raise Exception, None" -> "raise Exception".
+                pass
+            else:
+                # val is some other expression. If val evaluates to an instance
+                # of exc, it should just be raised. If val evaluates to None,
+                # a default instance of exc should be raised (as above). If val
+                # evaluates to a tuple, exc(*val) should be called (as
+                # above). Otherwise, exc(val) should be called. We can only
+                # tell what to do at runtime, so defer to future.utils.raise_(),
+                # which handles all of these cases.
+                touch_import_top(u"future.utils", u"raise_", node)
+                exc.prefix = u""
+                args = [exc, Comma(), val]
+                if tb is not None:
+                    args += [Comma(), tb]
+                return Call(Name(u"raise_"), args)
+
+        if tb is not None:
+            tb.prefix = ""
+            exc_list = Attr(exc, Name('with_traceback')) + [ArgList([tb])]
         else:
-            val.prefix = u""
-            args = [val]
+            exc_list = [exc]
 
         return pytree.Node(syms.raise_stmt,
-                           [Name(u"raise"), Call(exc, args)],
+                           [Name(u"raise")] + exc_list,
                            prefix=node.prefix)

From b3628048352a1a878139c354d0db50a88c3f1c84 Mon Sep 17 00:00:00 2001
From: Mark Huang <mark@tignis.com>
Date: Tue, 7 May 2019 21:51:20 -0700
Subject: [PATCH 041/183] Add tests for raise translation and raise_()
 functionality.

1. Uncomment the Test_raise tests now that support for tracebacks has been
added. Add test_unknown_value() to test for #455.

2. Run the test_raise_() test now that #455 is fixed. Add a test for #387
(raise_(E, V, T) now detects if V is an instance of E and no longer blindly
calls E(V)).
---
 tests/test_future/test_libfuturize_fixers.py | 261 ++++++++++---------
 tests/test_future/test_utils.py              |  21 +-
 2 files changed, 151 insertions(+), 131 deletions(-)

diff --git a/tests/test_future/test_libfuturize_fixers.py b/tests/test_future/test_libfuturize_fixers.py
index 8c4a9a3e..4ac0b7e1 100644
--- a/tests/test_future/test_libfuturize_fixers.py
+++ b/tests/test_future/test_libfuturize_fixers.py
@@ -702,133 +702,140 @@ def test_with_future_print_function(self):
 #             except (Exception, SystemExit):
 #                 pass"""
 #         self.unchanged(s)
-#
-# class Test_raise(FixerTestCase):
-#     fixer = "raise"
-#
-#     def test_basic(self):
-#         b = """raise Exception, 5"""
-#         a = """raise Exception(5)"""
-#         self.check(b, a)
-#
-#     def test_prefix_preservation(self):
-#         b = """raise Exception,5"""
-#         a = """raise Exception(5)"""
-#         self.check(b, a)
-#
-#         b = """raise   Exception,    5"""
-#         a = """raise   Exception(5)"""
-#         self.check(b, a)
-#
-#     def test_with_comments(self):
-#         b = """raise Exception, 5 # foo"""
-#         a = """raise Exception(5) # foo"""
-#         self.check(b, a)
-#
-#         b = """raise E, (5, 6) % (a, b) # foo"""
-#         a = """raise E((5, 6) % (a, b)) # foo"""
-#         self.check(b, a)
-#
-#         b = """def foo():
-#                     raise Exception, 5, 6 # foo"""
-#         a = """def foo():
-#                     raise Exception(5).with_traceback(6) # foo"""
-#         self.check(b, a)
-#
-#     def test_None_value(self):
-#         b = """raise Exception(5), None, tb"""
-#         a = """raise Exception(5).with_traceback(tb)"""
-#         self.check(b, a)
-#
-#     def test_tuple_value(self):
-#         b = """raise Exception, (5, 6, 7)"""
-#         a = """raise Exception(5, 6, 7)"""
-#         self.check(b, a)
-#
-#     def test_tuple_detection(self):
-#         b = """raise E, (5, 6) % (a, b)"""
-#         a = """raise E((5, 6) % (a, b))"""
-#         self.check(b, a)
-#
-#     def test_tuple_exc_1(self):
-#         b = """raise (((E1, E2), E3), E4), V"""
-#         a = """raise E1(V)"""
-#         self.check(b, a)
-#
-#     def test_tuple_exc_2(self):
-#         b = """raise (E1, (E2, E3), E4), V"""
-#         a = """raise E1(V)"""
-#         self.check(b, a)
-#
-#     # These should produce a warning
-#
-#     def test_string_exc(self):
-#         s = """raise 'foo'"""
-#         self.warns_unchanged(s, "Python 3 does not support string exceptions")
-#
-#     def test_string_exc_val(self):
-#         s = """raise "foo", 5"""
-#         self.warns_unchanged(s, "Python 3 does not support string exceptions")
-#
-#     def test_string_exc_val_tb(self):
-#         s = """raise "foo", 5, 6"""
-#         self.warns_unchanged(s, "Python 3 does not support string exceptions")
-#
-#     # These should result in traceback-assignment
-#
-#     def test_tb_1(self):
-#         b = """def foo():
-#                     raise Exception, 5, 6"""
-#         a = """def foo():
-#                     raise Exception(5).with_traceback(6)"""
-#         self.check(b, a)
-#
-#     def test_tb_2(self):
-#         b = """def foo():
-#                     a = 5
-#                     raise Exception, 5, 6
-#                     b = 6"""
-#         a = """def foo():
-#                     a = 5
-#                     raise Exception(5).with_traceback(6)
-#                     b = 6"""
-#         self.check(b, a)
-#
-#     def test_tb_3(self):
-#         b = """def foo():
-#                     raise Exception,5,6"""
-#         a = """def foo():
-#                     raise Exception(5).with_traceback(6)"""
-#         self.check(b, a)
-#
-#     def test_tb_4(self):
-#         b = """def foo():
-#                     a = 5
-#                     raise Exception,5,6
-#                     b = 6"""
-#         a = """def foo():
-#                     a = 5
-#                     raise Exception(5).with_traceback(6)
-#                     b = 6"""
-#         self.check(b, a)
-#
-#     def test_tb_5(self):
-#         b = """def foo():
-#                     raise Exception, (5, 6, 7), 6"""
-#         a = """def foo():
-#                     raise Exception(5, 6, 7).with_traceback(6)"""
-#         self.check(b, a)
-#
-#     def test_tb_6(self):
-#         b = """def foo():
-#                     a = 5
-#                     raise Exception, (5, 6, 7), 6
-#                     b = 6"""
-#         a = """def foo():
-#                     a = 5
-#                     raise Exception(5, 6, 7).with_traceback(6)
-#                     b = 6"""
-#         self.check(b, a)
+
+class Test_raise(FixerTestCase):
+    fixer = "raise"
+
+    def test_basic(self):
+        b = """raise Exception, 5"""
+        a = """raise Exception(5)"""
+        self.check(b, a)
+
+    def test_prefix_preservation(self):
+        b = """raise Exception,5"""
+        a = """raise Exception(5)"""
+        self.check(b, a)
+
+        b = """raise   Exception,    5"""
+        a = """raise   Exception(5)"""
+        self.check(b, a)
+
+    def test_with_comments(self):
+        b = """raise Exception, 5 # foo"""
+        a = """raise Exception(5) # foo"""
+        self.check(b, a)
+
+        b = """def foo():
+                    raise Exception, 5, 6 # foo"""
+        a = """def foo():
+                    raise Exception(5).with_traceback(6) # foo"""
+        self.check(b, a)
+
+    def test_None_value(self):
+        b = """raise Exception(5), None, tb"""
+        a = """raise Exception(5).with_traceback(tb)"""
+        self.check(b, a)
+
+    def test_tuple_value(self):
+        b = """raise Exception, (5, 6, 7)"""
+        a = """raise Exception(5, 6, 7)"""
+        self.check(b, a)
+
+    def test_tuple_exc_1(self):
+        b = """raise (((E1, E2), E3), E4), 5"""
+        a = """raise E1(5)"""
+        self.check(b, a)
+
+    def test_tuple_exc_2(self):
+        b = """raise (E1, (E2, E3), E4), 5"""
+        a = """raise E1(5)"""
+        self.check(b, a)
+
+    def test_unknown_value(self):
+        b = """
+        raise E, V"""
+        a = """
+        from future.utils import raise_
+        raise_(E, V)"""
+        self.check(b, a)
+
+    def test_unknown_value_with_traceback_with_comments(self):
+        b = """
+        raise E, Func(arg1, arg2, arg3), tb # foo"""
+        a = """
+        from future.utils import raise_
+        raise_(E, Func(arg1, arg2, arg3), tb) # foo"""
+        self.check(b, a)
+
+    # These should produce a warning
+
+    def test_string_exc(self):
+        s = """raise 'foo'"""
+        self.warns_unchanged(s, "Python 3 does not support string exceptions")
+
+    def test_string_exc_val(self):
+        s = """raise "foo", 5"""
+        self.warns_unchanged(s, "Python 3 does not support string exceptions")
+
+    def test_string_exc_val_tb(self):
+        s = """raise "foo", 5, 6"""
+        self.warns_unchanged(s, "Python 3 does not support string exceptions")
+
+    # These should result in traceback-assignment
+
+    def test_tb_1(self):
+        b = """def foo():
+                    raise Exception, 5, 6"""
+        a = """def foo():
+                    raise Exception(5).with_traceback(6)"""
+        self.check(b, a)
+
+    def test_tb_2(self):
+        b = """def foo():
+                    a = 5
+                    raise Exception, 5, 6
+                    b = 6"""
+        a = """def foo():
+                    a = 5
+                    raise Exception(5).with_traceback(6)
+                    b = 6"""
+        self.check(b, a)
+
+    def test_tb_3(self):
+        b = """def foo():
+                    raise Exception,5,6"""
+        a = """def foo():
+                    raise Exception(5).with_traceback(6)"""
+        self.check(b, a)
+
+    def test_tb_4(self):
+        b = """def foo():
+                    a = 5
+                    raise Exception,5,6
+                    b = 6"""
+        a = """def foo():
+                    a = 5
+                    raise Exception(5).with_traceback(6)
+                    b = 6"""
+        self.check(b, a)
+
+    def test_tb_5(self):
+        b = """def foo():
+                    raise Exception, (5, 6, 7), 6"""
+        a = """def foo():
+                    raise Exception(5, 6, 7).with_traceback(6)"""
+        self.check(b, a)
+
+    def test_tb_6(self):
+        b = """def foo():
+                    a = 5
+                    raise Exception, (5, 6, 7), 6
+                    b = 6"""
+        a = """def foo():
+                    a = 5
+                    raise Exception(5, 6, 7).with_traceback(6)
+                    b = 6"""
+        self.check(b, a)
 #
 # class Test_throw(FixerTestCase):
 #     fixer = "throw"
diff --git a/tests/test_future/test_utils.py b/tests/test_future/test_utils.py
index b80e8c17..488c0064 100644
--- a/tests/test_future/test_utils.py
+++ b/tests/test_future/test_utils.py
@@ -110,11 +110,7 @@ def test_isbytes(self):
         self.assertFalse(isbytes(self.s))
         self.assertFalse(isbytes(self.s2))
 
-    @unittest.skipIf(PY3, 'test_raise_ currently fails on Py3')
     def test_raise_(self):
-        """
-        The with_value() test currently fails on Py3
-        """
         def valerror():
             try:
                 raise ValueError("Apples!")
@@ -173,6 +169,23 @@ def bar():
             pass
         # incorrectly raises a TypeError on Py3 as of v0.15.2.
 
+    def test_raise_custom_exception(self):
+        """
+        Test issue #387.
+        """
+        class CustomException(Exception):
+            def __init__(self, severity, message):
+                super().__init__("custom message of severity %d: %s" % (
+                    severity, message))
+
+        def raise_custom_exception():
+            try:
+                raise CustomException(1, "hello")
+            except CustomException:
+                raise_(*sys.exc_info())
+
+        self.assertRaises(CustomException, raise_custom_exception)
+
     @skip26
     def test_as_native_str(self):
         """

From d87c56e7bd264a701395a9929c4f28af82d9ebf3 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Wed, 8 May 2019 12:17:50 -0400
Subject: [PATCH 042/183] Update behavior of newstr.__eq__() to match
 str.__eq__() as per reference docs

---
 src/future/types/newstr.py    |  2 +-
 tests/test_future/test_str.py | 10 ++++++++--
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/future/types/newstr.py b/src/future/types/newstr.py
index 76374656..d41ea969 100644
--- a/src/future/types/newstr.py
+++ b/src/future/types/newstr.py
@@ -291,7 +291,7 @@ def __eq__(self, other):
             isinstance(other, bytes) and not isnewbytes(other)):
             return super(newstr, self).__eq__(other)
         else:
-            return False
+            return NotImplemented
 
     def __hash__(self):
         if (isinstance(self, unicode) or
diff --git a/tests/test_future/test_str.py b/tests/test_future/test_str.py
index 5563abf3..51085481 100644
--- a/tests/test_future/test_str.py
+++ b/tests/test_future/test_str.py
@@ -363,18 +363,24 @@ def test_eq(self):
             self.assertFalse(b'ABCD' == s)
         self.assertFalse(bytes(b'ABCD') == s)
 
+        # We want to ensure comparison against unknown types return
+        # NotImplemented so that the interpreter can rerun the test with the
+        # other class.  We expect the operator to return False if both return
+        # NotImplemented.
         class OurCustomString(object):
             def __init__(self, string):
                 self.string = string
 
-            def __str__(self):
-                return self.string
+            def __eq__(self, other):
+                return NotImplemented
 
         our_str = OurCustomString("foobar")
         new_str = str("foobar")
 
         self.assertFalse(our_str == new_str)
         self.assertFalse(new_str == our_str)
+        self.assertIs(new_str.__eq__(our_str), NotImplemented)
+        self.assertIs(our_str.__eq__(new_str), NotImplemented)
 
     def test_hash(self):
         s = str('ABCD')

From a13917aff638ea9be99b33a55bda78268b475d76 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Wed, 8 May 2019 13:06:41 -0400
Subject: [PATCH 043/183] Revert "allow mixed string type inputs to misc
 urllib.parse.* functions"

This reverts commit 62b4c35d995aa5503855ffcc5c8fb2262e02c2a2.
---
 src/future/backports/urllib/parse.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/future/backports/urllib/parse.py b/src/future/backports/urllib/parse.py
index 2c26846d..04e52d49 100644
--- a/src/future/backports/urllib/parse.py
+++ b/src/future/backports/urllib/parse.py
@@ -107,11 +107,11 @@ def _coerce_args(*args):
     # an appropriate result coercion function
     #   - noop for str inputs
     #   - encoding function otherwise
-    str_input = isinstance(args[0], basestring)
+    str_input = isinstance(args[0], str)
     for arg in args[1:]:
         # We special-case the empty string to support the
         # "scheme=''" default argument to some functions
-        if arg and isinstance(arg, basestring) != str_input:
+        if arg and isinstance(arg, str) != str_input:
             raise TypeError("Cannot mix str and non-str arguments")
     if str_input:
         return args + (_noop,)

From 80ccca6024ca96a39e02b357af2147798a448a6e Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Wed, 8 May 2019 14:07:03 -0400
Subject: [PATCH 044/183] Remove guidance on recursively selecting only python
 files from a directory as shells are not portable

---
 docs/futurize_cheatsheet.rst | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/docs/futurize_cheatsheet.rst b/docs/futurize_cheatsheet.rst
index 1fcca365..82f211c6 100644
--- a/docs/futurize_cheatsheet.rst
+++ b/docs/futurize_cheatsheet.rst
@@ -31,10 +31,10 @@ The goal for this step is to modernize the Python 2 code without introducing any
           pip install future
 
 **1b**. Run ``futurize --stage1 -w *.py subdir1/*.py subdir2/*.py``. Note that with
-recursive globbing in ``bash`` or ``zsh``, you can apply stage 1 to all Python
-source files recursively with::
+recursive globbing in ``bash`` or ``zsh``, you can apply stage 1 to all source files
+recursively with::
 
-        futurize --stage1 -w **/*.py
+        futurize --stage1 -w .
 
 **1c**. Commit all changes
 
@@ -79,10 +79,9 @@ again with the help of the ``future`` package.
 
         futurize --stage2 myfolder1/*.py myfolder2/*.py
 
-Or, using recursive globbing with ``bash`` or ``zsh``, you can view the stage 2
-changes to all Python source files recursively with::
+You can view the stage 2 changes to all Python source files recursively with::
 
-    futurize --stage2 **/*.py
+    futurize --stage2 .
 
 To apply the changes, add the ``-w`` argument.
 

From 41de8ee03f86460f0c3fa236a4c8106b7cca0cb0 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Wed, 8 May 2019 14:14:46 -0400
Subject: [PATCH 045/183] Fix changelog

---
 docs/whatsnew.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index 68790527..a2b6fc05 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -48,7 +48,7 @@ effect on your system.
 
 This releases also fixes these bugs:
 
-- Fix ``newbytes`` constructor bug. (Issue #163)
+- Fix ``newbytes`` constructor bug. (Issue #171)
 - Fix semantics of ``bool()`` with ``newobject``. (Issue #211)
 - Fix ``standard_library.install_aliases()`` on PyPy. (Issue #205)
 - Fix assertRaises for ``pow`` and ``compile``` on Python 3.5. (Issue #183)

From a949bf4cc64d1d10ead87c4fe32e5d4f492f2ffc Mon Sep 17 00:00:00 2001
From: Jeremy Paige <jeremiahcpaige@gmail.com>
Date: Wed, 8 May 2019 14:25:52 -0400
Subject: [PATCH 046/183] Add fixer for itertools

Use of ifilter, imap, and izip as generator forms of PY2 builtins are replaced
with future.builtins versions that are always generators
---
 docs/futurize.rst                      |  1 +
 src/future/builtins/__init__.py        |  6 +++
 src/libfuturize/fixes/__init__.py      |  1 +
 src/libfuturize/fixes/fix_itertools.py | 54 +++++++++++++++++++
 tests/test_future/test_futurize.py     | 75 ++++++++++++++++++++++++++
 5 files changed, 137 insertions(+)
 create mode 100644 src/libfuturize/fixes/fix_itertools.py

diff --git a/docs/futurize.rst b/docs/futurize.rst
index 11520a6c..f5e9fddc 100644
--- a/docs/futurize.rst
+++ b/docs/futurize.rst
@@ -103,6 +103,7 @@ The complete set of fixers applied by ``futurize --stage1`` is:
     lib2to3.fixes.fix_ws_comma
     lib2to3.fixes.fix_xreadlines
     libfuturize.fixes.fix_absolute_import
+    libfuturize.fixes.fix_itertools
     libfuturize.fixes.fix_next_call
     libfuturize.fixes.fix_print_with_import
     libfuturize.fixes.fix_raise
diff --git a/src/future/builtins/__init__.py b/src/future/builtins/__init__.py
index 8bc1649d..6244959b 100644
--- a/src/future/builtins/__init__.py
+++ b/src/future/builtins/__init__.py
@@ -18,11 +18,14 @@
     import builtins
     bytes = builtins.bytes
     dict = builtins.dict
+    filter = builtins.filter
     int = builtins.int
     list = builtins.list
+    map = builtins.map
     object = builtins.object
     range = builtins.range
     str = builtins.str
+    zip = builtins.zip
     __all__ = []
 else:
     from future.types import (newbytes as bytes,
@@ -32,6 +35,9 @@
                               newobject as object,
                               newrange as range,
                               newstr as str)
+    from itertools import (ifilter as filter,
+                           imap as map,
+                           izip as zip)
 from future import utils
 
 
diff --git a/src/libfuturize/fixes/__init__.py b/src/libfuturize/fixes/__init__.py
index 7de304da..24d37bcf 100644
--- a/src/libfuturize/fixes/__init__.py
+++ b/src/libfuturize/fixes/__init__.py
@@ -69,6 +69,7 @@
 
 libfuturize_fix_names_stage1 = set([
     'libfuturize.fixes.fix_absolute_import',
+    'libfuturize.fixes.fix_itertools',
     'libfuturize.fixes.fix_next_call',  # obj.next() -> next(obj). Unlike
                                         # lib2to3.fixes.fix_next, doesn't change
                                         # the ``next`` method to ``__next__``.
diff --git a/src/libfuturize/fixes/fix_itertools.py b/src/libfuturize/fixes/fix_itertools.py
new file mode 100644
index 00000000..f8234839
--- /dev/null
+++ b/src/libfuturize/fixes/fix_itertools.py
@@ -0,0 +1,54 @@
+"""
+For the ``future`` package.
+
+Fixer for itertools methods that no longer deviate from builtins.
+
+This applies to imap, izip, and ifilter
+
+Adds this import line:
+
+    from builtins import filter, map, zip
+
+at the top.
+"""
+
+from lib2to3 import fixer_base
+
+from libfuturize.fixer_util import touch_import_top
+
+filter_expression = "name='ifilter'"
+map_expression = "name='imap'"
+zip_expression = "name='izip'"
+
+class FixFilter(fixer_base.BaseFix):
+
+    PATTERN = """
+              power<
+                 ({0}) trailer< '(' args=[any] ')' >
+              rest=any* >
+              """.format(filter_expression)
+
+    def transform(self, node, results):
+        touch_import_top(u'builtins', 'filter', node)
+
+class FixMap(fixer_base.BaseFix):
+
+    PATTERN = """
+              power<
+                 ({0}) trailer< '(' args=[any] ')' >
+              rest=any* >
+              """.format(map_rexpression)
+
+    def transform(self, node, results):
+        touch_import_top(u'builtins', 'map', node)
+
+class FixZip(fixer_base.BaseFix):
+
+    PATTERN = """
+              power<
+                 ({0}) trailer< '(' args=[any] ')' >
+              rest=any* >
+              """.format(zip_expression)
+
+    def transform(self, node, results):
+        touch_import_top(u'builtins', 'zip', node)
diff --git a/tests/test_future/test_futurize.py b/tests/test_future/test_futurize.py
index f2201141..7170103b 100644
--- a/tests/test_future/test_futurize.py
+++ b/tests/test_future/test_futurize.py
@@ -453,6 +453,81 @@ def test_xrange(self):
         """
         self.convert_check(before, after, ignore_imports=False)
 
+    def test_filter(self):
+        """
+        Tests correct handling of itertools.ifilter
+        """
+        before = """
+        import itertools
+        itertools.ifilter(lambda x: x%2, [1, 2, 3, 4])
+        """
+        after = """
+        from builtins import filter
+        import itertools
+        filter(lambda x: x%2, [1, 2, 3, 4])
+        """
+        self.convert_check(before, after, ignore_imports=False)
+        before = """
+        from itertools import ifilter
+        ifilter(lambda x: x%2, [1, 2, 3, 4])
+        """
+        after = """
+        from builtins import filter
+
+        filter(lambda x: x%2, [1, 2, 3, 4])
+        """
+        self.convert_check(before, after, ignore_imports=False)
+
+    def test_map(self):
+        """
+        Tests correct handling of itertools.imap
+        """
+        before = """
+        import itertools
+        itertools.imap(pow, (2,3,10), (5,2,3))
+        """
+        after = """
+        from builtins import map
+        import itertools
+        map(pow, (2,3,10), (5,2,3))
+        """
+        self.convert_check(before, after, ignore_imports=False)
+        before = """
+        from itertools import imap
+        imap(pow, (2,3,10), (5,2,3))
+        """
+        after = """
+        from builtins import map
+
+        map(pow, (2,3,10), (5,2,3))
+        """
+        self.convert_check(before, after, ignore_imports=False)
+
+    def test_zip(self):
+        """
+        Tests correct handling of itertools.izip
+        """
+        before = """
+        import itertools
+        itertools.izip('ABCD', 'xy')
+        """
+        after = """
+        from builtins import zip
+        import itertools
+        zip('ABCD', 'xy')
+        """
+        self.convert_check(before, after, ignore_imports=False)
+        before = """
+        from itertools import izip
+        izip('ABCD', 'xy')
+        """
+        after = """
+        from builtins import zip
+
+        zip('ABCD', 'xy')
+        """
+        self.convert_check(before, after, ignore_imports=False)
+
     def test_source_coding_utf8(self):
         """
         Tests to ensure that the source coding line is not corrupted or

From 6a4064b9a106800cbfcfb2cf47fbcce282a53a5d Mon Sep 17 00:00:00 2001
From: Jeremy Paige <jeremiahcpaige@gmail.com>
Date: Wed, 8 May 2019 14:39:01 -0400
Subject: [PATCH 047/183] Fix typo in a949bf4

---
 src/libfuturize/fixes/fix_itertools.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libfuturize/fixes/fix_itertools.py b/src/libfuturize/fixes/fix_itertools.py
index f8234839..de746307 100644
--- a/src/libfuturize/fixes/fix_itertools.py
+++ b/src/libfuturize/fixes/fix_itertools.py
@@ -37,7 +37,7 @@ class FixMap(fixer_base.BaseFix):
               power<
                  ({0}) trailer< '(' args=[any] ')' >
               rest=any* >
-              """.format(map_rexpression)
+              """.format(map_expression)
 
     def transform(self, node, results):
         touch_import_top(u'builtins', 'map', node)

From ede6f096dae610781e673f1fd91dd8c19c1060b4 Mon Sep 17 00:00:00 2001
From: "Gregory P. Smith" <greg@krypto.org>
Date: Thu, 16 May 2019 13:13:26 -0700
Subject: [PATCH 048/183] Make future.moves.copyreg just _be_ copyreg on PY3

The existing `from copyreg import *` is insufficient on Python 3 as `copyreg.__all__` does not include all of the public API names.
---
 src/future/moves/copyreg.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/future/moves/copyreg.py b/src/future/moves/copyreg.py
index 21c7a42f..9d08cdc5 100644
--- a/src/future/moves/copyreg.py
+++ b/src/future/moves/copyreg.py
@@ -2,7 +2,11 @@
 from future.utils import PY3
 
 if PY3:
-    from copyreg import *
+    import copyreg, sys
+    # A "*" import uses Python 3's copyreg.__all__ which does not include
+    # all public names in the API surface for copyreg, this avoids that
+    # problem by just making our module _be_ a reference to the actual module.
+    sys.modules['future.moves.copyreg'] = copyreg
 else:
     __future_module__ = True
     from copy_reg import *

From e4b7fa1d3346ef2561fd424fd1cc51558c44f192 Mon Sep 17 00:00:00 2001
From: Sorin Sbarnea <ssbarnea@redhat.com>
Date: Wed, 3 Jul 2019 16:48:59 +0100
Subject: [PATCH 049/183] Avoid DeprecationWarning caused by invalid escape

Fixes: #479
---
 src/past/types/oldstr.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/past/types/oldstr.py b/src/past/types/oldstr.py
index a477d884..5a0e3789 100644
--- a/src/past/types/oldstr.py
+++ b/src/past/types/oldstr.py
@@ -20,7 +20,7 @@ def __instancecheck__(cls, instance):
 
 
 def unescape(s):
-    """
+    r"""
     Interprets strings with escape sequences
 
     Example:

From d42f0784860e2ac2acdde712799673032645ef7f Mon Sep 17 00:00:00 2001
From: "Jordan M. Adler" <jordan.m.adler@gmail.com>
Date: Mon, 8 Jul 2019 15:53:39 -0700
Subject: [PATCH 050/183] Revert "Add fixer for itertools"

---
 docs/futurize.rst                      |  1 -
 src/future/builtins/__init__.py        |  6 ---
 src/libfuturize/fixes/__init__.py      |  1 -
 src/libfuturize/fixes/fix_itertools.py | 54 -------------------
 tests/test_future/test_futurize.py     | 75 --------------------------
 5 files changed, 137 deletions(-)
 delete mode 100644 src/libfuturize/fixes/fix_itertools.py

diff --git a/docs/futurize.rst b/docs/futurize.rst
index f5e9fddc..11520a6c 100644
--- a/docs/futurize.rst
+++ b/docs/futurize.rst
@@ -103,7 +103,6 @@ The complete set of fixers applied by ``futurize --stage1`` is:
     lib2to3.fixes.fix_ws_comma
     lib2to3.fixes.fix_xreadlines
     libfuturize.fixes.fix_absolute_import
-    libfuturize.fixes.fix_itertools
     libfuturize.fixes.fix_next_call
     libfuturize.fixes.fix_print_with_import
     libfuturize.fixes.fix_raise
diff --git a/src/future/builtins/__init__.py b/src/future/builtins/__init__.py
index 6244959b..8bc1649d 100644
--- a/src/future/builtins/__init__.py
+++ b/src/future/builtins/__init__.py
@@ -18,14 +18,11 @@
     import builtins
     bytes = builtins.bytes
     dict = builtins.dict
-    filter = builtins.filter
     int = builtins.int
     list = builtins.list
-    map = builtins.map
     object = builtins.object
     range = builtins.range
     str = builtins.str
-    zip = builtins.zip
     __all__ = []
 else:
     from future.types import (newbytes as bytes,
@@ -35,9 +32,6 @@
                               newobject as object,
                               newrange as range,
                               newstr as str)
-    from itertools import (ifilter as filter,
-                           imap as map,
-                           izip as zip)
 from future import utils
 
 
diff --git a/src/libfuturize/fixes/__init__.py b/src/libfuturize/fixes/__init__.py
index 24d37bcf..7de304da 100644
--- a/src/libfuturize/fixes/__init__.py
+++ b/src/libfuturize/fixes/__init__.py
@@ -69,7 +69,6 @@
 
 libfuturize_fix_names_stage1 = set([
     'libfuturize.fixes.fix_absolute_import',
-    'libfuturize.fixes.fix_itertools',
     'libfuturize.fixes.fix_next_call',  # obj.next() -> next(obj). Unlike
                                         # lib2to3.fixes.fix_next, doesn't change
                                         # the ``next`` method to ``__next__``.
diff --git a/src/libfuturize/fixes/fix_itertools.py b/src/libfuturize/fixes/fix_itertools.py
deleted file mode 100644
index de746307..00000000
--- a/src/libfuturize/fixes/fix_itertools.py
+++ /dev/null
@@ -1,54 +0,0 @@
-"""
-For the ``future`` package.
-
-Fixer for itertools methods that no longer deviate from builtins.
-
-This applies to imap, izip, and ifilter
-
-Adds this import line:
-
-    from builtins import filter, map, zip
-
-at the top.
-"""
-
-from lib2to3 import fixer_base
-
-from libfuturize.fixer_util import touch_import_top
-
-filter_expression = "name='ifilter'"
-map_expression = "name='imap'"
-zip_expression = "name='izip'"
-
-class FixFilter(fixer_base.BaseFix):
-
-    PATTERN = """
-              power<
-                 ({0}) trailer< '(' args=[any] ')' >
-              rest=any* >
-              """.format(filter_expression)
-
-    def transform(self, node, results):
-        touch_import_top(u'builtins', 'filter', node)
-
-class FixMap(fixer_base.BaseFix):
-
-    PATTERN = """
-              power<
-                 ({0}) trailer< '(' args=[any] ')' >
-              rest=any* >
-              """.format(map_expression)
-
-    def transform(self, node, results):
-        touch_import_top(u'builtins', 'map', node)
-
-class FixZip(fixer_base.BaseFix):
-
-    PATTERN = """
-              power<
-                 ({0}) trailer< '(' args=[any] ')' >
-              rest=any* >
-              """.format(zip_expression)
-
-    def transform(self, node, results):
-        touch_import_top(u'builtins', 'zip', node)
diff --git a/tests/test_future/test_futurize.py b/tests/test_future/test_futurize.py
index 7170103b..f2201141 100644
--- a/tests/test_future/test_futurize.py
+++ b/tests/test_future/test_futurize.py
@@ -453,81 +453,6 @@ def test_xrange(self):
         """
         self.convert_check(before, after, ignore_imports=False)
 
-    def test_filter(self):
-        """
-        Tests correct handling of itertools.ifilter
-        """
-        before = """
-        import itertools
-        itertools.ifilter(lambda x: x%2, [1, 2, 3, 4])
-        """
-        after = """
-        from builtins import filter
-        import itertools
-        filter(lambda x: x%2, [1, 2, 3, 4])
-        """
-        self.convert_check(before, after, ignore_imports=False)
-        before = """
-        from itertools import ifilter
-        ifilter(lambda x: x%2, [1, 2, 3, 4])
-        """
-        after = """
-        from builtins import filter
-
-        filter(lambda x: x%2, [1, 2, 3, 4])
-        """
-        self.convert_check(before, after, ignore_imports=False)
-
-    def test_map(self):
-        """
-        Tests correct handling of itertools.imap
-        """
-        before = """
-        import itertools
-        itertools.imap(pow, (2,3,10), (5,2,3))
-        """
-        after = """
-        from builtins import map
-        import itertools
-        map(pow, (2,3,10), (5,2,3))
-        """
-        self.convert_check(before, after, ignore_imports=False)
-        before = """
-        from itertools import imap
-        imap(pow, (2,3,10), (5,2,3))
-        """
-        after = """
-        from builtins import map
-
-        map(pow, (2,3,10), (5,2,3))
-        """
-        self.convert_check(before, after, ignore_imports=False)
-
-    def test_zip(self):
-        """
-        Tests correct handling of itertools.izip
-        """
-        before = """
-        import itertools
-        itertools.izip('ABCD', 'xy')
-        """
-        after = """
-        from builtins import zip
-        import itertools
-        zip('ABCD', 'xy')
-        """
-        self.convert_check(before, after, ignore_imports=False)
-        before = """
-        from itertools import izip
-        izip('ABCD', 'xy')
-        """
-        after = """
-        from builtins import zip
-
-        zip('ABCD', 'xy')
-        """
-        self.convert_check(before, after, ignore_imports=False)
-
     def test_source_coding_utf8(self):
         """
         Tests to ensure that the source coding line is not corrupted or

From d711695da6e54fb9351d0f989ea5cd6dd5d3e4c4 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Mon, 8 Jul 2019 16:32:06 -0700
Subject: [PATCH 051/183] Remove flakiness in test_single_exception

---
 tests/test_future/test_utils.py | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/tests/test_future/test_utils.py b/tests/test_future/test_utils.py
index 488c0064..30eac7a6 100644
--- a/tests/test_future/test_utils.py
+++ b/tests/test_future/test_utils.py
@@ -4,7 +4,7 @@
 """
 
 from __future__ import absolute_import, unicode_literals, print_function
-import sys, traceback
+import re, sys, traceback
 from future.builtins import *
 from future.utils import (old_div, istext, isbytes, native, PY2, PY3,
                          native_str, raise_, as_native_str, ensure_new_type,
@@ -335,12 +335,14 @@ def test_single_exception_stacktrace(self):
         if PY2:
             expected += 'CustomException: ERROR\n'
         else:
-            expected += 'tests.test_future.test_utils.CustomException: ERROR\n'
+            expected += 'test_future.test_utils.CustomException: ERROR\n'
 
         try:
             raise CustomException('ERROR')
         except:
-            self.assertEqual(expected, traceback.format_exc())
+            ret = re.sub(r'"[^"]*tests/test_future', '"/opt/python-future/tests/test_future', traceback.format_exc())
+            ret = re.sub(r', line \d+,', ', line 328,', ret)
+            self.assertEqual(expected, ret)
         else:
             self.fail('No exception raised')
 

From 2b82a8818005755920e6978c5ac3055731e43997 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Mon, 8 Jul 2019 16:50:37 -0700
Subject: [PATCH 052/183] Remove flakiness in test_chained_exception

---
 tests/test_future/test_utils.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/test_future/test_utils.py b/tests/test_future/test_utils.py
index 30eac7a6..314d7698 100644
--- a/tests/test_future/test_utils.py
+++ b/tests/test_future/test_utils.py
@@ -368,7 +368,8 @@ def test_chained_exceptions_stacktrace(self):
                 except ValueError as val_err:
                     raise_from(CustomException('ERROR'), val_err)
             except Exception as err:
-                self.assertEqual(expected.splitlines(), traceback.format_exc().splitlines())
+                ret = re.sub(r'"[^"]*tests/test_future', '"/opt/python-future/tests/test_future', traceback.format_exc())
+                self.assertEqual(expected.splitlines(), ret.splitlines())
             else:
                 self.fail('No exception raised')
 

From f7c7ebd8dc7db9580927de6644271c784d204b0e Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Mon, 8 Jul 2019 16:54:09 -0700
Subject: [PATCH 053/183] iter

---
 tests/test_future/test_utils.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/tests/test_future/test_utils.py b/tests/test_future/test_utils.py
index 314d7698..afe14041 100644
--- a/tests/test_future/test_utils.py
+++ b/tests/test_future/test_utils.py
@@ -349,15 +349,15 @@ def test_single_exception_stacktrace(self):
     if PY2:
         def test_chained_exceptions_stacktrace(self):
             expected = '''Traceback (most recent call last):
-  File "/opt/python-future/tests/test_future/test_utils.py", line 354, in test_chained_exceptions_stacktrace
+  File "/opt/python-future/tests/test_future/test_utils.py", line 1, in test_chained_exceptions_stacktrace
     raise_from(CustomException('ERROR'), val_err)
-  File "/opt/python-future/src/future/utils/__init__.py", line 456, in raise_from
+  File "/opt/python-future/src/future/utils/__init__.py", line 1, in raise_from
     raise e
 CustomException: ERROR
 
 The above exception was the direct cause of the following exception:
 
-  File "/opt/python-future/tests/test_future/test_utils.py", line 352, in test_chained_exceptions_stacktrace
+  File "/opt/python-future/tests/test_future/test_utils.py", line 1, in test_chained_exceptions_stacktrace
     raise ValueError('Wooops')
 ValueError: Wooops
 '''
@@ -369,6 +369,7 @@ def test_chained_exceptions_stacktrace(self):
                     raise_from(CustomException('ERROR'), val_err)
             except Exception as err:
                 ret = re.sub(r'"[^"]*tests/test_future', '"/opt/python-future/tests/test_future', traceback.format_exc())
+                ret = re.sub(r', line \d+,', ', line 1,', ret)
                 self.assertEqual(expected.splitlines(), ret.splitlines())
             else:
                 self.fail('No exception raised')

From a0e400767763c7fca39546514f14621e625caa4d Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Mon, 8 Jul 2019 17:00:57 -0700
Subject: [PATCH 054/183] iter

---
 tests/test_future/test_utils.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/test_future/test_utils.py b/tests/test_future/test_utils.py
index afe14041..95ef4d67 100644
--- a/tests/test_future/test_utils.py
+++ b/tests/test_future/test_utils.py
@@ -369,6 +369,7 @@ def test_chained_exceptions_stacktrace(self):
                     raise_from(CustomException('ERROR'), val_err)
             except Exception as err:
                 ret = re.sub(r'"[^"]*tests/test_future', '"/opt/python-future/tests/test_future', traceback.format_exc())
+                ret = re.sub(r'"[^"]*src/future', '"/opt/python-future/src/future', traceback.format_exc())
                 ret = re.sub(r', line \d+,', ', line 1,', ret)
                 self.assertEqual(expected.splitlines(), ret.splitlines())
             else:

From 95550463a8fcb699096d9864e97a5e4d3cc43549 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Mon, 8 Jul 2019 17:04:24 -0700
Subject: [PATCH 055/183] bugfix

---
 tests/test_future/test_utils.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/test_future/test_utils.py b/tests/test_future/test_utils.py
index 95ef4d67..73de3076 100644
--- a/tests/test_future/test_utils.py
+++ b/tests/test_future/test_utils.py
@@ -369,7 +369,7 @@ def test_chained_exceptions_stacktrace(self):
                     raise_from(CustomException('ERROR'), val_err)
             except Exception as err:
                 ret = re.sub(r'"[^"]*tests/test_future', '"/opt/python-future/tests/test_future', traceback.format_exc())
-                ret = re.sub(r'"[^"]*src/future', '"/opt/python-future/src/future', traceback.format_exc())
+                ret = re.sub(r'"[^"]*src/future', '"/opt/python-future/src/future', ret)
                 ret = re.sub(r', line \d+,', ', line 1,', ret)
                 self.assertEqual(expected.splitlines(), ret.splitlines())
             else:

From 015e837df95580e164fb8595236aea382b279c37 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Mon, 8 Jul 2019 17:10:21 -0700
Subject: [PATCH 056/183] bugfix

---
 tests/test_future/test_utils.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/test_future/test_utils.py b/tests/test_future/test_utils.py
index 73de3076..9034400f 100644
--- a/tests/test_future/test_utils.py
+++ b/tests/test_future/test_utils.py
@@ -369,7 +369,7 @@ def test_chained_exceptions_stacktrace(self):
                     raise_from(CustomException('ERROR'), val_err)
             except Exception as err:
                 ret = re.sub(r'"[^"]*tests/test_future', '"/opt/python-future/tests/test_future', traceback.format_exc())
-                ret = re.sub(r'"[^"]*src/future', '"/opt/python-future/src/future', ret)
+                ret = re.sub(r'"[^"]*future/utils/__init__.py', '"/opt/python-future/src/future/utils/__init__.py', ret)
                 ret = re.sub(r', line \d+,', ', line 1,', ret)
                 self.assertEqual(expected.splitlines(), ret.splitlines())
             else:

From d2a61c10550423abad7e88701b1e9ef210f59400 Mon Sep 17 00:00:00 2001
From: Chad Dombrova <chadrik@gmail.com>
Date: Mon, 22 Jul 2019 12:47:42 -0700
Subject: [PATCH 057/183] Don't import past.translation by default when
 importing past

---
 README.rst                       |  4 ++--
 docs/changelog.rst               | 10 +++++-----
 docs/quickstart.rst              |  2 +-
 docs/translation.rst             |  4 ++--
 src/past/__init__.py             |  4 +---
 src/past/translation/__init__.py |  6 +++++-
 6 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/README.rst b/README.rst
index 807ea2a0..6266618c 100644
--- a/README.rst
+++ b/README.rst
@@ -243,7 +243,7 @@ Example:
 
     $ python3
 
-    >>> from past import autotranslate
+    >>> from past.translation import autotranslate
     >>> autotranslate(['plotrique'])
     >>> import plotrique
 
@@ -270,7 +270,7 @@ Licensing
 
 :Sponsors: Python Charmers Pty Ltd, Australia, and Python Charmers Pte
            Ltd, Singapore. http://pythoncharmers.com
-           
+
            Pinterest https://opensource.pinterest.com/
 
 :Licence: MIT. See ``LICENSE.txt`` or `here <http://python-future.org/credits.html>`_.
diff --git a/docs/changelog.rst b/docs/changelog.rst
index aa317b96..059ad4f5 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -616,10 +616,10 @@ it like this::
     $ pip3 install plotrique==0.2.5-7 --no-compile   # to ignore SyntaxErrors
     $ python3
 
-Then pass in a whitelist of module name prefixes to the ``past.autotranslate()``
-function. Example::
+Then pass in a whitelist of module name prefixes to the
+``past.translation.autotranslate()`` function. Example::
 
-    >>> from past import autotranslate
+    >>> from past.translation import autotranslate
     >>> autotranslate(['plotrique'])
     >>> import plotrique
 
@@ -949,8 +949,8 @@ v0.11.3:
     objects as on Py3.
 
 v0.11.2:
-  * The ``past.autotranslate`` feature now finds modules to import more
-    robustly and works with Python eggs.
+  * The ``past.translation.autotranslate`` feature now finds modules to import
+    more robustly and works with Python eggs.
 
 v0.11.1:
   * Update to ``requirements_py26.txt`` for Python 2.6. Small updates to
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index a5e464f9..6042e059 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -132,7 +132,7 @@ environment::
 Then add the following code at the top of your (Py3 or Py2/3-compatible)
 code::
 
-    from past import autotranslate
+    from past.translation import autotranslate
     autotranslate(['mypackagename'])
     import mypackagename
 
diff --git a/docs/translation.rst b/docs/translation.rst
index 49f558b0..632c46b1 100644
--- a/docs/translation.rst
+++ b/docs/translation.rst
@@ -21,9 +21,9 @@ Here is how to use it::
     $ python3
 
 Then pass in a whitelist of module name prefixes to the
-``past.autotranslate()`` function. Example::
+``past.translation.autotranslate()`` function. Example::
 
-    >>> from past import autotranslate
+    >>> from past.translation import autotranslate
     >>> autotranslate(['plotrique'])
     >>> import plotrique
 
diff --git a/src/past/__init__.py b/src/past/__init__.py
index 3b5d9db1..07422a03 100644
--- a/src/past/__init__.py
+++ b/src/past/__init__.py
@@ -61,7 +61,7 @@
 
     $ python3
 
-    >>> from past import autotranslate
+    >>> from past.translation import autotranslate
     >>> authotranslate('mypy2module')
     >>> import mypy2module
 
@@ -84,8 +84,6 @@
 The software is distributed under an MIT licence. See LICENSE.txt.
 """
 
-
-from past.translation import install_hooks as autotranslate
 from future import __version__, __copyright__, __license__
 
 __title__ = 'past'
diff --git a/src/past/translation/__init__.py b/src/past/translation/__init__.py
index 1b941662..7c678866 100644
--- a/src/past/translation/__init__.py
+++ b/src/past/translation/__init__.py
@@ -16,7 +16,7 @@
 Once your Py2 package is installed in the usual module search path, the import
 hook is invoked as follows:
 
-    >>> from past import autotranslate
+    >>> from past.translation import autotranslate
     >>> autotranslate('mypackagename')
 
 Or:
@@ -479,3 +479,7 @@ def __enter__(self):
     def __exit__(self, *args):
         if self.hooks_were_installed:
             install_hooks()
+
+
+# alias
+autotranslate = install_hooks

From 6ff061e57f772c19f7afd04dce5f15212609352f Mon Sep 17 00:00:00 2001
From: Christian Clauss <cclauss@me.com>
Date: Sat, 3 Aug 2019 14:15:25 +0200
Subject: [PATCH 058/183] Use print() function in both Python 2 and Python 3

__futurize --stage1 -w docs/other/find_pattern.py__
---
 docs/other/find_pattern.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/docs/other/find_pattern.py b/docs/other/find_pattern.py
index 679a1d64..1a5da35e 100644
--- a/docs/other/find_pattern.py
+++ b/docs/other/find_pattern.py
@@ -38,6 +38,7 @@
 Larger snippets can be placed in a file (as opposed to a command-line
 arg) and processed with the -f option.
 """
+from __future__ import print_function
 
 __author__ = "Collin Winter <collinw@gmail.com>"
 
@@ -65,7 +66,7 @@ def main(args):
     elif len(args) > 1:
         tree = driver.parse_stream(StringIO(args[1] + "\n"))
     else:
-        print >>sys.stderr, "You must specify an input file or an input string"
+        print("You must specify an input file or an input string", file=sys.stderr)
         return 1
 
     examine_tree(tree)
@@ -75,10 +76,10 @@ def examine_tree(tree):
     for node in tree.post_order():
         if isinstance(node, pytree.Leaf):
             continue
-        print repr(str(node))
+        print(repr(str(node)))
         verdict = raw_input()
         if verdict.strip():
-            print find_pattern(node)
+            print(find_pattern(node))
             return
 
 def find_pattern(node):

From e2c320bf627c501656a8c2dc3acf51be1f716faf Mon Sep 17 00:00:00 2001
From: Tobias Megies <megies@geophysik.uni-muenchen.de>
Date: Fri, 13 Sep 2019 15:14:20 +0200
Subject: [PATCH 059/183] fix collections abc import for py38

---
 src/future/backports/http/client.py | 8 ++++++--
 src/future/types/newbytes.py        | 8 ++++++--
 src/future/types/newint.py          | 6 ++++--
 src/future/types/newmemoryview.py   | 8 +++++---
 src/future/types/newrange.py        | 7 ++++++-
 src/future/types/newstr.py          | 4 +++-
 6 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/src/future/backports/http/client.py b/src/future/backports/http/client.py
index 1a43f013..e663d125 100644
--- a/src/future/backports/http/client.py
+++ b/src/future/backports/http/client.py
@@ -79,11 +79,15 @@
 import io
 import os
 import socket
-import collections
 from future.backports.urllib.parse import urlsplit
 import warnings
 from array import array
 
+if PY2:
+    from collections import Iterable
+else:
+    from collections.abc import Iterable
+
 __all__ = ["HTTPResponse", "HTTPConnection",
            "HTTPException", "NotConnected", "UnknownProtocol",
            "UnknownTransferEncoding", "UnimplementedFileMode",
@@ -902,7 +906,7 @@ def send(self, data):
         try:
             self.sock.sendall(data)
         except TypeError:
-            if isinstance(data, collections.Iterable):
+            if isinstance(data, Iterable):
                 for d in data:
                     self.sock.sendall(d)
             else:
diff --git a/src/future/types/newbytes.py b/src/future/types/newbytes.py
index 2a337c86..c9d584a7 100644
--- a/src/future/types/newbytes.py
+++ b/src/future/types/newbytes.py
@@ -5,15 +5,19 @@
 different beast to the Python 3 bytes object.
 """
 
-from collections import Iterable
 from numbers import Integral
 import string
 import copy
 
-from future.utils import istext, isbytes, PY3, with_metaclass
+from future.utils import istext, isbytes, PY2, PY3, with_metaclass
 from future.types import no, issubset
 from future.types.newobject import newobject
 
+if PY2:
+    from collections import Iterable
+else:
+    from collections.abc import Iterable
+
 
 _builtin_bytes = bytes
 
diff --git a/src/future/types/newint.py b/src/future/types/newint.py
index 705b8fa9..748dba9d 100644
--- a/src/future/types/newint.py
+++ b/src/future/types/newint.py
@@ -8,7 +8,6 @@
 from __future__ import division
 
 import struct
-import collections
 
 from future.types.newbytes import newbytes
 from future.types.newobject import newobject
@@ -17,6 +16,9 @@
 
 if PY3:
     long = int
+    from collections.abc import Iterable
+else:
+    from collections import Iterable
 
 
 class BaseNewInt(type):
@@ -356,7 +358,7 @@ def from_bytes(cls, mybytes, byteorder='big', signed=False):
             raise TypeError("cannot convert unicode objects to bytes")
         # mybytes can also be passed as a sequence of integers on Py3.
         # Test for this:
-        elif isinstance(mybytes, collections.Iterable):
+        elif isinstance(mybytes, Iterable):
             mybytes = newbytes(mybytes)
         b = mybytes if byteorder == 'big' else mybytes[::-1]
         if len(b) == 0:
diff --git a/src/future/types/newmemoryview.py b/src/future/types/newmemoryview.py
index 72c6990a..09f804dc 100644
--- a/src/future/types/newmemoryview.py
+++ b/src/future/types/newmemoryview.py
@@ -1,14 +1,16 @@
 """
 A pretty lame implementation of a memoryview object for Python 2.6.
 """
-
-from collections import Iterable
 from numbers import Integral
 import string
 
-from future.utils import istext, isbytes, PY3, with_metaclass
+from future.utils import istext, isbytes, PY2, with_metaclass
 from future.types import no, issubset
 
+if PY2:
+    from collections import Iterable
+else:
+    from collections.abc import Iterable
 
 # class BaseNewBytes(type):
 #     def __instancecheck__(cls, instance):
diff --git a/src/future/types/newrange.py b/src/future/types/newrange.py
index 9173b050..eda01a5a 100644
--- a/src/future/types/newrange.py
+++ b/src/future/types/newrange.py
@@ -19,7 +19,12 @@
 """
 from __future__ import absolute_import
 
-from collections import Sequence, Iterator
+from future.utils import PY2
+
+if PY2:
+    from collections import Sequence, Iterator
+else:
+    from collections.abc import Sequence, Iterator
 from itertools import islice
 
 from future.backports.misc import count   # with step parameter on Py2.6
diff --git a/src/future/types/newstr.py b/src/future/types/newstr.py
index d41ea969..8ca191f9 100644
--- a/src/future/types/newstr.py
+++ b/src/future/types/newstr.py
@@ -40,7 +40,6 @@
 
 """
 
-from collections import Iterable
 from numbers import Number
 
 from future.utils import PY3, istext, with_metaclass, isnewbytes
@@ -51,6 +50,9 @@
 if PY3:
     # We'll probably never use newstr on Py3 anyway...
     unicode = str
+    from collections.abc import Iterable
+else:
+    from collections import Iterable
 
 
 class BaseNewStr(type):

From 52b434d2cc0bfa01185524a82198558ee4272e5b Mon Sep 17 00:00:00 2001
From: Anika Mukherji <amukherji@pinterest.com>
Date: Thu, 26 Sep 2019 18:35:59 -0700
Subject: [PATCH 060/183] remove import for isnewbytes

---
 src/future/utils/__init__.py | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index 59920077..34e08fcc 100644
--- a/src/future/utils/__init__.py
+++ b/src/future/utils/__init__.py
@@ -577,15 +577,13 @@ def isbytes(obj):
 
 def isnewbytes(obj):
     """
-    Equivalent to the result of ``isinstance(obj, newbytes)`` were
-    ``__instancecheck__`` not overridden on the newbytes subclass. In
-    other words, it is REALLY a newbytes instance, not a Py2 native str
+    Equivalent to the result of ``type(obj)  == type(newbytes)``
+    in other words, it is REALLY a newbytes instance, not a Py2 native str
     object?
+    Note that this does not cover subclasses of newbytes, and it is not
+    equivalent to ininstance(obj, newbytes)
     """
-    # TODO: generalize this so that it works with subclasses of newbytes
-    # Import is here to avoid circular imports:
-    from future.types.newbytes import newbytes
-    return type(obj) == newbytes
+    return type(obj).__name__ == 'newbytes'
 
 
 def isint(obj):

From 934cebb76e861d4ebab1b97957f5f9522a2ea4c3 Mon Sep 17 00:00:00 2001
From: Anika Mukherji <amukherji@pinterest.com>
Date: Fri, 27 Sep 2019 08:44:05 -0700
Subject: [PATCH 061/183] edit yml file per
 https://travis-ci.community/t/python-builds-started-failing-to-download-2-7-12/3516

---
 .travis.yml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.travis.yml b/.travis.yml
index eb28e459..fc2e14ea 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,6 +2,9 @@ sudo: false
 language: python
 cache: pip
 
+dist:
+  - trusty
+
 matrix:
   include:
     - python: 2.6

From 08dec19511b7fdae5d24e7ff8bfbeba0fccdbd18 Mon Sep 17 00:00:00 2001
From: Anika Mukherji <amukherji@pinterest.com>
Date: Fri, 27 Sep 2019 09:03:50 -0700
Subject: [PATCH 062/183] minor change to run travis again

---
 src/future/utils/__init__.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index 34e08fcc..4b2f1f66 100644
--- a/src/future/utils/__init__.py
+++ b/src/future/utils/__init__.py
@@ -580,6 +580,7 @@ def isnewbytes(obj):
     Equivalent to the result of ``type(obj)  == type(newbytes)``
     in other words, it is REALLY a newbytes instance, not a Py2 native str
     object?
+
     Note that this does not cover subclasses of newbytes, and it is not
     equivalent to ininstance(obj, newbytes)
     """

From 95ed95e067795d0b236b04cc94cd95458c5d4a2a Mon Sep 17 00:00:00 2001
From: Anika Mukherji <amukherji@pinterest.com>
Date: Fri, 27 Sep 2019 09:15:47 -0700
Subject: [PATCH 063/183] add check for output attr

---
 src/future/tests/base.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/future/tests/base.py b/src/future/tests/base.py
index a7cc8ec1..958067ac 100644
--- a/src/future/tests/base.py
+++ b/src/future/tests/base.py
@@ -342,6 +342,9 @@ def _futurize_test_script(self, filename='mytestscript.py', stages=(1, 2),
                         '----\n%s\n----' % f.read(),
                     )
             ErrorClass = (FuturizeError if 'futurize' in script else PasteurizeError)
+            if not hasattr(e, 'output'):
+                # The attribute CalledProcessError.output doesn't exist on Py2.6
+                e.output = None
             raise ErrorClass(msg, e.returncode, e.cmd, output=e.output)
         return output
 

From 2c84ded5494ce22f4de15966f9a840d94945047e Mon Sep 17 00:00:00 2001
From: Anika Mukherji <amukherji@pinterest.com>
Date: Fri, 27 Sep 2019 11:51:15 -0700
Subject: [PATCH 064/183] drop py 2.6

---
 .travis.yml | 6 ++----
 setup.py    | 3 +--
 tox.ini     | 3 +--
 3 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index fc2e14ea..a6d7113f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,17 +2,15 @@ sudo: false
 language: python
 cache: pip
 
-dist:
-  - trusty
 
 matrix:
   include:
-    - python: 2.6
-      env: TOXENV=py26
     - python: 2.7
       env: TOXENV=py27
     - python: 3.3
       env: TOXENV=py33
+      dist:
+        - trusty
     - python: 3.4
       env: TOXENV=py34
     - python: 3.5
diff --git a/setup.py b/setup.py
index 11d694c2..ad137db6 100755
--- a/setup.py
+++ b/setup.py
@@ -95,7 +95,6 @@
 CLASSIFIERS = [
     "Programming Language :: Python",
     "Programming Language :: Python :: 2",
-    "Programming Language :: Python :: 2.6",
     "Programming Language :: Python :: 2.7",
     "Programming Language :: Python :: 3",
     "Programming Language :: Python :: 3.3",
@@ -175,7 +174,7 @@
       packages=PACKAGES,
       package_data=PACKAGE_DATA,
       include_package_data=True,
-      python_requires=">=2.6, !=3.0.*, !=3.1.*, !=3.2.*",
+      python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*",
       classifiers=CLASSIFIERS,
       **setup_kwds
      )
diff --git a/tox.ini b/tox.ini
index f5c013f8..1e4cb150 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,9 +1,8 @@
 [tox]
-envlist = py26,py27,py33,py34,py35,py36,py37
+envlist = py27,py33,py34,py35,py36,py37
 
 [testenv]
 deps =
     pytest
     unittest2
-    py26: importlib
 commands = pytest {posargs}

From 94a86f9ec543087ec96c11ac4f3c60eafd9e85cc Mon Sep 17 00:00:00 2001
From: Anika Mukherji <amukherji@pinterest.com>
Date: Fri, 27 Sep 2019 12:14:18 -0700
Subject: [PATCH 065/183] inline

---
 .travis.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index a6d7113f..ed4618e3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,8 +9,8 @@ matrix:
       env: TOXENV=py27
     - python: 3.3
       env: TOXENV=py33
-      dist:
-        - trusty
+      dist: trusty
+      sudo: false
     - python: 3.4
       env: TOXENV=py34
     - python: 3.5

From 730e6b7caf73773609a3913f5fc4a26a5456a405 Mon Sep 17 00:00:00 2001
From: Anika Mukherji <amukherji@pinterest.com>
Date: Fri, 27 Sep 2019 12:19:40 -0700
Subject: [PATCH 066/183] removing hasattr checks since removing py2.6

---
 src/future/tests/base.py | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/src/future/tests/base.py b/src/future/tests/base.py
index 958067ac..f0fae871 100644
--- a/src/future/tests/base.py
+++ b/src/future/tests/base.py
@@ -342,9 +342,6 @@ def _futurize_test_script(self, filename='mytestscript.py', stages=(1, 2),
                         '----\n%s\n----' % f.read(),
                     )
             ErrorClass = (FuturizeError if 'futurize' in script else PasteurizeError)
-            if not hasattr(e, 'output'):
-                # The attribute CalledProcessError.output doesn't exist on Py2.6
-                e.output = None
             raise ErrorClass(msg, e.returncode, e.cmd, output=e.output)
         return output
 
@@ -368,9 +365,6 @@ def _run_test_script(self, filename='mytestscript.py',
                         fn,
                         '----\n%s\n----' % f.read(),
                     )
-            if not hasattr(e, 'output'):
-                # The attribute CalledProcessError.output doesn't exist on Py2.6
-                e.output = None
             raise VerboseCalledProcessError(msg, e.returncode, e.cmd, output=e.output)
         return output
 

From 7ba867f670840898f1027f7af38d1a89331e6db7 Mon Sep 17 00:00:00 2001
From: Anika Mukherji <amukherji@pinterest.com>
Date: Mon, 30 Sep 2019 11:48:51 -0700
Subject: [PATCH 067/183] add back in py2.6

---
 .travis.yml              | 3 +++
 setup.py                 | 2 +-
 src/future/tests/base.py | 7 +++++++
 tox.ini                  | 3 ++-
 4 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index ed4618e3..4b74e8d2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,6 +5,9 @@ cache: pip
 
 matrix:
   include:
+    - python: 2.6
+      env: TOXENV=py26
+      dist: trusty
     - python: 2.7
       env: TOXENV=py27
     - python: 3.3
diff --git a/setup.py b/setup.py
index ad137db6..7a91c57a 100755
--- a/setup.py
+++ b/setup.py
@@ -174,7 +174,7 @@
       packages=PACKAGES,
       package_data=PACKAGE_DATA,
       include_package_data=True,
-      python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*",
+      python_requires=">=2.6, !=3.0.*, !=3.1.*, !=3.2.*",
       classifiers=CLASSIFIERS,
       **setup_kwds
      )
diff --git a/src/future/tests/base.py b/src/future/tests/base.py
index f0fae871..4ef437ba 100644
--- a/src/future/tests/base.py
+++ b/src/future/tests/base.py
@@ -342,6 +342,10 @@ def _futurize_test_script(self, filename='mytestscript.py', stages=(1, 2),
                         '----\n%s\n----' % f.read(),
                     )
             ErrorClass = (FuturizeError if 'futurize' in script else PasteurizeError)
+
+            if not hasattr(e, 'output'):
+                # The attribute CalledProcessError.output doesn't exist on Py2.6
+                e.output = None
             raise ErrorClass(msg, e.returncode, e.cmd, output=e.output)
         return output
 
@@ -365,6 +369,9 @@ def _run_test_script(self, filename='mytestscript.py',
                         fn,
                         '----\n%s\n----' % f.read(),
                     )
+            if not hasattr(e, 'output'):
+                # The attribute CalledProcessError.output doesn't exist on Py2.6
+                e.output = None
             raise VerboseCalledProcessError(msg, e.returncode, e.cmd, output=e.output)
         return output
 
diff --git a/tox.ini b/tox.ini
index 1e4cb150..f5c013f8 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,8 +1,9 @@
 [tox]
-envlist = py27,py33,py34,py35,py36,py37
+envlist = py26,py27,py33,py34,py35,py36,py37
 
 [testenv]
 deps =
     pytest
     unittest2
+    py26: importlib
 commands = pytest {posargs}

From a27243bda70583e1cbc9e06c6d3bf07a26da0684 Mon Sep 17 00:00:00 2001
From: Anika Mukherji <amukherji@pinterest.com>
Date: Wed, 2 Oct 2019 18:25:19 -0700
Subject: [PATCH 068/183] add py2.6 classifier

---
 setup.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/setup.py b/setup.py
index 7a91c57a..11d694c2 100755
--- a/setup.py
+++ b/setup.py
@@ -95,6 +95,7 @@
 CLASSIFIERS = [
     "Programming Language :: Python",
     "Programming Language :: Python :: 2",
+    "Programming Language :: Python :: 2.6",
     "Programming Language :: Python :: 2.7",
     "Programming Language :: Python :: 3",
     "Programming Language :: Python :: 3.3",

From 600344df9288810bf5092336e17abfc80d2e6665 Mon Sep 17 00:00:00 2001
From: Anika Mukherji <amukherji@pinterest.com>
Date: Wed, 2 Oct 2019 23:27:07 -0700
Subject: [PATCH 069/183] attempt to fix another py2.6 test

---
 tests/test_future/test_utils.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/test_future/test_utils.py b/tests/test_future/test_utils.py
index 9034400f..ed2f6da8 100644
--- a/tests/test_future/test_utils.py
+++ b/tests/test_future/test_utils.py
@@ -382,7 +382,7 @@ def __str__(self):
             out = Exception.__str__(self)
             if hasattr(self, '__cause__') and self.__cause__ and hasattr(self.__cause__, '__traceback__') and self.__cause__.__traceback__:
                 out += '\n\nThe above exception was the direct cause of the following exception:\n\n'
-                out += ''.join(traceback.format_tb(self.__cause__.__traceback__) + ['{}: {}'.format(self.__cause__.__class__.__name__, self.__cause__)])
+                out += ''.join(traceback.format_tb(self.__cause__.__traceback__) + ['{}: {}'.format(self.__cause__.__class__.__name__, self.__cause__.message)])
             return out
     else:
         pass

From 8cb18050eae94ba5a4abeace32ed0f7533cc1c84 Mon Sep 17 00:00:00 2001
From: Anika Mukherji <amukherji@pinterest.com>
Date: Thu, 3 Oct 2019 09:16:46 -0700
Subject: [PATCH 070/183] wrap str method in try/except to see why its fialing

---
 tests/test_future/test_utils.py | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/tests/test_future/test_utils.py b/tests/test_future/test_utils.py
index ed2f6da8..a115593b 100644
--- a/tests/test_future/test_utils.py
+++ b/tests/test_future/test_utils.py
@@ -379,11 +379,14 @@ def test_chained_exceptions_stacktrace(self):
 class CustomException(Exception):
     if PY2:
         def __str__(self):
-            out = Exception.__str__(self)
-            if hasattr(self, '__cause__') and self.__cause__ and hasattr(self.__cause__, '__traceback__') and self.__cause__.__traceback__:
-                out += '\n\nThe above exception was the direct cause of the following exception:\n\n'
-                out += ''.join(traceback.format_tb(self.__cause__.__traceback__) + ['{}: {}'.format(self.__cause__.__class__.__name__, self.__cause__.message)])
-            return out
+            try:
+                out = Exception.__str__(self)
+                if hasattr(self, '__cause__') and self.__cause__ and hasattr(self.__cause__, '__traceback__') and self.__cause__.__traceback__:
+                    out += '\n\nThe above exception was the direct cause of the following exception:\n\n'
+                    out += ''.join(traceback.format_tb(self.__cause__.__traceback__) + ['{}: {}'.format(self.__cause__.__class__.__name__, self.__cause__)])
+                return out
+            except Exception as e:
+                print(e)
     else:
         pass
 

From 5cfebd1c0e1a326c96c94d1b7e19454ef8769db6 Mon Sep 17 00:00:00 2001
From: Anika Mukherji <amukherji@pinterest.com>
Date: Thu, 3 Oct 2019 09:30:17 -0700
Subject: [PATCH 071/183] add indices

---
 tests/test_future/test_utils.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/test_future/test_utils.py b/tests/test_future/test_utils.py
index a115593b..b6a27d98 100644
--- a/tests/test_future/test_utils.py
+++ b/tests/test_future/test_utils.py
@@ -383,7 +383,7 @@ def __str__(self):
                 out = Exception.__str__(self)
                 if hasattr(self, '__cause__') and self.__cause__ and hasattr(self.__cause__, '__traceback__') and self.__cause__.__traceback__:
                     out += '\n\nThe above exception was the direct cause of the following exception:\n\n'
-                    out += ''.join(traceback.format_tb(self.__cause__.__traceback__) + ['{}: {}'.format(self.__cause__.__class__.__name__, self.__cause__)])
+                    out += ''.join(traceback.format_tb(self.__cause__.__traceback__) + ['{0}: {1}'.format(self.__cause__.__class__.__name__, self.__cause__)])
                 return out
             except Exception as e:
                 print(e)

From 4aaf34d92e4ac9e3b1828a84a78aaaa3f607ba6b Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Wed, 9 Oct 2019 17:04:52 -0700
Subject: [PATCH 072/183] Prepare 0.18.0 release

---
 docs/compatible_idioms.rst                    |  2 +-
 docs/conf.py                                  |  2 +-
 docs/credits.rst                              | 24 ++++++++++++++++-
 .../Writing Python 2-3 compatible code.ipynb  |  2 +-
 docs/whatsnew.rst                             | 27 ++++++++++++++++++-
 src/future/__init__.py                        |  6 ++---
 6 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/docs/compatible_idioms.rst b/docs/compatible_idioms.rst
index 9ba94280..b0cb05a3 100644
--- a/docs/compatible_idioms.rst
+++ b/docs/compatible_idioms.rst
@@ -3,7 +3,7 @@
 Cheat Sheet: Writing Python 2-3 compatible code
 ===============================================
 
--  **Copyright (c):** 2013-2018 Python Charmers Pty Ltd, Australia.
+-  **Copyright (c):** 2013-2019 Python Charmers Pty Ltd, Australia.
 -  **Author:** Ed Schofield.
 -  **Licence:** Creative Commons Attribution.
 
diff --git a/docs/conf.py b/docs/conf.py
index 72911405..fd106fa0 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -51,7 +51,7 @@
 
 # General information about the project.
 project = u'Python-Future'
-copyright = u'2013-2018, Python Charmers Pty Ltd, Australia'
+copyright = u'2013-2019, Python Charmers Pty Ltd, Australia'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
diff --git a/docs/credits.rst b/docs/credits.rst
index 23e00f3b..e66136d5 100644
--- a/docs/credits.rst
+++ b/docs/credits.rst
@@ -8,7 +8,7 @@ Licence
 The software is distributed under an MIT licence. The text is as follows
 (from ``LICENSE.txt``)::
 
-    Copyright (c) 2013-2018 Python Charmers Pty Ltd, Australia
+    Copyright (c) 2013-2019 Python Charmers Pty Ltd, Australia
 
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
@@ -47,16 +47,25 @@ Authors
 -------
 Python-Future is largely written by Ed Schofield <ed@pythoncharmers.com> with the help of various contributors:
 
+- Jordan Adler
+- Kyle Altendorf
 - Grant Bakker
 - Jacob Beck
+- Fumihiro (Ben) Bessho
+- Shiva Bhusal
 - Nate Bogdanowicz
+- Tomer Chachamu
 - Christian Clauss
 - Denis Cornehl
 - Nicolas Delaby
+- Chad Dombrova
 - Jon Dufresne
 - Corey Farwell
 - Eric Firing
+- Joe Gordon
 - Maximilian Hils
+- Miro Hrončok
+- Mark Huang
 - Martijn Jacobs
 - Michael Joseph
 - Waldemar Kornewald
@@ -66,23 +75,36 @@ Python-Future is largely written by Ed Schofield <ed@pythoncharmers.com> with th
 - Joshua Landau
 - German Larrain
 - Chris Lasher
+- ghanshyam lele
 - Calum Lind
+- Tobias Megies
+- Anika Mukherji
 - Jon Parise
 - Matthew Parnell
 - Miga Purg
 - Éloi Rivard
+- Sesh Sadasivam
 - Elliott Sales de Andrade
+- Aiden Scandella
 - Yury Selivanov
 - Tim Shaffer
+- Sameera Somisetty
 - Louis Sautier
+- Gregory P. Smith
 - Daniel Szoska
 - Flaviu Tamas
 - Jeff Tratner
 - Tim Tröndle
 - Brad Walker
+- Andrew Wason
+- Jeff Widman
+- Dan Yeaw
+- Hackalog (GitHub user)
 - lsm (GiHub user)
 - Mystic-Mirage (GitHub user)
 - str4d (GitHub user)
+- ucodery (GitHub user)
+- urain39 (GitHub user)
 - 9seconds (GitHub user)
 - Varriount (GitHub user)
 
diff --git a/docs/notebooks/Writing Python 2-3 compatible code.ipynb b/docs/notebooks/Writing Python 2-3 compatible code.ipynb
index 40859c4e..0f585d29 100644
--- a/docs/notebooks/Writing Python 2-3 compatible code.ipynb	
+++ b/docs/notebooks/Writing Python 2-3 compatible code.ipynb	
@@ -11,7 +11,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "- **Copyright (c):** 2013-2018 Python Charmers Pty Ltd, Australia.\n",
+    "- **Copyright (c):** 2013-2019 Python Charmers Pty Ltd, Australia.\n",
     "- **Author:** Ed Schofield.\n",
     "- **Licence:** Creative Commons Attribution.\n",
     "\n",
diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index a2b6fc05..50b2a99c 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -3,7 +3,32 @@
 What's New
 **********
 
-What's new in version 0.17.1 (2019-10-30)
+What's new in version 0.18.0 (2019-10-09)
+=========================================
+This is a major bug-fix and feature release, including:
+
+- Fix collections.abc import for py38+
+- Remove import for isnewbytes() function, reducing CPU cost significantly
+- Fix bug with importing past.translation when importing past which breaks zipped python installations
+- Fix an issue with copyreg import under Py3 that results in unexposed stdlib functionality
+- Export and document types in future.utils
+- Update behavior of newstr.__eq__() to match str.__eq__() as per reference docs
+- Fix raising and the raising fixer to handle cases where the syntax is ambigious
+- Allow "default" parameter in min() and max() (Issue #334)
+- Implement __hash__() in newstr (Issue #454)
+- Future proof some version checks to handle the fact that Py4 won't be a major breaking release
+- Fix urllib.request imports for Python 3.8 compatibility (Issue #447)
+- Fix future import ordering (Issue #445)
+- Fixed bug in fix_division_safe fixture (Issue #434)
+- Do not globally destroy re.ASCII in PY3
+- Fix a bug in email.Message.set_boundary() (Issue #429)
+- Implement format_map() in str
+- Implement readinto() for socket.fp
+
+As well as a number of corrections to a variety of documentation, and updates to
+test infrastructure.
+
+What's new in version 0.17.1 (2018-10-30)
 =========================================
 This release address a packaging error because of an erroneous declaration that
 any built wheels are universal.
diff --git a/src/future/__init__.py b/src/future/__init__.py
index f7a6fbeb..bbb77594 100644
--- a/src/future/__init__.py
+++ b/src/future/__init__.py
@@ -68,7 +68,7 @@
 Credits
 -------
 
-:Author:  Ed Schofield
+:Author:  Ed Schofield, Jordan M. Adler, et al
 :Sponsor: Python Charmers Pty Ltd, Australia, and Python Charmers Pte
           Ltd, Singapore. http://pythoncharmers.com
 :Others:  See docs/credits.rst or http://python-future.org/credits.html
@@ -86,8 +86,8 @@
 __license__ = 'MIT'
 __copyright__ = 'Copyright 2013-2018 Python Charmers Pty Ltd'
 __ver_major__ = 0
-__ver_minor__ = 17
-__ver_patch__ = 1
+__ver_minor__ = 18
+__ver_patch__ = 0
 __ver_sub__ = ''
 __version__ = "%d.%d.%d%s" % (__ver_major__, __ver_minor__,
                               __ver_patch__, __ver_sub__)

From c2e26d4ef17508b9270f4e0e49242fd3b0423913 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Wed, 9 Oct 2019 17:10:28 -0700
Subject: [PATCH 073/183] Update dates

---
 LICENSE.txt            | 2 +-
 README.rst             | 2 +-
 futurize.py            | 2 +-
 pasteurize.py          | 2 +-
 src/future/__init__.py | 4 ++--
 src/past/__init__.py   | 4 ++--
 6 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/LICENSE.txt b/LICENSE.txt
index d41c85d1..4c904dba 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2013-2018 Python Charmers Pty Ltd, Australia
+Copyright (c) 2013-2019 Python Charmers Pty Ltd, Australia
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.rst b/README.rst
index 6266618c..ea806538 100644
--- a/README.rst
+++ b/README.rst
@@ -266,7 +266,7 @@ Licensing
 
 :Author:  Ed Schofield, Jordan M. Adler, et al
 
-:Copyright: 2013-2018 Python Charmers Pty Ltd, Australia.
+:Copyright: 2013-2019 Python Charmers Pty Ltd, Australia.
 
 :Sponsors: Python Charmers Pty Ltd, Australia, and Python Charmers Pte
            Ltd, Singapore. http://pythoncharmers.com
diff --git a/futurize.py b/futurize.py
index 41080cf0..cb446ab2 100755
--- a/futurize.py
+++ b/futurize.py
@@ -13,7 +13,7 @@
 
 Licensing
 ---------
-Copyright 2013-2018 Python Charmers Pty Ltd, Australia.
+Copyright 2013-2019 Python Charmers Pty Ltd, Australia.
 The software is distributed under an MIT licence. See LICENSE.txt.
 """
 
diff --git a/pasteurize.py b/pasteurize.py
index c0bd7e09..2b98327c 100755
--- a/pasteurize.py
+++ b/pasteurize.py
@@ -12,7 +12,7 @@
 
 Licensing
 ---------
-Copyright 2013-2018 Python Charmers Pty Ltd, Australia.
+Copyright 2013-2019 Python Charmers Pty Ltd, Australia.
 The software is distributed under an MIT licence. See LICENSE.txt.
 """
 
diff --git a/src/future/__init__.py b/src/future/__init__.py
index bbb77594..d44e16c8 100644
--- a/src/future/__init__.py
+++ b/src/future/__init__.py
@@ -76,7 +76,7 @@
 
 Licensing
 ---------
-Copyright 2013-2018 Python Charmers Pty Ltd, Australia.
+Copyright 2013-2019 Python Charmers Pty Ltd, Australia.
 The software is distributed under an MIT licence. See LICENSE.txt.
 
 """
@@ -84,7 +84,7 @@
 __title__ = 'future'
 __author__ = 'Ed Schofield'
 __license__ = 'MIT'
-__copyright__ = 'Copyright 2013-2018 Python Charmers Pty Ltd'
+__copyright__ = 'Copyright 2013-2019 Python Charmers Pty Ltd'
 __ver_major__ = 0
 __ver_minor__ = 18
 __ver_patch__ = 0
diff --git a/src/past/__init__.py b/src/past/__init__.py
index 07422a03..14713039 100644
--- a/src/past/__init__.py
+++ b/src/past/__init__.py
@@ -74,13 +74,13 @@
 Credits
 -------
 
-:Author:  Ed Schofield
+:Author:  Ed Schofield, Jordan M. Adler, et al
 :Sponsor: Python Charmers Pty Ltd, Australia: http://pythoncharmers.com
 
 
 Licensing
 ---------
-Copyright 2013-2018 Python Charmers Pty Ltd, Australia.
+Copyright 2013-2019 Python Charmers Pty Ltd, Australia.
 The software is distributed under an MIT licence. See LICENSE.txt.
 """
 

From 25d1141d1c04365d80e7b1858b446e2689878345 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Wed, 9 Oct 2019 17:10:28 -0700
Subject: [PATCH 074/183] Update dates

---
 LICENSE.txt            | 2 +-
 README.rst             | 2 +-
 futurize.py            | 2 +-
 pasteurize.py          | 2 +-
 src/future/__init__.py | 4 ++--
 src/past/__init__.py   | 4 ++--
 6 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/LICENSE.txt b/LICENSE.txt
index d41c85d1..4c904dba 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2013-2018 Python Charmers Pty Ltd, Australia
+Copyright (c) 2013-2019 Python Charmers Pty Ltd, Australia
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.rst b/README.rst
index 6266618c..ea806538 100644
--- a/README.rst
+++ b/README.rst
@@ -266,7 +266,7 @@ Licensing
 
 :Author:  Ed Schofield, Jordan M. Adler, et al
 
-:Copyright: 2013-2018 Python Charmers Pty Ltd, Australia.
+:Copyright: 2013-2019 Python Charmers Pty Ltd, Australia.
 
 :Sponsors: Python Charmers Pty Ltd, Australia, and Python Charmers Pte
            Ltd, Singapore. http://pythoncharmers.com
diff --git a/futurize.py b/futurize.py
index 41080cf0..cb446ab2 100755
--- a/futurize.py
+++ b/futurize.py
@@ -13,7 +13,7 @@
 
 Licensing
 ---------
-Copyright 2013-2018 Python Charmers Pty Ltd, Australia.
+Copyright 2013-2019 Python Charmers Pty Ltd, Australia.
 The software is distributed under an MIT licence. See LICENSE.txt.
 """
 
diff --git a/pasteurize.py b/pasteurize.py
index c0bd7e09..2b98327c 100755
--- a/pasteurize.py
+++ b/pasteurize.py
@@ -12,7 +12,7 @@
 
 Licensing
 ---------
-Copyright 2013-2018 Python Charmers Pty Ltd, Australia.
+Copyright 2013-2019 Python Charmers Pty Ltd, Australia.
 The software is distributed under an MIT licence. See LICENSE.txt.
 """
 
diff --git a/src/future/__init__.py b/src/future/__init__.py
index bbb77594..d44e16c8 100644
--- a/src/future/__init__.py
+++ b/src/future/__init__.py
@@ -76,7 +76,7 @@
 
 Licensing
 ---------
-Copyright 2013-2018 Python Charmers Pty Ltd, Australia.
+Copyright 2013-2019 Python Charmers Pty Ltd, Australia.
 The software is distributed under an MIT licence. See LICENSE.txt.
 
 """
@@ -84,7 +84,7 @@
 __title__ = 'future'
 __author__ = 'Ed Schofield'
 __license__ = 'MIT'
-__copyright__ = 'Copyright 2013-2018 Python Charmers Pty Ltd'
+__copyright__ = 'Copyright 2013-2019 Python Charmers Pty Ltd'
 __ver_major__ = 0
 __ver_minor__ = 18
 __ver_patch__ = 0
diff --git a/src/past/__init__.py b/src/past/__init__.py
index 07422a03..14713039 100644
--- a/src/past/__init__.py
+++ b/src/past/__init__.py
@@ -74,13 +74,13 @@
 Credits
 -------
 
-:Author:  Ed Schofield
+:Author:  Ed Schofield, Jordan M. Adler, et al
 :Sponsor: Python Charmers Pty Ltd, Australia: http://pythoncharmers.com
 
 
 Licensing
 ---------
-Copyright 2013-2018 Python Charmers Pty Ltd, Australia.
+Copyright 2013-2019 Python Charmers Pty Ltd, Australia.
 The software is distributed under an MIT licence. See LICENSE.txt.
 """
 

From c24dfc48ea045f40c348e444bc2add983dd149e3 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Thu, 10 Oct 2019 15:07:22 -0700
Subject: [PATCH 075/183] bugfix for issubclass check

---
 src/future/utils/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index a6b10210..443dae65 100644
--- a/src/future/utils/__init__.py
+++ b/src/future/utils/__init__.py
@@ -415,7 +415,7 @@ def raise_(tp, value=None, tb=None):
             if value is not None:
                 raise TypeError("instance exception may not have a separate value")
             exc = tp
-        elif not issubclass(tp, Exception):
+        elif isinstance(tp, type) and not issubclass(tp, Exception):
             # If the first object is a class, it becomes the type of the
             # exception.
             raise TypeError("class must derive from Exception")

From a4f0145b105a6bb1d8d669991ed71d55eecd3187 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jadler@pinterest.com>
Date: Thu, 10 Oct 2019 15:07:22 -0700
Subject: [PATCH 076/183] bugfix for issubclass check

---
 src/future/utils/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index a6b10210..443dae65 100644
--- a/src/future/utils/__init__.py
+++ b/src/future/utils/__init__.py
@@ -415,7 +415,7 @@ def raise_(tp, value=None, tb=None):
             if value is not None:
                 raise TypeError("instance exception may not have a separate value")
             exc = tp
-        elif not issubclass(tp, Exception):
+        elif isinstance(tp, type) and not issubclass(tp, Exception):
             # If the first object is a class, it becomes the type of the
             # exception.
             raise TypeError("class must derive from Exception")

From c9e4e962478f16932d592584a0351d727ee71b6d Mon Sep 17 00:00:00 2001
From: Jordan Adler <jordan.m.adler@gmail.com>
Date: Thu, 17 Oct 2019 18:07:07 +0000
Subject: [PATCH 077/183] Prepare for 0.18.1 release

---
 docs/whatsnew.rst      | 6 ++++++
 src/future/__init__.py | 2 +-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index 50b2a99c..7d6a1071 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -3,6 +3,12 @@
 What's New
 **********
 
+What's new in version 0.18.1 (2019-10-09)
+=========================================
+This is a minor bug-fix release containing a fix for raise_() 
+when passed an exception that's not an Exception (e.g. BaseException
+subclasses)
+
 What's new in version 0.18.0 (2019-10-09)
 =========================================
 This is a major bug-fix and feature release, including:
diff --git a/src/future/__init__.py b/src/future/__init__.py
index d44e16c8..24f10fa1 100644
--- a/src/future/__init__.py
+++ b/src/future/__init__.py
@@ -87,7 +87,7 @@
 __copyright__ = 'Copyright 2013-2019 Python Charmers Pty Ltd'
 __ver_major__ = 0
 __ver_minor__ = 18
-__ver_patch__ = 0
+__ver_patch__ = 1
 __ver_sub__ = ''
 __version__ = "%d.%d.%d%s" % (__ver_major__, __ver_minor__,
                               __ver_patch__, __ver_sub__)

From 51509ad3aaefe5437dafa93954b39662133e6008 Mon Sep 17 00:00:00 2001
From: Jon Parise <jon@indelible.org>
Date: Thu, 17 Oct 2019 17:04:54 -0700
Subject: [PATCH 078/183] Use BaseException in raise_()

In both Python 2 and 3, the Exception type is derived from
BaseException:

  The base class for all built-in exceptions. It is not meant to be
  directly inherited by user-defined classes (for that, use Exception).

In practice, some libraries provide exception types that do derive
directly from BaseException (such as `gevent.Timeout`), and this code
should recognize them as valid exception types.

As further evidence, Python 2 explicitly states that raised types must
be derived from BaseException:

  exceptions must be old-style classes or derived from BaseException

Python 3 is more flexible here, which is why we provide a TypeError case
for non-BaseException-derived types. While I'm here, I made that message
a little more helpful by including the name of the incompatible type.
---
 src/future/utils/__init__.py    |  6 +++---
 tests/test_future/test_utils.py | 15 +++++++++++++--
 2 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index 443dae65..7a0954d1 100644
--- a/src/future/utils/__init__.py
+++ b/src/future/utils/__init__.py
@@ -408,17 +408,17 @@ def raise_(tp, value=None, tb=None):
         allows re-raising exceptions with the cls value and traceback on
         Python 2 and 3.
         """
-        if isinstance(tp, Exception):
+        if isinstance(tp, BaseException):
             # If the first object is an instance, the type of the exception
             # is the class of the instance, the instance itself is the value,
             # and the second object must be None.
             if value is not None:
                 raise TypeError("instance exception may not have a separate value")
             exc = tp
-        elif isinstance(tp, type) and not issubclass(tp, Exception):
+        elif isinstance(tp, type) and not issubclass(tp, BaseException):
             # If the first object is a class, it becomes the type of the
             # exception.
-            raise TypeError("class must derive from Exception")
+            raise TypeError("class must derive from BaseException, not %s" % tp.__name__)
         else:
             # The second object is used to determine the exception value: If it
             # is an instance of the class, the instance becomes the exception
diff --git a/tests/test_future/test_utils.py b/tests/test_future/test_utils.py
index b6a27d98..46f5196c 100644
--- a/tests/test_future/test_utils.py
+++ b/tests/test_future/test_utils.py
@@ -111,13 +111,13 @@ def test_isbytes(self):
         self.assertFalse(isbytes(self.s2))
 
     def test_raise_(self):
-        def valerror():
+        def valuerror():
             try:
                 raise ValueError("Apples!")
             except Exception as e:
                 raise_(e)
 
-        self.assertRaises(ValueError, valerror)
+        self.assertRaises(ValueError, valuerror)
 
         def with_value():
             raise_(IOError, "This is an error")
@@ -143,6 +143,17 @@ def with_traceback():
         except IOError as e:
             self.assertEqual(str(e), "An error")
 
+        class Timeout(BaseException):
+            pass
+
+        self.assertRaises(Timeout, raise_, Timeout)
+        self.assertRaises(Timeout, raise_, Timeout())
+
+        if PY3:
+            self.assertRaisesRegexp(
+                TypeError, "class must derive from BaseException",
+                raise_, int)
+
     def test_raise_from_None(self):
         try:
             try:

From f60f67269a861c3ff19dbee17342789a49a02cde Mon Sep 17 00:00:00 2001
From: Chase Sterling <chase.sterling@gmail.com>
Date: Thu, 17 Oct 2019 21:16:56 -0400
Subject: [PATCH 079/183] Add test for min/max with generators.

---
 tests/test_future/test_builtins.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tests/test_future/test_builtins.py b/tests/test_future/test_builtins.py
index d983f9d6..29f4c84a 100644
--- a/tests/test_future/test_builtins.py
+++ b/tests/test_future/test_builtins.py
@@ -1154,6 +1154,10 @@ def __getitem__(self, index):
         with self.assertRaises(TypeError):
             max(1, 2, default=0)
 
+        # Test iterables that can only be looped once #510
+        self.assertEqual(min(x for x in [5]), 5)
+        self.assertEqual(max(x for x in [5, 4, 3]), 5)
+
     def test_next(self):
         it = iter(range(2))
         self.assertEqual(next(it), 0)

From 03f94a325251f4ef19d4b4caabc72a5a88c60172 Mon Sep 17 00:00:00 2001
From: Chase Sterling <chase.sterling@gmail.com>
Date: Thu, 17 Oct 2019 21:19:25 -0400
Subject: [PATCH 080/183] Fix behavior of new min/max with generators. fix #510

---
 src/future/builtins/new_min_max.py | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/src/future/builtins/new_min_max.py b/src/future/builtins/new_min_max.py
index 8fd63fdf..cc7acbee 100644
--- a/src/future/builtins/new_min_max.py
+++ b/src/future/builtins/new_min_max.py
@@ -1,3 +1,5 @@
+import itertools
+
 from future import utils
 if utils.PY2:
     from __builtin__ import max as _builtin_max, min as _builtin_min
@@ -33,17 +35,20 @@ def new_min_max(_builtin_func, *args, **kwargs):
         raise TypeError
 
     if len(args) == 1:
+        iterator = iter(args[0])
         try:
-            next(iter(args[0]))
+            first = next(iterator)
         except StopIteration:
             if kwargs.get('default') is not None:
                 return kwargs.get('default')
             else:
                 raise ValueError('iterable is an empty sequence')
+        else:
+            iterator = itertools.chain([first], iterator)
         if kwargs.get('key') is not None:
-            return _builtin_func(args[0], key=kwargs.get('key'))
+            return _builtin_func(iterator, key=kwargs.get('key'))
         else:
-            return _builtin_func(args[0])
+            return _builtin_func(iterator)
 
     if len(args) > 1:
         if kwargs.get('key') is not None:

From 9774207b622966c8a5172156439c0d41c0335f33 Mon Sep 17 00:00:00 2001
From: Steve Kowalik <steven@wedontsleep.org>
Date: Fri, 18 Oct 2019 16:03:22 +1100
Subject: [PATCH 081/183] MIME guessing works in Python 3.8 in test_urllib2

To continue to support other versions of Python, use sys.hexversion to
check if we're using 3.8 and set the MIME type appropriately.

Fixes #508
---
 tests/test_future/test_urllib2.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/tests/test_future/test_urllib2.py b/tests/test_future/test_urllib2.py
index e7fb4dd7..2d69dad1 100644
--- a/tests/test_future/test_urllib2.py
+++ b/tests/test_future/test_urllib2.py
@@ -691,6 +691,10 @@ def connect_ftp(self, user, passwd, host, port, dirs,
         h = NullFTPHandler(data)
         h.parent = MockOpener()
 
+        # MIME guessing works in Python 3.8!
+        guessed_mime = None
+        if sys.hexversion >= 0x03080000:
+            guessed_mime = "image/gif"
         for url, host, port, user, passwd, type_, dirs, filename, mimetype in [
             ("ftp://localhost/foo/bar/baz.html",
              "localhost", ftplib.FTP_PORT, "", "", "I",
@@ -709,7 +713,7 @@ def connect_ftp(self, user, passwd, host, port, dirs,
              ["foo", "bar"], "", None),
             ("ftp://localhost/baz.gif;type=a",
              "localhost", ftplib.FTP_PORT, "", "", "A",
-             [], "baz.gif", None),  # XXX really this should guess image/gif
+             [], "baz.gif", guessed_mime),
             ]:
             req = Request(url)
             req.timeout = None

From b64245c8444d93aee6862bb0cd2361ddfe431eea Mon Sep 17 00:00:00 2001
From: Chase Sterling <chase.sterling@gmail.com>
Date: Fri, 18 Oct 2019 09:24:57 -0400
Subject: [PATCH 082/183] Move max test to appropriate test method

---
 tests/test_future/test_builtins.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/test_future/test_builtins.py b/tests/test_future/test_builtins.py
index 29f4c84a..d5ea8972 100644
--- a/tests/test_future/test_builtins.py
+++ b/tests/test_future/test_builtins.py
@@ -1123,6 +1123,7 @@ class BadSeq:
             def __getitem__(self, index):
                 raise ValueError
         self.assertRaises(ValueError, min, BadSeq())
+        self.assertEqual(max(x for x in [5, 4, 3]), 5)
 
         for stmt in (
             "min(key=int)",                 # no args
@@ -1156,7 +1157,6 @@ def __getitem__(self, index):
 
         # Test iterables that can only be looped once #510
         self.assertEqual(min(x for x in [5]), 5)
-        self.assertEqual(max(x for x in [5, 4, 3]), 5)
 
     def test_next(self):
         it = iter(range(2))

From dbc74ad81b384897970edd65fef1bc53ddee5acb Mon Sep 17 00:00:00 2001
From: Chase Sterling <chase.sterling@gmail.com>
Date: Fri, 18 Oct 2019 10:31:21 -0400
Subject: [PATCH 083/183] Fix min/max functions not allowing 'None' as default

---
 src/future/builtins/new_min_max.py | 6 ++++--
 tests/test_future/test_builtins.py | 2 ++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/future/builtins/new_min_max.py b/src/future/builtins/new_min_max.py
index cc7acbee..1bfd8a90 100644
--- a/src/future/builtins/new_min_max.py
+++ b/src/future/builtins/new_min_max.py
@@ -6,6 +6,8 @@
 else:
     from builtins import max as _builtin_max, min as _builtin_min
 
+_SENTINEL = object()
+
 
 def newmin(*args, **kwargs):
     return new_min_max(_builtin_min, *args, **kwargs)
@@ -31,7 +33,7 @@ def new_min_max(_builtin_func, *args, **kwargs):
     if len(args) == 0:
         raise TypeError
 
-    if len(args) != 1 and kwargs.get('default') is not None:
+    if len(args) != 1 and kwargs.get('default', _SENTINEL) is not _SENTINEL:
         raise TypeError
 
     if len(args) == 1:
@@ -39,7 +41,7 @@ def new_min_max(_builtin_func, *args, **kwargs):
         try:
             first = next(iterator)
         except StopIteration:
-            if kwargs.get('default') is not None:
+            if kwargs.get('default', _SENTINEL) is not _SENTINEL:
                 return kwargs.get('default')
             else:
                 raise ValueError('iterable is an empty sequence')
diff --git a/tests/test_future/test_builtins.py b/tests/test_future/test_builtins.py
index d5ea8972..ca07b9ef 100644
--- a/tests/test_future/test_builtins.py
+++ b/tests/test_future/test_builtins.py
@@ -1105,6 +1105,7 @@ def test_max(self):
         with self.assertRaises(TypeError):
             max(1, 2, default=0)
         self.assertEqual(max([], default=0), 0)
+        self.assertIs(max([], default=None), None)
 
     def test_min(self):
         self.assertEqual(min('123123'), '1')
@@ -1150,6 +1151,7 @@ def __getitem__(self, index):
                          sorted(data, key=f)[0])
         self.assertEqual(min([], default=5), 5)
         self.assertEqual(min([], default=0), 0)
+        self.assertIs(min([], default=None), None)
         with self.assertRaises(TypeError):
             max(None, default=5)
         with self.assertRaises(TypeError):

From f4926e51c940ef9afb4c94038c48b8a5382d04a2 Mon Sep 17 00:00:00 2001
From: Chase Sterling <chase.sterling@gmail.com>
Date: Fri, 18 Oct 2019 10:32:22 -0400
Subject: [PATCH 084/183] Make new min/max error message consistent builtin
 error message

---
 src/future/builtins/new_min_max.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/future/builtins/new_min_max.py b/src/future/builtins/new_min_max.py
index 1bfd8a90..6f0c2a86 100644
--- a/src/future/builtins/new_min_max.py
+++ b/src/future/builtins/new_min_max.py
@@ -44,7 +44,7 @@ def new_min_max(_builtin_func, *args, **kwargs):
             if kwargs.get('default', _SENTINEL) is not _SENTINEL:
                 return kwargs.get('default')
             else:
-                raise ValueError('iterable is an empty sequence')
+                raise ValueError('{}() arg is an empty sequence'.format(_builtin_func.__name__))
         else:
             iterator = itertools.chain([first], iterator)
         if kwargs.get('key') is not None:

From 53f4685e67142e234a4ccf4f01fd96228de855b7 Mon Sep 17 00:00:00 2001
From: Tom Picton <tom@tompicton.com>
Date: Tue, 22 Oct 2019 09:47:59 -0700
Subject: [PATCH 085/183] Fix round function failing on Decimal objects

---
 src/future/builtins/newround.py | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/future/builtins/newround.py b/src/future/builtins/newround.py
index 3943ebb6..b0e6a001 100644
--- a/src/future/builtins/newround.py
+++ b/src/future/builtins/newround.py
@@ -38,11 +38,14 @@ def newround(number, ndigits=None):
         if 'numpy' in repr(type(number)):
             number = float(number)
 
-    if not PY26:
-        d = Decimal.from_float(number).quantize(exponent,
-                                            rounding=ROUND_HALF_EVEN)
+    if isinstance(d, Decimal):
+        d = number
     else:
-        d = from_float_26(number).quantize(exponent, rounding=ROUND_HALF_EVEN)
+        if not PY26:
+            d = Decimal.from_float(number).quantize(exponent,
+                                                rounding=ROUND_HALF_EVEN)
+        else:
+            d = from_float_26(number).quantize(exponent, rounding=ROUND_HALF_EVEN)
 
     if return_int:
         return int(d)

From 8ba82a8ea5456f2d9b489aa1d23bba053999f18d Mon Sep 17 00:00:00 2001
From: Anika Mukherji <amukherji@pinterest.com>
Date: Tue, 22 Oct 2019 15:06:32 -0700
Subject: [PATCH 086/183] use object.setattr to bypass check for immutable
 objects

---
 src/future/utils/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index 59920077..7282a9cf 100644
--- a/src/future/utils/__init__.py
+++ b/src/future/utils/__init__.py
@@ -473,7 +473,7 @@ def raise_from(exc, cause):
             e.__suppress_context__ = True
         elif isinstance(cause, BaseException):
             e.__cause__ = cause
-            e.__cause__.__traceback__ = sys.exc_info()[2]
+            object.__setattr__(e.__cause__,  '__traceback__', sys.exc_info()[2])
             e.__suppress_context__ = True
         else:
             raise TypeError("exception causes must derive from BaseException")

From d7ff41c2d528f5af551e0fd065aa0c1364067ced Mon Sep 17 00:00:00 2001
From: Jeroen Akkerman <jeroen.akkerman@easyflex.nl>
Date: Wed, 23 Oct 2019 15:37:46 +0200
Subject: [PATCH 087/183] Add check for input import before running FixInput
 Fixes #427

---
 src/libfuturize/fixes/__init__.py  |  3 ++-
 src/libfuturize/fixes/fix_input.py | 32 ++++++++++++++++++++++++++++++
 tests/test_future/test_futurize.py | 21 ++++++++++++++++++++
 3 files changed, 55 insertions(+), 1 deletion(-)
 create mode 100644 src/libfuturize/fixes/fix_input.py

diff --git a/src/libfuturize/fixes/__init__.py b/src/libfuturize/fixes/__init__.py
index 7de304da..0b562501 100644
--- a/src/libfuturize/fixes/__init__.py
+++ b/src/libfuturize/fixes/__init__.py
@@ -50,7 +50,7 @@
     'lib2to3.fixes.fix_getcwdu',
     # 'lib2to3.fixes.fix_imports',   # called by libfuturize.fixes.fix_future_standard_library
     # 'lib2to3.fixes.fix_imports2',  # we don't handle this yet (dbm)
-    'lib2to3.fixes.fix_input',
+    # 'lib2to3.fixes.fix_input',     # Called conditionally by libfuturize.fixes.fix_input
     'lib2to3.fixes.fix_itertools',
     'lib2to3.fixes.fix_itertools_imports',
     'lib2to3.fixes.fix_filter',
@@ -86,6 +86,7 @@
     'libfuturize.fixes.fix_future_builtins',
     'libfuturize.fixes.fix_future_standard_library',
     'libfuturize.fixes.fix_future_standard_library_urllib',
+    'libfuturize.fixes.fix_input',
     'libfuturize.fixes.fix_metaclass',
     'libpasteurize.fixes.fix_newstyle',
     'libfuturize.fixes.fix_object',
diff --git a/src/libfuturize/fixes/fix_input.py b/src/libfuturize/fixes/fix_input.py
new file mode 100644
index 00000000..8a43882e
--- /dev/null
+++ b/src/libfuturize/fixes/fix_input.py
@@ -0,0 +1,32 @@
+"""
+Fixer for input.
+
+Does a check for `from builtins import input` before running the lib2to3 fixer.
+The fixer will not run when the input is already present.
+
+
+this:
+    a = input()
+becomes:
+    from builtins import input
+    a = eval(input())
+
+and this:
+    from builtins import input
+    a = input()
+becomes (no change):
+    from builtins import input
+    a = input()
+"""
+
+import lib2to3.fixes.fix_input
+from lib2to3.fixer_util import does_tree_import
+
+
+class FixInput(lib2to3.fixes.fix_input.FixInput):
+    def transform(self, node, results):
+
+        if does_tree_import('builtins', 'input', node):
+            return
+
+        return super(FixInput, self).transform(node, results)
diff --git a/tests/test_future/test_futurize.py b/tests/test_future/test_futurize.py
index f2201141..0d7c42de 100644
--- a/tests/test_future/test_futurize.py
+++ b/tests/test_future/test_futurize.py
@@ -436,6 +436,27 @@ def test_import_builtins(self):
         """
         self.convert_check(before, after, ignore_imports=False, run=False)
 
+    def test_input_without_import(self):
+        before = """
+        a = input()
+        """
+        after = """
+        from builtins import input
+        a = eval(input())
+        """
+        self.convert_check(before, after, ignore_imports=False, run=False)
+
+    def test_input_with_import(self):
+        before = """
+        from builtins import input
+        a = input()
+        """
+        after = """
+        from builtins import input
+        a = input()
+        """
+        self.convert_check(before, after, ignore_imports=False, run=False)
+
     def test_xrange(self):
         """
         The ``from builtins import range`` line was being added to the

From a1d6cd3980901b1904c7ed02c8abe76e5f3e1f7e Mon Sep 17 00:00:00 2001
From: Tom Picton <tom@tompicton.com>
Date: Thu, 24 Oct 2019 16:40:05 -0700
Subject: [PATCH 088/183] Fix typo in newround

---
 src/future/builtins/newround.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/future/builtins/newround.py b/src/future/builtins/newround.py
index b0e6a001..394a2c63 100644
--- a/src/future/builtins/newround.py
+++ b/src/future/builtins/newround.py
@@ -38,7 +38,7 @@ def newround(number, ndigits=None):
         if 'numpy' in repr(type(number)):
             number = float(number)
 
-    if isinstance(d, Decimal):
+    if isinstance(number, Decimal):
         d = number
     else:
         if not PY26:

From ad5738312fab39c9829d3e4617703b684d8fa4a2 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jordan.m.adler@gmail.com>
Date: Thu, 31 Oct 2019 00:49:27 +0000
Subject: [PATCH 089/183] Prepare for 0.18.2 release

---
 docs/credits.rst       |  4 ++++
 docs/whatsnew.rst      | 13 +++++++++++++
 src/future/__init__.py |  2 +-
 3 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/docs/credits.rst b/docs/credits.rst
index e66136d5..275e148e 100644
--- a/docs/credits.rst
+++ b/docs/credits.rst
@@ -48,6 +48,7 @@ Authors
 Python-Future is largely written by Ed Schofield <ed@pythoncharmers.com> with the help of various contributors:
 
 - Jordan Adler
+- Jeroen Akkerman
 - Kyle Altendorf
 - Grant Bakker
 - Jacob Beck
@@ -70,6 +71,7 @@ Python-Future is largely written by Ed Schofield <ed@pythoncharmers.com> with th
 - Michael Joseph
 - Waldemar Kornewald
 - Alexey Kotlyarov
+- Steve Kowalik
 - Lion Krischer
 - Marcin Kuzminski
 - Joshua Landau
@@ -81,6 +83,7 @@ Python-Future is largely written by Ed Schofield <ed@pythoncharmers.com> with th
 - Anika Mukherji
 - Jon Parise
 - Matthew Parnell
+- Tom Picton
 - Miga Purg
 - Éloi Rivard
 - Sesh Sadasivam
@@ -91,6 +94,7 @@ Python-Future is largely written by Ed Schofield <ed@pythoncharmers.com> with th
 - Sameera Somisetty
 - Louis Sautier
 - Gregory P. Smith
+- Chase Sterling
 - Daniel Szoska
 - Flaviu Tamas
 - Jeff Tratner
diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index 7d6a1071..e0b4603d 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -3,6 +3,19 @@
 What's New
 **********
 
+What's new in version 0.18.2 (2019-10-30)
+=========================================
+This is a minor bug-fix release containing a number of fixes:
+- Fix min/max functions with generators, and 'None' default (PR #514)
+- Use BaseException in raise_() (PR #515)
+- Fix builtins.round() for Decimals (Issue #501)
+- Fix raise_from() to prevent failures with immutable classes (PR #518)
+- Make FixInput idempotent (Issue #427)
+- Fix type in newround (PR #521)
+- Support mimetype guessing in urllib2 for Py3.8+ (Issue #508)
+
+Python 3.8 is not yet officially supported.
+
 What's new in version 0.18.1 (2019-10-09)
 =========================================
 This is a minor bug-fix release containing a fix for raise_() 
diff --git a/src/future/__init__.py b/src/future/__init__.py
index 24f10fa1..ad419d67 100644
--- a/src/future/__init__.py
+++ b/src/future/__init__.py
@@ -87,7 +87,7 @@
 __copyright__ = 'Copyright 2013-2019 Python Charmers Pty Ltd'
 __ver_major__ = 0
 __ver_minor__ = 18
-__ver_patch__ = 1
+__ver_patch__ = 2
 __ver_sub__ = ''
 __version__ = "%d.%d.%d%s" % (__ver_major__, __ver_minor__,
                               __ver_patch__, __ver_sub__)

From 4236061ae33897a1e6731297babb543417c06d6e Mon Sep 17 00:00:00 2001
From: Kaustubh Maske Patil <37668193+nikochiko@users.noreply.github.com>
Date: Thu, 31 Oct 2019 18:14:50 +0530
Subject: [PATCH 090/183] Fix typo

Fix typo extra ` .
`newstr`` --> `newstr`
---
 docs/str_object.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/str_object.rst b/docs/str_object.rst
index 4c5257a0..568b897a 100644
--- a/docs/str_object.rst
+++ b/docs/str_object.rst
@@ -14,7 +14,7 @@ There are also other differences, such as the ``repr`` of unicode strings in
 Py2 having a ``u'...'`` prefix, versus simply ``'...'``, and the removal of
 the :func:`str.decode` method in Py3.
 
-:mod:`future` contains a :class:`newstr`` type that is a backport of the
+:mod:`future` contains a :class:`newstr` type that is a backport of the
 :mod:`str` object from Python 3. This inherits from the Python 2
 :class:`unicode` class but has customizations to improve compatibility with
 Python 3's :class:`str` object. You can use it as follows::

From 1663dfa4be398c00ad5209f301854e905645e222 Mon Sep 17 00:00:00 2001
From: Brian Rutledge <brian@bhrutledge.com>
Date: Tue, 5 Nov 2019 17:15:23 -0500
Subject: [PATCH 091/183] Fix formatting in "What's new"

---
 docs/whatsnew.rst | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index e0b4603d..c6fa8f86 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -6,6 +6,7 @@ What's New
 What's new in version 0.18.2 (2019-10-30)
 =========================================
 This is a minor bug-fix release containing a number of fixes:
+
 - Fix min/max functions with generators, and 'None' default (PR #514)
 - Use BaseException in raise_() (PR #515)
 - Fix builtins.round() for Decimals (Issue #501)

From 4dfa099c49bc6c5bd0162ed2bab9b24a91de69ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Juan=20Jos=C3=A9=20Conti?= <jjconti@gmail.com>
Date: Wed, 6 Nov 2019 12:05:35 -0300
Subject: [PATCH 092/183] Fix typo

---
 docs/str_object.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/str_object.rst b/docs/str_object.rst
index 4c5257a0..568b897a 100644
--- a/docs/str_object.rst
+++ b/docs/str_object.rst
@@ -14,7 +14,7 @@ There are also other differences, such as the ``repr`` of unicode strings in
 Py2 having a ``u'...'`` prefix, versus simply ``'...'``, and the removal of
 the :func:`str.decode` method in Py3.
 
-:mod:`future` contains a :class:`newstr`` type that is a backport of the
+:mod:`future` contains a :class:`newstr` type that is a backport of the
 :mod:`str` object from Python 3. This inherits from the Python 2
 :class:`unicode` class but has customizations to improve compatibility with
 Python 3's :class:`str` object. You can use it as follows::

From 6126997eb8953c379f7686d0df692db030afd6ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= <mgorny@gentoo.org>
Date: Fri, 15 Nov 2019 21:23:42 +0100
Subject: [PATCH 093/183] Sort before comparing dicts in TestChainMap

Sort the results of items() of a dict before comparing them.  PyPy
apparently does not produce consistent ordering on .items().

Fixes one of the failures from bug #530
---
 tests/test_future/test_backports.py | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/tests/test_future/test_backports.py b/tests/test_future/test_backports.py
index 9eeb741b..63b1afea 100644
--- a/tests/test_future/test_backports.py
+++ b/tests/test_future/test_backports.py
@@ -87,7 +87,8 @@ def test_basics(self):
         d['b'] = 20
         d['c'] = 30
         self.assertEqual(d.maps, [{'b':20, 'c':30}, {'a':1, 'b':2}])  # check internal state
-        self.assertEqual(d.items(), dict(a=1, b=20, c=30).items())    # check items/iter/getitem
+        self.assertEqual(sorted(d.items()),
+                         sorted(dict(a=1, b=20, c=30).items()))       # check items/iter/getitem
         self.assertEqual(len(d), 3)                                   # check len
         for key in 'abc':                                             # check contains
             self.assertIn(key, d)
@@ -96,7 +97,8 @@ def test_basics(self):
 
         del d['b']                                                    # unmask a value
         self.assertEqual(d.maps, [{'c':30}, {'a':1, 'b':2}])          # check internal state
-        self.assertEqual(d.items(), dict(a=1, b=2, c=30).items())     # check items/iter/getitem
+        self.assertEqual(sorted(d.items()),
+                         sorted(dict(a=1, b=2, c=30).items()))        # check items/iter/getitem
         self.assertEqual(len(d), 3)                                   # check len
         for key in 'abc':                                             # check contains
             self.assertIn(key, d)

From f6a654993968a27d63d99ec1cf40e4ebf00f094d Mon Sep 17 00:00:00 2001
From: Kyle King <KyleKing@users.noreply.github.com>
Date: Fri, 6 Dec 2019 14:26:33 -0500
Subject: [PATCH 094/183] Add tkFileDialog to future.movers.tkinter

Related to issue #233 / Commit a6ed514
---
 src/future/moves/tkinter/filedialog.py | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/future/moves/tkinter/filedialog.py b/src/future/moves/tkinter/filedialog.py
index 973923e2..6a6f03ca 100644
--- a/src/future/moves/tkinter/filedialog.py
+++ b/src/future/moves/tkinter/filedialog.py
@@ -10,3 +10,9 @@
     except ImportError:
         raise ImportError('The FileDialog module is missing. Does your Py2 '
                           'installation include tkinter?')
+    
+    try:
+        from tkFileDialog import *
+    except ImportError:
+        raise ImportError('The tkFileDialog module is missing. Does your Py2 '
+                          'installation include tkinter?')

From a4911b9dd748aac4a419455a2bc94f270f050528 Mon Sep 17 00:00:00 2001
From: Kayne Barclay <kayne@benchling.com>
Date: Thu, 19 Dec 2019 13:18:35 -0800
Subject: [PATCH 095/183] #322 Add support for negative ndigits in round;
 additionally, fixing a bug so that it handles passing in Decimal properly

---
 src/future/builtins/newround.py    | 17 ++++++++++-------
 tests/test_future/test_builtins.py |  1 -
 2 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/src/future/builtins/newround.py b/src/future/builtins/newround.py
index 394a2c63..e2976a76 100644
--- a/src/future/builtins/newround.py
+++ b/src/future/builtins/newround.py
@@ -2,6 +2,7 @@
 ``python-future``: pure Python implementation of Python 3 round().
 """
 
+from __future__ import division
 from future.utils import PYPY, PY26, bind_method
 
 # Use the decimal module for simplicity of implementation (and
@@ -29,8 +30,6 @@ def newround(number, ndigits=None):
     if hasattr(number, '__round__'):
         return number.__round__(ndigits)
 
-    if ndigits < 0:
-        raise NotImplementedError('negative ndigits not supported yet')
     exponent = Decimal('10') ** (-ndigits)
 
     if PYPY:
@@ -42,15 +41,19 @@ def newround(number, ndigits=None):
         d = number
     else:
         if not PY26:
-            d = Decimal.from_float(number).quantize(exponent,
-                                                rounding=ROUND_HALF_EVEN)
+            d = Decimal.from_float(number)
         else:
-            d = from_float_26(number).quantize(exponent, rounding=ROUND_HALF_EVEN)
+            d = from_float_26(number)
+
+    if ndigits < 0:
+        result = newround(d / exponent) * exponent
+    else:
+        result = d.quantize(exponent, rounding=ROUND_HALF_EVEN)
 
     if return_int:
-        return int(d)
+        return int(result)
     else:
-        return float(d)
+        return float(result)
 
 
 ### From Python 2.7's decimal.py. Only needed to support Py2.6:
diff --git a/tests/test_future/test_builtins.py b/tests/test_future/test_builtins.py
index ca07b9ef..3921a608 100644
--- a/tests/test_future/test_builtins.py
+++ b/tests/test_future/test_builtins.py
@@ -146,7 +146,6 @@ def test_round(self):
         self.assertTrue(isinstance(round(123.5, 0), float))
         self.assertTrue(isinstance(round(123.5), Integral))
 
-    @unittest.skip('negative ndigits not implemented yet')
     def test_round_negative_ndigits(self):
         self.assertEqual(round(10.1350, 0), 10.0)
         self.assertEqual(round(10.1350, -1), 10.0)

From 3eaa8fd85cbc75e8b4b1fd797a650cce33d42d43 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Sampaio?= <joao.sampaio@close.com>
Date: Fri, 10 Jan 2020 11:38:50 -0300
Subject: [PATCH 096/183] Compare headers to correct definition of str

---
 src/future/backports/email/base64mime.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/future/backports/email/base64mime.py b/src/future/backports/email/base64mime.py
index 416d612e..296392a6 100644
--- a/src/future/backports/email/base64mime.py
+++ b/src/future/backports/email/base64mime.py
@@ -28,6 +28,7 @@
 from __future__ import absolute_import
 from future.builtins import range
 from future.builtins import bytes
+from future.builtins import str
 
 __all__ = [
     'body_decode',

From 461d77e2c4335b4f3cc3c4ff2d2af8c5a9cdf9bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Sampaio?= <joao.sampaio@close.com>
Date: Fri, 10 Jan 2020 14:45:04 -0300
Subject: [PATCH 097/183] Add a test for our fix

---
 tests/test_future/test_email_generation.py | 30 ++++++++++++++++++++++
 1 file changed, 30 insertions(+)
 create mode 100644 tests/test_future/test_email_generation.py

diff --git a/tests/test_future/test_email_generation.py b/tests/test_future/test_email_generation.py
new file mode 100644
index 00000000..10e61138
--- /dev/null
+++ b/tests/test_future/test_email_generation.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+"""Tests for email generation."""
+
+from __future__ import unicode_literals
+
+from future.backports.email.mime.multipart import MIMEMultipart
+from future.backports.email.mime.text import MIMEText
+from future.backports.email.utils import formatdate
+from future.tests.base import unittest
+
+
+class EmailGenerationTests(unittest.TestCase):
+    def test_email_custom_header_can_contain_unicode(self):
+        msg = MIMEMultipart()
+        alternative = MIMEMultipart('alternative')
+        alternative.attach(MIMEText('Plain content with Únicødê', _subtype='plain', _charset='utf-8'))
+        alternative.attach(MIMEText('HTML content with Únicødê', _subtype='html', _charset='utf-8'))
+        msg.attach(alternative)
+
+        msg['Subject'] = 'Subject with Únicødê'
+        msg['From'] = 'sender@test.com'
+        msg['To'] = 'recipient@test.com'
+        msg['Date'] = formatdate(None, localtime=True)
+        msg['Message-ID'] = 'anIdWithÚnicødêForThisEmail'
+
+        msg_lines = msg.as_string().split('\n')
+        self.assertEqual(msg_lines[2], 'Subject: =?utf-8?b?U3ViamVjdCB3aXRoIMOabmljw7hkw6o=?=')
+        self.assertEqual(msg_lines[6], 'Message-ID: =?utf-8?b?YW5JZFdpdGjDmm5pY8O4ZMOqRm9yVGhpc0VtYWls?=')
+        self.assertEqual(msg_lines[17], 'UGxhaW4gY29udGVudCB3aXRoIMOabmljw7hkw6o=')
+        self.assertEqual(msg_lines[24], 'SFRNTCBjb250ZW50IHdpdGggw5puaWPDuGTDqg==')

From 52b0ff92c8ad2c4dac274749a85c5f7a629f185a Mon Sep 17 00:00:00 2001
From: Glen Walker <glen@walker.gen.nz>
Date: Fri, 17 Jan 2020 13:29:33 +1300
Subject: [PATCH 098/183] Handling of __next__ and next by
 future.utils.get_next was reversed

I assume nobody is using future.utils.get_next, because the builtin next() function exists from Python 2.6 on and meets most needs, and because nobody has noticed this before. Fixing in case it saves anyone confusion in future.
---
 src/future/utils/__init__.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index 46bd96de..846d5da6 100644
--- a/src/future/utils/__init__.py
+++ b/src/future/utils/__init__.py
@@ -527,9 +527,9 @@ def __next__(self):
         return cls
 
 if PY3:
-    get_next = lambda x: x.next
-else:
     get_next = lambda x: x.__next__
+else:
+    get_next = lambda x: x.next
 
 
 def encode_filename(filename):

From a9c6a3762b5391986833737e7fea49a8b324b69c Mon Sep 17 00:00:00 2001
From: Dominik Kozaczko <dominik@kozaczko.info>
Date: Fri, 7 Feb 2020 17:30:49 +0100
Subject: [PATCH 099/183] Add pre-commit hooks

---
 .gitignore             |  3 +++
 .pre-commit-hooks.yaml | 14 ++++++++++++++
 2 files changed, 17 insertions(+)
 create mode 100644 .pre-commit-hooks.yaml

diff --git a/.gitignore b/.gitignore
index 8c52a551..1b142a7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,6 @@ nosetests.xml
 .mr.developer.cfg
 .project
 .pydevproject
+
+# PyCharm
+.idea
diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml
new file mode 100644
index 00000000..ea5abd63
--- /dev/null
+++ b/.pre-commit-hooks.yaml
@@ -0,0 +1,14 @@
+- id: futurize
+  name: futurize
+  description: Futurize your Py2 code to ensure it is runnable on Py3.
+  language: python
+  types: [python]
+  entry: futurize -w -n --no-diffs
+  args: [--stage1]
+
+- id: pasteurize
+  name: pasteurize
+  description: Pasteurize your Py3 code to ensure it is runnable on Py2.
+  language: python
+  types: [python]
+  entry: pasteurize -w -n --no-diffs

From 6c6e3aea6f9a26b2827e5987a4e1c47602a73b4d Mon Sep 17 00:00:00 2001
From: Dominik Kozaczko <dominik@kozaczko.info>
Date: Fri, 7 Feb 2020 22:38:04 +0100
Subject: [PATCH 100/183] Add simple documentation

---
 .pre-commit-hooks.yaml |  1 +
 README.rst             | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml
index ea5abd63..dd8d0d65 100644
--- a/.pre-commit-hooks.yaml
+++ b/.pre-commit-hooks.yaml
@@ -10,5 +10,6 @@
   name: pasteurize
   description: Pasteurize your Py3 code to ensure it is runnable on Py2.
   language: python
+  language_version: python3
   types: [python]
   entry: pasteurize -w -n --no-diffs
diff --git a/README.rst b/README.rst
index ea806538..c0c69905 100644
--- a/README.rst
+++ b/README.rst
@@ -57,6 +57,8 @@ Features
     decoding the backported ``str`` and ``bytes`` objects. [This feature is
     currently in alpha.]
 
+-   support for pre-commit hooks
+
 .. _code-examples:
 
 Code examples
@@ -261,6 +263,39 @@ development, and will likely never be perfect.
 
 For more info, see :ref:`translation`.
 
+Pre-commit hooks
+----------------
+
+`Pre-commit <https://pre-commit.com/>`_ is a framework for managing and maintaining
+multi-language pre-commit hooks.
+
+In case you need to port your project from Python 2 to Python 3, you might consider
+using such hook during the transition period.
+
+First:
+
+.. code-block:: bash
+
+    $ pip install pre-commit
+
+and then in your project's directory:
+
+.. code-block:: bash
+
+    $ pre-commit install
+
+Next, you need to add this entry to your ``.pre-commit-config.yaml``
+
+.. code-block:: yaml
+
+    -   repo: https://github.com/PythonCharmers/python-future
+        rev: feature/pre-commit
+        hooks:
+            - id: futurize
+              args: [--both-stages]
+
+The ``args`` part is optional, by default only stage1 is applied.
+
 Licensing
 ---------
 

From 681e78ce52d80012f115941665550aa0e96e9531 Mon Sep 17 00:00:00 2001
From: Dominik Kozaczko <dominik@kozaczko.info>
Date: Sat, 8 Feb 2020 01:01:39 +0100
Subject: [PATCH 101/183] Correct example in README

---
 README.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.rst b/README.rst
index c0c69905..5c090804 100644
--- a/README.rst
+++ b/README.rst
@@ -289,7 +289,7 @@ Next, you need to add this entry to your ``.pre-commit-config.yaml``
 .. code-block:: yaml
 
     -   repo: https://github.com/PythonCharmers/python-future
-        rev: feature/pre-commit
+        rev: master
         hooks:
             - id: futurize
               args: [--both-stages]

From c9bc0ff1807cb663be9bcaadd69e57f6ba60fe13 Mon Sep 17 00:00:00 2001
From: Yaseen Mowzer <yaseenm@j5int.com>
Date: Thu, 5 Mar 2020 08:54:27 +0200
Subject: [PATCH 102/183] Add __subclasscheck__ for past.types.basestring

Previously `__subclasscheck__` was not implemented which lead to
inconsistencies between `isinstance` and `issubclass` (i.e.
`isinstance("a string", basestring)` is true, but
`issubclass(str, basestring)` was false. This commit fixes this
incorrect behavior.
---
 src/past/types/basestring.py       |  5 ++---
 tests/test_past/test_basestring.py | 19 +++++++++++++++++++
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/src/past/types/basestring.py b/src/past/types/basestring.py
index 1cab22f6..9c21715a 100644
--- a/src/past/types/basestring.py
+++ b/src/past/types/basestring.py
@@ -25,9 +25,8 @@ class BaseBaseString(type):
     def __instancecheck__(cls, instance):
         return isinstance(instance, (bytes, str))
 
-    def __subclasshook__(cls, thing):
-        # TODO: What should go here?
-        raise NotImplemented
+    def __subclasscheck__(cls, subclass):
+        return super(BaseBaseString, cls).__subclasscheck__(subclass) or issubclass(subclass, (bytes, str))
 
 
 class basestring(with_metaclass(BaseBaseString)):
diff --git a/tests/test_past/test_basestring.py b/tests/test_past/test_basestring.py
index d002095e..6c224b3e 100644
--- a/tests/test_past/test_basestring.py
+++ b/tests/test_past/test_basestring.py
@@ -19,6 +19,25 @@ def test_isinstance(self):
         s2 = oldstr(b'abc')
         self.assertTrue(isinstance(s2, basestring))
 
+    def test_issubclass(self):
+        self.assertTrue(issubclass(str, basestring))
+        self.assertTrue(issubclass(bytes, basestring))
+        self.assertTrue(issubclass(basestring, basestring))
+        self.assertFalse(issubclass(int, basestring))
+        self.assertFalse(issubclass(list, basestring))
+        self.assertTrue(issubclass(basestring, object))
+
+        class CustomString(basestring):
+            pass
+        class NotString(object):
+            pass
+        class OldStyleClass:
+            pass
+        self.assertTrue(issubclass(CustomString, basestring))
+        self.assertFalse(issubclass(NotString, basestring))
+        self.assertFalse(issubclass(OldStyleClass, basestring))
+
+
 
 if __name__ == '__main__':
     unittest.main()

From 5f945723dc6218584dac0608117176759853738d Mon Sep 17 00:00:00 2001
From: Vitaly Goldshteyn <VitalyGoldstein@gmail.com>
Date: Mon, 6 Apr 2020 04:33:51 +0200
Subject: [PATCH 103/183] Added r""" to the docstring to avoid warnings in
 python3

Added r""" to the docstring to avoid warnings in python3

Warning example:
DeprecationWarning: invalid escape sequence \d
---
 src/past/types/oldstr.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/past/types/oldstr.py b/src/past/types/oldstr.py
index a477d884..5a0e3789 100644
--- a/src/past/types/oldstr.py
+++ b/src/past/types/oldstr.py
@@ -20,7 +20,7 @@ def __instancecheck__(cls, instance):
 
 
 def unescape(s):
-    """
+    r"""
     Interprets strings with escape sequences
 
     Example:

From b4b54f000efe3b6d9f0a9eed57865fdfc18fcd4f Mon Sep 17 00:00:00 2001
From: "Michael P. Nitowski" <mpnitowski@gmail.com>
Date: Mon, 6 Apr 2020 08:39:45 -0400
Subject: [PATCH 104/183] Support NumPy's specialized int types in
 builtins.round

---
 src/future/builtins/newround.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/future/builtins/newround.py b/src/future/builtins/newround.py
index e2976a76..b06c1169 100644
--- a/src/future/builtins/newround.py
+++ b/src/future/builtins/newround.py
@@ -32,10 +32,10 @@ def newround(number, ndigits=None):
 
     exponent = Decimal('10') ** (-ndigits)
 
-    if PYPY:
-        # Work around issue #24: round() breaks on PyPy with NumPy's types
-        if 'numpy' in repr(type(number)):
-            number = float(number)
+    # Work around issue #24: round() breaks on PyPy with NumPy's types
+    # Also breaks on CPython with NumPy's specialized int types like uint64
+    if 'numpy' in repr(type(number)):
+        number = float(number)
 
     if isinstance(number, Decimal):
         d = number

From 3ee9e7ffeec7fabed672e2240eac9eca318e4438 Mon Sep 17 00:00:00 2001
From: Liuyang Wan <tsfdye@gmail.com>
Date: Sat, 13 Jun 2020 10:03:05 +0800
Subject: [PATCH 105/183] Add docs building to tox.ini

---
 docs/conf.py |  1 -
 tox.ini      | 10 +++++++++-
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/docs/conf.py b/docs/conf.py
index fd106fa0..6f00536b 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -13,7 +13,6 @@
 
 from __future__ import absolute_import, print_function
 import sys, os
-from future import __version__
 import sphinx_bootstrap_theme
 
 # If extensions (or modules to document with autodoc) are in another directory,
diff --git a/tox.ini b/tox.ini
index f5c013f8..1ca3286b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,7 @@
 [tox]
-envlist = py26,py27,py33,py34,py35,py36,py37
+envlist =
+    py{26,27,33,34,35,36,37},
+    docs
 
 [testenv]
 deps =
@@ -7,3 +9,9 @@ deps =
     unittest2
     py26: importlib
 commands = pytest {posargs}
+
+[testenv:docs]
+deps =
+    sphinx
+    sphinx_bootstrap_theme
+commands = sphinx-build docs build

From e55f91595c88a528e2b305cf67e7d892e58fafdb Mon Sep 17 00:00:00 2001
From: Liuyang Wan <tsfdye@gmail.com>
Date: Sat, 13 Jun 2020 10:11:31 +0800
Subject: [PATCH 106/183] Add initial contributing guide with docs build
 instruction

---
 .github/CONTRIBUTING.md | 8 ++++++++
 1 file changed, 8 insertions(+)
 create mode 100644 .github/CONTRIBUTING.md

diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 00000000..f8a20eda
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,8 @@
+# Contributing
+
+## Build documentation locally
+
+Using tox:
+```shell
+$ tox -e docs
+```

From 9ca5a14504dae9bf317c671f30d9f56fecd8a844 Mon Sep 17 00:00:00 2001
From: Liuyang Wan <tsfdye@gmail.com>
Date: Sun, 14 Jun 2020 14:31:14 +0800
Subject: [PATCH 107/183] Fix various py26 unit test failures

---
 .gitignore                                 | 3 +++
 src/libfuturize/fixes/fix_division_safe.py | 7 ++++++-
 tests/test_future/test_futurize.py         | 1 +
 3 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index 1b142a7a..3b7bce98 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,3 +44,6 @@ nosetests.xml
 
 # PyCharm
 .idea
+
+# Generated test file
+mytempfile.py
diff --git a/src/libfuturize/fixes/fix_division_safe.py b/src/libfuturize/fixes/fix_division_safe.py
index 3d5909cc..65c8c1da 100644
--- a/src/libfuturize/fixes/fix_division_safe.py
+++ b/src/libfuturize/fixes/fix_division_safe.py
@@ -92,7 +92,12 @@ def match(self, node):
                 else:
                     children.append(child.clone())
             if matched:
-                return Node(node.type, children, fixers_applied=node.fixers_applied)
+                # In Python 2.6, `Node` does not have the fixers_applied attribute
+                # https://github.com/python/cpython/blob/8493c0cd66cfc181ac1517268a74f077e9998701/Lib/lib2to3/pytree.py#L235
+                if hasattr(Node, "fixers_applied"):
+                    return Node(node.type, children, fixers_applied=node.fixers_applied)
+                else:
+                    return Node(node.type, children)
 
         return False
 
diff --git a/tests/test_future/test_futurize.py b/tests/test_future/test_futurize.py
index 0d7c42de..c3696a54 100644
--- a/tests/test_future/test_futurize.py
+++ b/tests/test_future/test_futurize.py
@@ -436,6 +436,7 @@ def test_import_builtins(self):
         """
         self.convert_check(before, after, ignore_imports=False, run=False)
 
+    @expectedFailurePY26
     def test_input_without_import(self):
         before = """
         a = input()

From fa73942dfbfa3ca6d56632b70231c1d64daa6291 Mon Sep 17 00:00:00 2001
From: azjps <users.noreply.github.com>
Date: Tue, 16 Jun 2020 02:13:59 -0400
Subject: [PATCH 108/183] Bugfix with undefined children_hooks when package is
 None

touch_import_top() is always called with a package within
the futurize codebase, but it does have a code path where
package can be None (e.g. import six instead of from six
import string_types). Fixed a minor bug in this code path,
so that a custom fixer can use it.

Change-Id: Iec2891586fe852e35a91c69d2fb146645d7c53dd
---
 src/libfuturize/fixer_util.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/libfuturize/fixer_util.py b/src/libfuturize/fixer_util.py
index 48e4689d..b59ad3c2 100644
--- a/src/libfuturize/fixer_util.py
+++ b/src/libfuturize/fixer_util.py
@@ -390,6 +390,7 @@ def touch_import_top(package, name_to_import, node):
                 break
         insert_pos = idx
 
+    children_hooks = []
     if package is None:
         import_ = Node(syms.import_name, [
             Leaf(token.NAME, u"import"),
@@ -413,8 +414,6 @@ def touch_import_top(package, name_to_import, node):
                                  ]
                                 )
             children_hooks = [install_hooks, Newline()]
-        else:
-            children_hooks = []
 
         # FromImport(package, [Leaf(token.NAME, name_to_import, prefix=u" ")])
 

From 1a48f1b70d0b479c535f362ecc1e58b4a0c1c913 Mon Sep 17 00:00:00 2001
From: Liuyang Wan <tsfdye@gmail.com>
Date: Fri, 10 Jul 2020 10:14:08 +0800
Subject: [PATCH 109/183] Pin typing==3.7.4.1 for Python 3.3 compatiblity

---
 .travis.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.travis.yml b/.travis.yml
index 4b74e8d2..1be7fac8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,6 +26,7 @@ matrix:
       sudo: required  # required for Python 3.7 (travis-ci/travis-ci#9069)
 
 install:
+  - pip install typing==3.7.4.1  # required for Python 3.3
   - pip install tox==2.9.1
   - pip install virtualenv==15.2.0
   - pip install py==1.4.30

From 0d95a40fa7ac86d9e0bd4bd54693b42d25f5c0a5 Mon Sep 17 00:00:00 2001
From: "Roman A. Taycher" <rtaycher1987@gmail.com>
Date: Sun, 30 Aug 2020 19:24:54 -0700
Subject: [PATCH 110/183] improve cmp function, add unittest

---
 src/past/builtins/misc.py      |  63 ++++++++++-
 tests/test_past/test_misc.py   |  39 +++++++
 tests/test_past/test_values.py | 201 +++++++++++++++++++++++++++++++++
 3 files changed, 302 insertions(+), 1 deletion(-)
 create mode 100644 tests/test_past/test_misc.py
 create mode 100644 tests/test_past/test_values.py

diff --git a/src/past/builtins/misc.py b/src/past/builtins/misc.py
index ba50aa9e..889e0ff8 100644
--- a/src/past/builtins/misc.py
+++ b/src/past/builtins/misc.py
@@ -1,6 +1,8 @@
 from __future__ import unicode_literals
 
 import inspect
+import math
+import numbers
 
 from future.utils import PY2, PY3, exec_
 
@@ -29,8 +31,67 @@ def cmp(x, y):
         cmp(x, y) -> integer
 
         Return negative if x<y, zero if x==y, positive if x>y.
+        Python2 had looser comparison allowing cmp None and non Numerical types and collections.
+        Try to match the old behavior
         """
-        return (x > y) - (x < y)
+        if isinstance(x, set) and isinstance(y, set):
+            raise TypeError('cannot compare sets using cmp()',)
+        try:
+            if isinstance(x, numbers.Number) and math.isnan(x):
+                if not isinstance(y, numbers.Number):
+                    raise TypeError(f'cannot compare float("nan"), {type(y)} with cmp')
+                if isinstance(y, int):
+                    return 1
+                else:
+                    return -1
+            if isinstance(y, numbers.Number) and math.isnan(y):
+                if not isinstance(x, numbers.Number):
+                    raise TypeError(f'cannot compare {type(x)}, float("nan") with cmp')
+                if isinstance(x, int):
+                    return -1
+                else:
+                    return 1
+            return (x > y) - (x < y)
+        except TypeError:
+            if x == y:
+                return 0
+            type_order = [
+                type(None),
+                numbers.Number,
+                dict, list,
+                set,
+                (str, bytes),
+            ]
+            x_type_index = y_type_index = None
+            for i, type_match in enumerate(type_order):
+                if isinstance(x, type_match):
+                    x_type_index = i
+                if isinstance(y, type_match):
+                    y_type_index = i
+            if cmp(x_type_index, y_type_index) == 0:
+                if isinstance(x, bytes) and isinstance(y, str):
+                    return cmp(x.decode('ascii'), y)
+                if isinstance(y, bytes) and isinstance(x, str):
+                    return cmp(x, y.decode('ascii'))
+                elif isinstance(x, list):
+                    # if both arguments are lists take the comparison of the first non equal value
+                    for x_elem, y_elem in zip(x, y):
+                        elem_cmp_val = cmp(x_elem, y_elem)
+                        if elem_cmp_val != 0:
+                            return elem_cmp_val
+                    # if all elements are equal, return equal/0
+                    return 0
+                elif isinstance(x, dict):
+                    if len(x) != len(y):
+                        return cmp(len(x), len(y))
+                    else:
+                        x_key = min(a for a in x if a not in y or x[a] != y[a])
+                        y_key = min(b for b in y if b not in x or x[b] != y[b])
+                        if x_key != y_key:
+                            return cmp(x_key, y_key)
+                        else:
+                            return cmp(x[x_key], y[y_key])
+            return cmp(x_type_index, y_type_index)
 
     from sys import intern
 
diff --git a/tests/test_past/test_misc.py b/tests/test_past/test_misc.py
new file mode 100644
index 00000000..3d1b7a09
--- /dev/null
+++ b/tests/test_past/test_misc.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+"""
+Tests for the resurrected Py2-like cmp funtion
+"""
+
+from __future__ import absolute_import, unicode_literals, print_function
+
+import os.path
+import sys
+import traceback
+
+from future.tests.base import unittest
+from past.builtins import cmp
+
+_dir = os.path.dirname(os.path.abspath(__file__))
+sys.path.append(_dir)
+import test_values
+
+
+class TestCmp(unittest.TestCase):
+    def test_cmp(self):
+        for x, y, cmp_python2_value in test_values.cmp_python2_value:
+            with self.subTest(x=x, y=y):
+                try:
+                    past_cmp_value = cmp(x, y)
+                except Exception as ex:
+                    past_cmp_value = traceback.format_exc().strip().split('\n')[-1]
+
+                self.assertEqual(cmp_python2_value, past_cmp_value,
+                                 "expected result matching python2 __builtins__.cmp({x!r},{y!r}) "
+                                 "== {cmp_python2_value} "
+                                 "got past.builtins.cmp({x!r},{y!r}) "
+                                 "== {past_cmp_value} "
+                                 "".format(x=x, y=y, past_cmp_value=past_cmp_value,
+                                           cmp_python2_value=cmp_python2_value))
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/test_past/test_values.py b/tests/test_past/test_values.py
new file mode 100644
index 00000000..393ad761
--- /dev/null
+++ b/tests/test_past/test_values.py
@@ -0,0 +1,201 @@
+from math import pi
+
+inf, nan = float('inf'), float('nan')
+test_values = [
+    0, 1, 2, -1, -9999999999, 9999999,
+    0.0, inf, nan, pi,
+    # [], [[]], [1,2,3],
+    set(), set([1, 2, 3]),
+    " ", "", "1", "dsada saA.", "2", "dsa", b"", b"dsa", b" ",
+    {5: 3}, dict(), dict(a=99), dict(a=1, b=2, c=3), None
+]
+
+# cmp_python2_values are pre-calculated from running cmp under python2 first  values are x and y, last is results of cmp
+cmp_python2_value = [[0, 1, -1], [0, 2, -1], [0, -1, 1], [0, -9999999999999999, 1], [0, 9999999999999999, -1],
+                     [0, 0.0, 0], [0, inf, -1], [0, nan, -1], [0, 3.141592653589793, -1], [0, '', -1], [0, ' ', -1],
+                     [0, '1', -1], [0, 'a bee cd.', -1], [0, '', -1], [0, ' ', -1], [0, '1', -1], [0, 'a bee cd.', -1],
+                     [0, set([]), -1], [0, set([1, 2, 3]), -1], [0, {5: 3}, -1], [0, {}, -1], [0, {'a': 99}, -1],
+                     [0, {'a': 1, 'c': 3, 'b': 2}, -1], [0, {'a': 99, 'c': 3, 'b': 5}, -1], [0, None, 1], [1, 0, 1],
+                     [1, 2, -1], [1, -1, 1], [1, -9999999999999999, 1], [1, 9999999999999999, -1], [1, 0.0, 1],
+                     [1, inf, -1], [1, nan, -1], [1, 3.141592653589793, -1], [1, '', -1], [1, ' ', -1], [1, '1', -1],
+                     [1, 'a bee cd.', -1], [1, '', -1], [1, ' ', -1], [1, '1', -1], [1, 'a bee cd.', -1],
+                     [1, set([]), -1], [1, set([1, 2, 3]), -1], [1, {5: 3}, -1], [1, {}, -1], [1, {'a': 99}, -1],
+                     [1, {'a': 1, 'c': 3, 'b': 2}, -1], [1, {'a': 99, 'c': 3, 'b': 5}, -1], [1, None, 1], [2, 0, 1],
+                     [2, 1, 1], [2, -1, 1], [2, -9999999999999999, 1], [2, 9999999999999999, -1], [2, 0.0, 1],
+                     [2, inf, -1], [2, nan, -1], [2, 3.141592653589793, -1], [2, '', -1], [2, ' ', -1], [2, '1', -1],
+                     [2, 'a bee cd.', -1], [2, '', -1], [2, ' ', -1], [2, '1', -1], [2, 'a bee cd.', -1],
+                     [2, set([]), -1], [2, set([1, 2, 3]), -1], [2, {5: 3}, -1], [2, {}, -1], [2, {'a': 99}, -1],
+                     [2, {'a': 1, 'c': 3, 'b': 2}, -1], [2, {'a': 99, 'c': 3, 'b': 5}, -1], [2, None, 1], [-1, 0, -1],
+                     [-1, 1, -1], [-1, 2, -1], [-1, -9999999999999999, 1], [-1, 9999999999999999, -1], [-1, 0.0, -1],
+                     [-1, inf, -1], [-1, nan, -1], [-1, 3.141592653589793, -1], [-1, '', -1], [-1, ' ', -1],
+                     [-1, '1', -1], [-1, 'a bee cd.', -1], [-1, '', -1], [-1, ' ', -1], [-1, '1', -1],
+                     [-1, 'a bee cd.', -1], [-1, set([]), -1], [-1, set([1, 2, 3]), -1], [-1, {5: 3}, -1], [-1, {}, -1],
+                     [-1, {'a': 99}, -1], [-1, {'a': 1, 'c': 3, 'b': 2}, -1], [-1, {'a': 99, 'c': 3, 'b': 5}, -1],
+                     [-1, None, 1], [-9999999999999999, 0, -1], [-9999999999999999, 1, -1],
+                     [-9999999999999999, 2, -1], [-9999999999999999, -1, -1],
+                     [-9999999999999999, 9999999999999999, -1], [-9999999999999999, 0.0, -1],
+                     [-9999999999999999, inf, -1], [-9999999999999999, nan, -1],
+                     [-9999999999999999, 3.141592653589793, -1], [-9999999999999999, '', -1],
+                     [-9999999999999999, ' ', -1], [-9999999999999999, '1', -1],
+                     [-9999999999999999, 'a bee cd.', -1], [-9999999999999999, '', -1], [-9999999999999999, ' ', -1],
+                     [-9999999999999999, '1', -1], [-9999999999999999, 'a bee cd.', -1],
+                     [-9999999999999999, set([]), -1], [-9999999999999999, set([1, 2, 3]), -1],
+                     [-9999999999999999, {5: 3}, -1], [-9999999999999999, {}, -1],
+                     [-9999999999999999, {'a': 99}, -1], [-9999999999999999, {'a': 1, 'c': 3, 'b': 2}, -1],
+                     [-9999999999999999, {'a': 99, 'c': 3, 'b': 5}, -1], [-9999999999999999, None, 1],
+                     [9999999999999999, 0, 1], [9999999999999999, 1, 1], [9999999999999999, 2, 1],
+                     [9999999999999999, -1, 1], [9999999999999999, -9999999999999999, 1],
+                     [9999999999999999, 0.0, 1], [9999999999999999, inf, -1], [9999999999999999, nan, -1],
+                     [9999999999999999, 3.141592653589793, 1], [9999999999999999, '', -1],
+                     [9999999999999999, ' ', -1], [9999999999999999, '1', -1], [9999999999999999, 'a bee cd.', -1],
+                     [9999999999999999, '', -1], [9999999999999999, ' ', -1], [9999999999999999, '1', -1],
+                     [9999999999999999, 'a bee cd.', -1], [9999999999999999, set([]), -1],
+                     [9999999999999999, set([1, 2, 3]), -1], [9999999999999999, {5: 3}, -1],
+                     [9999999999999999, {}, -1], [9999999999999999, {'a': 99}, -1],
+                     [9999999999999999, {'a': 1, 'c': 3, 'b': 2}, -1],
+                     [9999999999999999, {'a': 99, 'c': 3, 'b': 5}, -1], [9999999999999999, None, 1], [0.0, 0, 0],
+                     [0.0, 1, -1], [0.0, 2, -1], [0.0, -1, 1], [0.0, -9999999999999999, 1],
+                     [0.0, 9999999999999999, -1], [0.0, inf, -1], [0.0, nan, 1], [0.0, 3.141592653589793, -1],
+                     [0.0, '', -1], [0.0, ' ', -1], [0.0, '1', -1], [0.0, 'a bee cd.', -1], [0.0, '', -1],
+                     [0.0, ' ', -1], [0.0, '1', -1], [0.0, 'a bee cd.', -1], [0.0, set([]), -1],
+                     [0.0, set([1, 2, 3]), -1], [0.0, {5: 3}, -1], [0.0, {}, -1], [0.0, {'a': 99}, -1],
+                     [0.0, {'a': 1, 'c': 3, 'b': 2}, -1], [0.0, {'a': 99, 'c': 3, 'b': 5}, -1], [0.0, None, 1],
+                     [inf, 0, 1], [inf, 1, 1], [inf, 2, 1], [inf, -1, 1], [inf, -9999999999999999, 1],
+                     [inf, 9999999999999999, 1], [inf, 0.0, 1], [inf, nan, 1], [inf, 3.141592653589793, 1],
+                     [inf, '', -1], [inf, ' ', -1], [inf, '1', -1], [inf, 'a bee cd.', -1], [inf, '', -1],
+                     [inf, ' ', -1], [inf, '1', -1], [inf, 'a bee cd.', -1], [inf, set([]), -1],
+                     [inf, set([1, 2, 3]), -1], [inf, {5: 3}, -1], [inf, {}, -1], [inf, {'a': 99}, -1],
+                     [inf, {'a': 1, 'c': 3, 'b': 2}, -1], [inf, {'a': 99, 'c': 3, 'b': 5}, -1], [inf, None, 1],
+                     [nan, 0, 1], [nan, 1, 1], [nan, 2, 1], [nan, -1, 1], [nan, -9999999999999999, 1],
+                     [nan, 9999999999999999, 1], [nan, 0.0, -1], [nan, inf, -1], [nan, 3.141592653589793, -1],
+                     [nan, '', -1], [nan, ' ', -1], [nan, '1', -1], [nan, 'a bee cd.', -1], [nan, '', -1],
+                     [nan, ' ', -1], [nan, '1', -1], [nan, 'a bee cd.', -1], [nan, set([]), -1],
+                     [nan, set([1, 2, 3]), -1], [nan, {5: 3}, -1], [nan, {}, -1], [nan, {'a': 99}, -1],
+                     [nan, {'a': 1, 'c': 3, 'b': 2}, -1], [nan, {'a': 99, 'c': 3, 'b': 5}, -1], [nan, None, 1],
+                     [3.141592653589793, 0, 1], [3.141592653589793, 1, 1], [3.141592653589793, 2, 1],
+                     [3.141592653589793, -1, 1], [3.141592653589793, -9999999999999999, 1],
+                     [3.141592653589793, 9999999999999999, -1], [3.141592653589793, 0.0, 1],
+                     [3.141592653589793, inf, -1], [3.141592653589793, nan, 1], [3.141592653589793, '', -1],
+                     [3.141592653589793, ' ', -1], [3.141592653589793, '1', -1], [3.141592653589793, 'a bee cd.', -1],
+                     [3.141592653589793, '', -1], [3.141592653589793, ' ', -1], [3.141592653589793, '1', -1],
+                     [3.141592653589793, 'a bee cd.', -1], [3.141592653589793, set([]), -1],
+                     [3.141592653589793, set([1, 2, 3]), -1], [3.141592653589793, {5: 3}, -1],
+                     [3.141592653589793, {}, -1], [3.141592653589793, {'a': 99}, -1],
+                     [3.141592653589793, {'a': 1, 'c': 3, 'b': 2}, -1],
+                     [3.141592653589793, {'a': 99, 'c': 3, 'b': 5}, -1], [3.141592653589793, None, 1], ['', 0, 1],
+                     ['', 1, 1], ['', 2, 1], ['', -1, 1], ['', -9999999999999999, 1], ['', 9999999999999999, 1],
+                     ['', 0.0, 1], ['', inf, 1], ['', nan, 1], ['', 3.141592653589793, 1], ['', ' ', -1], ['', '1', -1],
+                     ['', 'a bee cd.', -1], ['', '', 0], ['', ' ', -1], ['', '1', -1], ['', 'a bee cd.', -1],
+                     ['', set([]), 1], ['', set([1, 2, 3]), 1], ['', {5: 3}, 1], ['', {}, 1], ['', {'a': 99}, 1],
+                     ['', {'a': 1, 'c': 3, 'b': 2}, 1], ['', {'a': 99, 'c': 3, 'b': 5}, 1], ['', None, 1], [' ', 0, 1],
+                     [' ', 1, 1], [' ', 2, 1], [' ', -1, 1], [' ', -9999999999999999, 1], [' ', 9999999999999999, 1],
+                     [' ', 0.0, 1], [' ', inf, 1], [' ', nan, 1], [' ', 3.141592653589793, 1], [' ', '', 1],
+                     [' ', '1', -1], [' ', 'a bee cd.', -1], [' ', '', 1], [' ', ' ', 0], [' ', '1', -1],
+                     [' ', 'a bee cd.', -1], [' ', set([]), 1], [' ', set([1, 2, 3]), 1], [' ', {5: 3}, 1],
+                     [' ', {}, 1], [' ', {'a': 99}, 1], [' ', {'a': 1, 'c': 3, 'b': 2}, 1],
+                     [' ', {'a': 99, 'c': 3, 'b': 5}, 1], [' ', None, 1], ['1', 0, 1], ['1', 1, 1], ['1', 2, 1],
+                     ['1', -1, 1], ['1', -9999999999999999, 1], ['1', 9999999999999999, 1], ['1', 0.0, 1],
+                     ['1', inf, 1], ['1', nan, 1], ['1', 3.141592653589793, 1], ['1', '', 1], ['1', ' ', 1],
+                     ['1', 'a bee cd.', -1], ['1', '', 1], ['1', ' ', 1], ['1', '1', 0], ['1', 'a bee cd.', -1],
+                     ['1', set([]), 1], ['1', set([1, 2, 3]), 1], ['1', {5: 3}, 1], ['1', {}, 1], ['1', {'a': 99}, 1],
+                     ['1', {'a': 1, 'c': 3, 'b': 2}, 1], ['1', {'a': 99, 'c': 3, 'b': 5}, 1], ['1', None, 1],
+                     ['a bee cd.', 0, 1], ['a bee cd.', 1, 1], ['a bee cd.', 2, 1], ['a bee cd.', -1, 1],
+                     ['a bee cd.', -9999999999999999, 1], ['a bee cd.', 9999999999999999, 1], ['a bee cd.', 0.0, 1],
+                     ['a bee cd.', inf, 1], ['a bee cd.', nan, 1], ['a bee cd.', 3.141592653589793, 1],
+                     ['a bee cd.', '', 1], ['a bee cd.', ' ', 1], ['a bee cd.', '1', 1], ['a bee cd.', '', 1],
+                     ['a bee cd.', ' ', 1], ['a bee cd.', '1', 1], ['a bee cd.', 'a bee cd.', 0],
+                     ['a bee cd.', set([]), 1], ['a bee cd.', set([1, 2, 3]), 1], ['a bee cd.', {5: 3}, 1],
+                     ['a bee cd.', {}, 1], ['a bee cd.', {'a': 99}, 1], ['a bee cd.', {'a': 1, 'c': 3, 'b': 2}, 1],
+                     ['a bee cd.', {'a': 99, 'c': 3, 'b': 5}, 1], ['a bee cd.', None, 1], ['', 0, 1], ['', 1, 1],
+                     ['', 2, 1], ['', -1, 1], ['', -9999999999999999, 1], ['', 9999999999999999, 1], ['', 0.0, 1],
+                     ['', inf, 1], ['', nan, 1], ['', 3.141592653589793, 1], ['', '', 0], ['', ' ', -1], ['', '1', -1],
+                     ['', 'a bee cd.', -1], ['', ' ', -1], ['', '1', -1], ['', 'a bee cd.', -1], ['', set([]), 1],
+                     ['', set([1, 2, 3]), 1], ['', {5: 3}, 1], ['', {}, 1], ['', {'a': 99}, 1],
+                     ['', {'a': 1, 'c': 3, 'b': 2}, 1], ['', {'a': 99, 'c': 3, 'b': 5}, 1], ['', None, 1], [' ', 0, 1],
+                     [' ', 1, 1], [' ', 2, 1], [' ', -1, 1], [' ', -9999999999999999, 1], [' ', 9999999999999999, 1],
+                     [' ', 0.0, 1], [' ', inf, 1], [' ', nan, 1], [' ', 3.141592653589793, 1], [' ', '', 1],
+                     [' ', ' ', 0], [' ', '1', -1], [' ', 'a bee cd.', -1], [' ', '', 1], [' ', '1', -1],
+                     [' ', 'a bee cd.', -1], [' ', set([]), 1], [' ', set([1, 2, 3]), 1], [' ', {5: 3}, 1],
+                     [' ', {}, 1], [' ', {'a': 99}, 1], [' ', {'a': 1, 'c': 3, 'b': 2}, 1],
+                     [' ', {'a': 99, 'c': 3, 'b': 5}, 1], [' ', None, 1], ['1', 0, 1], ['1', 1, 1], ['1', 2, 1],
+                     ['1', -1, 1], ['1', -9999999999999999, 1], ['1', 9999999999999999, 1], ['1', 0.0, 1],
+                     ['1', inf, 1], ['1', nan, 1], ['1', 3.141592653589793, 1], ['1', '', 1], ['1', ' ', 1],
+                     ['1', '1', 0], ['1', 'a bee cd.', -1], ['1', '', 1], ['1', ' ', 1], ['1', 'a bee cd.', -1],
+                     ['1', set([]), 1], ['1', set([1, 2, 3]), 1], ['1', {5: 3}, 1], ['1', {}, 1], ['1', {'a': 99}, 1],
+                     ['1', {'a': 1, 'c': 3, 'b': 2}, 1], ['1', {'a': 99, 'c': 3, 'b': 5}, 1], ['1', None, 1],
+                     ['a bee cd.', 0, 1], ['a bee cd.', 1, 1], ['a bee cd.', 2, 1], ['a bee cd.', -1, 1],
+                     ['a bee cd.', -9999999999999999, 1], ['a bee cd.', 9999999999999999, 1], ['a bee cd.', 0.0, 1],
+                     ['a bee cd.', inf, 1], ['a bee cd.', nan, 1], ['a bee cd.', 3.141592653589793, 1],
+                     ['a bee cd.', '', 1], ['a bee cd.', ' ', 1], ['a bee cd.', '1', 1], ['a bee cd.', 'a bee cd.', 0],
+                     ['a bee cd.', '', 1], ['a bee cd.', ' ', 1], ['a bee cd.', '1', 1], ['a bee cd.', set([]), 1],
+                     ['a bee cd.', set([1, 2, 3]), 1], ['a bee cd.', {5: 3}, 1], ['a bee cd.', {}, 1],
+                     ['a bee cd.', {'a': 99}, 1], ['a bee cd.', {'a': 1, 'c': 3, 'b': 2}, 1],
+                     ['a bee cd.', {'a': 99, 'c': 3, 'b': 5}, 1], ['a bee cd.', None, 1], [set([]), 0, 1],
+                     [set([]), 1, 1], [set([]), 2, 1], [set([]), -1, 1], [set([]), -9999999999999999, 1],
+                     [set([]), 9999999999999999, 1], [set([]), 0.0, 1], [set([]), inf, 1], [set([]), nan, 1],
+                     [set([]), 3.141592653589793, 1], [set([]), '', -1], [set([]), ' ', -1], [set([]), '1', -1],
+                     [set([]), 'a bee cd.', -1], [set([]), '', -1], [set([]), ' ', -1], [set([]), '1', -1],
+                     [set([]), 'a bee cd.', -1],
+                     [set([]), set([1, 2, 3]), 'TypeError: cannot compare sets using cmp()'], [set([]), {5: 3}, 1],
+                     [set([]), {}, 1], [set([]), {'a': 99}, 1], [set([]), {'a': 1, 'c': 3, 'b': 2}, 1],
+                     [set([]), {'a': 99, 'c': 3, 'b': 5}, 1], [set([]), None, 1], [set([1, 2, 3]), 0, 1],
+                     [set([1, 2, 3]), 1, 1], [set([1, 2, 3]), 2, 1], [set([1, 2, 3]), -1, 1],
+                     [set([1, 2, 3]), -9999999999999999, 1], [set([1, 2, 3]), 9999999999999999, 1],
+                     [set([1, 2, 3]), 0.0, 1], [set([1, 2, 3]), inf, 1], [set([1, 2, 3]), nan, 1],
+                     [set([1, 2, 3]), 3.141592653589793, 1], [set([1, 2, 3]), '', -1], [set([1, 2, 3]), ' ', -1],
+                     [set([1, 2, 3]), '1', -1], [set([1, 2, 3]), 'a bee cd.', -1], [set([1, 2, 3]), '', -1],
+                     [set([1, 2, 3]), ' ', -1], [set([1, 2, 3]), '1', -1], [set([1, 2, 3]), 'a bee cd.', -1],
+                     [set([1, 2, 3]), set([]), 'TypeError: cannot compare sets using cmp()'],
+                     [set([1, 2, 3]), {5: 3}, 1], [set([1, 2, 3]), {}, 1], [set([1, 2, 3]), {'a': 99}, 1],
+                     [set([1, 2, 3]), {'a': 1, 'c': 3, 'b': 2}, 1], [set([1, 2, 3]), {'a': 99, 'c': 3, 'b': 5}, 1],
+                     [set([1, 2, 3]), None, 1], [{5: 3}, 0, 1], [{5: 3}, 1, 1], [{5: 3}, 2, 1], [{5: 3}, -1, 1],
+                     [{5: 3}, -9999999999999999, 1], [{5: 3}, 9999999999999999, 1], [{5: 3}, 0.0, 1],
+                     [{5: 3}, inf, 1], [{5: 3}, nan, 1], [{5: 3}, 3.141592653589793, 1], [{5: 3}, '', -1],
+                     [{5: 3}, ' ', -1], [{5: 3}, '1', -1], [{5: 3}, 'a bee cd.', -1], [{5: 3}, '', -1],
+                     [{5: 3}, ' ', -1], [{5: 3}, '1', -1], [{5: 3}, 'a bee cd.', -1], [{5: 3}, set([]), -1],
+                     [{5: 3}, set([1, 2, 3]), -1], [{5: 3}, {}, 1], [{5: 3}, {'a': 99}, -1],
+                     [{5: 3}, {'a': 1, 'c': 3, 'b': 2}, -1], [{5: 3}, {'a': 99, 'c': 3, 'b': 5}, -1], [{5: 3}, None, 1],
+                     [{}, 0, 1], [{}, 1, 1], [{}, 2, 1], [{}, -1, 1], [{}, -9999999999999999, 1],
+                     [{}, 9999999999999999, 1], [{}, 0.0, 1], [{}, inf, 1], [{}, nan, 1], [{}, 3.141592653589793, 1],
+                     [{}, '', -1], [{}, ' ', -1], [{}, '1', -1], [{}, 'a bee cd.', -1], [{}, '', -1], [{}, ' ', -1],
+                     [{}, '1', -1], [{}, 'a bee cd.', -1], [{}, set([]), -1], [{}, set([1, 2, 3]), -1],
+                     [{}, {5: 3}, -1], [{}, {'a': 99}, -1], [{}, {'a': 1, 'c': 3, 'b': 2}, -1],
+                     [{}, {'a': 99, 'c': 3, 'b': 5}, -1], [{}, None, 1], [{'a': 99}, 0, 1], [{'a': 99}, 1, 1],
+                     [{'a': 99}, 2, 1], [{'a': 99}, -1, 1], [{'a': 99}, -9999999999999999, 1],
+                     [{'a': 99}, 9999999999999999, 1], [{'a': 99}, 0.0, 1], [{'a': 99}, inf, 1], [{'a': 99}, nan, 1],
+                     [{'a': 99}, 3.141592653589793, 1], [{'a': 99}, '', -1], [{'a': 99}, ' ', -1], [{'a': 99}, '1', -1],
+                     [{'a': 99}, 'a bee cd.', -1], [{'a': 99}, '', -1], [{'a': 99}, ' ', -1], [{'a': 99}, '1', -1],
+                     [{'a': 99}, 'a bee cd.', -1], [{'a': 99}, set([]), -1], [{'a': 99}, set([1, 2, 3]), -1],
+                     [{'a': 99}, {5: 3}, 1], [{'a': 99}, {}, 1], [{'a': 99}, {'a': 1, 'c': 3, 'b': 2}, -1],
+                     [{'a': 99}, {'a': 99, 'c': 3, 'b': 5}, -1], [{'a': 99}, None, 1], [{'a': 1, 'c': 3, 'b': 2}, 0, 1],
+                     [{'a': 1, 'c': 3, 'b': 2}, 1, 1], [{'a': 1, 'c': 3, 'b': 2}, 2, 1],
+                     [{'a': 1, 'c': 3, 'b': 2}, -1, 1], [{'a': 1, 'c': 3, 'b': 2}, -9999999999999999, 1],
+                     [{'a': 1, 'c': 3, 'b': 2}, 9999999999999999, 1], [{'a': 1, 'c': 3, 'b': 2}, 0.0, 1],
+                     [{'a': 1, 'c': 3, 'b': 2}, inf, 1], [{'a': 1, 'c': 3, 'b': 2}, nan, 1],
+                     [{'a': 1, 'c': 3, 'b': 2}, 3.141592653589793, 1], [{'a': 1, 'c': 3, 'b': 2}, '', -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, ' ', -1], [{'a': 1, 'c': 3, 'b': 2}, '1', -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, 'a bee cd.', -1], [{'a': 1, 'c': 3, 'b': 2}, '', -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, ' ', -1], [{'a': 1, 'c': 3, 'b': 2}, '1', -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, 'a bee cd.', -1], [{'a': 1, 'c': 3, 'b': 2}, set([]), -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, set([1, 2, 3]), -1], [{'a': 1, 'c': 3, 'b': 2}, {5: 3}, 1],
+                     [{'a': 1, 'c': 3, 'b': 2}, {}, 1], [{'a': 1, 'c': 3, 'b': 2}, {'a': 99}, 1],
+                     [{'a': 1, 'c': 3, 'b': 2}, {'a': 99, 'c': 3, 'b': 5}, -1], [{'a': 1, 'c': 3, 'b': 2}, None, 1],
+                     [{'a': 99, 'c': 3, 'b': 5}, 0, 1], [{'a': 99, 'c': 3, 'b': 5}, 1, 1],
+                     [{'a': 99, 'c': 3, 'b': 5}, 2, 1], [{'a': 99, 'c': 3, 'b': 5}, -1, 1],
+                     [{'a': 99, 'c': 3, 'b': 5}, -9999999999999999, 1],
+                     [{'a': 99, 'c': 3, 'b': 5}, 9999999999999999, 1], [{'a': 99, 'c': 3, 'b': 5}, 0.0, 1],
+                     [{'a': 99, 'c': 3, 'b': 5}, inf, 1], [{'a': 99, 'c': 3, 'b': 5}, nan, 1],
+                     [{'a': 99, 'c': 3, 'b': 5}, 3.141592653589793, 1], [{'a': 99, 'c': 3, 'b': 5}, '', -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, ' ', -1], [{'a': 99, 'c': 3, 'b': 5}, '1', -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, 'a bee cd.', -1], [{'a': 99, 'c': 3, 'b': 5}, '', -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, ' ', -1], [{'a': 99, 'c': 3, 'b': 5}, '1', -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, 'a bee cd.', -1], [{'a': 99, 'c': 3, 'b': 5}, set([]), -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, set([1, 2, 3]), -1], [{'a': 99, 'c': 3, 'b': 5}, {5: 3}, 1],
+                     [{'a': 99, 'c': 3, 'b': 5}, {}, 1], [{'a': 99, 'c': 3, 'b': 5}, {'a': 99}, 1],
+                     [{'a': 99, 'c': 3, 'b': 5}, {'a': 1, 'c': 3, 'b': 2}, 1], [{'a': 99, 'c': 3, 'b': 5}, None, 1],
+                     [None, 0, -1], [None, 1, -1], [None, 2, -1], [None, -1, -1], [None, -9999999999999999, -1],
+                     [None, 9999999999999999, -1], [None, 0.0, -1], [None, inf, -1], [None, nan, -1],
+                     [None, 3.141592653589793, -1], [None, '', -1], [None, ' ', -1], [None, '1', -1],
+                     [None, 'a bee cd.', -1], [None, '', -1], [None, ' ', -1], [None, '1', -1], [None, 'a bee cd.', -1],
+                     [None, set([]), -1], [None, set([1, 2, 3]), -1], [None, {5: 3}, -1], [None, {}, -1],
+                     [None, {'a': 99}, -1], [None, {'a': 1, 'c': 3, 'b': 2}, -1], [None, {'a': 99, 'c': 3, 'b': 5}, -1]]

From 8302d8c8f1f61c9b1cb978337eddcdcc48701310 Mon Sep 17 00:00:00 2001
From: "Roman A. Taycher" <rtaycher1987@gmail.com>
Date: Sun, 30 Aug 2020 19:37:37 -0700
Subject: [PATCH 111/183] minor style/spelling fixes

---
 src/past/builtins/misc.py    | 4 ++--
 tests/test_past/test_misc.py | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/past/builtins/misc.py b/src/past/builtins/misc.py
index 889e0ff8..6342a8ec 100644
--- a/src/past/builtins/misc.py
+++ b/src/past/builtins/misc.py
@@ -103,7 +103,7 @@ def oct(number):
         return '0' + builtins.oct(number)[2:]
 
     raw_input = input
-    from imp import reload
+    from importlib import reload
     unicode = str
     unichr = chr
     xrange = range
@@ -143,7 +143,7 @@ def execfile(filename, myglobals=None, mylocals=None):
         if not isinstance(mylocals, Mapping):
             raise TypeError('locals must be a mapping')
         with open(filename, "rb") as fin:
-             source = fin.read()
+            source = fin.read()
         code = compile(source, filename, "exec")
         exec_(code, myglobals, mylocals)
 
diff --git a/tests/test_past/test_misc.py b/tests/test_past/test_misc.py
index 3d1b7a09..3952ab19 100644
--- a/tests/test_past/test_misc.py
+++ b/tests/test_past/test_misc.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 """
-Tests for the resurrected Py2-like cmp funtion
+Tests for the resurrected Py2-like cmp function
 """
 
 from __future__ import absolute_import, unicode_literals, print_function
@@ -23,7 +23,7 @@ def test_cmp(self):
             with self.subTest(x=x, y=y):
                 try:
                     past_cmp_value = cmp(x, y)
-                except Exception as ex:
+                except Exception:
                     past_cmp_value = traceback.format_exc().strip().split('\n')[-1]
 
                 self.assertEqual(cmp_python2_value, past_cmp_value,

From 4a687ea190fb3a18e808373fd571adb65218cf9e Mon Sep 17 00:00:00 2001
From: "Roman A. Taycher" <rtaycher1987@gmail.com>
Date: Sun, 30 Aug 2020 20:06:38 -0700
Subject: [PATCH 112/183] replace fstrings with format for python 3.4,3.5

---
 src/past/builtins/misc.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/past/builtins/misc.py b/src/past/builtins/misc.py
index 6342a8ec..dda6ff33 100644
--- a/src/past/builtins/misc.py
+++ b/src/past/builtins/misc.py
@@ -39,14 +39,14 @@ def cmp(x, y):
         try:
             if isinstance(x, numbers.Number) and math.isnan(x):
                 if not isinstance(y, numbers.Number):
-                    raise TypeError(f'cannot compare float("nan"), {type(y)} with cmp')
+                    raise TypeError('cannot compare float("nan"), {type_y} with cmp'.format(type_y=type(y)))
                 if isinstance(y, int):
                     return 1
                 else:
                     return -1
             if isinstance(y, numbers.Number) and math.isnan(y):
                 if not isinstance(x, numbers.Number):
-                    raise TypeError(f'cannot compare {type(x)}, float("nan") with cmp')
+                    raise TypeError('cannot compare {type_x}, float("nan") with cmp'.format(type_x=type(x)))
                 if isinstance(x, int):
                     return -1
                 else:

From fc84fa8dd46d5ccff8d80b5ac1cac68af5c83055 Mon Sep 17 00:00:00 2001
From: "Roman A. Taycher" <rtaycher1987@gmail.com>
Date: Sun, 30 Aug 2020 20:16:59 -0700
Subject: [PATCH 113/183] import from old imp library on older python versions

---
 src/past/builtins/misc.py | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/past/builtins/misc.py b/src/past/builtins/misc.py
index dda6ff33..3600695c 100644
--- a/src/past/builtins/misc.py
+++ b/src/past/builtins/misc.py
@@ -103,7 +103,13 @@ def oct(number):
         return '0' + builtins.oct(number)[2:]
 
     raw_input = input
-    from importlib import reload
+
+    try:
+        from importlib import reload
+    except ImportError:
+        # for python2, python3 <= 3.4
+        from imp import reload
+
     unicode = str
     unichr = chr
     xrange = range

From f006cad1cf66d691086df058698404cd4bdf2216 Mon Sep 17 00:00:00 2001
From: "Roman A. Taycher" <rtaycher1987@gmail.com>
Date: Sun, 30 Aug 2020 20:32:00 -0700
Subject: [PATCH 114/183] fix missing subTest

---
 tests/test_past/test_misc.py | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/tests/test_past/test_misc.py b/tests/test_past/test_misc.py
index 3952ab19..2bbdea2d 100644
--- a/tests/test_past/test_misc.py
+++ b/tests/test_past/test_misc.py
@@ -8,6 +8,7 @@
 import os.path
 import sys
 import traceback
+from contextlib import contextmanager
 
 from future.tests.base import unittest
 from past.builtins import cmp
@@ -17,10 +18,16 @@
 import test_values
 
 
+@contextmanager
+def empty_context_manager(*args, **kwargs):
+    return dict(args=args, kwargs=kwargs)
+
+
 class TestCmp(unittest.TestCase):
     def test_cmp(self):
         for x, y, cmp_python2_value in test_values.cmp_python2_value:
-            with self.subTest(x=x, y=y):
+            # to get this to run on python <3.4 which lacks subTest
+            with getattr(self, 'subTest', empty_context_manager)(x=x, y=y):
                 try:
                     past_cmp_value = cmp(x, y)
                 except Exception:

From c0510266fd62297ea54629bac89cbb4644c8786f Mon Sep 17 00:00:00 2001
From: "Roman A. Taycher" <rtaycher1987@gmail.com>
Date: Sun, 30 Aug 2020 20:42:10 -0700
Subject: [PATCH 115/183] fix other python2 test issues

---
 tests/test_past/test_misc.py | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/tests/test_past/test_misc.py b/tests/test_past/test_misc.py
index 2bbdea2d..db07896f 100644
--- a/tests/test_past/test_misc.py
+++ b/tests/test_past/test_misc.py
@@ -11,7 +11,10 @@
 from contextlib import contextmanager
 
 from future.tests.base import unittest
-from past.builtins import cmp
+from future.utils import PY3
+
+if PY3:
+    from past.builtins import cmp
 
 _dir = os.path.dirname(os.path.abspath(__file__))
 sys.path.append(_dir)
@@ -20,7 +23,7 @@
 
 @contextmanager
 def empty_context_manager(*args, **kwargs):
-    return dict(args=args, kwargs=kwargs)
+    yield dict(args=args, kwargs=kwargs)
 
 
 class TestCmp(unittest.TestCase):

From e3f1a128a978e23b2993222cc3379a2e64f54e9a Mon Sep 17 00:00:00 2001
From: "Roman A. Taycher" <rtaycher1987@gmail.com>
Date: Sun, 30 Aug 2020 21:10:26 -0700
Subject: [PATCH 116/183] include list test values

---
 tests/test_past/test_values.py | 281 +++++++++++++++++++--------------
 1 file changed, 160 insertions(+), 121 deletions(-)

diff --git a/tests/test_past/test_values.py b/tests/test_past/test_values.py
index 393ad761..7c85a8f2 100644
--- a/tests/test_past/test_values.py
+++ b/tests/test_past/test_values.py
@@ -4,7 +4,7 @@
 test_values = [
     0, 1, 2, -1, -9999999999, 9999999,
     0.0, inf, nan, pi,
-    # [], [[]], [1,2,3],
+    [], [[]], [1, 2, 3],
     set(), set([1, 2, 3]),
     " ", "", "1", "dsada saA.", "2", "dsa", b"", b"dsa", b" ",
     {5: 3}, dict(), dict(a=99), dict(a=1, b=2, c=3), None
@@ -12,62 +12,65 @@
 
 # cmp_python2_values are pre-calculated from running cmp under python2 first  values are x and y, last is results of cmp
 cmp_python2_value = [[0, 1, -1], [0, 2, -1], [0, -1, 1], [0, -9999999999999999, 1], [0, 9999999999999999, -1],
-                     [0, 0.0, 0], [0, inf, -1], [0, nan, -1], [0, 3.141592653589793, -1], [0, '', -1], [0, ' ', -1],
-                     [0, '1', -1], [0, 'a bee cd.', -1], [0, '', -1], [0, ' ', -1], [0, '1', -1], [0, 'a bee cd.', -1],
-                     [0, set([]), -1], [0, set([1, 2, 3]), -1], [0, {5: 3}, -1], [0, {}, -1], [0, {'a': 99}, -1],
-                     [0, {'a': 1, 'c': 3, 'b': 2}, -1], [0, {'a': 99, 'c': 3, 'b': 5}, -1], [0, None, 1], [1, 0, 1],
-                     [1, 2, -1], [1, -1, 1], [1, -9999999999999999, 1], [1, 9999999999999999, -1], [1, 0.0, 1],
-                     [1, inf, -1], [1, nan, -1], [1, 3.141592653589793, -1], [1, '', -1], [1, ' ', -1], [1, '1', -1],
-                     [1, 'a bee cd.', -1], [1, '', -1], [1, ' ', -1], [1, '1', -1], [1, 'a bee cd.', -1],
-                     [1, set([]), -1], [1, set([1, 2, 3]), -1], [1, {5: 3}, -1], [1, {}, -1], [1, {'a': 99}, -1],
-                     [1, {'a': 1, 'c': 3, 'b': 2}, -1], [1, {'a': 99, 'c': 3, 'b': 5}, -1], [1, None, 1], [2, 0, 1],
-                     [2, 1, 1], [2, -1, 1], [2, -9999999999999999, 1], [2, 9999999999999999, -1], [2, 0.0, 1],
-                     [2, inf, -1], [2, nan, -1], [2, 3.141592653589793, -1], [2, '', -1], [2, ' ', -1], [2, '1', -1],
+                     [0, 0.0, 0], [0, inf, -1], [0, nan, -1], [0, 3.141592653589793, -1], [0, [], -1], [0, [[]], -1],
+                     [0, [1, 2, 3], -1], [0, '', -1], [0, ' ', -1], [0, '1', -1], [0, 'a bee cd.', -1], [0, '', -1],
+                     [0, ' ', -1], [0, '1', -1], [0, 'a bee cd.', -1], [0, set([]), -1], [0, set([1, 2, 3]), -1],
+                     [0, {5: 3}, -1], [0, {}, -1], [0, {'a': 99}, -1], [0, {'a': 1, 'c': 3, 'b': 2}, -1],
+                     [0, {'a': 99, 'c': 3, 'b': 5}, -1], [0, None, 1], [1, 0, 1], [1, 2, -1], [1, -1, 1],
+                     [1, -9999999999999999, 1], [1, 9999999999999999, -1], [1, 0.0, 1], [1, inf, -1], [1, nan, -1],
+                     [1, 3.141592653589793, -1], [1, [], -1], [1, [[]], -1], [1, [1, 2, 3], -1], [1, '', -1],
+                     [1, ' ', -1], [1, '1', -1], [1, 'a bee cd.', -1], [1, '', -1], [1, ' ', -1], [1, '1', -1],
+                     [1, 'a bee cd.', -1], [1, set([]), -1], [1, set([1, 2, 3]), -1], [1, {5: 3}, -1], [1, {}, -1],
+                     [1, {'a': 99}, -1], [1, {'a': 1, 'c': 3, 'b': 2}, -1], [1, {'a': 99, 'c': 3, 'b': 5}, -1],
+                     [1, None, 1], [2, 0, 1], [2, 1, 1], [2, -1, 1], [2, -9999999999999999, 1],
+                     [2, 9999999999999999, -1], [2, 0.0, 1], [2, inf, -1], [2, nan, -1], [2, 3.141592653589793, -1],
+                     [2, [], -1], [2, [[]], -1], [2, [1, 2, 3], -1], [2, '', -1], [2, ' ', -1], [2, '1', -1],
                      [2, 'a bee cd.', -1], [2, '', -1], [2, ' ', -1], [2, '1', -1], [2, 'a bee cd.', -1],
                      [2, set([]), -1], [2, set([1, 2, 3]), -1], [2, {5: 3}, -1], [2, {}, -1], [2, {'a': 99}, -1],
                      [2, {'a': 1, 'c': 3, 'b': 2}, -1], [2, {'a': 99, 'c': 3, 'b': 5}, -1], [2, None, 1], [-1, 0, -1],
                      [-1, 1, -1], [-1, 2, -1], [-1, -9999999999999999, 1], [-1, 9999999999999999, -1], [-1, 0.0, -1],
-                     [-1, inf, -1], [-1, nan, -1], [-1, 3.141592653589793, -1], [-1, '', -1], [-1, ' ', -1],
-                     [-1, '1', -1], [-1, 'a bee cd.', -1], [-1, '', -1], [-1, ' ', -1], [-1, '1', -1],
-                     [-1, 'a bee cd.', -1], [-1, set([]), -1], [-1, set([1, 2, 3]), -1], [-1, {5: 3}, -1], [-1, {}, -1],
-                     [-1, {'a': 99}, -1], [-1, {'a': 1, 'c': 3, 'b': 2}, -1], [-1, {'a': 99, 'c': 3, 'b': 5}, -1],
-                     [-1, None, 1], [-9999999999999999, 0, -1], [-9999999999999999, 1, -1],
-                     [-9999999999999999, 2, -1], [-9999999999999999, -1, -1],
-                     [-9999999999999999, 9999999999999999, -1], [-9999999999999999, 0.0, -1],
-                     [-9999999999999999, inf, -1], [-9999999999999999, nan, -1],
-                     [-9999999999999999, 3.141592653589793, -1], [-9999999999999999, '', -1],
-                     [-9999999999999999, ' ', -1], [-9999999999999999, '1', -1],
-                     [-9999999999999999, 'a bee cd.', -1], [-9999999999999999, '', -1], [-9999999999999999, ' ', -1],
-                     [-9999999999999999, '1', -1], [-9999999999999999, 'a bee cd.', -1],
-                     [-9999999999999999, set([]), -1], [-9999999999999999, set([1, 2, 3]), -1],
-                     [-9999999999999999, {5: 3}, -1], [-9999999999999999, {}, -1],
-                     [-9999999999999999, {'a': 99}, -1], [-9999999999999999, {'a': 1, 'c': 3, 'b': 2}, -1],
+                     [-1, inf, -1], [-1, nan, -1], [-1, 3.141592653589793, -1], [-1, [], -1], [-1, [[]], -1],
+                     [-1, [1, 2, 3], -1], [-1, '', -1], [-1, ' ', -1], [-1, '1', -1], [-1, 'a bee cd.', -1],
+                     [-1, '', -1], [-1, ' ', -1], [-1, '1', -1], [-1, 'a bee cd.', -1], [-1, set([]), -1],
+                     [-1, set([1, 2, 3]), -1], [-1, {5: 3}, -1], [-1, {}, -1], [-1, {'a': 99}, -1],
+                     [-1, {'a': 1, 'c': 3, 'b': 2}, -1], [-1, {'a': 99, 'c': 3, 'b': 5}, -1], [-1, None, 1],
+                     [-9999999999999999, 0, -1], [-9999999999999999, 1, -1], [-9999999999999999, 2, -1],
+                     [-9999999999999999, -1, -1], [-9999999999999999, 9999999999999999, -1],
+                     [-9999999999999999, 0.0, -1], [-9999999999999999, inf, -1], [-9999999999999999, nan, -1],
+                     [-9999999999999999, 3.141592653589793, -1], [-9999999999999999, [], -1],
+                     [-9999999999999999, [[]], -1], [-9999999999999999, [1, 2, 3], -1], [-9999999999999999, '', -1],
+                     [-9999999999999999, ' ', -1], [-9999999999999999, '1', -1], [-9999999999999999, 'a bee cd.', -1],
+                     [-9999999999999999, '', -1], [-9999999999999999, ' ', -1], [-9999999999999999, '1', -1],
+                     [-9999999999999999, 'a bee cd.', -1], [-9999999999999999, set([]), -1],
+                     [-9999999999999999, set([1, 2, 3]), -1], [-9999999999999999, {5: 3}, -1],
+                     [-9999999999999999, {}, -1], [-9999999999999999, {'a': 99}, -1],
+                     [-9999999999999999, {'a': 1, 'c': 3, 'b': 2}, -1],
                      [-9999999999999999, {'a': 99, 'c': 3, 'b': 5}, -1], [-9999999999999999, None, 1],
                      [9999999999999999, 0, 1], [9999999999999999, 1, 1], [9999999999999999, 2, 1],
-                     [9999999999999999, -1, 1], [9999999999999999, -9999999999999999, 1],
-                     [9999999999999999, 0.0, 1], [9999999999999999, inf, -1], [9999999999999999, nan, -1],
-                     [9999999999999999, 3.141592653589793, 1], [9999999999999999, '', -1],
-                     [9999999999999999, ' ', -1], [9999999999999999, '1', -1], [9999999999999999, 'a bee cd.', -1],
+                     [9999999999999999, -1, 1], [9999999999999999, -9999999999999999, 1], [9999999999999999, 0.0, 1],
+                     [9999999999999999, inf, -1], [9999999999999999, nan, -1], [9999999999999999, 3.141592653589793, 1],
+                     [9999999999999999, [], -1], [9999999999999999, [[]], -1], [9999999999999999, [1, 2, 3], -1],
                      [9999999999999999, '', -1], [9999999999999999, ' ', -1], [9999999999999999, '1', -1],
-                     [9999999999999999, 'a bee cd.', -1], [9999999999999999, set([]), -1],
-                     [9999999999999999, set([1, 2, 3]), -1], [9999999999999999, {5: 3}, -1],
-                     [9999999999999999, {}, -1], [9999999999999999, {'a': 99}, -1],
-                     [9999999999999999, {'a': 1, 'c': 3, 'b': 2}, -1],
+                     [9999999999999999, 'a bee cd.', -1], [9999999999999999, '', -1], [9999999999999999, ' ', -1],
+                     [9999999999999999, '1', -1], [9999999999999999, 'a bee cd.', -1], [9999999999999999, set([]), -1],
+                     [9999999999999999, set([1, 2, 3]), -1], [9999999999999999, {5: 3}, -1], [9999999999999999, {}, -1],
+                     [9999999999999999, {'a': 99}, -1], [9999999999999999, {'a': 1, 'c': 3, 'b': 2}, -1],
                      [9999999999999999, {'a': 99, 'c': 3, 'b': 5}, -1], [9999999999999999, None, 1], [0.0, 0, 0],
-                     [0.0, 1, -1], [0.0, 2, -1], [0.0, -1, 1], [0.0, -9999999999999999, 1],
-                     [0.0, 9999999999999999, -1], [0.0, inf, -1], [0.0, nan, 1], [0.0, 3.141592653589793, -1],
-                     [0.0, '', -1], [0.0, ' ', -1], [0.0, '1', -1], [0.0, 'a bee cd.', -1], [0.0, '', -1],
-                     [0.0, ' ', -1], [0.0, '1', -1], [0.0, 'a bee cd.', -1], [0.0, set([]), -1],
+                     [0.0, 1, -1], [0.0, 2, -1], [0.0, -1, 1], [0.0, -9999999999999999, 1], [0.0, 9999999999999999, -1],
+                     [0.0, inf, -1], [0.0, nan, 1], [0.0, 3.141592653589793, -1], [0.0, [], -1], [0.0, [[]], -1],
+                     [0.0, [1, 2, 3], -1], [0.0, '', -1], [0.0, ' ', -1], [0.0, '1', -1], [0.0, 'a bee cd.', -1],
+                     [0.0, '', -1], [0.0, ' ', -1], [0.0, '1', -1], [0.0, 'a bee cd.', -1], [0.0, set([]), -1],
                      [0.0, set([1, 2, 3]), -1], [0.0, {5: 3}, -1], [0.0, {}, -1], [0.0, {'a': 99}, -1],
                      [0.0, {'a': 1, 'c': 3, 'b': 2}, -1], [0.0, {'a': 99, 'c': 3, 'b': 5}, -1], [0.0, None, 1],
                      [inf, 0, 1], [inf, 1, 1], [inf, 2, 1], [inf, -1, 1], [inf, -9999999999999999, 1],
                      [inf, 9999999999999999, 1], [inf, 0.0, 1], [inf, nan, 1], [inf, 3.141592653589793, 1],
-                     [inf, '', -1], [inf, ' ', -1], [inf, '1', -1], [inf, 'a bee cd.', -1], [inf, '', -1],
-                     [inf, ' ', -1], [inf, '1', -1], [inf, 'a bee cd.', -1], [inf, set([]), -1],
-                     [inf, set([1, 2, 3]), -1], [inf, {5: 3}, -1], [inf, {}, -1], [inf, {'a': 99}, -1],
-                     [inf, {'a': 1, 'c': 3, 'b': 2}, -1], [inf, {'a': 99, 'c': 3, 'b': 5}, -1], [inf, None, 1],
-                     [nan, 0, 1], [nan, 1, 1], [nan, 2, 1], [nan, -1, 1], [nan, -9999999999999999, 1],
-                     [nan, 9999999999999999, 1], [nan, 0.0, -1], [nan, inf, -1], [nan, 3.141592653589793, -1],
+                     [inf, [], -1], [inf, [[]], -1], [inf, [1, 2, 3], -1], [inf, '', -1], [inf, ' ', -1],
+                     [inf, '1', -1], [inf, 'a bee cd.', -1], [inf, '', -1], [inf, ' ', -1], [inf, '1', -1],
+                     [inf, 'a bee cd.', -1], [inf, set([]), -1], [inf, set([1, 2, 3]), -1], [inf, {5: 3}, -1],
+                     [inf, {}, -1], [inf, {'a': 99}, -1], [inf, {'a': 1, 'c': 3, 'b': 2}, -1],
+                     [inf, {'a': 99, 'c': 3, 'b': 5}, -1], [inf, None, 1], [nan, 0, 1], [nan, 1, 1], [nan, 2, 1],
+                     [nan, -1, 1], [nan, -9999999999999999, 1], [nan, 9999999999999999, 1], [nan, 0.0, -1],
+                     [nan, inf, -1], [nan, 3.141592653589793, -1], [nan, [], -1], [nan, [[]], -1], [nan, [1, 2, 3], -1],
                      [nan, '', -1], [nan, ' ', -1], [nan, '1', -1], [nan, 'a bee cd.', -1], [nan, '', -1],
                      [nan, ' ', -1], [nan, '1', -1], [nan, 'a bee cd.', -1], [nan, set([]), -1],
                      [nan, set([1, 2, 3]), -1], [nan, {5: 3}, -1], [nan, {}, -1], [nan, {'a': 99}, -1],
@@ -75,95 +78,126 @@
                      [3.141592653589793, 0, 1], [3.141592653589793, 1, 1], [3.141592653589793, 2, 1],
                      [3.141592653589793, -1, 1], [3.141592653589793, -9999999999999999, 1],
                      [3.141592653589793, 9999999999999999, -1], [3.141592653589793, 0.0, 1],
-                     [3.141592653589793, inf, -1], [3.141592653589793, nan, 1], [3.141592653589793, '', -1],
+                     [3.141592653589793, inf, -1], [3.141592653589793, nan, 1], [3.141592653589793, [], -1],
+                     [3.141592653589793, [[]], -1], [3.141592653589793, [1, 2, 3], -1], [3.141592653589793, '', -1],
                      [3.141592653589793, ' ', -1], [3.141592653589793, '1', -1], [3.141592653589793, 'a bee cd.', -1],
                      [3.141592653589793, '', -1], [3.141592653589793, ' ', -1], [3.141592653589793, '1', -1],
                      [3.141592653589793, 'a bee cd.', -1], [3.141592653589793, set([]), -1],
                      [3.141592653589793, set([1, 2, 3]), -1], [3.141592653589793, {5: 3}, -1],
                      [3.141592653589793, {}, -1], [3.141592653589793, {'a': 99}, -1],
                      [3.141592653589793, {'a': 1, 'c': 3, 'b': 2}, -1],
-                     [3.141592653589793, {'a': 99, 'c': 3, 'b': 5}, -1], [3.141592653589793, None, 1], ['', 0, 1],
-                     ['', 1, 1], ['', 2, 1], ['', -1, 1], ['', -9999999999999999, 1], ['', 9999999999999999, 1],
-                     ['', 0.0, 1], ['', inf, 1], ['', nan, 1], ['', 3.141592653589793, 1], ['', ' ', -1], ['', '1', -1],
-                     ['', 'a bee cd.', -1], ['', '', 0], ['', ' ', -1], ['', '1', -1], ['', 'a bee cd.', -1],
-                     ['', set([]), 1], ['', set([1, 2, 3]), 1], ['', {5: 3}, 1], ['', {}, 1], ['', {'a': 99}, 1],
-                     ['', {'a': 1, 'c': 3, 'b': 2}, 1], ['', {'a': 99, 'c': 3, 'b': 5}, 1], ['', None, 1], [' ', 0, 1],
-                     [' ', 1, 1], [' ', 2, 1], [' ', -1, 1], [' ', -9999999999999999, 1], [' ', 9999999999999999, 1],
-                     [' ', 0.0, 1], [' ', inf, 1], [' ', nan, 1], [' ', 3.141592653589793, 1], [' ', '', 1],
-                     [' ', '1', -1], [' ', 'a bee cd.', -1], [' ', '', 1], [' ', ' ', 0], [' ', '1', -1],
-                     [' ', 'a bee cd.', -1], [' ', set([]), 1], [' ', set([1, 2, 3]), 1], [' ', {5: 3}, 1],
-                     [' ', {}, 1], [' ', {'a': 99}, 1], [' ', {'a': 1, 'c': 3, 'b': 2}, 1],
+                     [3.141592653589793, {'a': 99, 'c': 3, 'b': 5}, -1], [3.141592653589793, None, 1], [[], 0, 1],
+                     [[], 1, 1], [[], 2, 1], [[], -1, 1], [[], -9999999999999999, 1], [[], 9999999999999999, 1],
+                     [[], 0.0, 1], [[], inf, 1], [[], nan, 1], [[], 3.141592653589793, 1], [[], [[]], -1],
+                     [[], [1, 2, 3], -1], [[], '', -1], [[], ' ', -1], [[], '1', -1], [[], 'a bee cd.', -1],
+                     [[], '', -1], [[], ' ', -1], [[], '1', -1], [[], 'a bee cd.', -1], [[], set([]), -1],
+                     [[], set([1, 2, 3]), -1], [[], {5: 3}, 1], [[], {}, 1], [[], {'a': 99}, 1],
+                     [[], {'a': 1, 'c': 3, 'b': 2}, 1], [[], {'a': 99, 'c': 3, 'b': 5}, 1], [[], None, 1], [[[]], 0, 1],
+                     [[[]], 1, 1], [[[]], 2, 1], [[[]], -1, 1], [[[]], -9999999999999999, 1],
+                     [[[]], 9999999999999999, 1], [[[]], 0.0, 1], [[[]], inf, 1], [[[]], nan, 1],
+                     [[[]], 3.141592653589793, 1], [[[]], [], 1], [[[]], [1, 2, 3], 1], [[[]], '', -1], [[[]], ' ', -1],
+                     [[[]], '1', -1], [[[]], 'a bee cd.', -1], [[[]], '', -1], [[[]], ' ', -1], [[[]], '1', -1],
+                     [[[]], 'a bee cd.', -1], [[[]], set([]), -1], [[[]], set([1, 2, 3]), -1], [[[]], {5: 3}, 1],
+                     [[[]], {}, 1], [[[]], {'a': 99}, 1], [[[]], {'a': 1, 'c': 3, 'b': 2}, 1],
+                     [[[]], {'a': 99, 'c': 3, 'b': 5}, 1], [[[]], None, 1], [[1, 2, 3], 0, 1], [[1, 2, 3], 1, 1],
+                     [[1, 2, 3], 2, 1], [[1, 2, 3], -1, 1], [[1, 2, 3], -9999999999999999, 1],
+                     [[1, 2, 3], 9999999999999999, 1], [[1, 2, 3], 0.0, 1], [[1, 2, 3], inf, 1], [[1, 2, 3], nan, 1],
+                     [[1, 2, 3], 3.141592653589793, 1], [[1, 2, 3], [], 1], [[1, 2, 3], [[]], -1], [[1, 2, 3], '', -1],
+                     [[1, 2, 3], ' ', -1], [[1, 2, 3], '1', -1], [[1, 2, 3], 'a bee cd.', -1], [[1, 2, 3], '', -1],
+                     [[1, 2, 3], ' ', -1], [[1, 2, 3], '1', -1], [[1, 2, 3], 'a bee cd.', -1], [[1, 2, 3], set([]), -1],
+                     [[1, 2, 3], set([1, 2, 3]), -1], [[1, 2, 3], {5: 3}, 1], [[1, 2, 3], {}, 1],
+                     [[1, 2, 3], {'a': 99}, 1], [[1, 2, 3], {'a': 1, 'c': 3, 'b': 2}, 1],
+                     [[1, 2, 3], {'a': 99, 'c': 3, 'b': 5}, 1], [[1, 2, 3], None, 1], ['', 0, 1], ['', 1, 1],
+                     ['', 2, 1], ['', -1, 1], ['', -9999999999999999, 1], ['', 9999999999999999, 1], ['', 0.0, 1],
+                     ['', inf, 1], ['', nan, 1], ['', 3.141592653589793, 1], ['', [], 1], ['', [[]], 1],
+                     ['', [1, 2, 3], 1], ['', ' ', -1], ['', '1', -1], ['', 'a bee cd.', -1], ['', '', 0],
+                     ['', ' ', -1], ['', '1', -1], ['', 'a bee cd.', -1], ['', set([]), 1], ['', set([1, 2, 3]), 1],
+                     ['', {5: 3}, 1], ['', {}, 1], ['', {'a': 99}, 1], ['', {'a': 1, 'c': 3, 'b': 2}, 1],
+                     ['', {'a': 99, 'c': 3, 'b': 5}, 1], ['', None, 1], [' ', 0, 1], [' ', 1, 1], [' ', 2, 1],
+                     [' ', -1, 1], [' ', -9999999999999999, 1], [' ', 9999999999999999, 1], [' ', 0.0, 1],
+                     [' ', inf, 1], [' ', nan, 1], [' ', 3.141592653589793, 1], [' ', [], 1], [' ', [[]], 1],
+                     [' ', [1, 2, 3], 1], [' ', '', 1], [' ', '1', -1], [' ', 'a bee cd.', -1], [' ', '', 1],
+                     [' ', ' ', 0], [' ', '1', -1], [' ', 'a bee cd.', -1], [' ', set([]), 1], [' ', set([1, 2, 3]), 1],
+                     [' ', {5: 3}, 1], [' ', {}, 1], [' ', {'a': 99}, 1], [' ', {'a': 1, 'c': 3, 'b': 2}, 1],
                      [' ', {'a': 99, 'c': 3, 'b': 5}, 1], [' ', None, 1], ['1', 0, 1], ['1', 1, 1], ['1', 2, 1],
                      ['1', -1, 1], ['1', -9999999999999999, 1], ['1', 9999999999999999, 1], ['1', 0.0, 1],
-                     ['1', inf, 1], ['1', nan, 1], ['1', 3.141592653589793, 1], ['1', '', 1], ['1', ' ', 1],
-                     ['1', 'a bee cd.', -1], ['1', '', 1], ['1', ' ', 1], ['1', '1', 0], ['1', 'a bee cd.', -1],
-                     ['1', set([]), 1], ['1', set([1, 2, 3]), 1], ['1', {5: 3}, 1], ['1', {}, 1], ['1', {'a': 99}, 1],
-                     ['1', {'a': 1, 'c': 3, 'b': 2}, 1], ['1', {'a': 99, 'c': 3, 'b': 5}, 1], ['1', None, 1],
-                     ['a bee cd.', 0, 1], ['a bee cd.', 1, 1], ['a bee cd.', 2, 1], ['a bee cd.', -1, 1],
-                     ['a bee cd.', -9999999999999999, 1], ['a bee cd.', 9999999999999999, 1], ['a bee cd.', 0.0, 1],
-                     ['a bee cd.', inf, 1], ['a bee cd.', nan, 1], ['a bee cd.', 3.141592653589793, 1],
-                     ['a bee cd.', '', 1], ['a bee cd.', ' ', 1], ['a bee cd.', '1', 1], ['a bee cd.', '', 1],
-                     ['a bee cd.', ' ', 1], ['a bee cd.', '1', 1], ['a bee cd.', 'a bee cd.', 0],
-                     ['a bee cd.', set([]), 1], ['a bee cd.', set([1, 2, 3]), 1], ['a bee cd.', {5: 3}, 1],
-                     ['a bee cd.', {}, 1], ['a bee cd.', {'a': 99}, 1], ['a bee cd.', {'a': 1, 'c': 3, 'b': 2}, 1],
-                     ['a bee cd.', {'a': 99, 'c': 3, 'b': 5}, 1], ['a bee cd.', None, 1], ['', 0, 1], ['', 1, 1],
-                     ['', 2, 1], ['', -1, 1], ['', -9999999999999999, 1], ['', 9999999999999999, 1], ['', 0.0, 1],
-                     ['', inf, 1], ['', nan, 1], ['', 3.141592653589793, 1], ['', '', 0], ['', ' ', -1], ['', '1', -1],
-                     ['', 'a bee cd.', -1], ['', ' ', -1], ['', '1', -1], ['', 'a bee cd.', -1], ['', set([]), 1],
-                     ['', set([1, 2, 3]), 1], ['', {5: 3}, 1], ['', {}, 1], ['', {'a': 99}, 1],
-                     ['', {'a': 1, 'c': 3, 'b': 2}, 1], ['', {'a': 99, 'c': 3, 'b': 5}, 1], ['', None, 1], [' ', 0, 1],
-                     [' ', 1, 1], [' ', 2, 1], [' ', -1, 1], [' ', -9999999999999999, 1], [' ', 9999999999999999, 1],
-                     [' ', 0.0, 1], [' ', inf, 1], [' ', nan, 1], [' ', 3.141592653589793, 1], [' ', '', 1],
+                     ['1', inf, 1], ['1', nan, 1], ['1', 3.141592653589793, 1], ['1', [], 1], ['1', [[]], 1],
+                     ['1', [1, 2, 3], 1], ['1', '', 1], ['1', ' ', 1], ['1', 'a bee cd.', -1], ['1', '', 1],
+                     ['1', ' ', 1], ['1', '1', 0], ['1', 'a bee cd.', -1], ['1', set([]), 1], ['1', set([1, 2, 3]), 1],
+                     ['1', {5: 3}, 1], ['1', {}, 1], ['1', {'a': 99}, 1], ['1', {'a': 1, 'c': 3, 'b': 2}, 1],
+                     ['1', {'a': 99, 'c': 3, 'b': 5}, 1], ['1', None, 1], ['a bee cd.', 0, 1], ['a bee cd.', 1, 1],
+                     ['a bee cd.', 2, 1], ['a bee cd.', -1, 1], ['a bee cd.', -9999999999999999, 1],
+                     ['a bee cd.', 9999999999999999, 1], ['a bee cd.', 0.0, 1], ['a bee cd.', inf, 1],
+                     ['a bee cd.', nan, 1], ['a bee cd.', 3.141592653589793, 1], ['a bee cd.', [], 1],
+                     ['a bee cd.', [[]], 1], ['a bee cd.', [1, 2, 3], 1], ['a bee cd.', '', 1], ['a bee cd.', ' ', 1],
+                     ['a bee cd.', '1', 1], ['a bee cd.', '', 1], ['a bee cd.', ' ', 1], ['a bee cd.', '1', 1],
+                     ['a bee cd.', 'a bee cd.', 0], ['a bee cd.', set([]), 1], ['a bee cd.', set([1, 2, 3]), 1],
+                     ['a bee cd.', {5: 3}, 1], ['a bee cd.', {}, 1], ['a bee cd.', {'a': 99}, 1],
+                     ['a bee cd.', {'a': 1, 'c': 3, 'b': 2}, 1], ['a bee cd.', {'a': 99, 'c': 3, 'b': 5}, 1],
+                     ['a bee cd.', None, 1], ['', 0, 1], ['', 1, 1], ['', 2, 1], ['', -1, 1],
+                     ['', -9999999999999999, 1], ['', 9999999999999999, 1], ['', 0.0, 1], ['', inf, 1], ['', nan, 1],
+                     ['', 3.141592653589793, 1], ['', [], 1], ['', [[]], 1], ['', [1, 2, 3], 1], ['', '', 0],
+                     ['', ' ', -1], ['', '1', -1], ['', 'a bee cd.', -1], ['', ' ', -1], ['', '1', -1],
+                     ['', 'a bee cd.', -1], ['', set([]), 1], ['', set([1, 2, 3]), 1], ['', {5: 3}, 1], ['', {}, 1],
+                     ['', {'a': 99}, 1], ['', {'a': 1, 'c': 3, 'b': 2}, 1], ['', {'a': 99, 'c': 3, 'b': 5}, 1],
+                     ['', None, 1], [' ', 0, 1], [' ', 1, 1], [' ', 2, 1], [' ', -1, 1], [' ', -9999999999999999, 1],
+                     [' ', 9999999999999999, 1], [' ', 0.0, 1], [' ', inf, 1], [' ', nan, 1],
+                     [' ', 3.141592653589793, 1], [' ', [], 1], [' ', [[]], 1], [' ', [1, 2, 3], 1], [' ', '', 1],
                      [' ', ' ', 0], [' ', '1', -1], [' ', 'a bee cd.', -1], [' ', '', 1], [' ', '1', -1],
                      [' ', 'a bee cd.', -1], [' ', set([]), 1], [' ', set([1, 2, 3]), 1], [' ', {5: 3}, 1],
                      [' ', {}, 1], [' ', {'a': 99}, 1], [' ', {'a': 1, 'c': 3, 'b': 2}, 1],
                      [' ', {'a': 99, 'c': 3, 'b': 5}, 1], [' ', None, 1], ['1', 0, 1], ['1', 1, 1], ['1', 2, 1],
                      ['1', -1, 1], ['1', -9999999999999999, 1], ['1', 9999999999999999, 1], ['1', 0.0, 1],
-                     ['1', inf, 1], ['1', nan, 1], ['1', 3.141592653589793, 1], ['1', '', 1], ['1', ' ', 1],
-                     ['1', '1', 0], ['1', 'a bee cd.', -1], ['1', '', 1], ['1', ' ', 1], ['1', 'a bee cd.', -1],
-                     ['1', set([]), 1], ['1', set([1, 2, 3]), 1], ['1', {5: 3}, 1], ['1', {}, 1], ['1', {'a': 99}, 1],
-                     ['1', {'a': 1, 'c': 3, 'b': 2}, 1], ['1', {'a': 99, 'c': 3, 'b': 5}, 1], ['1', None, 1],
-                     ['a bee cd.', 0, 1], ['a bee cd.', 1, 1], ['a bee cd.', 2, 1], ['a bee cd.', -1, 1],
-                     ['a bee cd.', -9999999999999999, 1], ['a bee cd.', 9999999999999999, 1], ['a bee cd.', 0.0, 1],
-                     ['a bee cd.', inf, 1], ['a bee cd.', nan, 1], ['a bee cd.', 3.141592653589793, 1],
-                     ['a bee cd.', '', 1], ['a bee cd.', ' ', 1], ['a bee cd.', '1', 1], ['a bee cd.', 'a bee cd.', 0],
-                     ['a bee cd.', '', 1], ['a bee cd.', ' ', 1], ['a bee cd.', '1', 1], ['a bee cd.', set([]), 1],
-                     ['a bee cd.', set([1, 2, 3]), 1], ['a bee cd.', {5: 3}, 1], ['a bee cd.', {}, 1],
-                     ['a bee cd.', {'a': 99}, 1], ['a bee cd.', {'a': 1, 'c': 3, 'b': 2}, 1],
-                     ['a bee cd.', {'a': 99, 'c': 3, 'b': 5}, 1], ['a bee cd.', None, 1], [set([]), 0, 1],
-                     [set([]), 1, 1], [set([]), 2, 1], [set([]), -1, 1], [set([]), -9999999999999999, 1],
-                     [set([]), 9999999999999999, 1], [set([]), 0.0, 1], [set([]), inf, 1], [set([]), nan, 1],
-                     [set([]), 3.141592653589793, 1], [set([]), '', -1], [set([]), ' ', -1], [set([]), '1', -1],
-                     [set([]), 'a bee cd.', -1], [set([]), '', -1], [set([]), ' ', -1], [set([]), '1', -1],
-                     [set([]), 'a bee cd.', -1],
+                     ['1', inf, 1], ['1', nan, 1], ['1', 3.141592653589793, 1], ['1', [], 1], ['1', [[]], 1],
+                     ['1', [1, 2, 3], 1], ['1', '', 1], ['1', ' ', 1], ['1', '1', 0], ['1', 'a bee cd.', -1],
+                     ['1', '', 1], ['1', ' ', 1], ['1', 'a bee cd.', -1], ['1', set([]), 1], ['1', set([1, 2, 3]), 1],
+                     ['1', {5: 3}, 1], ['1', {}, 1], ['1', {'a': 99}, 1], ['1', {'a': 1, 'c': 3, 'b': 2}, 1],
+                     ['1', {'a': 99, 'c': 3, 'b': 5}, 1], ['1', None, 1], ['a bee cd.', 0, 1], ['a bee cd.', 1, 1],
+                     ['a bee cd.', 2, 1], ['a bee cd.', -1, 1], ['a bee cd.', -9999999999999999, 1],
+                     ['a bee cd.', 9999999999999999, 1], ['a bee cd.', 0.0, 1], ['a bee cd.', inf, 1],
+                     ['a bee cd.', nan, 1], ['a bee cd.', 3.141592653589793, 1], ['a bee cd.', [], 1],
+                     ['a bee cd.', [[]], 1], ['a bee cd.', [1, 2, 3], 1], ['a bee cd.', '', 1], ['a bee cd.', ' ', 1],
+                     ['a bee cd.', '1', 1], ['a bee cd.', 'a bee cd.', 0], ['a bee cd.', '', 1], ['a bee cd.', ' ', 1],
+                     ['a bee cd.', '1', 1], ['a bee cd.', set([]), 1], ['a bee cd.', set([1, 2, 3]), 1],
+                     ['a bee cd.', {5: 3}, 1], ['a bee cd.', {}, 1], ['a bee cd.', {'a': 99}, 1],
+                     ['a bee cd.', {'a': 1, 'c': 3, 'b': 2}, 1], ['a bee cd.', {'a': 99, 'c': 3, 'b': 5}, 1],
+                     ['a bee cd.', None, 1], [set([]), 0, 1], [set([]), 1, 1], [set([]), 2, 1], [set([]), -1, 1],
+                     [set([]), -9999999999999999, 1], [set([]), 9999999999999999, 1], [set([]), 0.0, 1],
+                     [set([]), inf, 1], [set([]), nan, 1], [set([]), 3.141592653589793, 1], [set([]), [], 1],
+                     [set([]), [[]], 1], [set([]), [1, 2, 3], 1], [set([]), '', -1], [set([]), ' ', -1],
+                     [set([]), '1', -1], [set([]), 'a bee cd.', -1], [set([]), '', -1], [set([]), ' ', -1],
+                     [set([]), '1', -1], [set([]), 'a bee cd.', -1],
                      [set([]), set([1, 2, 3]), 'TypeError: cannot compare sets using cmp()'], [set([]), {5: 3}, 1],
                      [set([]), {}, 1], [set([]), {'a': 99}, 1], [set([]), {'a': 1, 'c': 3, 'b': 2}, 1],
                      [set([]), {'a': 99, 'c': 3, 'b': 5}, 1], [set([]), None, 1], [set([1, 2, 3]), 0, 1],
                      [set([1, 2, 3]), 1, 1], [set([1, 2, 3]), 2, 1], [set([1, 2, 3]), -1, 1],
                      [set([1, 2, 3]), -9999999999999999, 1], [set([1, 2, 3]), 9999999999999999, 1],
                      [set([1, 2, 3]), 0.0, 1], [set([1, 2, 3]), inf, 1], [set([1, 2, 3]), nan, 1],
-                     [set([1, 2, 3]), 3.141592653589793, 1], [set([1, 2, 3]), '', -1], [set([1, 2, 3]), ' ', -1],
+                     [set([1, 2, 3]), 3.141592653589793, 1], [set([1, 2, 3]), [], 1], [set([1, 2, 3]), [[]], 1],
+                     [set([1, 2, 3]), [1, 2, 3], 1], [set([1, 2, 3]), '', -1], [set([1, 2, 3]), ' ', -1],
                      [set([1, 2, 3]), '1', -1], [set([1, 2, 3]), 'a bee cd.', -1], [set([1, 2, 3]), '', -1],
                      [set([1, 2, 3]), ' ', -1], [set([1, 2, 3]), '1', -1], [set([1, 2, 3]), 'a bee cd.', -1],
                      [set([1, 2, 3]), set([]), 'TypeError: cannot compare sets using cmp()'],
                      [set([1, 2, 3]), {5: 3}, 1], [set([1, 2, 3]), {}, 1], [set([1, 2, 3]), {'a': 99}, 1],
                      [set([1, 2, 3]), {'a': 1, 'c': 3, 'b': 2}, 1], [set([1, 2, 3]), {'a': 99, 'c': 3, 'b': 5}, 1],
                      [set([1, 2, 3]), None, 1], [{5: 3}, 0, 1], [{5: 3}, 1, 1], [{5: 3}, 2, 1], [{5: 3}, -1, 1],
-                     [{5: 3}, -9999999999999999, 1], [{5: 3}, 9999999999999999, 1], [{5: 3}, 0.0, 1],
-                     [{5: 3}, inf, 1], [{5: 3}, nan, 1], [{5: 3}, 3.141592653589793, 1], [{5: 3}, '', -1],
-                     [{5: 3}, ' ', -1], [{5: 3}, '1', -1], [{5: 3}, 'a bee cd.', -1], [{5: 3}, '', -1],
-                     [{5: 3}, ' ', -1], [{5: 3}, '1', -1], [{5: 3}, 'a bee cd.', -1], [{5: 3}, set([]), -1],
-                     [{5: 3}, set([1, 2, 3]), -1], [{5: 3}, {}, 1], [{5: 3}, {'a': 99}, -1],
-                     [{5: 3}, {'a': 1, 'c': 3, 'b': 2}, -1], [{5: 3}, {'a': 99, 'c': 3, 'b': 5}, -1], [{5: 3}, None, 1],
-                     [{}, 0, 1], [{}, 1, 1], [{}, 2, 1], [{}, -1, 1], [{}, -9999999999999999, 1],
-                     [{}, 9999999999999999, 1], [{}, 0.0, 1], [{}, inf, 1], [{}, nan, 1], [{}, 3.141592653589793, 1],
+                     [{5: 3}, -9999999999999999, 1], [{5: 3}, 9999999999999999, 1], [{5: 3}, 0.0, 1], [{5: 3}, inf, 1],
+                     [{5: 3}, nan, 1], [{5: 3}, 3.141592653589793, 1], [{5: 3}, [], -1], [{5: 3}, [[]], -1],
+                     [{5: 3}, [1, 2, 3], -1], [{5: 3}, '', -1], [{5: 3}, ' ', -1], [{5: 3}, '1', -1],
+                     [{5: 3}, 'a bee cd.', -1], [{5: 3}, '', -1], [{5: 3}, ' ', -1], [{5: 3}, '1', -1],
+                     [{5: 3}, 'a bee cd.', -1], [{5: 3}, set([]), -1], [{5: 3}, set([1, 2, 3]), -1], [{5: 3}, {}, 1],
+                     [{5: 3}, {'a': 99}, -1], [{5: 3}, {'a': 1, 'c': 3, 'b': 2}, -1],
+                     [{5: 3}, {'a': 99, 'c': 3, 'b': 5}, -1], [{5: 3}, None, 1], [{}, 0, 1], [{}, 1, 1], [{}, 2, 1],
+                     [{}, -1, 1], [{}, -9999999999999999, 1], [{}, 9999999999999999, 1], [{}, 0.0, 1], [{}, inf, 1],
+                     [{}, nan, 1], [{}, 3.141592653589793, 1], [{}, [], -1], [{}, [[]], -1], [{}, [1, 2, 3], -1],
                      [{}, '', -1], [{}, ' ', -1], [{}, '1', -1], [{}, 'a bee cd.', -1], [{}, '', -1], [{}, ' ', -1],
                      [{}, '1', -1], [{}, 'a bee cd.', -1], [{}, set([]), -1], [{}, set([1, 2, 3]), -1],
                      [{}, {5: 3}, -1], [{}, {'a': 99}, -1], [{}, {'a': 1, 'c': 3, 'b': 2}, -1],
                      [{}, {'a': 99, 'c': 3, 'b': 5}, -1], [{}, None, 1], [{'a': 99}, 0, 1], [{'a': 99}, 1, 1],
                      [{'a': 99}, 2, 1], [{'a': 99}, -1, 1], [{'a': 99}, -9999999999999999, 1],
                      [{'a': 99}, 9999999999999999, 1], [{'a': 99}, 0.0, 1], [{'a': 99}, inf, 1], [{'a': 99}, nan, 1],
-                     [{'a': 99}, 3.141592653589793, 1], [{'a': 99}, '', -1], [{'a': 99}, ' ', -1], [{'a': 99}, '1', -1],
+                     [{'a': 99}, 3.141592653589793, 1], [{'a': 99}, [], -1], [{'a': 99}, [[]], -1],
+                     [{'a': 99}, [1, 2, 3], -1], [{'a': 99}, '', -1], [{'a': 99}, ' ', -1], [{'a': 99}, '1', -1],
                      [{'a': 99}, 'a bee cd.', -1], [{'a': 99}, '', -1], [{'a': 99}, ' ', -1], [{'a': 99}, '1', -1],
                      [{'a': 99}, 'a bee cd.', -1], [{'a': 99}, set([]), -1], [{'a': 99}, set([1, 2, 3]), -1],
                      [{'a': 99}, {5: 3}, 1], [{'a': 99}, {}, 1], [{'a': 99}, {'a': 1, 'c': 3, 'b': 2}, -1],
@@ -172,30 +206,35 @@
                      [{'a': 1, 'c': 3, 'b': 2}, -1, 1], [{'a': 1, 'c': 3, 'b': 2}, -9999999999999999, 1],
                      [{'a': 1, 'c': 3, 'b': 2}, 9999999999999999, 1], [{'a': 1, 'c': 3, 'b': 2}, 0.0, 1],
                      [{'a': 1, 'c': 3, 'b': 2}, inf, 1], [{'a': 1, 'c': 3, 'b': 2}, nan, 1],
-                     [{'a': 1, 'c': 3, 'b': 2}, 3.141592653589793, 1], [{'a': 1, 'c': 3, 'b': 2}, '', -1],
-                     [{'a': 1, 'c': 3, 'b': 2}, ' ', -1], [{'a': 1, 'c': 3, 'b': 2}, '1', -1],
-                     [{'a': 1, 'c': 3, 'b': 2}, 'a bee cd.', -1], [{'a': 1, 'c': 3, 'b': 2}, '', -1],
-                     [{'a': 1, 'c': 3, 'b': 2}, ' ', -1], [{'a': 1, 'c': 3, 'b': 2}, '1', -1],
-                     [{'a': 1, 'c': 3, 'b': 2}, 'a bee cd.', -1], [{'a': 1, 'c': 3, 'b': 2}, set([]), -1],
-                     [{'a': 1, 'c': 3, 'b': 2}, set([1, 2, 3]), -1], [{'a': 1, 'c': 3, 'b': 2}, {5: 3}, 1],
-                     [{'a': 1, 'c': 3, 'b': 2}, {}, 1], [{'a': 1, 'c': 3, 'b': 2}, {'a': 99}, 1],
+                     [{'a': 1, 'c': 3, 'b': 2}, 3.141592653589793, 1], [{'a': 1, 'c': 3, 'b': 2}, [], -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, [[]], -1], [{'a': 1, 'c': 3, 'b': 2}, [1, 2, 3], -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, '', -1], [{'a': 1, 'c': 3, 'b': 2}, ' ', -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, '1', -1], [{'a': 1, 'c': 3, 'b': 2}, 'a bee cd.', -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, '', -1], [{'a': 1, 'c': 3, 'b': 2}, ' ', -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, '1', -1], [{'a': 1, 'c': 3, 'b': 2}, 'a bee cd.', -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, set([]), -1], [{'a': 1, 'c': 3, 'b': 2}, set([1, 2, 3]), -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, {5: 3}, 1], [{'a': 1, 'c': 3, 'b': 2}, {}, 1],
+                     [{'a': 1, 'c': 3, 'b': 2}, {'a': 99}, 1],
                      [{'a': 1, 'c': 3, 'b': 2}, {'a': 99, 'c': 3, 'b': 5}, -1], [{'a': 1, 'c': 3, 'b': 2}, None, 1],
                      [{'a': 99, 'c': 3, 'b': 5}, 0, 1], [{'a': 99, 'c': 3, 'b': 5}, 1, 1],
                      [{'a': 99, 'c': 3, 'b': 5}, 2, 1], [{'a': 99, 'c': 3, 'b': 5}, -1, 1],
                      [{'a': 99, 'c': 3, 'b': 5}, -9999999999999999, 1],
                      [{'a': 99, 'c': 3, 'b': 5}, 9999999999999999, 1], [{'a': 99, 'c': 3, 'b': 5}, 0.0, 1],
                      [{'a': 99, 'c': 3, 'b': 5}, inf, 1], [{'a': 99, 'c': 3, 'b': 5}, nan, 1],
-                     [{'a': 99, 'c': 3, 'b': 5}, 3.141592653589793, 1], [{'a': 99, 'c': 3, 'b': 5}, '', -1],
-                     [{'a': 99, 'c': 3, 'b': 5}, ' ', -1], [{'a': 99, 'c': 3, 'b': 5}, '1', -1],
-                     [{'a': 99, 'c': 3, 'b': 5}, 'a bee cd.', -1], [{'a': 99, 'c': 3, 'b': 5}, '', -1],
-                     [{'a': 99, 'c': 3, 'b': 5}, ' ', -1], [{'a': 99, 'c': 3, 'b': 5}, '1', -1],
-                     [{'a': 99, 'c': 3, 'b': 5}, 'a bee cd.', -1], [{'a': 99, 'c': 3, 'b': 5}, set([]), -1],
-                     [{'a': 99, 'c': 3, 'b': 5}, set([1, 2, 3]), -1], [{'a': 99, 'c': 3, 'b': 5}, {5: 3}, 1],
-                     [{'a': 99, 'c': 3, 'b': 5}, {}, 1], [{'a': 99, 'c': 3, 'b': 5}, {'a': 99}, 1],
+                     [{'a': 99, 'c': 3, 'b': 5}, 3.141592653589793, 1], [{'a': 99, 'c': 3, 'b': 5}, [], -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, [[]], -1], [{'a': 99, 'c': 3, 'b': 5}, [1, 2, 3], -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, '', -1], [{'a': 99, 'c': 3, 'b': 5}, ' ', -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, '1', -1], [{'a': 99, 'c': 3, 'b': 5}, 'a bee cd.', -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, '', -1], [{'a': 99, 'c': 3, 'b': 5}, ' ', -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, '1', -1], [{'a': 99, 'c': 3, 'b': 5}, 'a bee cd.', -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, set([]), -1], [{'a': 99, 'c': 3, 'b': 5}, set([1, 2, 3]), -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, {5: 3}, 1], [{'a': 99, 'c': 3, 'b': 5}, {}, 1],
+                     [{'a': 99, 'c': 3, 'b': 5}, {'a': 99}, 1],
                      [{'a': 99, 'c': 3, 'b': 5}, {'a': 1, 'c': 3, 'b': 2}, 1], [{'a': 99, 'c': 3, 'b': 5}, None, 1],
                      [None, 0, -1], [None, 1, -1], [None, 2, -1], [None, -1, -1], [None, -9999999999999999, -1],
                      [None, 9999999999999999, -1], [None, 0.0, -1], [None, inf, -1], [None, nan, -1],
-                     [None, 3.141592653589793, -1], [None, '', -1], [None, ' ', -1], [None, '1', -1],
-                     [None, 'a bee cd.', -1], [None, '', -1], [None, ' ', -1], [None, '1', -1], [None, 'a bee cd.', -1],
-                     [None, set([]), -1], [None, set([1, 2, 3]), -1], [None, {5: 3}, -1], [None, {}, -1],
-                     [None, {'a': 99}, -1], [None, {'a': 1, 'c': 3, 'b': 2}, -1], [None, {'a': 99, 'c': 3, 'b': 5}, -1]]
+                     [None, 3.141592653589793, -1], [None, [], -1], [None, [[]], -1], [None, [1, 2, 3], -1],
+                     [None, '', -1], [None, ' ', -1], [None, '1', -1], [None, 'a bee cd.', -1], [None, '', -1],
+                     [None, ' ', -1], [None, '1', -1], [None, 'a bee cd.', -1], [None, set([]), -1],
+                     [None, set([1, 2, 3]), -1], [None, {5: 3}, -1], [None, {}, -1], [None, {'a': 99}, -1],
+                     [None, {'a': 1, 'c': 3, 'b': 2}, -1], [None, {'a': 99, 'c': 3, 'b': 5}, -1]]

From 4dbded1fa5a25087acf1dc073268f2d427fba402 Mon Sep 17 00:00:00 2001
From: "Roman A. Taycher" <rtaycher1987@gmail.com>
Date: Sun, 30 Aug 2020 21:23:37 -0700
Subject: [PATCH 117/183] remove nan test

---
 tests/test_past/test_values.py | 275 ++++++++++++++++-----------------
 1 file changed, 130 insertions(+), 145 deletions(-)

diff --git a/tests/test_past/test_values.py b/tests/test_past/test_values.py
index 7c85a8f2..11872084 100644
--- a/tests/test_past/test_values.py
+++ b/tests/test_past/test_values.py
@@ -3,7 +3,7 @@
 inf, nan = float('inf'), float('nan')
 test_values = [
     0, 1, 2, -1, -9999999999, 9999999,
-    0.0, inf, nan, pi,
+    0.0, inf, pi,
     [], [[]], [1, 2, 3],
     set(), set([1, 2, 3]),
     " ", "", "1", "dsada saA.", "2", "dsa", b"", b"dsa", b" ",
@@ -12,96 +12,86 @@
 
 # cmp_python2_values are pre-calculated from running cmp under python2 first  values are x and y, last is results of cmp
 cmp_python2_value = [[0, 1, -1], [0, 2, -1], [0, -1, 1], [0, -9999999999999999, 1], [0, 9999999999999999, -1],
-                     [0, 0.0, 0], [0, inf, -1], [0, nan, -1], [0, 3.141592653589793, -1], [0, [], -1], [0, [[]], -1],
+                     [0, 0.0, 0], [0, inf, -1], [0, 3.141592653589793, -1], [0, [], -1], [0, [[]], -1],
                      [0, [1, 2, 3], -1], [0, '', -1], [0, ' ', -1], [0, '1', -1], [0, 'a bee cd.', -1], [0, '', -1],
                      [0, ' ', -1], [0, '1', -1], [0, 'a bee cd.', -1], [0, set([]), -1], [0, set([1, 2, 3]), -1],
                      [0, {5: 3}, -1], [0, {}, -1], [0, {'a': 99}, -1], [0, {'a': 1, 'c': 3, 'b': 2}, -1],
                      [0, {'a': 99, 'c': 3, 'b': 5}, -1], [0, None, 1], [1, 0, 1], [1, 2, -1], [1, -1, 1],
-                     [1, -9999999999999999, 1], [1, 9999999999999999, -1], [1, 0.0, 1], [1, inf, -1], [1, nan, -1],
+                     [1, -9999999999999999, 1], [1, 9999999999999999, -1], [1, 0.0, 1], [1, inf, -1],
                      [1, 3.141592653589793, -1], [1, [], -1], [1, [[]], -1], [1, [1, 2, 3], -1], [1, '', -1],
                      [1, ' ', -1], [1, '1', -1], [1, 'a bee cd.', -1], [1, '', -1], [1, ' ', -1], [1, '1', -1],
                      [1, 'a bee cd.', -1], [1, set([]), -1], [1, set([1, 2, 3]), -1], [1, {5: 3}, -1], [1, {}, -1],
                      [1, {'a': 99}, -1], [1, {'a': 1, 'c': 3, 'b': 2}, -1], [1, {'a': 99, 'c': 3, 'b': 5}, -1],
                      [1, None, 1], [2, 0, 1], [2, 1, 1], [2, -1, 1], [2, -9999999999999999, 1],
-                     [2, 9999999999999999, -1], [2, 0.0, 1], [2, inf, -1], [2, nan, -1], [2, 3.141592653589793, -1],
-                     [2, [], -1], [2, [[]], -1], [2, [1, 2, 3], -1], [2, '', -1], [2, ' ', -1], [2, '1', -1],
-                     [2, 'a bee cd.', -1], [2, '', -1], [2, ' ', -1], [2, '1', -1], [2, 'a bee cd.', -1],
-                     [2, set([]), -1], [2, set([1, 2, 3]), -1], [2, {5: 3}, -1], [2, {}, -1], [2, {'a': 99}, -1],
+                     [2, 9999999999999999, -1], [2, 0.0, 1], [2, inf, -1], [2, 3.141592653589793, -1], [2, [], -1],
+                     [2, [[]], -1], [2, [1, 2, 3], -1], [2, '', -1], [2, ' ', -1], [2, '1', -1], [2, 'a bee cd.', -1],
+                     [2, '', -1], [2, ' ', -1], [2, '1', -1], [2, 'a bee cd.', -1], [2, set([]), -1],
+                     [2, set([1, 2, 3]), -1], [2, {5: 3}, -1], [2, {}, -1], [2, {'a': 99}, -1],
                      [2, {'a': 1, 'c': 3, 'b': 2}, -1], [2, {'a': 99, 'c': 3, 'b': 5}, -1], [2, None, 1], [-1, 0, -1],
                      [-1, 1, -1], [-1, 2, -1], [-1, -9999999999999999, 1], [-1, 9999999999999999, -1], [-1, 0.0, -1],
-                     [-1, inf, -1], [-1, nan, -1], [-1, 3.141592653589793, -1], [-1, [], -1], [-1, [[]], -1],
-                     [-1, [1, 2, 3], -1], [-1, '', -1], [-1, ' ', -1], [-1, '1', -1], [-1, 'a bee cd.', -1],
-                     [-1, '', -1], [-1, ' ', -1], [-1, '1', -1], [-1, 'a bee cd.', -1], [-1, set([]), -1],
-                     [-1, set([1, 2, 3]), -1], [-1, {5: 3}, -1], [-1, {}, -1], [-1, {'a': 99}, -1],
-                     [-1, {'a': 1, 'c': 3, 'b': 2}, -1], [-1, {'a': 99, 'c': 3, 'b': 5}, -1], [-1, None, 1],
-                     [-9999999999999999, 0, -1], [-9999999999999999, 1, -1], [-9999999999999999, 2, -1],
-                     [-9999999999999999, -1, -1], [-9999999999999999, 9999999999999999, -1],
-                     [-9999999999999999, 0.0, -1], [-9999999999999999, inf, -1], [-9999999999999999, nan, -1],
-                     [-9999999999999999, 3.141592653589793, -1], [-9999999999999999, [], -1],
-                     [-9999999999999999, [[]], -1], [-9999999999999999, [1, 2, 3], -1], [-9999999999999999, '', -1],
-                     [-9999999999999999, ' ', -1], [-9999999999999999, '1', -1], [-9999999999999999, 'a bee cd.', -1],
+                     [-1, inf, -1], [-1, 3.141592653589793, -1], [-1, [], -1], [-1, [[]], -1], [-1, [1, 2, 3], -1],
+                     [-1, '', -1], [-1, ' ', -1], [-1, '1', -1], [-1, 'a bee cd.', -1], [-1, '', -1], [-1, ' ', -1],
+                     [-1, '1', -1], [-1, 'a bee cd.', -1], [-1, set([]), -1], [-1, set([1, 2, 3]), -1],
+                     [-1, {5: 3}, -1], [-1, {}, -1], [-1, {'a': 99}, -1], [-1, {'a': 1, 'c': 3, 'b': 2}, -1],
+                     [-1, {'a': 99, 'c': 3, 'b': 5}, -1], [-1, None, 1], [-9999999999999999, 0, -1],
+                     [-9999999999999999, 1, -1], [-9999999999999999, 2, -1], [-9999999999999999, -1, -1],
+                     [-9999999999999999, 9999999999999999, -1], [-9999999999999999, 0.0, -1],
+                     [-9999999999999999, inf, -1], [-9999999999999999, 3.141592653589793, -1],
+                     [-9999999999999999, [], -1], [-9999999999999999, [[]], -1], [-9999999999999999, [1, 2, 3], -1],
                      [-9999999999999999, '', -1], [-9999999999999999, ' ', -1], [-9999999999999999, '1', -1],
-                     [-9999999999999999, 'a bee cd.', -1], [-9999999999999999, set([]), -1],
-                     [-9999999999999999, set([1, 2, 3]), -1], [-9999999999999999, {5: 3}, -1],
-                     [-9999999999999999, {}, -1], [-9999999999999999, {'a': 99}, -1],
+                     [-9999999999999999, 'a bee cd.', -1], [-9999999999999999, '', -1], [-9999999999999999, ' ', -1],
+                     [-9999999999999999, '1', -1], [-9999999999999999, 'a bee cd.', -1],
+                     [-9999999999999999, set([]), -1], [-9999999999999999, set([1, 2, 3]), -1],
+                     [-9999999999999999, {5: 3}, -1], [-9999999999999999, {}, -1], [-9999999999999999, {'a': 99}, -1],
                      [-9999999999999999, {'a': 1, 'c': 3, 'b': 2}, -1],
                      [-9999999999999999, {'a': 99, 'c': 3, 'b': 5}, -1], [-9999999999999999, None, 1],
                      [9999999999999999, 0, 1], [9999999999999999, 1, 1], [9999999999999999, 2, 1],
                      [9999999999999999, -1, 1], [9999999999999999, -9999999999999999, 1], [9999999999999999, 0.0, 1],
-                     [9999999999999999, inf, -1], [9999999999999999, nan, -1], [9999999999999999, 3.141592653589793, 1],
-                     [9999999999999999, [], -1], [9999999999999999, [[]], -1], [9999999999999999, [1, 2, 3], -1],
+                     [9999999999999999, inf, -1], [9999999999999999, 3.141592653589793, 1], [9999999999999999, [], -1],
+                     [9999999999999999, [[]], -1], [9999999999999999, [1, 2, 3], -1], [9999999999999999, '', -1],
+                     [9999999999999999, ' ', -1], [9999999999999999, '1', -1], [9999999999999999, 'a bee cd.', -1],
                      [9999999999999999, '', -1], [9999999999999999, ' ', -1], [9999999999999999, '1', -1],
-                     [9999999999999999, 'a bee cd.', -1], [9999999999999999, '', -1], [9999999999999999, ' ', -1],
-                     [9999999999999999, '1', -1], [9999999999999999, 'a bee cd.', -1], [9999999999999999, set([]), -1],
+                     [9999999999999999, 'a bee cd.', -1], [9999999999999999, set([]), -1],
                      [9999999999999999, set([1, 2, 3]), -1], [9999999999999999, {5: 3}, -1], [9999999999999999, {}, -1],
                      [9999999999999999, {'a': 99}, -1], [9999999999999999, {'a': 1, 'c': 3, 'b': 2}, -1],
                      [9999999999999999, {'a': 99, 'c': 3, 'b': 5}, -1], [9999999999999999, None, 1], [0.0, 0, 0],
                      [0.0, 1, -1], [0.0, 2, -1], [0.0, -1, 1], [0.0, -9999999999999999, 1], [0.0, 9999999999999999, -1],
-                     [0.0, inf, -1], [0.0, nan, 1], [0.0, 3.141592653589793, -1], [0.0, [], -1], [0.0, [[]], -1],
-                     [0.0, [1, 2, 3], -1], [0.0, '', -1], [0.0, ' ', -1], [0.0, '1', -1], [0.0, 'a bee cd.', -1],
-                     [0.0, '', -1], [0.0, ' ', -1], [0.0, '1', -1], [0.0, 'a bee cd.', -1], [0.0, set([]), -1],
+                     [0.0, inf, -1], [0.0, 3.141592653589793, -1], [0.0, [], -1], [0.0, [[]], -1], [0.0, [1, 2, 3], -1],
+                     [0.0, '', -1], [0.0, ' ', -1], [0.0, '1', -1], [0.0, 'a bee cd.', -1], [0.0, '', -1],
+                     [0.0, ' ', -1], [0.0, '1', -1], [0.0, 'a bee cd.', -1], [0.0, set([]), -1],
                      [0.0, set([1, 2, 3]), -1], [0.0, {5: 3}, -1], [0.0, {}, -1], [0.0, {'a': 99}, -1],
                      [0.0, {'a': 1, 'c': 3, 'b': 2}, -1], [0.0, {'a': 99, 'c': 3, 'b': 5}, -1], [0.0, None, 1],
                      [inf, 0, 1], [inf, 1, 1], [inf, 2, 1], [inf, -1, 1], [inf, -9999999999999999, 1],
-                     [inf, 9999999999999999, 1], [inf, 0.0, 1], [inf, nan, 1], [inf, 3.141592653589793, 1],
-                     [inf, [], -1], [inf, [[]], -1], [inf, [1, 2, 3], -1], [inf, '', -1], [inf, ' ', -1],
-                     [inf, '1', -1], [inf, 'a bee cd.', -1], [inf, '', -1], [inf, ' ', -1], [inf, '1', -1],
-                     [inf, 'a bee cd.', -1], [inf, set([]), -1], [inf, set([1, 2, 3]), -1], [inf, {5: 3}, -1],
-                     [inf, {}, -1], [inf, {'a': 99}, -1], [inf, {'a': 1, 'c': 3, 'b': 2}, -1],
-                     [inf, {'a': 99, 'c': 3, 'b': 5}, -1], [inf, None, 1], [nan, 0, 1], [nan, 1, 1], [nan, 2, 1],
-                     [nan, -1, 1], [nan, -9999999999999999, 1], [nan, 9999999999999999, 1], [nan, 0.0, -1],
-                     [nan, inf, -1], [nan, 3.141592653589793, -1], [nan, [], -1], [nan, [[]], -1], [nan, [1, 2, 3], -1],
-                     [nan, '', -1], [nan, ' ', -1], [nan, '1', -1], [nan, 'a bee cd.', -1], [nan, '', -1],
-                     [nan, ' ', -1], [nan, '1', -1], [nan, 'a bee cd.', -1], [nan, set([]), -1],
-                     [nan, set([1, 2, 3]), -1], [nan, {5: 3}, -1], [nan, {}, -1], [nan, {'a': 99}, -1],
-                     [nan, {'a': 1, 'c': 3, 'b': 2}, -1], [nan, {'a': 99, 'c': 3, 'b': 5}, -1], [nan, None, 1],
-                     [3.141592653589793, 0, 1], [3.141592653589793, 1, 1], [3.141592653589793, 2, 1],
+                     [inf, 9999999999999999, 1], [inf, 0.0, 1], [inf, 3.141592653589793, 1], [inf, [], -1],
+                     [inf, [[]], -1], [inf, [1, 2, 3], -1], [inf, '', -1], [inf, ' ', -1], [inf, '1', -1],
+                     [inf, 'a bee cd.', -1], [inf, '', -1], [inf, ' ', -1], [inf, '1', -1], [inf, 'a bee cd.', -1],
+                     [inf, set([]), -1], [inf, set([1, 2, 3]), -1], [inf, {5: 3}, -1], [inf, {}, -1],
+                     [inf, {'a': 99}, -1], [inf, {'a': 1, 'c': 3, 'b': 2}, -1], [inf, {'a': 99, 'c': 3, 'b': 5}, -1],
+                     [inf, None, 1], [3.141592653589793, 0, 1], [3.141592653589793, 1, 1], [3.141592653589793, 2, 1],
                      [3.141592653589793, -1, 1], [3.141592653589793, -9999999999999999, 1],
                      [3.141592653589793, 9999999999999999, -1], [3.141592653589793, 0.0, 1],
-                     [3.141592653589793, inf, -1], [3.141592653589793, nan, 1], [3.141592653589793, [], -1],
-                     [3.141592653589793, [[]], -1], [3.141592653589793, [1, 2, 3], -1], [3.141592653589793, '', -1],
+                     [3.141592653589793, inf, -1], [3.141592653589793, [], -1], [3.141592653589793, [[]], -1],
+                     [3.141592653589793, [1, 2, 3], -1], [3.141592653589793, '', -1], [3.141592653589793, ' ', -1],
+                     [3.141592653589793, '1', -1], [3.141592653589793, 'a bee cd.', -1], [3.141592653589793, '', -1],
                      [3.141592653589793, ' ', -1], [3.141592653589793, '1', -1], [3.141592653589793, 'a bee cd.', -1],
-                     [3.141592653589793, '', -1], [3.141592653589793, ' ', -1], [3.141592653589793, '1', -1],
-                     [3.141592653589793, 'a bee cd.', -1], [3.141592653589793, set([]), -1],
-                     [3.141592653589793, set([1, 2, 3]), -1], [3.141592653589793, {5: 3}, -1],
-                     [3.141592653589793, {}, -1], [3.141592653589793, {'a': 99}, -1],
+                     [3.141592653589793, set([]), -1], [3.141592653589793, set([1, 2, 3]), -1],
+                     [3.141592653589793, {5: 3}, -1], [3.141592653589793, {}, -1], [3.141592653589793, {'a': 99}, -1],
                      [3.141592653589793, {'a': 1, 'c': 3, 'b': 2}, -1],
                      [3.141592653589793, {'a': 99, 'c': 3, 'b': 5}, -1], [3.141592653589793, None, 1], [[], 0, 1],
                      [[], 1, 1], [[], 2, 1], [[], -1, 1], [[], -9999999999999999, 1], [[], 9999999999999999, 1],
-                     [[], 0.0, 1], [[], inf, 1], [[], nan, 1], [[], 3.141592653589793, 1], [[], [[]], -1],
-                     [[], [1, 2, 3], -1], [[], '', -1], [[], ' ', -1], [[], '1', -1], [[], 'a bee cd.', -1],
-                     [[], '', -1], [[], ' ', -1], [[], '1', -1], [[], 'a bee cd.', -1], [[], set([]), -1],
-                     [[], set([1, 2, 3]), -1], [[], {5: 3}, 1], [[], {}, 1], [[], {'a': 99}, 1],
-                     [[], {'a': 1, 'c': 3, 'b': 2}, 1], [[], {'a': 99, 'c': 3, 'b': 5}, 1], [[], None, 1], [[[]], 0, 1],
-                     [[[]], 1, 1], [[[]], 2, 1], [[[]], -1, 1], [[[]], -9999999999999999, 1],
-                     [[[]], 9999999999999999, 1], [[[]], 0.0, 1], [[[]], inf, 1], [[[]], nan, 1],
-                     [[[]], 3.141592653589793, 1], [[[]], [], 1], [[[]], [1, 2, 3], 1], [[[]], '', -1], [[[]], ' ', -1],
-                     [[[]], '1', -1], [[[]], 'a bee cd.', -1], [[[]], '', -1], [[[]], ' ', -1], [[[]], '1', -1],
-                     [[[]], 'a bee cd.', -1], [[[]], set([]), -1], [[[]], set([1, 2, 3]), -1], [[[]], {5: 3}, 1],
-                     [[[]], {}, 1], [[[]], {'a': 99}, 1], [[[]], {'a': 1, 'c': 3, 'b': 2}, 1],
+                     [[], 0.0, 1], [[], inf, 1], [[], 3.141592653589793, 1], [[], [[]], -1], [[], [1, 2, 3], -1],
+                     [[], '', -1], [[], ' ', -1], [[], '1', -1], [[], 'a bee cd.', -1], [[], '', -1], [[], ' ', -1],
+                     [[], '1', -1], [[], 'a bee cd.', -1], [[], set([]), -1], [[], set([1, 2, 3]), -1], [[], {5: 3}, 1],
+                     [[], {}, 1], [[], {'a': 99}, 1], [[], {'a': 1, 'c': 3, 'b': 2}, 1],
+                     [[], {'a': 99, 'c': 3, 'b': 5}, 1], [[], None, 1], [[[]], 0, 1], [[[]], 1, 1], [[[]], 2, 1],
+                     [[[]], -1, 1], [[[]], -9999999999999999, 1], [[[]], 9999999999999999, 1], [[[]], 0.0, 1],
+                     [[[]], inf, 1], [[[]], 3.141592653589793, 1], [[[]], [], 1], [[[]], [1, 2, 3], 1], [[[]], '', -1],
+                     [[[]], ' ', -1], [[[]], '1', -1], [[[]], 'a bee cd.', -1], [[[]], '', -1], [[[]], ' ', -1],
+                     [[[]], '1', -1], [[[]], 'a bee cd.', -1], [[[]], set([]), -1], [[[]], set([1, 2, 3]), -1],
+                     [[[]], {5: 3}, 1], [[[]], {}, 1], [[[]], {'a': 99}, 1], [[[]], {'a': 1, 'c': 3, 'b': 2}, 1],
                      [[[]], {'a': 99, 'c': 3, 'b': 5}, 1], [[[]], None, 1], [[1, 2, 3], 0, 1], [[1, 2, 3], 1, 1],
                      [[1, 2, 3], 2, 1], [[1, 2, 3], -1, 1], [[1, 2, 3], -9999999999999999, 1],
-                     [[1, 2, 3], 9999999999999999, 1], [[1, 2, 3], 0.0, 1], [[1, 2, 3], inf, 1], [[1, 2, 3], nan, 1],
+                     [[1, 2, 3], 9999999999999999, 1], [[1, 2, 3], 0.0, 1], [[1, 2, 3], inf, 1],
                      [[1, 2, 3], 3.141592653589793, 1], [[1, 2, 3], [], 1], [[1, 2, 3], [[]], -1], [[1, 2, 3], '', -1],
                      [[1, 2, 3], ' ', -1], [[1, 2, 3], '1', -1], [[1, 2, 3], 'a bee cd.', -1], [[1, 2, 3], '', -1],
                      [[1, 2, 3], ' ', -1], [[1, 2, 3], '1', -1], [[1, 2, 3], 'a bee cd.', -1], [[1, 2, 3], set([]), -1],
@@ -109,53 +99,51 @@
                      [[1, 2, 3], {'a': 99}, 1], [[1, 2, 3], {'a': 1, 'c': 3, 'b': 2}, 1],
                      [[1, 2, 3], {'a': 99, 'c': 3, 'b': 5}, 1], [[1, 2, 3], None, 1], ['', 0, 1], ['', 1, 1],
                      ['', 2, 1], ['', -1, 1], ['', -9999999999999999, 1], ['', 9999999999999999, 1], ['', 0.0, 1],
-                     ['', inf, 1], ['', nan, 1], ['', 3.141592653589793, 1], ['', [], 1], ['', [[]], 1],
-                     ['', [1, 2, 3], 1], ['', ' ', -1], ['', '1', -1], ['', 'a bee cd.', -1], ['', '', 0],
-                     ['', ' ', -1], ['', '1', -1], ['', 'a bee cd.', -1], ['', set([]), 1], ['', set([1, 2, 3]), 1],
-                     ['', {5: 3}, 1], ['', {}, 1], ['', {'a': 99}, 1], ['', {'a': 1, 'c': 3, 'b': 2}, 1],
-                     ['', {'a': 99, 'c': 3, 'b': 5}, 1], ['', None, 1], [' ', 0, 1], [' ', 1, 1], [' ', 2, 1],
-                     [' ', -1, 1], [' ', -9999999999999999, 1], [' ', 9999999999999999, 1], [' ', 0.0, 1],
-                     [' ', inf, 1], [' ', nan, 1], [' ', 3.141592653589793, 1], [' ', [], 1], [' ', [[]], 1],
-                     [' ', [1, 2, 3], 1], [' ', '', 1], [' ', '1', -1], [' ', 'a bee cd.', -1], [' ', '', 1],
-                     [' ', ' ', 0], [' ', '1', -1], [' ', 'a bee cd.', -1], [' ', set([]), 1], [' ', set([1, 2, 3]), 1],
-                     [' ', {5: 3}, 1], [' ', {}, 1], [' ', {'a': 99}, 1], [' ', {'a': 1, 'c': 3, 'b': 2}, 1],
-                     [' ', {'a': 99, 'c': 3, 'b': 5}, 1], [' ', None, 1], ['1', 0, 1], ['1', 1, 1], ['1', 2, 1],
-                     ['1', -1, 1], ['1', -9999999999999999, 1], ['1', 9999999999999999, 1], ['1', 0.0, 1],
-                     ['1', inf, 1], ['1', nan, 1], ['1', 3.141592653589793, 1], ['1', [], 1], ['1', [[]], 1],
-                     ['1', [1, 2, 3], 1], ['1', '', 1], ['1', ' ', 1], ['1', 'a bee cd.', -1], ['1', '', 1],
-                     ['1', ' ', 1], ['1', '1', 0], ['1', 'a bee cd.', -1], ['1', set([]), 1], ['1', set([1, 2, 3]), 1],
-                     ['1', {5: 3}, 1], ['1', {}, 1], ['1', {'a': 99}, 1], ['1', {'a': 1, 'c': 3, 'b': 2}, 1],
-                     ['1', {'a': 99, 'c': 3, 'b': 5}, 1], ['1', None, 1], ['a bee cd.', 0, 1], ['a bee cd.', 1, 1],
-                     ['a bee cd.', 2, 1], ['a bee cd.', -1, 1], ['a bee cd.', -9999999999999999, 1],
-                     ['a bee cd.', 9999999999999999, 1], ['a bee cd.', 0.0, 1], ['a bee cd.', inf, 1],
-                     ['a bee cd.', nan, 1], ['a bee cd.', 3.141592653589793, 1], ['a bee cd.', [], 1],
+                     ['', inf, 1], ['', 3.141592653589793, 1], ['', [], 1], ['', [[]], 1], ['', [1, 2, 3], 1],
+                     ['', ' ', -1], ['', '1', -1], ['', 'a bee cd.', -1], ['', '', 0], ['', ' ', -1], ['', '1', -1],
+                     ['', 'a bee cd.', -1], ['', set([]), 1], ['', set([1, 2, 3]), 1], ['', {5: 3}, 1], ['', {}, 1],
+                     ['', {'a': 99}, 1], ['', {'a': 1, 'c': 3, 'b': 2}, 1], ['', {'a': 99, 'c': 3, 'b': 5}, 1],
+                     ['', None, 1], [' ', 0, 1], [' ', 1, 1], [' ', 2, 1], [' ', -1, 1], [' ', -9999999999999999, 1],
+                     [' ', 9999999999999999, 1], [' ', 0.0, 1], [' ', inf, 1], [' ', 3.141592653589793, 1],
+                     [' ', [], 1], [' ', [[]], 1], [' ', [1, 2, 3], 1], [' ', '', 1], [' ', '1', -1],
+                     [' ', 'a bee cd.', -1], [' ', '', 1], [' ', ' ', 0], [' ', '1', -1], [' ', 'a bee cd.', -1],
+                     [' ', set([]), 1], [' ', set([1, 2, 3]), 1], [' ', {5: 3}, 1], [' ', {}, 1], [' ', {'a': 99}, 1],
+                     [' ', {'a': 1, 'c': 3, 'b': 2}, 1], [' ', {'a': 99, 'c': 3, 'b': 5}, 1], [' ', None, 1],
+                     ['1', 0, 1], ['1', 1, 1], ['1', 2, 1], ['1', -1, 1], ['1', -9999999999999999, 1],
+                     ['1', 9999999999999999, 1], ['1', 0.0, 1], ['1', inf, 1], ['1', 3.141592653589793, 1],
+                     ['1', [], 1], ['1', [[]], 1], ['1', [1, 2, 3], 1], ['1', '', 1], ['1', ' ', 1],
+                     ['1', 'a bee cd.', -1], ['1', '', 1], ['1', ' ', 1], ['1', '1', 0], ['1', 'a bee cd.', -1],
+                     ['1', set([]), 1], ['1', set([1, 2, 3]), 1], ['1', {5: 3}, 1], ['1', {}, 1], ['1', {'a': 99}, 1],
+                     ['1', {'a': 1, 'c': 3, 'b': 2}, 1], ['1', {'a': 99, 'c': 3, 'b': 5}, 1], ['1', None, 1],
+                     ['a bee cd.', 0, 1], ['a bee cd.', 1, 1], ['a bee cd.', 2, 1], ['a bee cd.', -1, 1],
+                     ['a bee cd.', -9999999999999999, 1], ['a bee cd.', 9999999999999999, 1], ['a bee cd.', 0.0, 1],
+                     ['a bee cd.', inf, 1], ['a bee cd.', 3.141592653589793, 1], ['a bee cd.', [], 1],
                      ['a bee cd.', [[]], 1], ['a bee cd.', [1, 2, 3], 1], ['a bee cd.', '', 1], ['a bee cd.', ' ', 1],
                      ['a bee cd.', '1', 1], ['a bee cd.', '', 1], ['a bee cd.', ' ', 1], ['a bee cd.', '1', 1],
                      ['a bee cd.', 'a bee cd.', 0], ['a bee cd.', set([]), 1], ['a bee cd.', set([1, 2, 3]), 1],
                      ['a bee cd.', {5: 3}, 1], ['a bee cd.', {}, 1], ['a bee cd.', {'a': 99}, 1],
                      ['a bee cd.', {'a': 1, 'c': 3, 'b': 2}, 1], ['a bee cd.', {'a': 99, 'c': 3, 'b': 5}, 1],
                      ['a bee cd.', None, 1], ['', 0, 1], ['', 1, 1], ['', 2, 1], ['', -1, 1],
-                     ['', -9999999999999999, 1], ['', 9999999999999999, 1], ['', 0.0, 1], ['', inf, 1], ['', nan, 1],
+                     ['', -9999999999999999, 1], ['', 9999999999999999, 1], ['', 0.0, 1], ['', inf, 1],
                      ['', 3.141592653589793, 1], ['', [], 1], ['', [[]], 1], ['', [1, 2, 3], 1], ['', '', 0],
                      ['', ' ', -1], ['', '1', -1], ['', 'a bee cd.', -1], ['', ' ', -1], ['', '1', -1],
                      ['', 'a bee cd.', -1], ['', set([]), 1], ['', set([1, 2, 3]), 1], ['', {5: 3}, 1], ['', {}, 1],
                      ['', {'a': 99}, 1], ['', {'a': 1, 'c': 3, 'b': 2}, 1], ['', {'a': 99, 'c': 3, 'b': 5}, 1],
                      ['', None, 1], [' ', 0, 1], [' ', 1, 1], [' ', 2, 1], [' ', -1, 1], [' ', -9999999999999999, 1],
-                     [' ', 9999999999999999, 1], [' ', 0.0, 1], [' ', inf, 1], [' ', nan, 1],
-                     [' ', 3.141592653589793, 1], [' ', [], 1], [' ', [[]], 1], [' ', [1, 2, 3], 1], [' ', '', 1],
-                     [' ', ' ', 0], [' ', '1', -1], [' ', 'a bee cd.', -1], [' ', '', 1], [' ', '1', -1],
-                     [' ', 'a bee cd.', -1], [' ', set([]), 1], [' ', set([1, 2, 3]), 1], [' ', {5: 3}, 1],
-                     [' ', {}, 1], [' ', {'a': 99}, 1], [' ', {'a': 1, 'c': 3, 'b': 2}, 1],
-                     [' ', {'a': 99, 'c': 3, 'b': 5}, 1], [' ', None, 1], ['1', 0, 1], ['1', 1, 1], ['1', 2, 1],
-                     ['1', -1, 1], ['1', -9999999999999999, 1], ['1', 9999999999999999, 1], ['1', 0.0, 1],
-                     ['1', inf, 1], ['1', nan, 1], ['1', 3.141592653589793, 1], ['1', [], 1], ['1', [[]], 1],
-                     ['1', [1, 2, 3], 1], ['1', '', 1], ['1', ' ', 1], ['1', '1', 0], ['1', 'a bee cd.', -1],
-                     ['1', '', 1], ['1', ' ', 1], ['1', 'a bee cd.', -1], ['1', set([]), 1], ['1', set([1, 2, 3]), 1],
-                     ['1', {5: 3}, 1], ['1', {}, 1], ['1', {'a': 99}, 1], ['1', {'a': 1, 'c': 3, 'b': 2}, 1],
-                     ['1', {'a': 99, 'c': 3, 'b': 5}, 1], ['1', None, 1], ['a bee cd.', 0, 1], ['a bee cd.', 1, 1],
-                     ['a bee cd.', 2, 1], ['a bee cd.', -1, 1], ['a bee cd.', -9999999999999999, 1],
-                     ['a bee cd.', 9999999999999999, 1], ['a bee cd.', 0.0, 1], ['a bee cd.', inf, 1],
-                     ['a bee cd.', nan, 1], ['a bee cd.', 3.141592653589793, 1], ['a bee cd.', [], 1],
+                     [' ', 9999999999999999, 1], [' ', 0.0, 1], [' ', inf, 1], [' ', 3.141592653589793, 1],
+                     [' ', [], 1], [' ', [[]], 1], [' ', [1, 2, 3], 1], [' ', '', 1], [' ', ' ', 0], [' ', '1', -1],
+                     [' ', 'a bee cd.', -1], [' ', '', 1], [' ', '1', -1], [' ', 'a bee cd.', -1], [' ', set([]), 1],
+                     [' ', set([1, 2, 3]), 1], [' ', {5: 3}, 1], [' ', {}, 1], [' ', {'a': 99}, 1],
+                     [' ', {'a': 1, 'c': 3, 'b': 2}, 1], [' ', {'a': 99, 'c': 3, 'b': 5}, 1], [' ', None, 1],
+                     ['1', 0, 1], ['1', 1, 1], ['1', 2, 1], ['1', -1, 1], ['1', -9999999999999999, 1],
+                     ['1', 9999999999999999, 1], ['1', 0.0, 1], ['1', inf, 1], ['1', 3.141592653589793, 1],
+                     ['1', [], 1], ['1', [[]], 1], ['1', [1, 2, 3], 1], ['1', '', 1], ['1', ' ', 1], ['1', '1', 0],
+                     ['1', 'a bee cd.', -1], ['1', '', 1], ['1', ' ', 1], ['1', 'a bee cd.', -1], ['1', set([]), 1],
+                     ['1', set([1, 2, 3]), 1], ['1', {5: 3}, 1], ['1', {}, 1], ['1', {'a': 99}, 1],
+                     ['1', {'a': 1, 'c': 3, 'b': 2}, 1], ['1', {'a': 99, 'c': 3, 'b': 5}, 1], ['1', None, 1],
+                     ['a bee cd.', 0, 1], ['a bee cd.', 1, 1], ['a bee cd.', 2, 1], ['a bee cd.', -1, 1],
+                     ['a bee cd.', -9999999999999999, 1], ['a bee cd.', 9999999999999999, 1], ['a bee cd.', 0.0, 1],
+                     ['a bee cd.', inf, 1], ['a bee cd.', 3.141592653589793, 1], ['a bee cd.', [], 1],
                      ['a bee cd.', [[]], 1], ['a bee cd.', [1, 2, 3], 1], ['a bee cd.', '', 1], ['a bee cd.', ' ', 1],
                      ['a bee cd.', '1', 1], ['a bee cd.', 'a bee cd.', 0], ['a bee cd.', '', 1], ['a bee cd.', ' ', 1],
                      ['a bee cd.', '1', 1], ['a bee cd.', set([]), 1], ['a bee cd.', set([1, 2, 3]), 1],
@@ -163,40 +151,39 @@
                      ['a bee cd.', {'a': 1, 'c': 3, 'b': 2}, 1], ['a bee cd.', {'a': 99, 'c': 3, 'b': 5}, 1],
                      ['a bee cd.', None, 1], [set([]), 0, 1], [set([]), 1, 1], [set([]), 2, 1], [set([]), -1, 1],
                      [set([]), -9999999999999999, 1], [set([]), 9999999999999999, 1], [set([]), 0.0, 1],
-                     [set([]), inf, 1], [set([]), nan, 1], [set([]), 3.141592653589793, 1], [set([]), [], 1],
-                     [set([]), [[]], 1], [set([]), [1, 2, 3], 1], [set([]), '', -1], [set([]), ' ', -1],
-                     [set([]), '1', -1], [set([]), 'a bee cd.', -1], [set([]), '', -1], [set([]), ' ', -1],
-                     [set([]), '1', -1], [set([]), 'a bee cd.', -1],
+                     [set([]), inf, 1], [set([]), 3.141592653589793, 1], [set([]), [], 1], [set([]), [[]], 1],
+                     [set([]), [1, 2, 3], 1], [set([]), '', -1], [set([]), ' ', -1], [set([]), '1', -1],
+                     [set([]), 'a bee cd.', -1], [set([]), '', -1], [set([]), ' ', -1], [set([]), '1', -1],
+                     [set([]), 'a bee cd.', -1],
                      [set([]), set([1, 2, 3]), 'TypeError: cannot compare sets using cmp()'], [set([]), {5: 3}, 1],
                      [set([]), {}, 1], [set([]), {'a': 99}, 1], [set([]), {'a': 1, 'c': 3, 'b': 2}, 1],
                      [set([]), {'a': 99, 'c': 3, 'b': 5}, 1], [set([]), None, 1], [set([1, 2, 3]), 0, 1],
                      [set([1, 2, 3]), 1, 1], [set([1, 2, 3]), 2, 1], [set([1, 2, 3]), -1, 1],
                      [set([1, 2, 3]), -9999999999999999, 1], [set([1, 2, 3]), 9999999999999999, 1],
-                     [set([1, 2, 3]), 0.0, 1], [set([1, 2, 3]), inf, 1], [set([1, 2, 3]), nan, 1],
-                     [set([1, 2, 3]), 3.141592653589793, 1], [set([1, 2, 3]), [], 1], [set([1, 2, 3]), [[]], 1],
-                     [set([1, 2, 3]), [1, 2, 3], 1], [set([1, 2, 3]), '', -1], [set([1, 2, 3]), ' ', -1],
-                     [set([1, 2, 3]), '1', -1], [set([1, 2, 3]), 'a bee cd.', -1], [set([1, 2, 3]), '', -1],
-                     [set([1, 2, 3]), ' ', -1], [set([1, 2, 3]), '1', -1], [set([1, 2, 3]), 'a bee cd.', -1],
+                     [set([1, 2, 3]), 0.0, 1], [set([1, 2, 3]), inf, 1], [set([1, 2, 3]), 3.141592653589793, 1],
+                     [set([1, 2, 3]), [], 1], [set([1, 2, 3]), [[]], 1], [set([1, 2, 3]), [1, 2, 3], 1],
+                     [set([1, 2, 3]), '', -1], [set([1, 2, 3]), ' ', -1], [set([1, 2, 3]), '1', -1],
+                     [set([1, 2, 3]), 'a bee cd.', -1], [set([1, 2, 3]), '', -1], [set([1, 2, 3]), ' ', -1],
+                     [set([1, 2, 3]), '1', -1], [set([1, 2, 3]), 'a bee cd.', -1],
                      [set([1, 2, 3]), set([]), 'TypeError: cannot compare sets using cmp()'],
                      [set([1, 2, 3]), {5: 3}, 1], [set([1, 2, 3]), {}, 1], [set([1, 2, 3]), {'a': 99}, 1],
                      [set([1, 2, 3]), {'a': 1, 'c': 3, 'b': 2}, 1], [set([1, 2, 3]), {'a': 99, 'c': 3, 'b': 5}, 1],
                      [set([1, 2, 3]), None, 1], [{5: 3}, 0, 1], [{5: 3}, 1, 1], [{5: 3}, 2, 1], [{5: 3}, -1, 1],
                      [{5: 3}, -9999999999999999, 1], [{5: 3}, 9999999999999999, 1], [{5: 3}, 0.0, 1], [{5: 3}, inf, 1],
-                     [{5: 3}, nan, 1], [{5: 3}, 3.141592653589793, 1], [{5: 3}, [], -1], [{5: 3}, [[]], -1],
-                     [{5: 3}, [1, 2, 3], -1], [{5: 3}, '', -1], [{5: 3}, ' ', -1], [{5: 3}, '1', -1],
-                     [{5: 3}, 'a bee cd.', -1], [{5: 3}, '', -1], [{5: 3}, ' ', -1], [{5: 3}, '1', -1],
-                     [{5: 3}, 'a bee cd.', -1], [{5: 3}, set([]), -1], [{5: 3}, set([1, 2, 3]), -1], [{5: 3}, {}, 1],
-                     [{5: 3}, {'a': 99}, -1], [{5: 3}, {'a': 1, 'c': 3, 'b': 2}, -1],
-                     [{5: 3}, {'a': 99, 'c': 3, 'b': 5}, -1], [{5: 3}, None, 1], [{}, 0, 1], [{}, 1, 1], [{}, 2, 1],
-                     [{}, -1, 1], [{}, -9999999999999999, 1], [{}, 9999999999999999, 1], [{}, 0.0, 1], [{}, inf, 1],
-                     [{}, nan, 1], [{}, 3.141592653589793, 1], [{}, [], -1], [{}, [[]], -1], [{}, [1, 2, 3], -1],
-                     [{}, '', -1], [{}, ' ', -1], [{}, '1', -1], [{}, 'a bee cd.', -1], [{}, '', -1], [{}, ' ', -1],
-                     [{}, '1', -1], [{}, 'a bee cd.', -1], [{}, set([]), -1], [{}, set([1, 2, 3]), -1],
-                     [{}, {5: 3}, -1], [{}, {'a': 99}, -1], [{}, {'a': 1, 'c': 3, 'b': 2}, -1],
-                     [{}, {'a': 99, 'c': 3, 'b': 5}, -1], [{}, None, 1], [{'a': 99}, 0, 1], [{'a': 99}, 1, 1],
-                     [{'a': 99}, 2, 1], [{'a': 99}, -1, 1], [{'a': 99}, -9999999999999999, 1],
-                     [{'a': 99}, 9999999999999999, 1], [{'a': 99}, 0.0, 1], [{'a': 99}, inf, 1], [{'a': 99}, nan, 1],
-                     [{'a': 99}, 3.141592653589793, 1], [{'a': 99}, [], -1], [{'a': 99}, [[]], -1],
+                     [{5: 3}, 3.141592653589793, 1], [{5: 3}, [], -1], [{5: 3}, [[]], -1], [{5: 3}, [1, 2, 3], -1],
+                     [{5: 3}, '', -1], [{5: 3}, ' ', -1], [{5: 3}, '1', -1], [{5: 3}, 'a bee cd.', -1],
+                     [{5: 3}, '', -1], [{5: 3}, ' ', -1], [{5: 3}, '1', -1], [{5: 3}, 'a bee cd.', -1],
+                     [{5: 3}, set([]), -1], [{5: 3}, set([1, 2, 3]), -1], [{5: 3}, {}, 1], [{5: 3}, {'a': 99}, -1],
+                     [{5: 3}, {'a': 1, 'c': 3, 'b': 2}, -1], [{5: 3}, {'a': 99, 'c': 3, 'b': 5}, -1], [{5: 3}, None, 1],
+                     [{}, 0, 1], [{}, 1, 1], [{}, 2, 1], [{}, -1, 1], [{}, -9999999999999999, 1],
+                     [{}, 9999999999999999, 1], [{}, 0.0, 1], [{}, inf, 1], [{}, 3.141592653589793, 1], [{}, [], -1],
+                     [{}, [[]], -1], [{}, [1, 2, 3], -1], [{}, '', -1], [{}, ' ', -1], [{}, '1', -1],
+                     [{}, 'a bee cd.', -1], [{}, '', -1], [{}, ' ', -1], [{}, '1', -1], [{}, 'a bee cd.', -1],
+                     [{}, set([]), -1], [{}, set([1, 2, 3]), -1], [{}, {5: 3}, -1], [{}, {'a': 99}, -1],
+                     [{}, {'a': 1, 'c': 3, 'b': 2}, -1], [{}, {'a': 99, 'c': 3, 'b': 5}, -1], [{}, None, 1],
+                     [{'a': 99}, 0, 1], [{'a': 99}, 1, 1], [{'a': 99}, 2, 1], [{'a': 99}, -1, 1],
+                     [{'a': 99}, -9999999999999999, 1], [{'a': 99}, 9999999999999999, 1], [{'a': 99}, 0.0, 1],
+                     [{'a': 99}, inf, 1], [{'a': 99}, 3.141592653589793, 1], [{'a': 99}, [], -1], [{'a': 99}, [[]], -1],
                      [{'a': 99}, [1, 2, 3], -1], [{'a': 99}, '', -1], [{'a': 99}, ' ', -1], [{'a': 99}, '1', -1],
                      [{'a': 99}, 'a bee cd.', -1], [{'a': 99}, '', -1], [{'a': 99}, ' ', -1], [{'a': 99}, '1', -1],
                      [{'a': 99}, 'a bee cd.', -1], [{'a': 99}, set([]), -1], [{'a': 99}, set([1, 2, 3]), -1],
@@ -205,36 +192,34 @@
                      [{'a': 1, 'c': 3, 'b': 2}, 1, 1], [{'a': 1, 'c': 3, 'b': 2}, 2, 1],
                      [{'a': 1, 'c': 3, 'b': 2}, -1, 1], [{'a': 1, 'c': 3, 'b': 2}, -9999999999999999, 1],
                      [{'a': 1, 'c': 3, 'b': 2}, 9999999999999999, 1], [{'a': 1, 'c': 3, 'b': 2}, 0.0, 1],
-                     [{'a': 1, 'c': 3, 'b': 2}, inf, 1], [{'a': 1, 'c': 3, 'b': 2}, nan, 1],
-                     [{'a': 1, 'c': 3, 'b': 2}, 3.141592653589793, 1], [{'a': 1, 'c': 3, 'b': 2}, [], -1],
-                     [{'a': 1, 'c': 3, 'b': 2}, [[]], -1], [{'a': 1, 'c': 3, 'b': 2}, [1, 2, 3], -1],
-                     [{'a': 1, 'c': 3, 'b': 2}, '', -1], [{'a': 1, 'c': 3, 'b': 2}, ' ', -1],
-                     [{'a': 1, 'c': 3, 'b': 2}, '1', -1], [{'a': 1, 'c': 3, 'b': 2}, 'a bee cd.', -1],
-                     [{'a': 1, 'c': 3, 'b': 2}, '', -1], [{'a': 1, 'c': 3, 'b': 2}, ' ', -1],
-                     [{'a': 1, 'c': 3, 'b': 2}, '1', -1], [{'a': 1, 'c': 3, 'b': 2}, 'a bee cd.', -1],
-                     [{'a': 1, 'c': 3, 'b': 2}, set([]), -1], [{'a': 1, 'c': 3, 'b': 2}, set([1, 2, 3]), -1],
-                     [{'a': 1, 'c': 3, 'b': 2}, {5: 3}, 1], [{'a': 1, 'c': 3, 'b': 2}, {}, 1],
-                     [{'a': 1, 'c': 3, 'b': 2}, {'a': 99}, 1],
+                     [{'a': 1, 'c': 3, 'b': 2}, inf, 1], [{'a': 1, 'c': 3, 'b': 2}, 3.141592653589793, 1],
+                     [{'a': 1, 'c': 3, 'b': 2}, [], -1], [{'a': 1, 'c': 3, 'b': 2}, [[]], -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, [1, 2, 3], -1], [{'a': 1, 'c': 3, 'b': 2}, '', -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, ' ', -1], [{'a': 1, 'c': 3, 'b': 2}, '1', -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, 'a bee cd.', -1], [{'a': 1, 'c': 3, 'b': 2}, '', -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, ' ', -1], [{'a': 1, 'c': 3, 'b': 2}, '1', -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, 'a bee cd.', -1], [{'a': 1, 'c': 3, 'b': 2}, set([]), -1],
+                     [{'a': 1, 'c': 3, 'b': 2}, set([1, 2, 3]), -1], [{'a': 1, 'c': 3, 'b': 2}, {5: 3}, 1],
+                     [{'a': 1, 'c': 3, 'b': 2}, {}, 1], [{'a': 1, 'c': 3, 'b': 2}, {'a': 99}, 1],
                      [{'a': 1, 'c': 3, 'b': 2}, {'a': 99, 'c': 3, 'b': 5}, -1], [{'a': 1, 'c': 3, 'b': 2}, None, 1],
                      [{'a': 99, 'c': 3, 'b': 5}, 0, 1], [{'a': 99, 'c': 3, 'b': 5}, 1, 1],
                      [{'a': 99, 'c': 3, 'b': 5}, 2, 1], [{'a': 99, 'c': 3, 'b': 5}, -1, 1],
                      [{'a': 99, 'c': 3, 'b': 5}, -9999999999999999, 1],
                      [{'a': 99, 'c': 3, 'b': 5}, 9999999999999999, 1], [{'a': 99, 'c': 3, 'b': 5}, 0.0, 1],
-                     [{'a': 99, 'c': 3, 'b': 5}, inf, 1], [{'a': 99, 'c': 3, 'b': 5}, nan, 1],
-                     [{'a': 99, 'c': 3, 'b': 5}, 3.141592653589793, 1], [{'a': 99, 'c': 3, 'b': 5}, [], -1],
-                     [{'a': 99, 'c': 3, 'b': 5}, [[]], -1], [{'a': 99, 'c': 3, 'b': 5}, [1, 2, 3], -1],
-                     [{'a': 99, 'c': 3, 'b': 5}, '', -1], [{'a': 99, 'c': 3, 'b': 5}, ' ', -1],
-                     [{'a': 99, 'c': 3, 'b': 5}, '1', -1], [{'a': 99, 'c': 3, 'b': 5}, 'a bee cd.', -1],
-                     [{'a': 99, 'c': 3, 'b': 5}, '', -1], [{'a': 99, 'c': 3, 'b': 5}, ' ', -1],
-                     [{'a': 99, 'c': 3, 'b': 5}, '1', -1], [{'a': 99, 'c': 3, 'b': 5}, 'a bee cd.', -1],
-                     [{'a': 99, 'c': 3, 'b': 5}, set([]), -1], [{'a': 99, 'c': 3, 'b': 5}, set([1, 2, 3]), -1],
-                     [{'a': 99, 'c': 3, 'b': 5}, {5: 3}, 1], [{'a': 99, 'c': 3, 'b': 5}, {}, 1],
-                     [{'a': 99, 'c': 3, 'b': 5}, {'a': 99}, 1],
+                     [{'a': 99, 'c': 3, 'b': 5}, inf, 1], [{'a': 99, 'c': 3, 'b': 5}, 3.141592653589793, 1],
+                     [{'a': 99, 'c': 3, 'b': 5}, [], -1], [{'a': 99, 'c': 3, 'b': 5}, [[]], -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, [1, 2, 3], -1], [{'a': 99, 'c': 3, 'b': 5}, '', -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, ' ', -1], [{'a': 99, 'c': 3, 'b': 5}, '1', -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, 'a bee cd.', -1], [{'a': 99, 'c': 3, 'b': 5}, '', -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, ' ', -1], [{'a': 99, 'c': 3, 'b': 5}, '1', -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, 'a bee cd.', -1], [{'a': 99, 'c': 3, 'b': 5}, set([]), -1],
+                     [{'a': 99, 'c': 3, 'b': 5}, set([1, 2, 3]), -1], [{'a': 99, 'c': 3, 'b': 5}, {5: 3}, 1],
+                     [{'a': 99, 'c': 3, 'b': 5}, {}, 1], [{'a': 99, 'c': 3, 'b': 5}, {'a': 99}, 1],
                      [{'a': 99, 'c': 3, 'b': 5}, {'a': 1, 'c': 3, 'b': 2}, 1], [{'a': 99, 'c': 3, 'b': 5}, None, 1],
                      [None, 0, -1], [None, 1, -1], [None, 2, -1], [None, -1, -1], [None, -9999999999999999, -1],
-                     [None, 9999999999999999, -1], [None, 0.0, -1], [None, inf, -1], [None, nan, -1],
-                     [None, 3.141592653589793, -1], [None, [], -1], [None, [[]], -1], [None, [1, 2, 3], -1],
-                     [None, '', -1], [None, ' ', -1], [None, '1', -1], [None, 'a bee cd.', -1], [None, '', -1],
-                     [None, ' ', -1], [None, '1', -1], [None, 'a bee cd.', -1], [None, set([]), -1],
-                     [None, set([1, 2, 3]), -1], [None, {5: 3}, -1], [None, {}, -1], [None, {'a': 99}, -1],
-                     [None, {'a': 1, 'c': 3, 'b': 2}, -1], [None, {'a': 99, 'c': 3, 'b': 5}, -1]]
+                     [None, 9999999999999999, -1], [None, 0.0, -1], [None, inf, -1], [None, 3.141592653589793, -1],
+                     [None, [], -1], [None, [[]], -1], [None, [1, 2, 3], -1], [None, '', -1], [None, ' ', -1],
+                     [None, '1', -1], [None, 'a bee cd.', -1], [None, '', -1], [None, ' ', -1], [None, '1', -1],
+                     [None, 'a bee cd.', -1], [None, set([]), -1], [None, set([1, 2, 3]), -1], [None, {5: 3}, -1],
+                     [None, {}, -1], [None, {'a': 99}, -1], [None, {'a': 1, 'c': 3, 'b': 2}, -1],
+                     [None, {'a': 99, 'c': 3, 'b': 5}, -1]]

From 3f1ff7eddff8df70137bcd7d627e7af59c8816ed Mon Sep 17 00:00:00 2001
From: "Roman A. Taycher" <rtaycher1987@gmail.com>
Date: Sun, 30 Aug 2020 22:01:21 -0700
Subject: [PATCH 118/183] fix 2.6 test

---
 tests/test_past/test_misc.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/tests/test_past/test_misc.py b/tests/test_past/test_misc.py
index db07896f..ab67bfdd 100644
--- a/tests/test_past/test_misc.py
+++ b/tests/test_past/test_misc.py
@@ -11,7 +11,7 @@
 from contextlib import contextmanager
 
 from future.tests.base import unittest
-from future.utils import PY3
+from future.utils import PY3, PY26
 
 if PY3:
     from past.builtins import cmp
@@ -29,6 +29,10 @@ def empty_context_manager(*args, **kwargs):
 class TestCmp(unittest.TestCase):
     def test_cmp(self):
         for x, y, cmp_python2_value in test_values.cmp_python2_value:
+            if PY26:
+                # set comparison works a bit differently in 2.6
+                if isinstance(x, set) or isinstance(y, set):
+                    continue
             # to get this to run on python <3.4 which lacks subTest
             with getattr(self, 'subTest', empty_context_manager)(x=x, y=y):
                 try:

From ddedcb9abf98fd1cc85fb43ee35a12b2e898a5d7 Mon Sep 17 00:00:00 2001
From: "Roman A. Taycher" <rtaycher1987@gmail.com>
Date: Sun, 30 Aug 2020 22:06:42 -0700
Subject: [PATCH 119/183] fix 2.6 test, better comment

---
 tests/test_past/test_misc.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/test_past/test_misc.py b/tests/test_past/test_misc.py
index ab67bfdd..0367b3db 100644
--- a/tests/test_past/test_misc.py
+++ b/tests/test_past/test_misc.py
@@ -30,7 +30,7 @@ class TestCmp(unittest.TestCase):
     def test_cmp(self):
         for x, y, cmp_python2_value in test_values.cmp_python2_value:
             if PY26:
-                # set comparison works a bit differently in 2.6
+                # set cmp works a bit differently in 2.6, we try to emulate 2.7 behavior, so skip set cmp tests
                 if isinstance(x, set) or isinstance(y, set):
                     continue
             # to get this to run on python <3.4 which lacks subTest

From 05bc771c8d8770da5b7e82db77078e6b2fef8146 Mon Sep 17 00:00:00 2001
From: Joseph Curtis <jccurtis@lbl.gov>
Date: Wed, 16 Sep 2020 11:22:09 -0700
Subject: [PATCH 120/183] Update imp to importlib for py3

---
 docs/other/auto2to3.py                     | 6 +++++-
 src/future/backports/test/support.py       | 6 +++++-
 src/future/standard_library/__init__.py    | 6 +++++-
 src/past/builtins/misc.py                  | 3 ++-
 src/past/translation/__init__.py           | 8 ++++++--
 tests/test_future/test_standard_library.py | 6 +++++-
 6 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/docs/other/auto2to3.py b/docs/other/auto2to3.py
index 3abd3703..1f56aa14 100644
--- a/docs/other/auto2to3.py
+++ b/docs/other/auto2to3.py
@@ -19,7 +19,11 @@
 import argparse
 import os
 import sys
-import imp
+# imp was deprecated in python 3.6
+if sys.version_info >= (3, 6):
+    import importlib as imp
+else:
+    import imp
 import runpy
 from io import StringIO
 from pkgutil import ImpImporter, ImpLoader
diff --git a/src/future/backports/test/support.py b/src/future/backports/test/support.py
index 1999e208..4e05e972 100644
--- a/src/future/backports/test/support.py
+++ b/src/future/backports/test/support.py
@@ -28,7 +28,11 @@
 # import collections.abc    # not present on Py2.7
 import re
 import subprocess
-import imp
+# imp was deprecated in python 3.6
+if sys.version_info >= (3, 6):
+    import importlib as imp
+else:
+    import imp
 import time
 try:
     import sysconfig
diff --git a/src/future/standard_library/__init__.py b/src/future/standard_library/__init__.py
index cff02f95..0a447871 100644
--- a/src/future/standard_library/__init__.py
+++ b/src/future/standard_library/__init__.py
@@ -62,7 +62,11 @@
 
 import sys
 import logging
-import imp
+# imp was deprecated in python 3.6
+if sys.version_info >= (3, 6):
+    import importlib as imp
+else:
+    import imp
 import contextlib
 import types
 import copy
diff --git a/src/past/builtins/misc.py b/src/past/builtins/misc.py
index ba50aa9e..eaaef410 100644
--- a/src/past/builtins/misc.py
+++ b/src/past/builtins/misc.py
@@ -42,7 +42,8 @@ def oct(number):
         return '0' + builtins.oct(number)[2:]
 
     raw_input = input
-    from imp import reload
+    # imp was deprecated in python 3.6
+    from importlib import reload
     unicode = str
     unichr = chr
     xrange = range
diff --git a/src/past/translation/__init__.py b/src/past/translation/__init__.py
index 7c678866..e70d4bcd 100644
--- a/src/past/translation/__init__.py
+++ b/src/past/translation/__init__.py
@@ -32,11 +32,15 @@
 Inspired by and based on ``uprefix`` by Vinay M. Sajip.
 """
 
-import imp
+import sys
+# imp was deprecated in python 3.6
+if sys.version_info >= (3, 6):
+    import importlib as imp
+else:
+    import imp
 import logging
 import marshal
 import os
-import sys
 import copy
 from lib2to3.pgen2.parse import ParseError
 from lib2to3.refactor import RefactoringTool
diff --git a/tests/test_future/test_standard_library.py b/tests/test_future/test_standard_library.py
index 3ac5d2d7..630a43f2 100644
--- a/tests/test_future/test_standard_library.py
+++ b/tests/test_future/test_standard_library.py
@@ -447,7 +447,11 @@ def test_reload(self):
         """
         reload has been moved to the imp module
         """
-        import imp
+        # imp was deprecated in python 3.6
+        if sys.version_info >= (3, 6):
+            import importlib as imp
+        else:
+            import imp
         imp.reload(imp)
         self.assertTrue(True)
 

From dbd3cd0270ce48bb27a633b9b88a1a9bb287011d Mon Sep 17 00:00:00 2001
From: Joseph Curtis <jccurtis@lbl.gov>
Date: Wed, 16 Sep 2020 11:35:23 -0700
Subject: [PATCH 121/183] fix import reload for python<3.6

---
 src/past/builtins/misc.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/past/builtins/misc.py b/src/past/builtins/misc.py
index eaaef410..78210d37 100644
--- a/src/past/builtins/misc.py
+++ b/src/past/builtins/misc.py
@@ -1,6 +1,7 @@
 from __future__ import unicode_literals
 
 import inspect
+import sys
 
 from future.utils import PY2, PY3, exec_
 
@@ -43,7 +44,10 @@ def oct(number):
 
     raw_input = input
     # imp was deprecated in python 3.6
-    from importlib import reload
+    if sys.version_info >= (3, 6):
+        from importlib import reload
+    else:
+        from imp import reload
     unicode = str
     unichr = chr
     xrange = range

From bee0247d43a7e7fafaaa18a1557ab4815ed072d6 Mon Sep 17 00:00:00 2001
From: Jon Parise <jon@indelible.org>
Date: Thu, 8 Oct 2020 09:07:51 -0700
Subject: [PATCH 122/183] Add PY37_PLUS, PY38_PLUS, and PY39_PLUS

This extend the set of existing PY3?_PLUS constants to include recent
Python 3.x releases.
---
 src/future/utils/__init__.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index 846d5da6..ec1b1027 100644
--- a/src/future/utils/__init__.py
+++ b/src/future/utils/__init__.py
@@ -61,6 +61,9 @@
 PY34_PLUS = sys.version_info[0:2] >= (3, 4)
 PY35_PLUS = sys.version_info[0:2] >= (3, 5)
 PY36_PLUS = sys.version_info[0:2] >= (3, 6)
+PY37_PLUS = sys.version_info[0:2] >= (3, 7)
+PY38_PLUS = sys.version_info[0:2] >= (3, 8)
+PY39_PLUS = sys.version_info[0:2] >= (3, 9)
 PY2 = sys.version_info[0] == 2
 PY26 = sys.version_info[0:2] == (2, 6)
 PY27 = sys.version_info[0:2] == (2, 7)

From c341d5497788923cc6ea0bd1358279f2147aa167 Mon Sep 17 00:00:00 2001
From: Alexander Shadchin <shadchin@yandex-team.ru>
Date: Sun, 15 Nov 2020 13:01:39 +0300
Subject: [PATCH 123/183] Add support Python 3.9

---
 src/future/backports/xmlrpc/client.py   | 7 ++++---
 src/future/moves/_dummy_thread.py       | 5 ++++-
 src/future/standard_library/__init__.py | 2 +-
 3 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/future/backports/xmlrpc/client.py b/src/future/backports/xmlrpc/client.py
index b78e5bad..0bcd90c9 100644
--- a/src/future/backports/xmlrpc/client.py
+++ b/src/future/backports/xmlrpc/client.py
@@ -134,10 +134,11 @@
 from future.builtins import bytes, dict, int, range, str
 
 import base64
-# Py2.7 compatibility hack
-base64.encodebytes = base64.encodestring
-base64.decodebytes = base64.decodestring
 import sys
+if sys.version_info < (3, 9):
+    # Py2.7 compatibility hack
+    base64.encodebytes = base64.encodestring
+    base64.decodebytes = base64.decodestring
 import time
 from datetime import datetime
 from future.backports.http import client as http_client
diff --git a/src/future/moves/_dummy_thread.py b/src/future/moves/_dummy_thread.py
index 688d249b..e5dca348 100644
--- a/src/future/moves/_dummy_thread.py
+++ b/src/future/moves/_dummy_thread.py
@@ -2,7 +2,10 @@
 from future.utils import PY3
 
 if PY3:
-    from _dummy_thread import *
+    try:
+        from _dummy_thread import *
+    except ImportError:
+        from _thread import *
 else:
     __future_module__ = True
     from dummy_thread import *
diff --git a/src/future/standard_library/__init__.py b/src/future/standard_library/__init__.py
index cff02f95..41c4f36d 100644
--- a/src/future/standard_library/__init__.py
+++ b/src/future/standard_library/__init__.py
@@ -125,7 +125,7 @@
            # 'Tkinter': 'tkinter',
            '_winreg': 'winreg',
            'thread': '_thread',
-           'dummy_thread': '_dummy_thread',
+           'dummy_thread': '_dummy_thread' if sys.version_info < (3, 9) else '_thread',
            # 'anydbm': 'dbm',   # causes infinite import loop
            # 'whichdb': 'dbm',  # causes infinite import loop
            # anydbm and whichdb are handled by fix_imports2

From c4411998f3935b5588ef0ba2fdff09b11edf2bcd Mon Sep 17 00:00:00 2001
From: Alexander Shadchin <shadchin@yandex-team.ru>
Date: Sun, 15 Nov 2020 13:04:51 +0300
Subject: [PATCH 124/183] Add Python 3.8 and 3.9 in .travis.yml

---
 .travis.yml | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 1be7fac8..e77b37aa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,8 +22,13 @@ matrix:
       env: TOXENV=py36
     - python: 3.7
       env: TOXENV=py37
-      dist: xenial    # required for Python 3.7 (travis-ci/travis-ci#9069)
-      sudo: required  # required for Python 3.7 (travis-ci/travis-ci#9069)
+      dist: xenial
+    - python: 3.8
+      env: TOXENV=py38
+      dist: xenial
+    - python: 3.9
+      env: TOXENV=py39
+      dist: bionic
 
 install:
   - pip install typing==3.7.4.1  # required for Python 3.3

From 90e3e4d7146324bd88a5453ba17a43412174f013 Mon Sep 17 00:00:00 2001
From: Alexander Shadchin <shadchin@yandex-team.ru>
Date: Sun, 15 Nov 2020 13:09:44 +0300
Subject: [PATCH 125/183] Fix tests

---
 tests/test_future/test_standard_library.py | 3 ++-
 tests/test_future/test_urllib_toplevel.py  | 5 +++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/tests/test_future/test_standard_library.py b/tests/test_future/test_standard_library.py
index 3ac5d2d7..820bf47a 100644
--- a/tests/test_future/test_standard_library.py
+++ b/tests/test_future/test_standard_library.py
@@ -422,7 +422,8 @@ def test_urllib_imports_install_hooks(self):
 
     def test_underscore_prefixed_modules(self):
         import _thread
-        import _dummy_thread
+        if sys.version_info < (3, 9):
+            import _dummy_thread
         import _markupbase
         self.assertTrue(True)
 
diff --git a/tests/test_future/test_urllib_toplevel.py b/tests/test_future/test_urllib_toplevel.py
index 11e77201..68bc4c96 100644
--- a/tests/test_future/test_urllib_toplevel.py
+++ b/tests/test_future/test_urllib_toplevel.py
@@ -781,8 +781,9 @@ def test_unquoting(self):
                          "%s" % result)
         self.assertRaises((TypeError, AttributeError), urllib_parse.unquote, None)
         self.assertRaises((TypeError, AttributeError), urllib_parse.unquote, ())
-        with support.check_warnings(('', BytesWarning), quiet=True):
-            self.assertRaises((TypeError, AttributeError), urllib_parse.unquote, bytes(b''))
+        if sys.version_info < (3, 9):
+            with support.check_warnings(('', BytesWarning), quiet=True):
+                self.assertRaises((TypeError, AttributeError), urllib_parse.unquote, bytes(b''))
 
     def test_unquoting_badpercent(self):
         # Test unquoting on bad percent-escapes

From 3e43aa2cdb7a25cf7a8c5cb382f3aa5ee8793918 Mon Sep 17 00:00:00 2001
From: Alexander Shadchin <shadchin@yandex-team.ru>
Date: Sun, 15 Nov 2020 13:16:00 +0300
Subject: [PATCH 126/183] Add Python 3.8 and 3.9 in tox.ini

---
 tox.ini | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tox.ini b/tox.ini
index 1ca3286b..48e17601 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
 [tox]
 envlist =
-    py{26,27,33,34,35,36,37},
+    py{26,27,33,34,35,36,37,38,39},
     docs
 
 [testenv]

From 32641e1e8f22e326f8cb77dbcd8b2172ece797e2 Mon Sep 17 00:00:00 2001
From: Alexander Shadchin <shadchin@yandex-team.ru>
Date: Sun, 15 Nov 2020 13:30:06 +0300
Subject: [PATCH 127/183] Fix test_pow for Python 3.8+

---
 tests/test_future/test_builtins.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/test_future/test_builtins.py b/tests/test_future/test_builtins.py
index 3921a608..f5dbec64 100644
--- a/tests/test_future/test_builtins.py
+++ b/tests/test_future/test_builtins.py
@@ -1304,7 +1304,8 @@ def test_pow(self):
         self.assertAlmostEqual(pow(-1, 1/3), 0.5 + 0.8660254037844386j)
 
         # Raises TypeError in Python < v3.5, ValueError in v3.5:
-        self.assertRaises((TypeError, ValueError), pow, -1, -2, 3)
+        if sys.version_info < (3, 8):
+            self.assertRaises((TypeError, ValueError), pow, -1, -2, 3)
         self.assertRaises(ValueError, pow, 1, 2, 0)
 
         self.assertRaises(TypeError, pow)

From e7a2f76afa12ac4201c25dac9f93197016a19a7c Mon Sep 17 00:00:00 2001
From: Alexander Shadchin <shadchin@yandex-team.ru>
Date: Sun, 15 Nov 2020 13:53:28 +0300
Subject: [PATCH 128/183] Fix test_ftp for Python 3.8+

---
 tests/test_future/test_urllib2.py | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/tests/test_future/test_urllib2.py b/tests/test_future/test_urllib2.py
index 2d69dad1..bd8e75c5 100644
--- a/tests/test_future/test_urllib2.py
+++ b/tests/test_future/test_urllib2.py
@@ -691,10 +691,6 @@ def connect_ftp(self, user, passwd, host, port, dirs,
         h = NullFTPHandler(data)
         h.parent = MockOpener()
 
-        # MIME guessing works in Python 3.8!
-        guessed_mime = None
-        if sys.hexversion >= 0x03080000:
-            guessed_mime = "image/gif"
         for url, host, port, user, passwd, type_, dirs, filename, mimetype in [
             ("ftp://localhost/foo/bar/baz.html",
              "localhost", ftplib.FTP_PORT, "", "", "I",
@@ -713,7 +709,10 @@ def connect_ftp(self, user, passwd, host, port, dirs,
              ["foo", "bar"], "", None),
             ("ftp://localhost/baz.gif;type=a",
              "localhost", ftplib.FTP_PORT, "", "", "A",
-             [], "baz.gif", guessed_mime),
+             [], "baz.gif", None),
+            ("ftp://localhost/baz.gif",
+             "localhost", ftplib.FTP_PORT, "", "", "I",
+             [], "baz.gif", "image/gif"),
             ]:
             req = Request(url)
             req.timeout = None

From 5f9893f1207380754a85be51e86652bc6d2b8ecc Mon Sep 17 00:00:00 2001
From: Liuyang Wan <tsfdye@gmail.com>
Date: Mon, 23 Nov 2020 22:25:52 +0800
Subject: [PATCH 129/183] Add docs/requirements.txt

---
 docs/requirements.txt | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 docs/requirements.txt

diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 00000000..c5e7e301
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,2 @@
+sphinx==3.2.1
+sphinx_bootstrap_theme==0.7.1

From 18ecc5a199c324b344bfc2f99f9456f806acde41 Mon Sep 17 00:00:00 2001
From: Liuyang Wan <tsfdye@gmail.com>
Date: Mon, 23 Nov 2020 22:36:39 +0800
Subject: [PATCH 130/183] Use same docs requirements in tox

---
 tox.ini | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/tox.ini b/tox.ini
index 1ca3286b..bc3300ab 100644
--- a/tox.ini
+++ b/tox.ini
@@ -12,6 +12,5 @@ commands = pytest {posargs}
 
 [testenv:docs]
 deps =
-    sphinx
-    sphinx_bootstrap_theme
+    -rdocs/requirements.txt
 commands = sphinx-build docs build

From 3f40bd75d6cc3efee1e72413bc685e7bbf9b5a46 Mon Sep 17 00:00:00 2001
From: Liuyang Wan <tsfdye@gmail.com>
Date: Mon, 23 Nov 2020 22:50:38 +0800
Subject: [PATCH 131/183] Add docs build status badge to README.md

---
 README.rst | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/README.rst b/README.rst
index 5c090804..1ab43e53 100644
--- a/README.rst
+++ b/README.rst
@@ -3,6 +3,12 @@
 Overview: Easy, clean, reliable Python 2/3 compatibility
 ========================================================
 
+.. image:: https://travis-ci.org/PythonCharmers/python-future.svg?branch=master
+    :target: https://travis-ci.org/PythonCharmers/python-future
+
+.. image:: https://readthedocs.org/projects/python-future/badge/?version=latest
+    :target: https://python-future.readthedocs.io/en/latest/?badge=latest
+
 ``python-future`` is the missing compatibility layer between Python 2 and
 Python 3. It allows you to use a single, clean Python 3.x-compatible
 codebase to support both Python 2 and Python 3 with minimal overhead.
@@ -22,9 +28,6 @@ are `Mezzanine <http://mezzanine.jupo.org/>`_ and `ObsPy
 Features
 --------
 
-.. image:: https://travis-ci.org/PythonCharmers/python-future.svg?branch=master
-       :target: https://travis-ci.org/PythonCharmers/python-future
-
 -   ``future.builtins`` package (also available as ``builtins`` on Py2) provides
     backports and remappings for 20 builtins with different semantics on Py3
     versus Py2

From 42b30253a6968193965414d9eff82aca1d98094a Mon Sep 17 00:00:00 2001
From: Jordan Adler <jordan.m.adler@gmail.com>
Date: Fri, 10 Jul 2020 00:42:47 +0000
Subject: [PATCH 132/183] Build System

---
 .gitignore  |  2 ++
 .travis.yml | 47 ++++++-----------------------------------------
 Dockerfile  | 38 ++++++++++++++++++++++++++++++++++++++
 TESTING.txt |  8 +++-----
 build.sh    | 14 ++++++++++++++
 lint.sh     |  3 +++
 setup.sh    | 20 ++++++++++++++++++++
 tox.ini     | 16 ----------------
 8 files changed, 86 insertions(+), 62 deletions(-)
 create mode 100644 Dockerfile
 create mode 100755 build.sh
 create mode 100644 lint.sh
 create mode 100755 setup.sh
 delete mode 100644 tox.ini

diff --git a/.gitignore b/.gitignore
index 3b7bce98..01c37c9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,8 @@ develop-eggs
 .installed.cfg
 lib
 lib64
+MANIFEST
+MANIFEST.in
 
 # Backup files
 *.bak
diff --git a/.travis.yml b/.travis.yml
index 1be7fac8..d3a8b5ad 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,46 +1,11 @@
-sudo: false
-language: python
-cache: pip
+language: generic
 
-
-matrix:
-  include:
-    - python: 2.6
-      env: TOXENV=py26
-      dist: trusty
-    - python: 2.7
-      env: TOXENV=py27
-    - python: 3.3
-      env: TOXENV=py33
-      dist: trusty
-      sudo: false
-    - python: 3.4
-      env: TOXENV=py34
-    - python: 3.5
-      env: TOXENV=py35
-    - python: 3.6
-      env: TOXENV=py36
-    - python: 3.7
-      env: TOXENV=py37
-      dist: xenial    # required for Python 3.7 (travis-ci/travis-ci#9069)
-      sudo: required  # required for Python 3.7 (travis-ci/travis-ci#9069)
-
-install:
-  - pip install typing==3.7.4.1  # required for Python 3.3
-  - pip install tox==2.9.1
-  - pip install virtualenv==15.2.0
-  - pip install py==1.4.30
-  - pip install pluggy==0.5.2
+services:
+    - docker
 
 before_script:
-  # Run flake8 tests only on Python 2.7 and 3.7...
-  # 1) stop the build if there are Python syntax errors or undefined names
-  # 2) exit-zero treats all errors as warnings.  The GitHub editor is 127 chars wide
-  - if [[ $TRAVIS_PYTHON_VERSION == *.7 ]]; then
-      pip install flake8;
-      flake8 . --count --exit-zero --select=E901,E999,F821,F822,F823 --show-source --statistics;
-      flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics; 
-    fi
+    - docker pull jmadler/python-future-builder:latest
 
 script:
-  - tox
+  - ./lint.sh
+  - ./build.sh
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..5ed3387f
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,38 @@
+FROM debian:9
+# This docker image has a copy of a wide array of Pythons installed
+RUN apt-get update
+RUN apt-get install --yes --no-install-recommends make build-essential zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libffi-dev liblzma-dev libssl1.0-dev 
+RUN apt-get install --yes git vim
+RUN apt-get install --yes python3-pip
+ENV PYENV_ROOT=/opt/pyenv
+RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash 
+RUN echo export PATH="/opt/pyenv/bin:$PATH" >> ~/.bashrc
+RUN echo 'eval "$(pyenv init -)"' >> ~/.bashrc
+RUN echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc
+# venv 15.2.0 is the last to support Python 2.6. 
+RUN pip3 install virtualenv==15.2.0
+RUN PATH=/opt/pyenv/bin:$PATH pyenv install 2.6.9
+RUN virtualenv /root/py26 --python /opt/pyenv/versions/2.6.9/bin/python
+RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.3.7
+RUN virtualenv /root/py33 --python /opt/pyenv/versions/3.3.7/bin/python
+RUN pip3 install virtualenv==20.0.21
+RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.4.10
+RUN virtualenv /root/py34 --python /opt/pyenv/versions/3.4.10/bin/python
+RUN apt-get install --yes libssl-dev libxmlsec1-dev
+RUN PATH=/opt/pyenv/bin:$PATH pyenv install 2.7.18
+RUN virtualenv /root/py27 --python /opt/pyenv/versions/2.7.18/bin/python
+RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.5.9
+RUN virtualenv /root/py35 --python /opt/pyenv/versions/3.5.9/bin/python
+RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.6.10
+RUN virtualenv /root/py36 --python /opt/pyenv/versions/3.6.10/bin/python
+RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.7.7
+RUN virtualenv /root/py37 --python /opt/pyenv/versions/3.7.7/bin/python
+RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.8.3
+RUN virtualenv /root/py38 --python /opt/pyenv/versions/3.8.3/bin/python
+RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.9.0
+RUN virtualenv /root/py39 --python /opt/pyenv/versions/3.9.0/bin/python
+RUN ln -s /usr/bin/python3 /usr/bin/python
+ENV LC_ALL=C.UTF-8
+ENV LANG=C.UTF-8
+WORKDIR /root/python-future
+ADD . /root/python-future
diff --git a/TESTING.txt b/TESTING.txt
index 0e9b96a3..b2ad5c65 100644
--- a/TESTING.txt
+++ b/TESTING.txt
@@ -1,8 +1,6 @@
-Currently the tests are passing on OS X and Linux on Python 2.7 and 3.4.
+A docker image, python-future-builder, is used to do testing and building.  The test suite can be run with: 
 
-The test suite can be run with:
-
-    $ tox
+    $ bash build.sh
 
 which tests the module under a number of different python versions, where available, or with:
 
@@ -10,4 +8,4 @@ which tests the module under a number of different python versions, where availa
 
 To execute a single test:
 
-    $ pytest -k test_chained_exceptions_stacktrace
\ No newline at end of file
+    $ pytest -k test_chained_exceptions_stacktrace
diff --git a/build.sh b/build.sh
new file mode 100755
index 00000000..9dbba568
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,14 @@
+# XXX: TODO: we should make this include -e once tests pass
+set -xuo pipefail
+
+docker build . -t jmadler/python-future-builder
+
+version=0.18.2
+
+for i in py26 py27 py33 py34 py35 py36 py37 py38 py39; do
+    docker run -ti -v $(realpath dist):/root/python-future/dist python-future-builder /root/python-future/setup.sh $version $(basename $i)
+done
+
+python setup.py sdist
+python setup.py clean
+echo You may now run: "twine upload dist/*"
diff --git a/lint.sh b/lint.sh
new file mode 100644
index 00000000..234b3f14
--- /dev/null
+++ b/lint.sh
@@ -0,0 +1,3 @@
+# Run under Python 2.7 and 3.7
+flake8 . --count --exit-zero --select=E901,E999,F821,F822,F823 --show-source --statistics
+flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
diff --git a/setup.sh b/setup.sh
new file mode 100755
index 00000000..8e8dc150
--- /dev/null
+++ b/setup.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+set -exo pipefail
+
+version=$1
+pytag=$2
+
+if [ $pytag = 'py33' ]; then
+    pip3 install virtualenv==16.2.0
+fi
+
+source /root/$pytag/bin/activate
+
+if [ $pytag = 'py26' ]; then
+    pip install importlib
+fi
+pip install pytest unittest2
+python setup.py bdist_wheel --python-tag=$pytag
+pip install dist/future-$version-$pytag-none-any.whl
+pytest tests/
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index bc3300ab..00000000
--- a/tox.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[tox]
-envlist =
-    py{26,27,33,34,35,36,37},
-    docs
-
-[testenv]
-deps =
-    pytest
-    unittest2
-    py26: importlib
-commands = pytest {posargs}
-
-[testenv:docs]
-deps =
-    -rdocs/requirements.txt
-commands = sphinx-build docs build

From 01e8440942888dd44a10bc8fe976633a707721e2 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jordan.m.adler@gmail.com>
Date: Fri, 27 Nov 2020 11:13:19 -0800
Subject: [PATCH 133/183] Add docker push to optimize CI

---
 build.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/build.sh b/build.sh
index 9dbba568..04737215 100755
--- a/build.sh
+++ b/build.sh
@@ -2,6 +2,7 @@
 set -xuo pipefail
 
 docker build . -t jmadler/python-future-builder
+docker push jmadler/python-future-builder:latest
 
 version=0.18.2
 

From 58cc9849c7cbee1f6e9884190be82da55792d3ae Mon Sep 17 00:00:00 2001
From: Jordan Adler <jordan.m.adler@gmail.com>
Date: Fri, 27 Nov 2020 13:16:11 -0800
Subject: [PATCH 134/183] Make lint.sh executable

---
 lint.sh | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 mode change 100644 => 100755 lint.sh

diff --git a/lint.sh b/lint.sh
old mode 100644
new mode 100755

From 046ff1842a0b4464ebb0cdc936a24e626494968a Mon Sep 17 00:00:00 2001
From: Jordan Adler <jordan.m.adler@gmail.com>
Date: Fri, 27 Nov 2020 17:36:13 -0800
Subject: [PATCH 135/183] Add flake8 to image

---
 Dockerfile | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Dockerfile b/Dockerfile
index 5ed3387f..6b94c0a8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -31,6 +31,8 @@ RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.8.3
 RUN virtualenv /root/py38 --python /opt/pyenv/versions/3.8.3/bin/python
 RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.9.0
 RUN virtualenv /root/py39 --python /opt/pyenv/versions/3.9.0/bin/python
+# Lint tools
+RUN pip3 install flake8
 RUN ln -s /usr/bin/python3 /usr/bin/python
 ENV LC_ALL=C.UTF-8
 ENV LANG=C.UTF-8

From f96a219a966d04f7b60a05501ecee52ca5326055 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jordan.m.adler@gmail.com>
Date: Fri, 27 Nov 2020 18:52:15 -0800
Subject: [PATCH 136/183] fix order

---
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index d3a8b5ad..3fe6a983 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,5 +7,5 @@ before_script:
     - docker pull jmadler/python-future-builder:latest
 
 script:
-  - ./lint.sh
   - ./build.sh
+  - ./lint.sh

From 45cf38295a36fb8b59bd1921c926b940764492f3 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jordan.m.adler@gmail.com>
Date: Fri, 27 Nov 2020 21:32:17 -0800
Subject: [PATCH 137/183] Update docker image and parcel out to constant
 variable.  Add comment to update version constant

---
 build.sh | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/build.sh b/build.sh
index 04737215..d17fa7ce 100755
--- a/build.sh
+++ b/build.sh
@@ -1,13 +1,15 @@
 # XXX: TODO: we should make this include -e once tests pass
 set -xuo pipefail
 
-docker build . -t jmadler/python-future-builder
-docker push jmadler/python-future-builder:latest
-
+DOCKER_IMAGE=jmadler/python-future-builder
+# XXX: TODO: Perhaps this version shouldn't be hardcoded
 version=0.18.2
 
+docker build . -t $DOCKER_IMAGE
+docker push $DOCKER_IMAGE:latest
+
 for i in py26 py27 py33 py34 py35 py36 py37 py38 py39; do
-    docker run -ti -v $(realpath dist):/root/python-future/dist python-future-builder /root/python-future/setup.sh $version $(basename $i)
+    docker run -ti -v $(realpath dist):/root/python-future/dist $DOCKER_IMAGE /root/python-future/setup.sh $version $(basename $i)
 done
 
 python setup.py sdist

From 2abe00d562445f93f72a7cdc00439b7539cc0402 Mon Sep 17 00:00:00 2001
From: Jordan Adler <jordan.m.adler@gmail.com>
Date: Sat, 28 Nov 2020 09:46:28 -0800
Subject: [PATCH 138/183] Pass if lint fails

---
 lint.sh | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lint.sh b/lint.sh
index 234b3f14..667b258f 100755
--- a/lint.sh
+++ b/lint.sh
@@ -1,3 +1,3 @@
-# Run under Python 2.7 and 3.7
-flake8 . --count --exit-zero --select=E901,E999,F821,F822,F823 --show-source --statistics
-flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
+# TODO: Run under Python 2.7 and 3.7
+flake8 . --count --exit-zero --select=E901,E999,F821,F822,F823 --show-source --statistics || true
+flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics || true

From c780bf5e15ce00939d0eb7b477b4236a54447c85 Mon Sep 17 00:00:00 2001
From: Tomer Chachamu <tomer.chachamu@gmail.com>
Date: Tue, 29 Dec 2020 14:10:28 +0000
Subject: [PATCH 139/183] Correct __eq__

---
 src/future/types/newrange.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/future/types/newrange.py b/src/future/types/newrange.py
index eda01a5a..6d4ebe2f 100644
--- a/src/future/types/newrange.py
+++ b/src/future/types/newrange.py
@@ -87,7 +87,7 @@ def __eq__(self, other):
         return (isinstance(other, newrange) and
                 (self._len == 0 == other._len or
                  (self._start, self._step, self._len) ==
-                 (other._start, other._step, self._len)))
+                 (other._start, other._step, other._len)))
 
     def __len__(self):
         return self._len

From 974eb1ff2fb3982498574bbb8d8084501c477527 Mon Sep 17 00:00:00 2001
From: Tim Gates <tim.gates@iress.com>
Date: Thu, 31 Dec 2020 07:30:41 +1100
Subject: [PATCH 140/183] docs: fix simple typo, reqest -> request

There is a small typo in tests/test_future/test_urllibnet.py.

Should read `request` rather than `reqest`.
---
 tests/test_future/test_urllibnet.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/test_future/test_urllibnet.py b/tests/test_future/test_urllibnet.py
index f9639bfc..6a7b6d64 100644
--- a/tests/test_future/test_urllibnet.py
+++ b/tests/test_future/test_urllibnet.py
@@ -38,7 +38,7 @@ def testURLread(self):
 
 
 class urlopenNetworkTests(unittest.TestCase):
-    """Tests urllib.reqest.urlopen using the network.
+    """Tests urllib.request.urlopen using the network.
 
     These tests are not exhaustive.  Assuming that testing using files does a
     good job overall of some of the basic interface features.  There are no

From 6e27aacadfcee99607894b84e219ba4c571688ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nuno=20Andr=C3=A9?= <mail@nunoand.re>
Date: Wed, 20 Jan 2021 19:45:30 +0100
Subject: [PATCH 141/183] Fix bug in super() with metaclasses

---
 src/future/builtins/newsuper.py | 71 ++++++++++++++++-----------------
 tests/test_future/test_super.py | 12 ++++++
 2 files changed, 47 insertions(+), 36 deletions(-)

diff --git a/src/future/builtins/newsuper.py b/src/future/builtins/newsuper.py
index 5d3402bd..3e8cc80f 100644
--- a/src/future/builtins/newsuper.py
+++ b/src/future/builtins/newsuper.py
@@ -60,44 +60,15 @@ def newsuper(typ=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1):
             raise RuntimeError('super() used in a function with no args')
 
         try:
-            # Get the MRO so we can crawl it.
-            mro = type_or_obj.__mro__
-        except (AttributeError, RuntimeError):  # see issue #160
+            typ = find_owner(type_or_obj, f.f_code)
+        except (AttributeError, RuntimeError, TypeError):
+            # see issues #160, #267
             try:
-                mro = type_or_obj.__class__.__mro__
+                typ = find_owner(type_or_obj.__class__, f.f_code)
             except AttributeError:
-                raise RuntimeError('super() used with a non-newstyle class')
-
-        #   A ``for...else`` block?  Yes!  It's odd, but useful.
-        #   If unfamiliar with for...else, see:
-        #
-        #       http://psung.blogspot.com/2007/12/for-else-in-python.html
-        for typ in mro:
-            #  Find the class that owns the currently-executing method.
-            for meth in typ.__dict__.values():
-                # Drill down through any wrappers to the underlying func.
-                # This handles e.g. classmethod() and staticmethod().
-                try:
-                    while not isinstance(meth,FunctionType):
-                        if isinstance(meth, property):
-                            # Calling __get__ on the property will invoke
-                            # user code which might throw exceptions or have
-                            # side effects
-                            meth = meth.fget
-                        else:
-                            try:
-                                meth = meth.__func__
-                            except AttributeError:
-                                meth = meth.__get__(type_or_obj, typ)
-                except (AttributeError, TypeError):
-                    continue
-                if meth.func_code is f.f_code:
-                    break   # Aha!  Found you.
-            else:
-                continue    #  Not found! Move onto the next class in MRO.
-            break    #  Found! Break out of the search loop.
-        else:
-            raise RuntimeError('super() called outside a method')
+                raise RuntimeError('super() used with an old-style class')
+            except TypeError:
+                raise RuntimeError('super() called outside a method')
 
     #  Dispatch to builtin super().
     if type_or_obj is not _SENTINEL:
@@ -105,6 +76,34 @@ def newsuper(typ=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1):
     return _builtin_super(typ)
 
 
+def find_owner(cls, code):
+    '''Find the class that owns the currently-executing method.
+    '''
+    for typ in cls.__mro__:
+        for meth in typ.__dict__.values():
+            # Drill down through any wrappers to the underlying func.
+            # This handles e.g. classmethod() and staticmethod().
+            try:
+                while not isinstance(meth,FunctionType):
+                    if isinstance(meth, property):
+                        # Calling __get__ on the property will invoke
+                        # user code which might throw exceptions or have
+                        # side effects
+                        meth = meth.fget
+                    else:
+                        try:
+                            meth = meth.__func__
+                        except AttributeError:
+                            meth = meth.__get__(cls, typ)
+            except (AttributeError, TypeError):
+                continue
+            if meth.func_code is code:
+                return typ   # Aha!  Found you.
+        #  Not found! Move onto the next class in MRO.
+
+    raise TypeError
+
+
 def superm(*args, **kwds):
     f = sys._getframe(1)
     nm = f.f_code.co_name
diff --git a/tests/test_future/test_super.py b/tests/test_future/test_super.py
index 0376c1d8..3cb23d69 100644
--- a/tests/test_future/test_super.py
+++ b/tests/test_future/test_super.py
@@ -170,6 +170,18 @@ class Elite(Dangerous):
 
         self.assertEqual(Elite().walk(), 'Defused')
 
+    def test_metaclass(self):
+        class Meta(type):
+            def __init__(cls, name, bases, clsdict):
+                super().__init__(name, bases, clsdict)
+
+        try:
+            class Base(object):
+                __metaclass__ = Meta
+        except Exception as e:
+            self.fail('raised %s with a custom metaclass'
+                      % type(e).__name__)
+
 
 class TestSuperFromTestDescrDotPy(unittest.TestCase):
     """

From fe645ba312a02e6d676265ece5dbf5aa31512e9a Mon Sep 17 00:00:00 2001
From: zihzihtw <zihzihtw@gmail.com>
Date: Fri, 16 Apr 2021 16:36:27 +0800
Subject: [PATCH 142/183] Fix newint bool in py3

---
 src/future/types/newint.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/future/types/newint.py b/src/future/types/newint.py
index 748dba9d..04a411e9 100644
--- a/src/future/types/newint.py
+++ b/src/future/types/newint.py
@@ -284,6 +284,9 @@ def __bool__(self):
         """
         So subclasses can override this, Py3-style
         """
+        if PY3:
+            return super(newint, self).__bool__()
+
         return super(newint, self).__nonzero__()
 
     def __native__(self):

From 21ae5c76eb49edfff73d5a2319bf42ab0378da48 Mon Sep 17 00:00:00 2001
From: Alexander Shadchin <shadchin@yandex-team.ru>
Date: Fri, 11 Jun 2021 22:03:18 +0300
Subject: [PATCH 143/183] Fix tests

---
 tests/test_future/test_urllib_toplevel.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/test_future/test_urllib_toplevel.py b/tests/test_future/test_urllib_toplevel.py
index 68bc4c96..923b2e8a 100644
--- a/tests/test_future/test_urllib_toplevel.py
+++ b/tests/test_future/test_urllib_toplevel.py
@@ -120,7 +120,7 @@ def setUp(self):
         finally:
             f.close()
         self.pathname = support.TESTFN
-        self.returned_obj = urlopen("file:%s" % self.pathname)
+        self.returned_obj = urlopen("file:%s" % urllib_parse.quote(self.pathname))
 
     def tearDown(self):
         """Shut down the open object"""
@@ -167,7 +167,7 @@ def test_info(self):
         self.assertIsInstance(self.returned_obj.info(), email_message.Message)
 
     def test_geturl(self):
-        self.assertEqual(self.returned_obj.geturl(), self.pathname)
+        self.assertEqual(self.returned_obj.geturl(), urllib_parse.quote(self.pathname))
 
     def test_getcode(self):
         self.assertIsNone(self.returned_obj.getcode())

From 3401099e77d5838715044902b1b6ef8ae118b2fd Mon Sep 17 00:00:00 2001
From: Andrew Bjonnes <abjonnes@foundationmedicine.com>
Date: Wed, 3 Nov 2021 21:02:35 -0400
Subject: [PATCH 144/183] Fix bug in fix_raise.py fixer

The replacement node for the fix of a raise statement with an unknown
value should inherit the prefix of the source node.
---
 src/libfuturize/fixes/fix_raise.py           |  2 +-
 tests/test_future/test_libfuturize_fixers.py | 14 ++++++++++++++
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/libfuturize/fixes/fix_raise.py b/src/libfuturize/fixes/fix_raise.py
index f7518416..d113401c 100644
--- a/src/libfuturize/fixes/fix_raise.py
+++ b/src/libfuturize/fixes/fix_raise.py
@@ -94,7 +94,7 @@ def transform(self, node, results):
                 args = [exc, Comma(), val]
                 if tb is not None:
                     args += [Comma(), tb]
-                return Call(Name(u"raise_"), args)
+                return Call(Name(u"raise_"), args, prefix=node.prefix)
 
         if tb is not None:
             tb.prefix = ""
diff --git a/tests/test_future/test_libfuturize_fixers.py b/tests/test_future/test_libfuturize_fixers.py
index 4ac0b7e1..2080696a 100644
--- a/tests/test_future/test_libfuturize_fixers.py
+++ b/tests/test_future/test_libfuturize_fixers.py
@@ -767,6 +767,20 @@ def test_unknown_value_with_traceback_with_comments(self):
         raise_(E, Func(arg1, arg2, arg3), tb) # foo"""
         self.check(b, a)
 
+    def test_unknown_value_with_indent(self):
+        b = """
+        while True:
+            print()  # another expression in the same block triggers different parsing
+            raise E, V
+        """
+        a = """
+        from future.utils import raise_
+        while True:
+            print()  # another expression in the same block triggers different parsing
+            raise_(E, V)
+        """
+        self.check(b, a)
+
     # These should produce a warning
 
     def test_string_exc(self):

From dffc579dbb7c882fc01fa0c0dfa6b59acef7827d Mon Sep 17 00:00:00 2001
From: Andrew Bjonnes <abjonnes@foundationmedicine.com>
Date: Wed, 10 Nov 2021 22:19:41 -0500
Subject: [PATCH 145/183] Fix bug in fix_print.py fixer

When the print ends with a non-space whitespace character, an extra
space character should not be printed at the end.
---
 src/libfuturize/fixes/fix_print.py           | 10 +++++++
 tests/test_future/test_libfuturize_fixers.py | 31 ++++++++++++++++++++
 2 files changed, 41 insertions(+)

diff --git a/src/libfuturize/fixes/fix_print.py b/src/libfuturize/fixes/fix_print.py
index 247b91b8..2554717c 100644
--- a/src/libfuturize/fixes/fix_print.py
+++ b/src/libfuturize/fixes/fix_print.py
@@ -57,6 +57,16 @@ def transform(self, node, results):
         if args and args[-1] == Comma():
             args = args[:-1]
             end = " "
+
+            # try to determine if the string ends in a non-space whitespace character, in which
+            # case there should be no space at the end of the conversion
+            string_leaves = [leaf for leaf in args[-1].leaves() if leaf.type == token.STRING]
+            if (
+                string_leaves
+                and string_leaves[-1].value[0] != "r"  # "raw" string
+                and string_leaves[-1].value[-3:-1] in (r"\t", r"\n", r"\r")
+            ):
+                end = ""
         if args and args[0] == pytree.Leaf(token.RIGHTSHIFT, u">>"):
             assert len(args) >= 2
             file = args[1].clone()
diff --git a/tests/test_future/test_libfuturize_fixers.py b/tests/test_future/test_libfuturize_fixers.py
index 2080696a..2146d1f2 100644
--- a/tests/test_future/test_libfuturize_fixers.py
+++ b/tests/test_future/test_libfuturize_fixers.py
@@ -307,6 +307,37 @@ def test_trailing_comma_3(self):
         a = """print(1, end=' ')"""
         self.check(b, a)
 
+    def test_trailing_comma_4(self):
+        b = """print "a ","""
+        a = """print("a ", end=' ')"""
+        self.check(b, a)
+
+    def test_trailing_comma_5(self):
+        b = r"""print "b\t","""
+        a = r"""print("b\t", end='')"""
+        self.check(b, a)
+
+    def test_trailing_comma_6(self):
+        b = r"""print "c\n","""
+        a = r"""print("c\n", end='')"""
+        self.check(b, a)
+
+    def test_trailing_comma_7(self):
+        b = r"""print "d\r","""
+        a = r"""print("d\r", end='')"""
+        self.check(b, a)
+
+    def test_trailing_comma_8(self):
+        b = r"""print "%s\n" % (1,),"""
+        a = r"""print("%s\n" % (1,), end='')"""
+        self.check(b, a)
+
+
+    def test_trailing_comma_9(self):
+        b = r"""print r"e\n","""
+        a = r"""print(r"e\n", end=' ')"""
+        self.check(b, a)
+
     # >> stuff
 
     def test_vargs_without_trailing_comma(self):

From f16e1c18821532aa7635265a590fbd7ad967d514 Mon Sep 17 00:00:00 2001
From: Kian-Meng Ang <kianmeng@cpan.org>
Date: Wed, 12 Jan 2022 19:38:48 +0800
Subject: [PATCH 146/183] Fix typos

---
 docs/3rd-party-py3k-compat-code/jinja2_compat.py              | 2 +-
 docs/notebooks/object special methods (next, bool, ...).ipynb | 2 +-
 docs/standard_library_imports.rst                             | 2 +-
 docs/whatsnew.rst                                             | 4 ++--
 src/future/backports/datetime.py                              | 2 +-
 src/future/backports/email/_header_value_parser.py            | 2 +-
 src/future/backports/email/parser.py                          | 4 ++--
 src/future/backports/http/cookiejar.py                        | 2 +-
 src/future/backports/xmlrpc/client.py                         | 2 +-
 src/future/standard_library/__init__.py                       | 2 +-
 src/future/types/newint.py                                    | 2 +-
 src/future/types/newrange.py                                  | 2 +-
 src/libfuturize/fixer_util.py                                 | 2 +-
 src/libfuturize/fixes/fix_metaclass.py                        | 4 ++--
 tests/test_future/test_http_cookiejar.py                      | 4 ++--
 tests/test_future/test_standard_library.py                    | 2 +-
 tests/test_future/test_urllib.py                              | 4 ++--
 tests/test_future/test_urllib_toplevel.py                     | 4 ++--
 18 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/docs/3rd-party-py3k-compat-code/jinja2_compat.py b/docs/3rd-party-py3k-compat-code/jinja2_compat.py
index 1326cbc6..0456faae 100644
--- a/docs/3rd-party-py3k-compat-code/jinja2_compat.py
+++ b/docs/3rd-party-py3k-compat-code/jinja2_compat.py
@@ -85,7 +85,7 @@ def encode_filename(filename):
 
 def with_metaclass(meta, *bases):
     # This requires a bit of explanation: the basic idea is to make a
-    # dummy metaclass for one level of class instanciation that replaces
+    # dummy metaclass for one level of class instantiation that replaces
     # itself with the actual metaclass.  Because of internal type checks
     # we also need to make sure that we downgrade the custom metaclass
     # for one level to something closer to type (that's why __call__ and
diff --git a/docs/notebooks/object special methods (next, bool, ...).ipynb b/docs/notebooks/object special methods (next, bool, ...).ipynb
index 5729ddc5..7da31856 100644
--- a/docs/notebooks/object special methods (next, bool, ...).ipynb	
+++ b/docs/notebooks/object special methods (next, bool, ...).ipynb	
@@ -63,7 +63,7 @@
      "collapsed": false,
      "input": [
       "# Py3-style iterators written as new-style classes (subclasses of\n",
-      "# future.builtins.object) are backward compatibile with Py2:\n",
+      "# future.builtins.object) are backward compatible with Py2:\n",
       "class Upper(object):\n",
       "    def __init__(self, iterable):\n",
       "        self._iter = iter(iterable)\n",
diff --git a/docs/standard_library_imports.rst b/docs/standard_library_imports.rst
index 60442541..c09e9e30 100644
--- a/docs/standard_library_imports.rst
+++ b/docs/standard_library_imports.rst
@@ -15,7 +15,7 @@ As of version 0.14, the ``future`` package comes with top-level packages for
 Python 2.x that provide access to the reorganized standard library modules
 under their Python 3.x names.
 
-Direct imports are the preferred mechanism for accesing the renamed standard
+Direct imports are the preferred mechanism for accessing the renamed standard
 library modules in Python 2/3 compatible code. For example, the following clean
 Python 3 code runs unchanged on Python 2 after installing ``future``::
 
diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index c6fa8f86..c9df5120 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -33,7 +33,7 @@ This is a major bug-fix and feature release, including:
 - Fix an issue with copyreg import under Py3 that results in unexposed stdlib functionality
 - Export and document types in future.utils
 - Update behavior of newstr.__eq__() to match str.__eq__() as per reference docs
-- Fix raising and the raising fixer to handle cases where the syntax is ambigious
+- Fix raising and the raising fixer to handle cases where the syntax is ambiguous
 - Allow "default" parameter in min() and max() (Issue #334)
 - Implement __hash__() in newstr (Issue #454)
 - Future proof some version checks to handle the fact that Py4 won't be a major breaking release
@@ -65,7 +65,7 @@ This is a major bug-fix release, including:
 - Fix ``past.translation`` on read-only file systems
 - Fix Tkinter import bug introduced in Python 2.7.4 (issue #262)
 - Correct TypeError to ValueError in a specific edge case for newrange
-- Support inequality tests betwen newstrs and newbytes
+- Support inequality tests between newstrs and newbytes
 - Add type check to __get__ in newsuper
 - Fix fix_divsion_safe to support better conversion of complex expressions, and
   skip obvious float division.
diff --git a/src/future/backports/datetime.py b/src/future/backports/datetime.py
index 3261014e..8cd62ddf 100644
--- a/src/future/backports/datetime.py
+++ b/src/future/backports/datetime.py
@@ -689,7 +689,7 @@ def today(cls):
 
     @classmethod
     def fromordinal(cls, n):
-        """Contruct a date from a proleptic Gregorian ordinal.
+        """Construct a date from a proleptic Gregorian ordinal.
 
         January 1 of year 1 is day 1.  Only the year, month and day are
         non-zero in the result.
diff --git a/src/future/backports/email/_header_value_parser.py b/src/future/backports/email/_header_value_parser.py
index 43957edc..59b1b318 100644
--- a/src/future/backports/email/_header_value_parser.py
+++ b/src/future/backports/email/_header_value_parser.py
@@ -2867,7 +2867,7 @@ def parse_content_type_header(value):
         _find_mime_parameters(ctype, value)
         return ctype
     ctype.append(token)
-    # XXX: If we really want to follow the formal grammer we should make
+    # XXX: If we really want to follow the formal grammar we should make
     # mantype and subtype specialized TokenLists here.  Probably not worth it.
     if not value or value[0] != '/':
         ctype.defects.append(errors.InvalidHeaderDefect(
diff --git a/src/future/backports/email/parser.py b/src/future/backports/email/parser.py
index df1c6e28..79f0e5a3 100644
--- a/src/future/backports/email/parser.py
+++ b/src/future/backports/email/parser.py
@@ -26,7 +26,7 @@ def __init__(self, _class=Message, **_3to2kwargs):
         textual representation of the message.
 
         The string must be formatted as a block of RFC 2822 headers and header
-        continuation lines, optionally preceeded by a `Unix-from' header.  The
+        continuation lines, optionally preceded by a `Unix-from' header.  The
         header block is terminated either by the end of the string or by a
         blank line.
 
@@ -92,7 +92,7 @@ def __init__(self, *args, **kw):
         textual representation of the message.
 
         The input must be formatted as a block of RFC 2822 headers and header
-        continuation lines, optionally preceeded by a `Unix-from' header.  The
+        continuation lines, optionally preceded by a `Unix-from' header.  The
         header block is terminated either by the end of the input or by a
         blank line.
 
diff --git a/src/future/backports/http/cookiejar.py b/src/future/backports/http/cookiejar.py
index af3ef415..1d301b1c 100644
--- a/src/future/backports/http/cookiejar.py
+++ b/src/future/backports/http/cookiejar.py
@@ -1845,7 +1845,7 @@ def lwp_cookie_str(cookie):
 class LWPCookieJar(FileCookieJar):
     """
     The LWPCookieJar saves a sequence of "Set-Cookie3" lines.
-    "Set-Cookie3" is the format used by the libwww-perl libary, not known
+    "Set-Cookie3" is the format used by the libwww-perl library, not known
     to be compatible with any browser, but which is easy to read and
     doesn't lose information about RFC 2965 cookies.
 
diff --git a/src/future/backports/xmlrpc/client.py b/src/future/backports/xmlrpc/client.py
index b78e5bad..cab75390 100644
--- a/src/future/backports/xmlrpc/client.py
+++ b/src/future/backports/xmlrpc/client.py
@@ -1251,7 +1251,7 @@ def close(self):
     # Send HTTP request.
     #
     # @param host Host descriptor (URL or (URL, x509 info) tuple).
-    # @param handler Targer RPC handler (a path relative to host)
+    # @param handler Target RPC handler (a path relative to host)
     # @param request_body The XML-RPC request body
     # @param debug Enable debugging if debug is true.
     # @return An HTTPConnection.
diff --git a/src/future/standard_library/__init__.py b/src/future/standard_library/__init__.py
index cff02f95..24124b26 100644
--- a/src/future/standard_library/__init__.py
+++ b/src/future/standard_library/__init__.py
@@ -17,7 +17,7 @@
     import socketserver
     import winreg    # on Windows only
     import test.support
-    import html, html.parser, html.entites
+    import html, html.parser, html.entities
     import http, http.client, http.server
     import http.cookies, http.cookiejar
     import urllib.parse, urllib.request, urllib.response, urllib.error, urllib.robotparser
diff --git a/src/future/types/newint.py b/src/future/types/newint.py
index 04a411e9..2c86ce18 100644
--- a/src/future/types/newint.py
+++ b/src/future/types/newint.py
@@ -318,7 +318,7 @@ def to_bytes(self, length, byteorder='big', signed=False):
             bits = length * 8
             num = (2**bits) + self
             if num <= 0:
-                raise OverflowError("int too smal to convert")
+                raise OverflowError("int too small to convert")
         else:
             if self < 0:
                 raise OverflowError("can't convert negative int to unsigned")
diff --git a/src/future/types/newrange.py b/src/future/types/newrange.py
index 6d4ebe2f..dc5eb802 100644
--- a/src/future/types/newrange.py
+++ b/src/future/types/newrange.py
@@ -105,7 +105,7 @@ def index(self, value):
         raise ValueError('%r is not in range' % value)
 
     def count(self, value):
-        """Return the number of ocurrences of integer `value`
+        """Return the number of occurrences of integer `value`
         in the sequence this range represents."""
         # a value can occur exactly zero or one times
         return int(value in self)
diff --git a/src/libfuturize/fixer_util.py b/src/libfuturize/fixer_util.py
index 48e4689d..cf646b61 100644
--- a/src/libfuturize/fixer_util.py
+++ b/src/libfuturize/fixer_util.py
@@ -116,7 +116,7 @@ def suitify(parent):
     """
     for node in parent.children:
         if node.type == syms.suite:
-            # already in the prefered format, do nothing
+            # already in the preferred format, do nothing
             return
 
     # One-liners have no suite node, we have to fake one up
diff --git a/src/libfuturize/fixes/fix_metaclass.py b/src/libfuturize/fixes/fix_metaclass.py
index 2ac41c97..a7eee40d 100644
--- a/src/libfuturize/fixes/fix_metaclass.py
+++ b/src/libfuturize/fixes/fix_metaclass.py
@@ -37,7 +37,7 @@
 
 def has_metaclass(parent):
     """ we have to check the cls_node without changing it.
-        There are two possiblities:
+        There are two possibilities:
           1)  clsdef => suite => simple_stmt => expr_stmt => Leaf('__meta')
           2)  clsdef => simple_stmt => expr_stmt => Leaf('__meta')
     """
@@ -63,7 +63,7 @@ def fixup_parse_tree(cls_node):
             # already in the preferred format, do nothing
             return
 
-    # !%@#! oneliners have no suite node, we have to fake one up
+    # !%@#! one-liners have no suite node, we have to fake one up
     for i, node in enumerate(cls_node.children):
         if node.type == token.COLON:
             break
diff --git a/tests/test_future/test_http_cookiejar.py b/tests/test_future/test_http_cookiejar.py
index 079026bc..8a98ed68 100644
--- a/tests/test_future/test_http_cookiejar.py
+++ b/tests/test_future/test_http_cookiejar.py
@@ -380,7 +380,7 @@ class CookieTests(unittest.TestCase):
 ##   comma-separated list, it'll be a headache to parse (at least my head
 ##   starts hurting every time I think of that code).
 ## - Expires: You'll get all sorts of date formats in the expires,
-##   including emtpy expires attributes ("expires="). Be as flexible as you
+##   including empty expires attributes ("expires="). Be as flexible as you
 ##   can, and certainly don't expect the weekday to be there; if you can't
 ##   parse it, just ignore it and pretend it's a session cookie.
 ## - Domain-matching: Netscape uses the 2-dot rule for _all_ domains, not
@@ -1734,7 +1734,7 @@ def test_session_cookies(self):
             key = "%s_after" % cookie.value
             counter[key] = counter[key] + 1
 
-            # a permanent cookie got lost accidently
+            # a permanent cookie got lost accidentally
         self.assertEqual(counter["perm_after"], counter["perm_before"])
             # a session cookie hasn't been cleared
         self.assertEqual(counter["session_after"], 0)
diff --git a/tests/test_future/test_standard_library.py b/tests/test_future/test_standard_library.py
index 3ac5d2d7..44c61a9b 100644
--- a/tests/test_future/test_standard_library.py
+++ b/tests/test_future/test_standard_library.py
@@ -591,7 +591,7 @@ def test_future_moves_dbm(self):
         from future.moves.dbm import ndbm
 
 
-# Running the following tkinter test causes the following bizzare test failure:
+# Running the following tkinter test causes the following bizarre test failure:
 #
 # ======================================================================
 # FAIL: test_open_default_encoding (future.tests.test_builtins.BuiltinTest)
diff --git a/tests/test_future/test_urllib.py b/tests/test_future/test_urllib.py
index 278bafb5..64e89760 100644
--- a/tests/test_future/test_urllib.py
+++ b/tests/test_future/test_urllib.py
@@ -1,4 +1,4 @@
-"""Regresssion tests for urllib"""
+"""Regression tests for urllib"""
 from __future__ import absolute_import, division, unicode_literals
 
 import io
@@ -1229,7 +1229,7 @@ def open_spam(self, url):
 # Everywhere else they work ok, but on those machines, sometimes
 # fail in one of the tests, sometimes in other. I have a linux, and
 # the tests go ok.
-# If anybody has one of the problematic enviroments, please help!
+# If anybody has one of the problematic environments, please help!
 # .   Facundo
 #
 # def server(evt):
diff --git a/tests/test_future/test_urllib_toplevel.py b/tests/test_future/test_urllib_toplevel.py
index 11e77201..86c75619 100644
--- a/tests/test_future/test_urllib_toplevel.py
+++ b/tests/test_future/test_urllib_toplevel.py
@@ -1,4 +1,4 @@
-"""Regresssion tests for urllib"""
+"""Regression tests for urllib"""
 from __future__ import absolute_import, division, unicode_literals
 
 import io
@@ -1244,7 +1244,7 @@ def open_spam(self, url):
 # Everywhere else they work ok, but on those machines, sometimes
 # fail in one of the tests, sometimes in other. I have a linux, and
 # the tests go ok.
-# If anybody has one of the problematic enviroments, please help!
+# If anybody has one of the problematic environments, please help!
 # .   Facundo
 #
 # def server(evt):

From 4cafc9d860d19135f5edb6e9d35ddf9f11b6d989 Mon Sep 17 00:00:00 2001
From: Linux User <user@DESKTOP-LVO3GEU.localdomain>
Date: Sun, 27 Feb 2022 05:30:15 +0000
Subject: [PATCH 147/183] modified fix_unpacking.py to support both the python2
 unicode function and use the str function instead on python3

---
 src/libpasteurize/fixes/fix_unpacking.py | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/libpasteurize/fixes/fix_unpacking.py b/src/libpasteurize/fixes/fix_unpacking.py
index c2d3207a..e47efe0e 100644
--- a/src/libpasteurize/fixes/fix_unpacking.py
+++ b/src/libpasteurize/fixes/fix_unpacking.py
@@ -18,8 +18,14 @@ def assignment_source(num_pre, num_post, LISTNAME, ITERNAME):
     Returns a source fit for Assign() from fixer_util
     """
     children = []
-    pre = unicode(num_pre)
-    post = unicode(num_post)
+    try:
+        pre = unicode(num_pre)
+    except NameError:
+        pre = str(num_pre)
+    try:
+        post = unicode(num_post)
+    except NameError:
+        post = str(num_post)
     # This code builds the assignment source from lib2to3 tree primitives.
     # It's not very readable, but it seems like the most correct way to do it.
     if num_pre > 0:

From 8e5ea186d385dbdc89b16cd81f58754ac05ad0a0 Mon Sep 17 00:00:00 2001
From: Linux User <user@DESKTOP-LVO3GEU.localdomain>
Date: Sun, 27 Feb 2022 06:42:43 +0000
Subject: [PATCH 148/183] condensed/refactored fix_unpacking.py change

---
 src/libpasteurize/fixes/fix_unpacking.py | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/libpasteurize/fixes/fix_unpacking.py b/src/libpasteurize/fixes/fix_unpacking.py
index e47efe0e..6e839e6b 100644
--- a/src/libpasteurize/fixes/fix_unpacking.py
+++ b/src/libpasteurize/fixes/fix_unpacking.py
@@ -20,11 +20,9 @@ def assignment_source(num_pre, num_post, LISTNAME, ITERNAME):
     children = []
     try:
         pre = unicode(num_pre)
-    except NameError:
-        pre = str(num_pre)
-    try:
         post = unicode(num_post)
     except NameError:
+        pre = str(num_pre)
         post = str(num_post)
     # This code builds the assignment source from lib2to3 tree primitives.
     # It's not very readable, but it seems like the most correct way to do it.

From 33977e4c10cd68cce2ab262a73d565173d06c0a6 Mon Sep 17 00:00:00 2001
From: Andrii Oriekhov <andriyorehov@gmail.com>
Date: Mon, 28 Feb 2022 15:04:59 +0200
Subject: [PATCH 149/183] add GitHub URL for PyPi

---
 setup.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/setup.py b/setup.py
index 11d694c2..41b0df96 100755
--- a/setup.py
+++ b/setup.py
@@ -161,6 +161,9 @@
       author=AUTHOR,
       author_email=AUTHOR_EMAIL,
       url=URL,
+      project_urls={
+          'Source': 'https://github.com/PythonCharmers/python-future',
+      },
       description=DESCRIPTION,
       long_description=LONG_DESC,
       license=LICENSE,

From f1100c8b9eda51e7c242d6153b04946517080f3c Mon Sep 17 00:00:00 2001
From: matthew <stidmatt@gmail.com>
Date: Fri, 18 Mar 2022 09:31:05 -0700
Subject: [PATCH 150/183] future cannot install without setuptools

Signed-off-by: matthew <stidmatt@gmail.com>
---
 docs/requirements.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/requirements.txt b/docs/requirements.txt
index c5e7e301..0f06b13f 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,2 +1,3 @@
 sphinx==3.2.1
 sphinx_bootstrap_theme==0.7.1
+setuptools==0.18.2

From c91d70b34ef0402aef3e9d04364ba98509dca76f Mon Sep 17 00:00:00 2001
From: Will Shanks <wshaos@posteo.net>
Date: Fri, 23 Dec 2022 13:38:26 -0500
Subject: [PATCH 151/183] Backport fix for bpo-38804

The regex http.cookiejar.LOOSE_HTTP_DATE_RE was vulnerable to regular
expression denial of service (REDoS). The regex contained multiple
overlapping \s* capture groups. A long sequence of spaces can trigger
bad performance.

See https://github.com/python/cpython/pull/17157 and https://pyup.io/posts/pyup-discovers-redos-vulnerabilities-in-top-python-packages/
---
 src/future/backports/http/cookiejar.py | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/src/future/backports/http/cookiejar.py b/src/future/backports/http/cookiejar.py
index af3ef415..0ad80a02 100644
--- a/src/future/backports/http/cookiejar.py
+++ b/src/future/backports/http/cookiejar.py
@@ -225,10 +225,14 @@ def _str2time(day, mon, yr, hr, min, sec, tz):
        (?::(\d\d))?    # optional seconds
     )?                 # optional clock
        \s*
-    ([-+]?\d{2,4}|(?![APap][Mm]\b)[A-Za-z]+)? # timezone
+    (?:
+       ([-+]?\d{2,4}|(?![APap][Mm]\b)[A-Za-z]+) # timezone
+       \s*
+    )?
+    (?:
+       \(\w+\)         # ASCII representation of timezone in parens.
        \s*
-    (?:\(\w+\))?       # ASCII representation of timezone in parens.
-       \s*$""", re.X | re.ASCII)
+    )?$""", re.X | re.ASCII)
 def http2time(text):
     """Returns time in seconds since epoch of time represented by a string.
 
@@ -298,9 +302,11 @@ def http2time(text):
       (?::?(\d\d(?:\.\d*)?))?  # optional seconds (and fractional)
    )?                    # optional clock
       \s*
-   ([-+]?\d\d?:?(:?\d\d)?
-    |Z|z)?               # timezone  (Z is "zero meridian", i.e. GMT)
-      \s*$""", re.X | re. ASCII)
+   (?:
+      ([-+]?\d\d?:?(:?\d\d)?
+       |Z|z)             # timezone  (Z is "zero meridian", i.e. GMT)
+      \s*
+   )?$""", re.X | re. ASCII)
 def iso2time(text):
     """
     As for http2time, but parses the ISO 8601 formats:

From 079ee9b75441d36447cec9981fa1b0032862f64d Mon Sep 17 00:00:00 2001
From: Liuyang Wan <tsfdye@gmail.com>
Date: Fri, 13 Jan 2023 11:11:30 +0800
Subject: [PATCH 152/183] Prepare for 0.18.3 release

---
 Dockerfile             |  5 ++--
 build.sh               |  2 +-
 docs/whatsnew.rst      | 56 ++++++++++++++++++++++++++++++++++++++++++
 src/future/__init__.py |  2 +-
 4 files changed, 61 insertions(+), 4 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index 6b94c0a8..678de2e2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -11,8 +11,9 @@ RUN echo 'eval "$(pyenv init -)"' >> ~/.bashrc
 RUN echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc
 # venv 15.2.0 is the last to support Python 2.6. 
 RUN pip3 install virtualenv==15.2.0
-RUN PATH=/opt/pyenv/bin:$PATH pyenv install 2.6.9
-RUN virtualenv /root/py26 --python /opt/pyenv/versions/2.6.9/bin/python
+# Can't get python 2.6 to build anymore
+# RUN PATH=/opt/pyenv/bin:$PATH pyenv install 2.6.9
+# RUN virtualenv /root/py26 --python /opt/pyenv/versions/2.6.9/bin/python
 RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.3.7
 RUN virtualenv /root/py33 --python /opt/pyenv/versions/3.3.7/bin/python
 RUN pip3 install virtualenv==20.0.21
diff --git a/build.sh b/build.sh
index d17fa7ce..df1f00f7 100755
--- a/build.sh
+++ b/build.sh
@@ -3,7 +3,7 @@ set -xuo pipefail
 
 DOCKER_IMAGE=jmadler/python-future-builder
 # XXX: TODO: Perhaps this version shouldn't be hardcoded
-version=0.18.2
+version=0.18.3
 
 docker build . -t $DOCKER_IMAGE
 docker push $DOCKER_IMAGE:latest
diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index c6fa8f86..40f7191f 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -3,6 +3,62 @@
 What's New
 **********
 
+What's new in version 0.18.3 (2023-01-13)
+=========================================
+This is a minor bug-fix release containing a number of fixes:
+
+- Backport fix for bpo-38804 (c91d70b)
+- Fix bug in fix_print.py fixer (dffc579)
+- Fix bug in fix_raise.py fixer (3401099)
+- Fix newint bool in py3 (fe645ba)
+- Fix bug in super() with metaclasses (6e27aac)
+- docs: fix simple typo, reqest -> request (974eb1f)
+- Correct __eq__ (c780bf5)
+- Pass if lint fails (2abe00d)
+- Update docker image and parcel out to constant variable.  Add comment to update version constant (45cf382)
+- fix order (f96a219)
+- Add flake8 to image (046ff18)
+- Make lint.sh executable (58cc984)
+- Add docker push to optimize CI (01e8440)
+- Build System (42b3025)
+- Add docs build status badge to README.md (3f40bd7)
+- Use same docs requirements in tox (18ecc5a)
+- Add docs/requirements.txt (5f9893f)
+- Add PY37_PLUS, PY38_PLUS, and PY39_PLUS (bee0247)
+- fix 2.6 test, better comment (ddedcb9)
+- fix 2.6 test (3f1ff7e)
+- remove nan test (4dbded1)
+- include list test values (e3f1a12)
+- fix other python2 test issues (c051026)
+- fix missing subTest (f006cad)
+- import from old imp library on older python versions (fc84fa8)
+- replace fstrings with format for python 3.4,3.5 (4a687ea)
+- minor style/spelling fixes (8302d8c)
+- improve cmp function, add unittest (0d95a40)
+- Pin typing==3.7.4.1 for Python 3.3 compatiblity (1a48f1b)
+- Fix various py26 unit test failures (9ca5a14)
+- Add initial contributing guide with docs build instruction (e55f915)
+- Add docs building to tox.ini (3ee9e7f)
+- Support NumPy's specialized int types in builtins.round (b4b54f0)
+- Added r""" to the docstring to avoid warnings in python3 (5f94572)
+- Add __subclasscheck__ for past.types.basestring (c9bc0ff)
+- Correct example in README (681e78c)
+- Add simple documentation (6c6e3ae)
+- Add pre-commit hooks (a9c6a37)
+- Handling of __next__ and next by future.utils.get_next was reversed (52b0ff9)
+- Add a test for our fix (461d77e)
+- Compare headers to correct definition of str (3eaa8fd)
+- #322 Add support for negative ndigits in round; additionally, fixing a bug so that it handles passing in Decimal properly (a4911b9)
+- Add tkFileDialog to future.movers.tkinter (f6a6549)
+- Sort before comparing dicts in TestChainMap (6126997)
+- Fix typo (4dfa099)
+- Fix formatting in "What's new" (1663dfa)
+- Fix typo (4236061)
+- Avoid DeprecationWarning caused by invalid escape (e4b7fa1)
+- Fixup broken link to external django documentation re: porting to Python 3 and unicode_literals (d87713e)
+- Fixed newdict checking version every time (99030ec)
+- Add count from 2.7 to 2.6 (1b8ef51)
+
 What's new in version 0.18.2 (2019-10-30)
 =========================================
 This is a minor bug-fix release containing a number of fixes:
diff --git a/src/future/__init__.py b/src/future/__init__.py
index ad419d67..b609299a 100644
--- a/src/future/__init__.py
+++ b/src/future/__init__.py
@@ -87,7 +87,7 @@
 __copyright__ = 'Copyright 2013-2019 Python Charmers Pty Ltd'
 __ver_major__ = 0
 __ver_minor__ = 18
-__ver_patch__ = 2
+__ver_patch__ = 3
 __ver_sub__ = ''
 __version__ = "%d.%d.%d%s" % (__ver_major__, __ver_minor__,
                               __ver_patch__, __ver_sub__)

From a914e33e9f48a77356006cd54e538d9d36ba8629 Mon Sep 17 00:00:00 2001
From: slycordinator <68940237+slycordinator@users.noreply.github.com>
Date: Thu, 23 Feb 2023 16:13:34 +0900
Subject: [PATCH 153/183] Fixes according to output from shellcheck

* Added "!#" to top of files that didn't have it.

* Added quotes around variables/commands as recommended

* Changed some to be /bin/sh instead of /bin/bash for portability
* removed -o pipefail, especiallly because the scripts seem to not have any piped commands, but also due to it being a bashism
---
 build.sh |  9 +++++----
 lint.sh  |  1 +
 setup.sh | 14 +++++++-------
 3 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/build.sh b/build.sh
index df1f00f7..71e1cdec 100755
--- a/build.sh
+++ b/build.sh
@@ -1,15 +1,16 @@
+#!/bin/sh
 # XXX: TODO: we should make this include -e once tests pass
-set -xuo pipefail
+set -xu
 
 DOCKER_IMAGE=jmadler/python-future-builder
 # XXX: TODO: Perhaps this version shouldn't be hardcoded
 version=0.18.3
 
-docker build . -t $DOCKER_IMAGE
-docker push $DOCKER_IMAGE:latest
+docker build . -t "$DOCKER_IMAGE"
+docker push "$DOCKER_IMAGE:latest"
 
 for i in py26 py27 py33 py34 py35 py36 py37 py38 py39; do
-    docker run -ti -v $(realpath dist):/root/python-future/dist $DOCKER_IMAGE /root/python-future/setup.sh $version $(basename $i)
+    docker run -ti -v "$(realpath dist)":/root/python-future/dist "$DOCKER_IMAGE" /root/python-future/setup.sh "$version" "$i"
 done
 
 python setup.py sdist
diff --git a/lint.sh b/lint.sh
index 667b258f..b3c41cd4 100755
--- a/lint.sh
+++ b/lint.sh
@@ -1,3 +1,4 @@
+#!/bin/sh
 # TODO: Run under Python 2.7 and 3.7
 flake8 . --count --exit-zero --select=E901,E999,F821,F822,F823 --show-source --statistics || true
 flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics || true
diff --git a/setup.sh b/setup.sh
index 8e8dc150..767cbd55 100755
--- a/setup.sh
+++ b/setup.sh
@@ -1,20 +1,20 @@
-#!/bin/bash
+#!/bin/sh
 
-set -exo pipefail
+set -ex
 
 version=$1
 pytag=$2
 
-if [ $pytag = 'py33' ]; then
+if [ "$pytag" = 'py33' ]; then
     pip3 install virtualenv==16.2.0
 fi
 
-source /root/$pytag/bin/activate
+. /root/"$pytag"/bin/activate
 
-if [ $pytag = 'py26' ]; then
+if [ "$pytag" = 'py26' ]; then
     pip install importlib
 fi
 pip install pytest unittest2
-python setup.py bdist_wheel --python-tag=$pytag
-pip install dist/future-$version-$pytag-none-any.whl
+python setup.py bdist_wheel --python-tag="$pytag"
+pip install "dist/future-$version-$pytag-none-any.whl"
 pytest tests/

From 61f6cf1d999dddc8e1b1dddbbf53e929b0df1cdb Mon Sep 17 00:00:00 2001
From: lilinjie <lilinjie@uniontech.com>
Date: Mon, 3 Apr 2023 10:36:37 +0800
Subject: [PATCH 154/183] fix typo

Signed-off-by: lilinjie <lilinjie@uniontech.com>
---
 src/future/backports/http/cookiejar.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/future/backports/http/cookiejar.py b/src/future/backports/http/cookiejar.py
index 0ad80a02..a39242c0 100644
--- a/src/future/backports/http/cookiejar.py
+++ b/src/future/backports/http/cookiejar.py
@@ -1851,7 +1851,7 @@ def lwp_cookie_str(cookie):
 class LWPCookieJar(FileCookieJar):
     """
     The LWPCookieJar saves a sequence of "Set-Cookie3" lines.
-    "Set-Cookie3" is the format used by the libwww-perl libary, not known
+    "Set-Cookie3" is the format used by the libwww-perl library, not known
     to be compatible with any browser, but which is easy to read and
     doesn't lose information about RFC 2965 cookies.
 

From a6135542dffb6b1b8254d6daac779d119d4fc08c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= <thrnciar@redhat.com>
Date: Wed, 17 May 2023 14:03:26 +0200
Subject: [PATCH 155/183] Adjust tests to the repr changes in CPython

---
 tests/test_future/test_backports.py | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/tests/test_future/test_backports.py b/tests/test_future/test_backports.py
index 63b1afea..5d46b115 100644
--- a/tests/test_future/test_backports.py
+++ b/tests/test_future/test_backports.py
@@ -599,8 +599,12 @@ def test_yaml_linkage(self):
 
     def test_repr(self):
         od = OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])
-        self.assertEqual(repr(od),
-            "OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])")
+        if sys.version_info[0] == 3 and sys.version_info[1] >= 12:
+            self.assertEqual(repr(od),
+                "OrderedDict({'c': 1, 'b': 2, 'a': 3, 'd': 4, 'e': 5, 'f': 6})")
+        else:
+            self.assertEqual(repr(od),
+                "OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])")
         self.assertEqual(eval(repr(od)), od)
         self.assertEqual(repr(OrderedDict()), "OrderedDict()")
 
@@ -608,8 +612,12 @@ def test_repr_recursive(self):
         # See issue #9826
         od = OrderedDict.fromkeys('abc')
         od['x'] = od
-        self.assertEqual(repr(od),
-            "OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])")
+        if sys.version_info[0] == 3 and sys.version_info[1] >= 12:
+            self.assertEqual(repr(od),
+                "OrderedDict({'a': None, 'b': None, 'c': None, 'x': ...})")
+        else:
+            self.assertEqual(repr(od),
+                "OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])")
 
     def test_setdefault(self):
         pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]

From d7dc44e88b77fea57b9001421428cd7d95abb3bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= <thrnciar@redhat.com>
Date: Wed, 17 May 2023 14:42:09 +0200
Subject: [PATCH 156/183] Adjust test to the change in CPython, parser now
 raises SyntaxError instead of ValueError when source code contains null bytes

---
 tests/test_future/test_builtins.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/test_future/test_builtins.py b/tests/test_future/test_builtins.py
index 3921a608..d41d1254 100644
--- a/tests/test_future/test_builtins.py
+++ b/tests/test_future/test_builtins.py
@@ -523,8 +523,8 @@ def test_compile(self):
         self.assertRaises(TypeError, compile)
         self.assertRaises(ValueError, compile, 'print(42)\n', '<string>', 'badmode')
         self.assertRaises(ValueError, compile, 'print(42)\n', '<string>', 'single', 0xff)
-        # Raises TypeError in Python < v3.5, ValueError in v3.5:
-        self.assertRaises((TypeError, ValueError), compile, chr(0), 'f', 'exec')
+        # Raises TypeError in Python < v3.5, ValueError in v3.5, SyntaxError in >= 3.12:
+        self.assertRaises((TypeError, ValueError, SyntaxError), compile, chr(0), 'f', 'exec')
         self.assertRaises(TypeError, compile, 'pass', '?', 'exec',
                           mode='eval', source='0', filename='tmp')
         compile('print("\xe5")\n', '', 'exec')

From 331d5fb105a70bce1e2c415218fbaf5a002b1f9a Mon Sep 17 00:00:00 2001
From: Gabriela Gutierrez <gabigutierrez@google.com>
Date: Mon, 5 Jun 2023 15:04:38 -0300
Subject: [PATCH 157/183] Create SECURITY.md

Signed-off-by: Gabriela Gutierrez <gabigutierrez@google.com>
---
 .github/SECURITY.md | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 .github/SECURITY.md

diff --git a/.github/SECURITY.md b/.github/SECURITY.md
new file mode 100644
index 00000000..e4f0e0b3
--- /dev/null
+++ b/.github/SECURITY.md
@@ -0,0 +1,13 @@
+# Security Policy
+
+## Supported Versions
+
+Security updates are applied only to the latest release.
+
+## Reporting a Vulnerability
+
+If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
+
+Please disclose it at [security advisory](https://github.com/PythonCharmers/python-future/security/advisories/new).
+
+This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.

From 36ad5ba6df8c3e71b30c66d707e156b67c785eb5 Mon Sep 17 00:00:00 2001
From: Greg Roodt <greg@canva.com>
Date: Sun, 13 Aug 2023 14:47:27 +1000
Subject: [PATCH 158/183] Clarify how distributions are built and uploaded

---
 Dockerfile | 40 +---------------------------------------
 build.sh   |  4 ++--
 setup.py   |  5 -----
 setup.sh   | 25 ++++++++++---------------
 4 files changed, 13 insertions(+), 61 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index 678de2e2..cf9ffd3c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,41 +1,3 @@
-FROM debian:9
-# This docker image has a copy of a wide array of Pythons installed
-RUN apt-get update
-RUN apt-get install --yes --no-install-recommends make build-essential zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libffi-dev liblzma-dev libssl1.0-dev 
-RUN apt-get install --yes git vim
-RUN apt-get install --yes python3-pip
-ENV PYENV_ROOT=/opt/pyenv
-RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash 
-RUN echo export PATH="/opt/pyenv/bin:$PATH" >> ~/.bashrc
-RUN echo 'eval "$(pyenv init -)"' >> ~/.bashrc
-RUN echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc
-# venv 15.2.0 is the last to support Python 2.6. 
-RUN pip3 install virtualenv==15.2.0
-# Can't get python 2.6 to build anymore
-# RUN PATH=/opt/pyenv/bin:$PATH pyenv install 2.6.9
-# RUN virtualenv /root/py26 --python /opt/pyenv/versions/2.6.9/bin/python
-RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.3.7
-RUN virtualenv /root/py33 --python /opt/pyenv/versions/3.3.7/bin/python
-RUN pip3 install virtualenv==20.0.21
-RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.4.10
-RUN virtualenv /root/py34 --python /opt/pyenv/versions/3.4.10/bin/python
-RUN apt-get install --yes libssl-dev libxmlsec1-dev
-RUN PATH=/opt/pyenv/bin:$PATH pyenv install 2.7.18
-RUN virtualenv /root/py27 --python /opt/pyenv/versions/2.7.18/bin/python
-RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.5.9
-RUN virtualenv /root/py35 --python /opt/pyenv/versions/3.5.9/bin/python
-RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.6.10
-RUN virtualenv /root/py36 --python /opt/pyenv/versions/3.6.10/bin/python
-RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.7.7
-RUN virtualenv /root/py37 --python /opt/pyenv/versions/3.7.7/bin/python
-RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.8.3
-RUN virtualenv /root/py38 --python /opt/pyenv/versions/3.8.3/bin/python
-RUN PATH=/opt/pyenv/bin:$PATH pyenv install 3.9.0
-RUN virtualenv /root/py39 --python /opt/pyenv/versions/3.9.0/bin/python
-# Lint tools
-RUN pip3 install flake8
-RUN ln -s /usr/bin/python3 /usr/bin/python
-ENV LC_ALL=C.UTF-8
-ENV LANG=C.UTF-8
+FROM quay.io/pypa/manylinux1_x86_64
 WORKDIR /root/python-future
 ADD . /root/python-future
diff --git a/build.sh b/build.sh
index df1f00f7..15dac763 100755
--- a/build.sh
+++ b/build.sh
@@ -6,9 +6,9 @@ DOCKER_IMAGE=jmadler/python-future-builder
 version=0.18.3
 
 docker build . -t $DOCKER_IMAGE
-docker push $DOCKER_IMAGE:latest
+#docker push $DOCKER_IMAGE:latest
 
-for i in py26 py27 py33 py34 py35 py36 py37 py38 py39; do
+for i in cp27-cp27m cp35-cp35m cp36-cp36m cp37-cp37m cp38-cp38 cp39-cp39; do
     docker run -ti -v $(realpath dist):/root/python-future/dist $DOCKER_IMAGE /root/python-future/setup.sh $version $(basename $i)
 done
 
diff --git a/setup.py b/setup.py
index 41b0df96..eef5d230 100755
--- a/setup.py
+++ b/setup.py
@@ -13,11 +13,6 @@
     from distutils.core import setup
 
 
-if sys.argv[-1] == 'publish':
-    os.system('python setup.py sdist upload')
-    sys.exit()
-
-
 NAME = "future"
 PACKAGES = ["future",
             "future.builtins",
diff --git a/setup.sh b/setup.sh
index 8e8dc150..047dd953 100755
--- a/setup.sh
+++ b/setup.sh
@@ -3,18 +3,13 @@
 set -exo pipefail
 
 version=$1
-pytag=$2
-
-if [ $pytag = 'py33' ]; then
-    pip3 install virtualenv==16.2.0
-fi
-
-source /root/$pytag/bin/activate
-
-if [ $pytag = 'py26' ]; then
-    pip install importlib
-fi
-pip install pytest unittest2
-python setup.py bdist_wheel --python-tag=$pytag
-pip install dist/future-$version-$pytag-none-any.whl
-pytest tests/
+pyabitag=$2
+
+py="/opt/python/${pyabitag}/bin/python"
+pytag=${pyabitag%-*}
+pytag="${pytag//cp/py}"
+$py -m pip install pytest unittest2
+$py setup.py bdist_wheel --python-tag=$pytag
+$py -m pip install dist/future-$version-$pytag-none-any.whl
+# Ignore test failures for now
+$py -m pytest tests/ || true

From 1901c1c347bcad603e8404b64656994eb2cc0439 Mon Sep 17 00:00:00 2001
From: David Bern <david@livebuildingsystems.com>
Date: Tue, 10 Oct 2023 22:13:54 -0500
Subject: [PATCH 159/183] WIP Python 3.12 support for removal of imp module

---
 build.sh                                   |  2 +-
 docs/whatsnew.rst                          |  6 ++++++
 src/future/__init__.py                     |  2 +-
 src/future/backports/test/support.py       | 11 +++++++----
 src/future/standard_library/__init__.py    | 12 +++++++++---
 src/past/translation/__init__.py           |  5 ++++-
 tests/test_future/test_standard_library.py | 11 ++++++++---
 7 files changed, 36 insertions(+), 13 deletions(-)

diff --git a/build.sh b/build.sh
index df1f00f7..ef52cb41 100755
--- a/build.sh
+++ b/build.sh
@@ -3,7 +3,7 @@ set -xuo pipefail
 
 DOCKER_IMAGE=jmadler/python-future-builder
 # XXX: TODO: Perhaps this version shouldn't be hardcoded
-version=0.18.3
+version=0.18.4
 
 docker build . -t $DOCKER_IMAGE
 docker push $DOCKER_IMAGE:latest
diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index 40f7191f..9018fdfe 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -3,6 +3,12 @@
 What's New
 **********
 
+What's new in version 0.18.4 (2023-10-10)
+=========================================
+This is a minor bug-fix release containing a number of fixes:
+
+- Fix for Python 3.12's removal of the imp module
+
 What's new in version 0.18.3 (2023-01-13)
 =========================================
 This is a minor bug-fix release containing a number of fixes:
diff --git a/src/future/__init__.py b/src/future/__init__.py
index b609299a..64b66f43 100644
--- a/src/future/__init__.py
+++ b/src/future/__init__.py
@@ -87,7 +87,7 @@
 __copyright__ = 'Copyright 2013-2019 Python Charmers Pty Ltd'
 __ver_major__ = 0
 __ver_minor__ = 18
-__ver_patch__ = 3
+__ver_patch__ = 4
 __ver_sub__ = ''
 __version__ = "%d.%d.%d%s" % (__ver_major__, __ver_minor__,
                               __ver_patch__, __ver_sub__)
diff --git a/src/future/backports/test/support.py b/src/future/backports/test/support.py
index 1999e208..08b59829 100644
--- a/src/future/backports/test/support.py
+++ b/src/future/backports/test/support.py
@@ -28,7 +28,10 @@
 # import collections.abc    # not present on Py2.7
 import re
 import subprocess
-import imp
+try:
+    from imp import cache_from_source
+except ImportError:
+    from importlib.util import cache_from_source
 import time
 try:
     import sysconfig
@@ -351,7 +354,7 @@ def make_legacy_pyc(source):
         does not need to exist, however the PEP 3147 pyc file must exist.
     :return: The file system path to the legacy pyc file.
     """
-    pyc_file = imp.cache_from_source(source)
+    pyc_file = cache_from_source(source)
     up_one = os.path.dirname(os.path.abspath(source))
     legacy_pyc = os.path.join(up_one, source + ('c' if __debug__ else 'o'))
     os.rename(pyc_file, legacy_pyc)
@@ -370,8 +373,8 @@ def forget(modname):
         # combinations of PEP 3147 and legacy pyc and pyo files.
         unlink(source + 'c')
         unlink(source + 'o')
-        unlink(imp.cache_from_source(source, debug_override=True))
-        unlink(imp.cache_from_source(source, debug_override=False))
+        unlink(cache_from_source(source, debug_override=True))
+        unlink(cache_from_source(source, debug_override=False))
 
 # On some platforms, should not run gui test even if it is allowed
 # in `use_resources'.
diff --git a/src/future/standard_library/__init__.py b/src/future/standard_library/__init__.py
index cff02f95..24d9287f 100644
--- a/src/future/standard_library/__init__.py
+++ b/src/future/standard_library/__init__.py
@@ -62,7 +62,10 @@
 
 import sys
 import logging
-import imp
+try:
+    import importlib
+except ImportError:
+    import imp
 import contextlib
 import types
 import copy
@@ -297,8 +300,11 @@ def _find_and_load_module(self, name, path=None):
                 flog.debug('What to do here?')
 
         name = bits[0]
-        module_info = imp.find_module(name, path)
-        return imp.load_module(name, *module_info)
+        try:
+            module_info = imp.find_module(name, path)
+            return imp.load_module(name, *module_info)
+        except AttributeError:
+            return importlib.import_module(name, path)
 
 
 class hooks(object):
diff --git a/src/past/translation/__init__.py b/src/past/translation/__init__.py
index 7c678866..6e6ccf74 100644
--- a/src/past/translation/__init__.py
+++ b/src/past/translation/__init__.py
@@ -32,7 +32,10 @@
 Inspired by and based on ``uprefix`` by Vinay M. Sajip.
 """
 
-import imp
+try:
+    import imp
+except ImportError:
+    import importlib
 import logging
 import marshal
 import os
diff --git a/tests/test_future/test_standard_library.py b/tests/test_future/test_standard_library.py
index 3ac5d2d7..f3477210 100644
--- a/tests/test_future/test_standard_library.py
+++ b/tests/test_future/test_standard_library.py
@@ -447,9 +447,14 @@ def test_reload(self):
         """
         reload has been moved to the imp module
         """
-        import imp
-        imp.reload(imp)
-        self.assertTrue(True)
+        try:
+            import imp
+            imp.reload(imp)
+            self.assertTrue(True)
+        except ImportError:
+            import importlib
+            importlib.reload(importlib)
+            self.assertTrue(True)
 
     def test_install_aliases(self):
         """

From 4fcbe2e873100e1fdc064edd23f8501f65c4eae8 Mon Sep 17 00:00:00 2001
From: Nicola Soranzo <nicola.soranzo@earlham.ac.uk>
Date: Fri, 19 Jan 2024 15:09:45 +0000
Subject: [PATCH 160/183] Use importlib instead of imp for Python 3.12 support

Also remove some unused imports.
---
 src/future/backports/test/support.py       |  35 ---
 src/future/standard_library/__init__.py    |  15 +-
 src/past/translation/__init__.py           | 254 +++++++++------------
 tests/test_future/test_standard_library.py |  11 +-
 tests/test_past/test_builtins.py           |   1 -
 tests/test_past/test_translation.py        |  20 +-
 6 files changed, 126 insertions(+), 210 deletions(-)

diff --git a/src/future/backports/test/support.py b/src/future/backports/test/support.py
index 08b59829..6639372b 100644
--- a/src/future/backports/test/support.py
+++ b/src/future/backports/test/support.py
@@ -28,10 +28,6 @@
 # import collections.abc    # not present on Py2.7
 import re
 import subprocess
-try:
-    from imp import cache_from_source
-except ImportError:
-    from importlib.util import cache_from_source
 import time
 try:
     import sysconfig
@@ -344,37 +340,6 @@ def rmtree(path):
         if error.errno != errno.ENOENT:
             raise
 
-def make_legacy_pyc(source):
-    """Move a PEP 3147 pyc/pyo file to its legacy pyc/pyo location.
-
-    The choice of .pyc or .pyo extension is done based on the __debug__ flag
-    value.
-
-    :param source: The file system path to the source file.  The source file
-        does not need to exist, however the PEP 3147 pyc file must exist.
-    :return: The file system path to the legacy pyc file.
-    """
-    pyc_file = cache_from_source(source)
-    up_one = os.path.dirname(os.path.abspath(source))
-    legacy_pyc = os.path.join(up_one, source + ('c' if __debug__ else 'o'))
-    os.rename(pyc_file, legacy_pyc)
-    return legacy_pyc
-
-def forget(modname):
-    """'Forget' a module was ever imported.
-
-    This removes the module from sys.modules and deletes any PEP 3147 or
-    legacy .pyc and .pyo files.
-    """
-    unload(modname)
-    for dirname in sys.path:
-        source = os.path.join(dirname, modname + '.py')
-        # It doesn't matter if they exist or not, unlink all possible
-        # combinations of PEP 3147 and legacy pyc and pyo files.
-        unlink(source + 'c')
-        unlink(source + 'o')
-        unlink(cache_from_source(source, debug_override=True))
-        unlink(cache_from_source(source, debug_override=False))
 
 # On some platforms, should not run gui test even if it is allowed
 # in `use_resources'.
diff --git a/src/future/standard_library/__init__.py b/src/future/standard_library/__init__.py
index 24d9287f..2cee75db 100644
--- a/src/future/standard_library/__init__.py
+++ b/src/future/standard_library/__init__.py
@@ -62,12 +62,7 @@
 
 import sys
 import logging
-try:
-    import importlib
-except ImportError:
-    import imp
 import contextlib
-import types
 import copy
 import os
 
@@ -82,6 +77,9 @@
 
 from future.utils import PY2, PY3
 
+if PY2:
+    import imp
+
 # The modules that are defined under the same names on Py3 but with
 # different contents in a significant way (e.g. submodules) are:
 #   pickle (fast one)
@@ -300,11 +298,8 @@ def _find_and_load_module(self, name, path=None):
                 flog.debug('What to do here?')
 
         name = bits[0]
-        try:
-            module_info = imp.find_module(name, path)
-            return imp.load_module(name, *module_info)
-        except AttributeError:
-            return importlib.import_module(name, path)
+        module_info = imp.find_module(name, path)
+        return imp.load_module(name, *module_info)
 
 
 class hooks(object):
diff --git a/src/past/translation/__init__.py b/src/past/translation/__init__.py
index 6e6ccf74..db96982b 100644
--- a/src/past/translation/__init__.py
+++ b/src/past/translation/__init__.py
@@ -32,12 +32,7 @@
 Inspired by and based on ``uprefix`` by Vinay M. Sajip.
 """
 
-try:
-    import imp
-except ImportError:
-    import importlib
 import logging
-import marshal
 import os
 import sys
 import copy
@@ -46,6 +41,17 @@
 
 from libfuturize import fixes
 
+try:
+    from importlib.machinery import (
+        PathFinder,
+        SourceFileLoader,
+    )
+except ImportError:
+    PathFinder = None
+    SourceFileLoader = object
+
+if sys.version_info[:2] < (3, 4):
+    import imp
 
 logger = logging.getLogger(__name__)
 logger.setLevel(logging.DEBUG)
@@ -228,6 +234,81 @@ def detect_python2(source, pathname):
         return False
 
 
+def transform(source, pathname):
+    # This implementation uses lib2to3,
+    # you can override and use something else
+    # if that's better for you
+
+    # lib2to3 likes a newline at the end
+    RTs.setup()
+    source += '\n'
+    try:
+        tree = RTs._rt.refactor_string(source, pathname)
+    except ParseError as e:
+        if e.msg != 'bad input' or e.value != '=':
+            raise
+        tree = RTs._rtp.refactor_string(source, pathname)
+    # could optimise a bit for only doing str(tree) if
+    # getattr(tree, 'was_changed', False) returns True
+    return str(tree)[:-1]  # remove added newline
+
+
+class PastSourceFileLoader(SourceFileLoader):
+    exclude_paths = []
+    include_paths = []
+
+    def _convert_needed(self):
+        fullname = self.name
+        if any(fullname.startswith(path) for path in self.exclude_paths):
+            convert = False
+        elif any(fullname.startswith(path) for path in self.include_paths):
+            convert = True
+        else:
+            convert = False
+        return convert
+
+    def _exec_transformed_module(self, module):
+        source = self.get_source(self.name)
+        pathname = self.path
+        if detect_python2(source, pathname):
+            source = transform(source, pathname)
+        code = compile(source, pathname, "exec")
+        exec(code, module.__dict__)
+
+    # For Python 3.3
+    def load_module(self, fullname):
+        logger.debug("Running load_module for %s", fullname)
+        if fullname in sys.modules:
+            mod = sys.modules[fullname]
+        else:
+            if self._convert_needed():
+                logger.debug("Autoconverting %s", fullname)
+                mod = imp.new_module(fullname)
+                sys.modules[fullname] = mod
+
+                # required by PEP 302
+                mod.__file__ = self.path
+                mod.__loader__ = self
+                if self.is_package(fullname):
+                    mod.__path__ = []
+                    mod.__package__ = fullname
+                else:
+                    mod.__package__ = fullname.rpartition('.')[0]
+                self._exec_transformed_module(mod)
+            else:
+                mod = super().load_module(fullname)
+        return mod
+
+    # For Python >=3.4
+    def exec_module(self, module):
+        logger.debug("Running exec_module for %s", module)
+        if self._convert_needed():
+            logger.debug("Autoconverting %s", self.name)
+            self._exec_transformed_module(module)
+        else:
+            super().exec_module(module)
+
+
 class Py2Fixer(object):
     """
     An import hook class that uses lib2to3 for source-to-source translation of
@@ -261,151 +342,30 @@ def exclude(self, paths):
         """
         self.exclude_paths += paths
 
+    # For Python 3.3
     def find_module(self, fullname, path=None):
-        logger.debug('Running find_module: {0}...'.format(fullname))
-        if '.' in fullname:
-            parent, child = fullname.rsplit('.', 1)
-            if path is None:
-                loader = self.find_module(parent, path)
-                mod = loader.load_module(parent)
-                path = mod.__path__
-            fullname = child
-
-        # Perhaps we should try using the new importlib functionality in Python
-        # 3.3: something like this?
-        # thing = importlib.machinery.PathFinder.find_module(fullname, path)
-        try:
-            self.found = imp.find_module(fullname, path)
-        except Exception as e:
-            logger.debug('Py2Fixer could not find {0}')
-            logger.debug('Exception was: {0})'.format(fullname, e))
+        logger.debug("Running find_module: (%s, %s)", fullname, path)
+        loader = PathFinder.find_module(fullname, path)
+        if not loader:
+            logger.debug("Py2Fixer could not find %s", fullname)
             return None
-        self.kind = self.found[-1][-1]
-        if self.kind == imp.PKG_DIRECTORY:
-            self.pathname = os.path.join(self.found[1], '__init__.py')
-        elif self.kind == imp.PY_SOURCE:
-            self.pathname = self.found[1]
-        return self
-
-    def transform(self, source):
-        # This implementation uses lib2to3,
-        # you can override and use something else
-        # if that's better for you
-
-        # lib2to3 likes a newline at the end
-        RTs.setup()
-        source += '\n'
-        try:
-            tree = RTs._rt.refactor_string(source, self.pathname)
-        except ParseError as e:
-            if e.msg != 'bad input' or e.value != '=':
-                raise
-            tree = RTs._rtp.refactor_string(source, self.pathname)
-        # could optimise a bit for only doing str(tree) if
-        # getattr(tree, 'was_changed', False) returns True
-        return str(tree)[:-1] # remove added newline
-
-    def load_module(self, fullname):
-        logger.debug('Running load_module for {0}...'.format(fullname))
-        if fullname in sys.modules:
-            mod = sys.modules[fullname]
-        else:
-            if self.kind in (imp.PY_COMPILED, imp.C_EXTENSION, imp.C_BUILTIN,
-                             imp.PY_FROZEN):
-                convert = False
-            # elif (self.pathname.startswith(_stdlibprefix)
-            #       and 'site-packages' not in self.pathname):
-            #     # We assume it's a stdlib package in this case. Is this too brittle?
-            #     # Please file a bug report at https://github.com/PythonCharmers/python-future
-            #     # if so.
-            #     convert = False
-            # in theory, other paths could be configured to be excluded here too
-            elif any([fullname.startswith(path) for path in self.exclude_paths]):
-                convert = False
-            elif any([fullname.startswith(path) for path in self.include_paths]):
-                convert = True
-            else:
-                convert = False
-            if not convert:
-                logger.debug('Excluded {0} from translation'.format(fullname))
-                mod = imp.load_module(fullname, *self.found)
-            else:
-                logger.debug('Autoconverting {0} ...'.format(fullname))
-                mod = imp.new_module(fullname)
-                sys.modules[fullname] = mod
-
-                # required by PEP 302
-                mod.__file__ = self.pathname
-                mod.__name__ = fullname
-                mod.__loader__ = self
-
-                # This:
-                #     mod.__package__ = '.'.join(fullname.split('.')[:-1])
-                # seems to result in "SystemError: Parent module '' not loaded,
-                # cannot perform relative import" for a package's __init__.py
-                # file. We use the approach below. Another option to try is the
-                # minimal load_module pattern from the PEP 302 text instead.
-
-                # Is the test in the next line more or less robust than the
-                # following one? Presumably less ...
-                # ispkg = self.pathname.endswith('__init__.py')
-
-                if self.kind == imp.PKG_DIRECTORY:
-                    mod.__path__ = [ os.path.dirname(self.pathname) ]
-                    mod.__package__ = fullname
-                else:
-                    #else, regular module
-                    mod.__path__ = []
-                    mod.__package__ = fullname.rpartition('.')[0]
+        loader.__class__ = PastSourceFileLoader
+        loader.exclude_paths = self.exclude_paths
+        loader.include_paths = self.include_paths
+        return loader
+
+    # For Python >=3.4
+    def find_spec(self, fullname, path=None, target=None):
+        logger.debug("Running find_spec: (%s, %s, %s)", fullname, path, target)
+        spec = PathFinder.find_spec(fullname, path, target)
+        if not spec:
+            logger.debug("Py2Fixer could not find %s", fullname)
+            return None
+        spec.loader.__class__ = PastSourceFileLoader
+        spec.loader.exclude_paths = self.exclude_paths
+        spec.loader.include_paths = self.include_paths
+        return spec
 
-                try:
-                    cachename = imp.cache_from_source(self.pathname)
-                    if not os.path.exists(cachename):
-                        update_cache = True
-                    else:
-                        sourcetime = os.stat(self.pathname).st_mtime
-                        cachetime = os.stat(cachename).st_mtime
-                        update_cache = cachetime < sourcetime
-                    # # Force update_cache to work around a problem with it being treated as Py3 code???
-                    # update_cache = True
-                    if not update_cache:
-                        with open(cachename, 'rb') as f:
-                            data = f.read()
-                            try:
-                                code = marshal.loads(data)
-                            except Exception:
-                                # pyc could be corrupt. Regenerate it
-                                update_cache = True
-                    if update_cache:
-                        if self.found[0]:
-                            source = self.found[0].read()
-                        elif self.kind == imp.PKG_DIRECTORY:
-                            with open(self.pathname) as f:
-                                source = f.read()
-
-                        if detect_python2(source, self.pathname):
-                            source = self.transform(source)
-
-                        code = compile(source, self.pathname, 'exec')
-
-                        dirname = os.path.dirname(cachename)
-                        try:
-                            if not os.path.exists(dirname):
-                                os.makedirs(dirname)
-                            with open(cachename, 'wb') as f:
-                                data = marshal.dumps(code)
-                                f.write(data)
-                        except Exception:   # could be write-protected
-                            pass
-                    exec(code, mod.__dict__)
-                except Exception as e:
-                    # must remove module from sys.modules
-                    del sys.modules[fullname]
-                    raise # keep it simple
-
-        if self.found[0]:
-            self.found[0].close()
-        return mod
 
 _hook = Py2Fixer()
 
diff --git a/tests/test_future/test_standard_library.py b/tests/test_future/test_standard_library.py
index f3477210..1028f6fc 100644
--- a/tests/test_future/test_standard_library.py
+++ b/tests/test_future/test_standard_library.py
@@ -9,7 +9,6 @@
 
 import sys
 import tempfile
-import os
 import copy
 import textwrap
 from subprocess import CalledProcessError
@@ -448,13 +447,11 @@ def test_reload(self):
         reload has been moved to the imp module
         """
         try:
-            import imp
-            imp.reload(imp)
-            self.assertTrue(True)
+            from importlib import reload
         except ImportError:
-            import importlib
-            importlib.reload(importlib)
-            self.assertTrue(True)
+            from imp import reload
+        reload(sys)
+        self.assertTrue(True)
 
     def test_install_aliases(self):
         """
diff --git a/tests/test_past/test_builtins.py b/tests/test_past/test_builtins.py
index d16978ee..98d3c8c1 100644
--- a/tests/test_past/test_builtins.py
+++ b/tests/test_past/test_builtins.py
@@ -6,7 +6,6 @@
 from past.builtins import apply, cmp, execfile, intern, raw_input
 from past.builtins import reduce, reload, unichr, unicode, xrange
 
-from future import standard_library
 from future.backports.test.support import TESTFN     #, run_unittest
 import tempfile
 import os
diff --git a/tests/test_past/test_translation.py b/tests/test_past/test_translation.py
index 2b442d96..58d8d000 100644
--- a/tests/test_past/test_translation.py
+++ b/tests/test_past/test_translation.py
@@ -7,18 +7,18 @@
 import os
 import textwrap
 import sys
-import pprint
 import tempfile
-import os
 import io
-from subprocess import Popen, PIPE
-
-from past import utils
-from past.builtins import basestring, str as oldstr, unicode
 
+from future.tests.base import (
+    expectedFailurePY3,
+    unittest,
+)
+from past.builtins import (
+    str as oldstr,
+    unicode,
+)
 from past.translation import install_hooks, remove_hooks, common_substring
-from future.tests.base import (unittest, CodeHandler, skip26,
-                               expectedFailurePY3, expectedFailurePY26)
 
 
 class TestTranslate(unittest.TestCase):
@@ -58,8 +58,8 @@ def write_and_import(self, code, modulename='mymodule'):
         sys.path.insert(0, self.tempdir)
         try:
             module = __import__(modulename)
-        except SyntaxError:
-            print('Bombed!')
+        except SyntaxError as e:
+            print('Import failed: %s' % e)
         else:
             print('Succeeded!')
         finally:

From 74d834334d1f97cf5f874d601e88edae978da578 Mon Sep 17 00:00:00 2001
From: Nicola Soranzo <nicola.soranzo@earlham.ac.uk>
Date: Fri, 19 Jan 2024 17:51:46 +0000
Subject: [PATCH 161/183] Test fixes

---
 src/future/moves/test/support.py          | 9 +++++++++
 tests/test_future/test_builtins.py        | 7 +++++--
 tests/test_future/test_urllib2.py         | 6 +-----
 tests/test_future/test_urllib_toplevel.py | 7 +++++--
 tests/test_future/test_utils.py           | 2 +-
 5 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/src/future/moves/test/support.py b/src/future/moves/test/support.py
index e9aa0f48..f70c9d7d 100644
--- a/src/future/moves/test/support.py
+++ b/src/future/moves/test/support.py
@@ -1,9 +1,18 @@
 from __future__ import absolute_import
+
+import sys
+
 from future.standard_library import suspend_hooks
 from future.utils import PY3
 
 if PY3:
     from test.support import *
+    if sys.version_info[:2] >= (3, 10):
+        from test.support.os_helper import (
+            EnvironmentVarGuard,
+            TESTFN,
+        )
+        from test.support.warnings_helper import check_warnings
 else:
     __future_module__ = True
     with suspend_hooks():
diff --git a/tests/test_future/test_builtins.py b/tests/test_future/test_builtins.py
index 3921a608..0bf2a520 100644
--- a/tests/test_future/test_builtins.py
+++ b/tests/test_future/test_builtins.py
@@ -1303,8 +1303,11 @@ def test_pow(self):
         self.assertAlmostEqual(pow(-1, 0.5), 1j)
         self.assertAlmostEqual(pow(-1, 1/3), 0.5 + 0.8660254037844386j)
 
-        # Raises TypeError in Python < v3.5, ValueError in v3.5:
-        self.assertRaises((TypeError, ValueError), pow, -1, -2, 3)
+        # Raises TypeError in Python < v3.5, ValueError in v3.5-v3.7:
+        if sys.version_info[:2] < (3, 8):
+            self.assertRaises((TypeError, ValueError), pow, -1, -2, 3)
+        else:
+            self.assertEqual(pow(-1, -2, 3), 1)
         self.assertRaises(ValueError, pow, 1, 2, 0)
 
         self.assertRaises(TypeError, pow)
diff --git a/tests/test_future/test_urllib2.py b/tests/test_future/test_urllib2.py
index 2d69dad1..87bc585a 100644
--- a/tests/test_future/test_urllib2.py
+++ b/tests/test_future/test_urllib2.py
@@ -691,10 +691,6 @@ def connect_ftp(self, user, passwd, host, port, dirs,
         h = NullFTPHandler(data)
         h.parent = MockOpener()
 
-        # MIME guessing works in Python 3.8!
-        guessed_mime = None
-        if sys.hexversion >= 0x03080000:
-            guessed_mime = "image/gif"
         for url, host, port, user, passwd, type_, dirs, filename, mimetype in [
             ("ftp://localhost/foo/bar/baz.html",
              "localhost", ftplib.FTP_PORT, "", "", "I",
@@ -713,7 +709,7 @@ def connect_ftp(self, user, passwd, host, port, dirs,
              ["foo", "bar"], "", None),
             ("ftp://localhost/baz.gif;type=a",
              "localhost", ftplib.FTP_PORT, "", "", "A",
-             [], "baz.gif", guessed_mime),
+             [], "baz.gif", None),
             ]:
             req = Request(url)
             req.timeout = None
diff --git a/tests/test_future/test_urllib_toplevel.py b/tests/test_future/test_urllib_toplevel.py
index 11e77201..93364e6d 100644
--- a/tests/test_future/test_urllib_toplevel.py
+++ b/tests/test_future/test_urllib_toplevel.py
@@ -781,8 +781,11 @@ def test_unquoting(self):
                          "%s" % result)
         self.assertRaises((TypeError, AttributeError), urllib_parse.unquote, None)
         self.assertRaises((TypeError, AttributeError), urllib_parse.unquote, ())
-        with support.check_warnings(('', BytesWarning), quiet=True):
-            self.assertRaises((TypeError, AttributeError), urllib_parse.unquote, bytes(b''))
+        if sys.version_info[:2] < (3, 9):
+            with support.check_warnings(('', BytesWarning), quiet=True):
+                self.assertRaises((TypeError, AttributeError), urllib_parse.unquote, bytes(b''))
+        else:
+            self.assertEqual(urllib_parse.unquote(bytes(b"")), "")
 
     def test_unquoting_badpercent(self):
         # Test unquoting on bad percent-escapes
diff --git a/tests/test_future/test_utils.py b/tests/test_future/test_utils.py
index 46f5196c..a496bcaf 100644
--- a/tests/test_future/test_utils.py
+++ b/tests/test_future/test_utils.py
@@ -150,7 +150,7 @@ class Timeout(BaseException):
         self.assertRaises(Timeout, raise_, Timeout())
 
         if PY3:
-            self.assertRaisesRegexp(
+            self.assertRaisesRegex(
                 TypeError, "class must derive from BaseException",
                 raise_, int)
 

From 9ef05b386ce45dd40d2dab5915aec3ed78d81ed9 Mon Sep 17 00:00:00 2001
From: Nicola Soranzo <nicola.soranzo@earlham.ac.uk>
Date: Mon, 22 Jan 2024 15:50:50 +0000
Subject: [PATCH 162/183] Add Python 3.8-3.12 classifiers

---
 setup.py | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/setup.py b/setup.py
index 41b0df96..9c62269b 100755
--- a/setup.py
+++ b/setup.py
@@ -103,6 +103,11 @@
     "Programming Language :: Python :: 3.5",
     "Programming Language :: Python :: 3.6",
     "Programming Language :: Python :: 3.7",
+    "Programming Language :: Python :: 3.8",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: 3.12",
     "License :: OSI Approved",
     "License :: OSI Approved :: MIT License",
     "Development Status :: 4 - Beta",

From 343f952f3a1eb6df902c201581d36268cf144a93 Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 13:14:03 +1100
Subject: [PATCH 163/183] Update instructions for uploading docs

---
 docs/other/upload_future_docs.sh | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/docs/other/upload_future_docs.sh b/docs/other/upload_future_docs.sh
index d5c272d2..c5201f90 100644
--- a/docs/other/upload_future_docs.sh
+++ b/docs/other/upload_future_docs.sh
@@ -3,14 +3,14 @@
 git checkout v0.16.0  # or whatever
 rm -Rf docs/build/
 cd docs; make html
-cp cheatsheet.pdf /shared/
+cp cheatsheet.pdf ~/shared/
 cd build
-touch /shared/python-future-html-docs.zip
-rm /shared/python-future-html-docs.zip
-zip -r /shared/python-future-html-docs.zip *
+touch ~/shared/python-future-html-docs.zip
+rm ~/shared/python-future-html-docs.zip
+zip -r ~/shared/python-future-html-docs.zip *
 
-scp /shared/python-future-html-docs.zip python-future.org:
-scp /shared/cheatsheet.pdf python-future.org:
+scp ~/shared/python-future-html-docs.zip python-future.org:
+scp ~/shared/cheatsheet.pdf python-future.org:
 ssh python-future.org
 
 

From 8b930f5725fb1f4e9947c2364511c9abd528a33c Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 13:59:30 +1100
Subject: [PATCH 164/183] Update whatsnew.rst

---
 docs/whatsnew.rst | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index 9018fdfe..a86c9bfd 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -3,11 +3,12 @@
 What's New
 **********
 
-What's new in version 0.18.4 (2023-10-10)
+What's new in version 0.18.4 (2024-02-21)
 =========================================
 This is a minor bug-fix release containing a number of fixes:
 
 - Fix for Python 3.12's removal of the imp module
+- Small updates to the docs
 
 What's new in version 0.18.3 (2023-01-13)
 =========================================

From 3f96b16ca5f220e07e0f90aa2480ac3cbce44e2a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 21 Feb 2024 03:16:14 +0000
Subject: [PATCH 165/183] Bump setuptools from 0.18.2 to 65.5.1 in /docs

Bumps [setuptools](https://github.com/pypa/setuptools) from 0.18.2 to 65.5.1.
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/setuptools/commits/v65.5.1)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 docs/requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/requirements.txt b/docs/requirements.txt
index 0f06b13f..de74d8b2 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,3 +1,3 @@
 sphinx==3.2.1
 sphinx_bootstrap_theme==0.7.1
-setuptools==0.18.2
+setuptools==65.5.1

From d6785dd102876eecbbf5ae8b4ff47bc66b14fba6 Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 14:45:01 +1100
Subject: [PATCH 166/183] Add more meaningful comments about Python
 deprecations

Thanks to Bruno Alla (@browniebroke)
---
 src/future/moves/_dummy_thread.py  | 2 ++
 tests/test_future/test_builtins.py | 3 +++
 2 files changed, 5 insertions(+)

diff --git a/src/future/moves/_dummy_thread.py b/src/future/moves/_dummy_thread.py
index e5dca348..96cf99e1 100644
--- a/src/future/moves/_dummy_thread.py
+++ b/src/future/moves/_dummy_thread.py
@@ -2,6 +2,8 @@
 from future.utils import PY3
 
 if PY3:
+    # _dummy_thread and dummy_threading modules were both deprecated in
+    # Python 3.7 and removed in Python 3.9
     try:
         from _dummy_thread import *
     except ImportError:
diff --git a/tests/test_future/test_builtins.py b/tests/test_future/test_builtins.py
index d4e4f977..0da3fc2d 100644
--- a/tests/test_future/test_builtins.py
+++ b/tests/test_future/test_builtins.py
@@ -1307,6 +1307,9 @@ def test_pow(self):
         if sys.version_info[:2] < (3, 8):
             self.assertRaises((TypeError, ValueError), pow, -1, -2, 3)
         else:
+            # Changed in version 3.8: For int operands, the three-argument form
+            # of pow now allows the second argument to be negative, permitting
+            # computation of modular inverses.
             self.assertEqual(pow(-1, -2, 3), 1)
         self.assertRaises(ValueError, pow, 1, 2, 0)
 

From 8cd11e8cdb8e096989a384787c739fa5c82739d3 Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 15:00:32 +1100
Subject: [PATCH 167/183] Move CI to GitHub Actions & split Python versions
 into jobs (#603)

Thanks to Bruno Alla (@browniebroke)
---
 .github/workflows/ci.yml          | 46 +++++++++++++++++++++++++++++++
 .travis.yml                       | 11 --------
 2.6.Dockerfile                    | 26 +++++++++++++++++
 Dockerfile                        |  6 +++-
 README.rst                        |  4 +--
 build.sh                          | 18 ------------
 lint.sh                           |  4 ---
 setup.sh                          | 21 --------------
 src/future/moves/_dummy_thread.py | 10 +++----
 test.sh                           | 18 ++++++++++++
 10 files changed, 102 insertions(+), 62 deletions(-)
 create mode 100644 .github/workflows/ci.yml
 delete mode 100644 .travis.yml
 create mode 100644 2.6.Dockerfile
 delete mode 100755 build.sh
 delete mode 100755 lint.sh
 delete mode 100755 setup.sh
 create mode 100755 test.sh

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..fb7f5064
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,46 @@
+name: CI
+
+on:
+  pull_request:
+  push:
+
+concurrency:
+  group: ${{ github.head_ref || github.run_id }}
+  cancel-in-progress: true
+
+jobs:
+  test:
+    strategy:
+      fail-fast: false
+      matrix:
+        versions:
+          - python: "2.6"
+          - python: "2.7"
+          - python: "3.3"
+          - python: "3.4"
+          - python: "3.5"
+          - python: "3.6"
+          - python: "3.7"
+          - python: "3.8"
+          - python: "3.9"
+
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - if: ${{ matrix.versions.python != '2.6' }}
+        run: |
+          docker build \
+          . \
+          --build-arg PYTHON_VERSION=${{ matrix.versions.python }} \
+          -t jmadler/python-future-builder:${{ matrix.versions.python }}
+      - if: ${{ matrix.versions.python == '2.6' }}
+        run: |
+          docker build \
+          . \
+          -f 2.6.Dockerfile \
+          -t jmadler/python-future-builder:${{ matrix.versions.python }}
+      - run: |
+          docker run \
+          -e PYTHON_VERSION=${{ matrix.versions.python }} \
+          jmadler/python-future-builder:${{ matrix.versions.python }} \
+          /root/python-future/test.sh
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 3fe6a983..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-language: generic
-
-services:
-    - docker
-
-before_script:
-    - docker pull jmadler/python-future-builder:latest
-
-script:
-  - ./build.sh
-  - ./lint.sh
diff --git a/2.6.Dockerfile b/2.6.Dockerfile
new file mode 100644
index 00000000..efaf3809
--- /dev/null
+++ b/2.6.Dockerfile
@@ -0,0 +1,26 @@
+FROM mrupgrade/deadsnakes:2.6
+
+RUN mkdir -p ~/.pip/ && echo '[global] \n\
+trusted-host = pypi.python.org\n\
+               pypi.org\n\
+               files.pythonhosted.org\n\
+' >> ~/.pip/pip.conf
+
+RUN apt-get update && \
+    apt-get install -y curl
+
+RUN mkdir -p /root/pip && \
+    cd /root/pip && \
+    curl -O https://files.pythonhosted.org/packages/8a/e9/8468cd68b582b06ef554be0b96b59f59779627131aad48f8a5bce4b13450/wheel-0.29.0-py2.py3-none-any.whl && \
+    curl -O https://files.pythonhosted.org/packages/31/77/3781f65cafe55480b56914def99022a5d2965a4bb269655c89ef2f1de3cd/importlib-1.0.4.zip && \
+    curl -O https://files.pythonhosted.org/packages/ef/41/d8a61f1b2ba308e96b36106e95024977e30129355fd12087f23e4b9852a1/pytest-3.2.5-py2.py3-none-any.whl && \
+    curl -O https://files.pythonhosted.org/packages/f2/94/3af39d34be01a24a6e65433d19e107099374224905f1e0cc6bbe1fd22a2f/argparse-1.4.0-py2.py3-none-any.whl && \
+    curl -O https://files.pythonhosted.org/packages/72/20/7f0f433060a962200b7272b8c12ba90ef5b903e218174301d0abfd523813/unittest2-1.1.0-py2.py3-none-any.whl && \
+    curl -O https://files.pythonhosted.org/packages/53/67/9620edf7803ab867b175e4fd23c7b8bd8eba11cb761514dcd2e726ef07da/py-1.4.34-py2.py3-none-any.whl && \
+    curl -O https://files.pythonhosted.org/packages/53/25/ef88e8e45db141faa9598fbf7ad0062df8f50f881a36ed6a0073e1572126/ordereddict-1.1.tar.gz && \
+    curl -O https://files.pythonhosted.org/packages/17/0a/6ac05a3723017a967193456a2efa0aa9ac4b51456891af1e2353bb9de21e/traceback2-1.4.0-py2.py3-none-any.whl && \
+    curl -O https://files.pythonhosted.org/packages/65/26/32b8464df2a97e6dd1b656ed26b2c194606c16fe163c695a992b36c11cdf/six-1.13.0-py2.py3-none-any.whl && \
+    curl -O https://files.pythonhosted.org/packages/c7/a3/c5da2a44c85bfbb6eebcfc1dde24933f8704441b98fdde6528f4831757a6/linecache2-1.0.0-py2.py3-none-any.whl
+
+WORKDIR /root/python-future
+ADD . /root/python-future
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index cf9ffd3c..c859757f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,3 +1,7 @@
-FROM quay.io/pypa/manylinux1_x86_64
+ARG PYTHON_VERSION
+FROM python:${PYTHON_VERSION}-slim
+
+ENV LC_ALL=C.UTF-8
+
 WORKDIR /root/python-future
 ADD . /root/python-future
diff --git a/README.rst b/README.rst
index 1ab43e53..cce7605c 100644
--- a/README.rst
+++ b/README.rst
@@ -3,8 +3,8 @@
 Overview: Easy, clean, reliable Python 2/3 compatibility
 ========================================================
 
-.. image:: https://travis-ci.org/PythonCharmers/python-future.svg?branch=master
-    :target: https://travis-ci.org/PythonCharmers/python-future
+.. image:: https://github.com/PythonCharmers/python-future/actions/workflows/ci.yml/badge.svg?branch=master
+    :target: https://github.com/PythonCharmers/python-future/actions/workflows/ci.yml?query=branch%3Amaster
 
 .. image:: https://readthedocs.org/projects/python-future/badge/?version=latest
     :target: https://python-future.readthedocs.io/en/latest/?badge=latest
diff --git a/build.sh b/build.sh
deleted file mode 100755
index d4b92d9a..00000000
--- a/build.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/sh
-# XXX: TODO: we should make this include -e once tests pass
-set -xu
-
-DOCKER_IMAGE=jmadler/python-future-builder
-# XXX: TODO: Perhaps this version shouldn't be hardcoded
-version=0.18.4
-
-docker build . -t "$DOCKER_IMAGE"
-#docker push "$DOCKER_IMAGE:latest"
-
-for i in cp27-cp27m cp35-cp35m cp36-cp36m cp37-cp37m cp38-cp38 cp39-cp39; do
-    docker run -ti -v "$(realpath dist)":/root/python-future/dist "$DOCKER_IMAGE" /root/python-future/setup.sh "$version" $(basename $i)
-done
-
-python setup.py sdist
-python setup.py clean
-echo You may now run: "twine upload dist/*"
diff --git a/lint.sh b/lint.sh
deleted file mode 100755
index b3c41cd4..00000000
--- a/lint.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-# TODO: Run under Python 2.7 and 3.7
-flake8 . --count --exit-zero --select=E901,E999,F821,F822,F823 --show-source --statistics || true
-flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics || true
diff --git a/setup.sh b/setup.sh
deleted file mode 100755
index fa89d431..00000000
--- a/setup.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/sh
-
-set -ex
-
-version=$1
-pytag=$2
-
-if [ "$pytag" = 'py33' ]; then
-    pip3 install virtualenv==16.2.0
-fi
-
-. /root/"$pytag"/bin/activate
-
-if [ "$pytag" = 'py26' ]; then
-    pip install importlib
-fi
-pip install pytest unittest2
-python setup.py bdist_wheel --python-tag="$pytag"
-pip install "dist/future-$version-$pytag-none-any.whl"
-# Ignore test failures for now
-pytest tests/ || true
diff --git a/src/future/moves/_dummy_thread.py b/src/future/moves/_dummy_thread.py
index 96cf99e1..6633f42e 100644
--- a/src/future/moves/_dummy_thread.py
+++ b/src/future/moves/_dummy_thread.py
@@ -1,13 +1,13 @@
 from __future__ import absolute_import
-from future.utils import PY3
+from future.utils import PY3, PY39_PLUS
 
-if PY3:
+
+if PY39_PLUS:
     # _dummy_thread and dummy_threading modules were both deprecated in
     # Python 3.7 and removed in Python 3.9
-    try:
+    from _thread import *
+elif PY3:
         from _dummy_thread import *
-    except ImportError:
-        from _thread import *
 else:
     __future_module__ = True
     from dummy_thread import *
diff --git a/test.sh b/test.sh
new file mode 100755
index 00000000..d45e98d3
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -exo pipefail
+
+python --version
+
+if [ -e "/root/pip" ]
+then
+  pip install /root/pip/*.zip /root/pip/*.whl /root/pip/*tar.gz
+else
+  pip install pytest unittest2
+fi
+
+pytag="py${PYTHON_VERSION//./}"
+
+python setup.py bdist_wheel --python-tag="${pytag}"
+pip install dist/future-*-${pytag}-none-any.whl
+pytest tests/

From e8806ebe3b690c06dadf1dc6c18fc85ef6bdf311 Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 15:37:42 +1100
Subject: [PATCH 168/183] Update Trove classifier to "Mature"

---
 setup.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index 961e1b8c..13b0f435 100755
--- a/setup.py
+++ b/setup.py
@@ -105,7 +105,7 @@
     "Programming Language :: Python :: 3.12",
     "License :: OSI Approved",
     "License :: OSI Approved :: MIT License",
-    "Development Status :: 4 - Beta",
+    "Development Status :: 6 - Mature",
     "Intended Audience :: Developers",
 ]
 

From 87f7ed425814e40e010bae7cc860480504850cbb Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 15:41:20 +1100
Subject: [PATCH 169/183] Remove references to dead `python-porting` mailing
 list (issue #615)

---
 docs/faq.rst                      | 3 +--
 docs/quickstart.rst               | 4 +---
 docs/stdlib_incompatibilities.rst | 3 +--
 3 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/docs/faq.rst b/docs/faq.rst
index 9b1eab00..046c0167 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -287,8 +287,7 @@ Support
 Is there a mailing list?
 ------------------------
 
-Yes, please ask any questions on the `python-porting
-<https://mail.python.org/mailman/listinfo/python-porting>`_ mailing list.
+There was (`python-porting`), but it's now dead.
 
 
 .. _contributing:
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index 6042e059..8461a1a2 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -138,9 +138,7 @@ code::
 
 This feature is experimental, and we would appreciate your feedback on
 how well this works or doesn't work for you. Please file an issue `here
-<https://github.com/PythonCharmers/python-future>`_ or post to the
-`python-porting <https://mail.python.org/mailman/listinfo/python-porting>`_
-mailing list.
+<https://github.com/PythonCharmers/python-future>`_.
 
 For more information on the automatic translation feature, see :ref:`translation`.
 
diff --git a/docs/stdlib_incompatibilities.rst b/docs/stdlib_incompatibilities.rst
index 5f2217d2..e93f96ba 100644
--- a/docs/stdlib_incompatibilities.rst
+++ b/docs/stdlib_incompatibilities.rst
@@ -18,8 +18,7 @@ Here we will attempt to document these, together with known workarounds:
    ``base64``, ``decodebytes()`` function, :ref:`stdlib-base64-decodebytes`
    ``re``, ``ASCII`` mode, :ref:`stdlib-re-ASCII`
 
-To contribute to this, please email the python-porting list or send a
-pull request. See :ref:`contributing`.
+To contribute to this list, please send a pull request. See :ref:`contributing`.
 
 
 .. _stdlib-array-constructor:

From a7dd2cb2e9d24a77c8a448bce4ba09b4e91099e8 Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 15:45:48 +1100
Subject: [PATCH 170/183] Fix pow() with negative newint (issue #568)

---
 src/future/types/newint.py | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/future/types/newint.py b/src/future/types/newint.py
index 2c86ce18..ebc5715e 100644
--- a/src/future/types/newint.py
+++ b/src/future/types/newint.py
@@ -223,9 +223,11 @@ def __pow__(self, other):
 
     def __rpow__(self, other):
         value = super(newint, self).__rpow__(other)
-        if value is NotImplemented:
+        if isint(value):
+            return newint(value)
+        elif value is NotImplemented:
             return other ** long(self)
-        return newint(value)
+        return value
 
     def __lshift__(self, other):
         if not isint(other):

From 3bc1977d32e9f6c7f2cf1a0fd022959325e8e454 Mon Sep 17 00:00:00 2001
From: Christian Clauss <cclauss@me.com>
Date: Wed, 21 Feb 2024 06:00:43 +0100
Subject: [PATCH 171/183] Upgrade to actions/checkout@v4

---
 .github/workflows/ci.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fb7f5064..4458e9d9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -26,7 +26,7 @@ jobs:
 
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
       - if: ${{ matrix.versions.python != '2.6' }}
         run: |
           docker build \
@@ -43,4 +43,4 @@ jobs:
           docker run \
           -e PYTHON_VERSION=${{ matrix.versions.python }} \
           jmadler/python-future-builder:${{ matrix.versions.python }} \
-          /root/python-future/test.sh
\ No newline at end of file
+          /root/python-future/test.sh

From 6eef83de7db1086633c98ba2e104de1b471944d0 Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 17:03:50 +1100
Subject: [PATCH 172/183] Update copyright dates, add end-of-life notice

---
 LICENSE.txt                                   |  2 +-
 README.rst                                    | 28 ++++++++---
 docs/compatible_idioms.rst                    |  2 +-
 docs/credits.rst                              | 50 +++++++++++++++----
 .../Writing Python 2-3 compatible code.ipynb  |  2 +-
 docs/whatsnew.rst                             |  8 +--
 futurize.py                                   |  2 +-
 pasteurize.py                                 |  2 +-
 src/future/__init__.py                        |  7 ++-
 src/past/__init__.py                          |  4 +-
 10 files changed, 76 insertions(+), 31 deletions(-)

diff --git a/LICENSE.txt b/LICENSE.txt
index 4c904dba..275cafd3 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2013-2019 Python Charmers Pty Ltd, Australia
+Copyright (c) 2013-2024 Python Charmers, Australia
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.rst b/README.rst
index cce7605c..65999990 100644
--- a/README.rst
+++ b/README.rst
@@ -19,9 +19,21 @@ ports of features from Python 3 and 2. It also comes with ``futurize`` and
 either Py2 or Py3 code easily to support both Python 2 and 3 in a single
 clean Py3-style codebase, module by module.
 
-Notable projects that use ``python-future`` for Python 2/3 compatibility
-are `Mezzanine <http://mezzanine.jupo.org/>`_ and `ObsPy
-<http://obspy.org>`_.
+The ``python-future`` project has been downloaded over 1.7 billion times.
+
+.. _status
+
+Status
+------
+
+The ``python-future`` project was created in 2013 to attempt to save Python from
+the schism of version incompatibility that was threatening to tear apart the
+language (as Perl 6 contributed to the death of Perl).
+
+That time is now past. Thanks to a huge porting effort across the Python
+community, Python 3 eventually thrived. Python 2 reached its end of life in
+2020 and the ``python-future`` package should no longer be necessary. Use it to
+help with porting legacy code to Python 3 but don't depend on it for new code.
 
 .. _features:
 
@@ -223,6 +235,9 @@ into this code which runs on both Py2 and Py3:
     name = input()
     greet(name)
 
+The first four lines have no effect under Python 3 and can be removed from
+the codebase when Python 2 compatibility is no longer required.
+
 See :ref:`forwards-conversion` and :ref:`backwards-conversion` for more details.
 
 
@@ -304,12 +319,11 @@ Licensing
 
 :Author:  Ed Schofield, Jordan M. Adler, et al
 
-:Copyright: 2013-2019 Python Charmers Pty Ltd, Australia.
+:Copyright: 2013-2024 Python Charmers, Australia.
 
-:Sponsors: Python Charmers Pty Ltd, Australia, and Python Charmers Pte
-           Ltd, Singapore. http://pythoncharmers.com
+:Sponsors: Python Charmers: https://pythoncharmers.com
 
-           Pinterest https://opensource.pinterest.com/
+           Pinterest https://opensource.pinterest.com
 
 :Licence: MIT. See ``LICENSE.txt`` or `here <http://python-future.org/credits.html>`_.
 
diff --git a/docs/compatible_idioms.rst b/docs/compatible_idioms.rst
index b0cb05a3..f7087699 100644
--- a/docs/compatible_idioms.rst
+++ b/docs/compatible_idioms.rst
@@ -3,7 +3,7 @@
 Cheat Sheet: Writing Python 2-3 compatible code
 ===============================================
 
--  **Copyright (c):** 2013-2019 Python Charmers Pty Ltd, Australia.
+-  **Copyright (c):** 2013-2024 Python Charmers, Australia.
 -  **Author:** Ed Schofield.
 -  **Licence:** Creative Commons Attribution.
 
diff --git a/docs/credits.rst b/docs/credits.rst
index 275e148e..4c029efd 100644
--- a/docs/credits.rst
+++ b/docs/credits.rst
@@ -8,7 +8,7 @@ Licence
 The software is distributed under an MIT licence. The text is as follows
 (from ``LICENSE.txt``)::
 
-    Copyright (c) 2013-2019 Python Charmers Pty Ltd, Australia
+    Copyright (c) 2013-2024 Python Charmers, Australia
 
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
@@ -32,39 +32,59 @@ The software is distributed under an MIT licence. The text is as follows
 
 Sponsors
 --------
-Python Charmers Pty Ltd, Australia, and Python Charmers Pte Ltd, Singapore.
-http://pythoncharmers.com
 
-Pinterest https://opensource.pinterest.com/
+Python Charmers: https://pythoncharmers.com
 
 .. _authors:
 
-Maintainer
-----------
-Python-Future is currently maintained by Jordan M. Adler <jordan.m.adler@gmail.com>.
-
-Authors
+Author
 -------
-Python-Future is largely written by Ed Schofield <ed@pythoncharmers.com> with the help of various contributors:
+
+Python-Future was largely written by Ed Schofield <ed@pythoncharmers.com>.
+
+Maintainers
+-----------
+
+The project is no longer being actively maintained. Like Python 2, it should be
+considered end-of-life.
+
+Past maintainers include:
+
+- Jordan M. Adler
+- Liuyang Wan
+- Ed Schofield
+
+Contributors
+------------
+
+Thanks to the following people for helping to improve the package:
 
 - Jordan Adler
 - Jeroen Akkerman
+- Bruno Alla
 - Kyle Altendorf
+- Nuno André
+- Kian-Meng Ang
 - Grant Bakker
 - Jacob Beck
+- David Bern
 - Fumihiro (Ben) Bessho
 - Shiva Bhusal
+- Andrew Bjonnes
 - Nate Bogdanowicz
 - Tomer Chachamu
 - Christian Clauss
 - Denis Cornehl
+- Joseph Curtis
 - Nicolas Delaby
 - Chad Dombrova
 - Jon Dufresne
 - Corey Farwell
 - Eric Firing
 - Joe Gordon
+- Gabriela Gutierrez
 - Maximilian Hils
+- Tomáš Hrnčiar
 - Miro Hrončok
 - Mark Huang
 - Martijn Jacobs
@@ -84,22 +104,31 @@ Python-Future is largely written by Ed Schofield <ed@pythoncharmers.com> with th
 - Jon Parise
 - Matthew Parnell
 - Tom Picton
+- Sebastian Potasiak
 - Miga Purg
 - Éloi Rivard
+- Greg Roodt
 - Sesh Sadasivam
 - Elliott Sales de Andrade
 - Aiden Scandella
 - Yury Selivanov
+- Alexander Shadchin
 - Tim Shaffer
+- Christopher Slycord
 - Sameera Somisetty
+- Nicola Soranzo
 - Louis Sautier
+- Will Shanks
 - Gregory P. Smith
 - Chase Sterling
+- Matthew Stidham
 - Daniel Szoska
 - Flaviu Tamas
+- Roman A. Taycher
 - Jeff Tratner
 - Tim Tröndle
 - Brad Walker
+- Liuyang Wan
 - Andrew Wason
 - Jeff Widman
 - Dan Yeaw
@@ -111,6 +140,7 @@ Python-Future is largely written by Ed Schofield <ed@pythoncharmers.com> with th
 - urain39 (GitHub user)
 - 9seconds (GitHub user)
 - Varriount (GitHub user)
+- zihzihtw (GitHub user)
 
 Suggestions and Feedback
 ~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/notebooks/Writing Python 2-3 compatible code.ipynb b/docs/notebooks/Writing Python 2-3 compatible code.ipynb
index 0f585d29..e2af9c7e 100644
--- a/docs/notebooks/Writing Python 2-3 compatible code.ipynb	
+++ b/docs/notebooks/Writing Python 2-3 compatible code.ipynb	
@@ -11,7 +11,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "- **Copyright (c):** 2013-2019 Python Charmers Pty Ltd, Australia.\n",
+    "- **Copyright (c):** 2013-2024 Python Charmers, Australia.\n",
     "- **Author:** Ed Schofield.\n",
     "- **Licence:** Creative Commons Attribution.\n",
     "\n",
diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index cc7fc242..1514bf40 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -3,12 +3,14 @@
 What's New
 **********
 
-What's new in version 0.18.4 (2024-02-21)
-=========================================
-This is a minor bug-fix release containing a number of fixes:
+What's new in version 0.8.4 (2024-02-21)
+========================================
+This is a minor bug-fix release containing a small number of fixes:
 
 - Fix for Python 3.12's removal of the imp module
 - Small updates to the docs
+- Add a note to the docs that the package, like Python 2, is done.
+- Fix pasteurize: NameError: name 'unicode' is not defined (de68c10)
 
 What's new in version 0.18.3 (2023-01-13)
 =========================================
diff --git a/futurize.py b/futurize.py
index cb446ab2..09feaf59 100755
--- a/futurize.py
+++ b/futurize.py
@@ -13,7 +13,7 @@
 
 Licensing
 ---------
-Copyright 2013-2019 Python Charmers Pty Ltd, Australia.
+Copyright 2013-2024 Python Charmers, Australia.
 The software is distributed under an MIT licence. See LICENSE.txt.
 """
 
diff --git a/pasteurize.py b/pasteurize.py
index 2b98327c..658955f6 100755
--- a/pasteurize.py
+++ b/pasteurize.py
@@ -12,7 +12,7 @@
 
 Licensing
 ---------
-Copyright 2013-2019 Python Charmers Pty Ltd, Australia.
+Copyright 2013-2024 Python Charmers, Australia.
 The software is distributed under an MIT licence. See LICENSE.txt.
 """
 
diff --git a/src/future/__init__.py b/src/future/__init__.py
index 64b66f43..3bac3563 100644
--- a/src/future/__init__.py
+++ b/src/future/__init__.py
@@ -69,14 +69,13 @@
 -------
 
 :Author:  Ed Schofield, Jordan M. Adler, et al
-:Sponsor: Python Charmers Pty Ltd, Australia, and Python Charmers Pte
-          Ltd, Singapore. http://pythoncharmers.com
+:Sponsor: Python Charmers: https://pythoncharmers.com
 :Others:  See docs/credits.rst or http://python-future.org/credits.html
 
 
 Licensing
 ---------
-Copyright 2013-2019 Python Charmers Pty Ltd, Australia.
+Copyright 2013-2024 Python Charmers, Australia.
 The software is distributed under an MIT licence. See LICENSE.txt.
 
 """
@@ -84,7 +83,7 @@
 __title__ = 'future'
 __author__ = 'Ed Schofield'
 __license__ = 'MIT'
-__copyright__ = 'Copyright 2013-2019 Python Charmers Pty Ltd'
+__copyright__ = 'Copyright 2013-2024 Python Charmers (https://pythoncharmers.com)'
 __ver_major__ = 0
 __ver_minor__ = 18
 __ver_patch__ = 4
diff --git a/src/past/__init__.py b/src/past/__init__.py
index 14713039..54619e0a 100644
--- a/src/past/__init__.py
+++ b/src/past/__init__.py
@@ -75,12 +75,12 @@
 -------
 
 :Author:  Ed Schofield, Jordan M. Adler, et al
-:Sponsor: Python Charmers Pty Ltd, Australia: http://pythoncharmers.com
+:Sponsor: Python Charmers: https://pythoncharmers.com
 
 
 Licensing
 ---------
-Copyright 2013-2019 Python Charmers Pty Ltd, Australia.
+Copyright 2013-2024 Python Charmers, Australia.
 The software is distributed under an MIT licence. See LICENSE.txt.
 """
 

From 9f33c2ca45c96a3991cb7573d2c038eb121b1e1e Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 17:06:38 +1100
Subject: [PATCH 173/183] Bump version to 1.0.0

---
 docs/whatsnew.rst      | 4 ++--
 src/future/__init__.py | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index 1514bf40..b92de81b 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -3,8 +3,8 @@
 What's New
 **********
 
-What's new in version 0.8.4 (2024-02-21)
-========================================
+What's new in version 1.0.0 (2024-02-21)
+=========================================
 This is a minor bug-fix release containing a small number of fixes:
 
 - Fix for Python 3.12's removal of the imp module
diff --git a/src/future/__init__.py b/src/future/__init__.py
index 3bac3563..ec2623f7 100644
--- a/src/future/__init__.py
+++ b/src/future/__init__.py
@@ -84,9 +84,9 @@
 __author__ = 'Ed Schofield'
 __license__ = 'MIT'
 __copyright__ = 'Copyright 2013-2024 Python Charmers (https://pythoncharmers.com)'
-__ver_major__ = 0
-__ver_minor__ = 18
-__ver_patch__ = 4
+__ver_major__ = 1
+__ver_minor__ = 0
+__ver_patch__ = 0
 __ver_sub__ = ''
 __version__ = "%d.%d.%d%s" % (__ver_major__, __ver_minor__,
                               __ver_patch__, __ver_sub__)

From 70b36a8a514e45a2582589991be4a2c1fd1944b7 Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 17:09:10 +1100
Subject: [PATCH 174/183] Disable Python 2.6 CI tests for now

---
 .github/workflows/ci.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fb7f5064..e436f3cd 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,7 +14,7 @@ jobs:
       fail-fast: false
       matrix:
         versions:
-          - python: "2.6"
+          # - python: "2.6"
           - python: "2.7"
           - python: "3.3"
           - python: "3.4"
@@ -43,4 +43,4 @@ jobs:
           docker run \
           -e PYTHON_VERSION=${{ matrix.versions.python }} \
           jmadler/python-future-builder:${{ matrix.versions.python }} \
-          /root/python-future/test.sh
\ No newline at end of file
+          /root/python-future/test.sh

From f4a1f0406b47e8ef91838fcff9d679ee6fe186d9 Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 17:19:55 +1100
Subject: [PATCH 175/183] Docs: replace http links with https

---
 README.rst                                         | 14 +++++++-------
 docs/_templates/sidebarintro.html                  |  2 +-
 docs/compatible_idioms.rst                         |  8 ++++----
 docs/faq.rst                                       |  4 ++--
 .../Writing Python 2-3 compatible code.ipynb       | 10 +++++-----
 src/future/__init__.py                             |  6 +++---
 src/future/builtins/__init__.py                    |  2 +-
 7 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/README.rst b/README.rst
index 65999990..ca533976 100644
--- a/README.rst
+++ b/README.rst
@@ -6,9 +6,6 @@ Overview: Easy, clean, reliable Python 2/3 compatibility
 .. image:: https://github.com/PythonCharmers/python-future/actions/workflows/ci.yml/badge.svg?branch=master
     :target: https://github.com/PythonCharmers/python-future/actions/workflows/ci.yml?query=branch%3Amaster
 
-.. image:: https://readthedocs.org/projects/python-future/badge/?version=latest
-    :target: https://python-future.readthedocs.io/en/latest/?badge=latest
-
 ``python-future`` is the missing compatibility layer between Python 2 and
 Python 3. It allows you to use a single, clean Python 3.x-compatible
 codebase to support both Python 2 and Python 3 with minimal overhead.
@@ -325,16 +322,19 @@ Licensing
 
            Pinterest https://opensource.pinterest.com
 
-:Licence: MIT. See ``LICENSE.txt`` or `here <http://python-future.org/credits.html>`_.
+:Licence: MIT. See ``LICENSE.txt`` or `here <https://python-future.org/credits.html>`_.
 
-:Other credits:  See `here <http://python-future.org/credits.html>`_.
+:Other credits:  See `here <https://python-future.org/credits.html>`_.
 
+Docs
+----
+See the docs `here <https://python-future.org>`.
 
 Next steps
 ----------
 
 If you are new to Python-Future, check out the `Quickstart Guide
-<http://python-future.org/quickstart.html>`_.
+<https://python-future.org/quickstart.html>`_.
 
 For an update on changes in the latest version, see the `What's New
-<http://python-future.org/whatsnew.html>`_ page.
+<https://python-future.org/whatsnew.html>`_ page.
diff --git a/docs/_templates/sidebarintro.html b/docs/_templates/sidebarintro.html
index e4433221..1e1a512f 100644
--- a/docs/_templates/sidebarintro.html
+++ b/docs/_templates/sidebarintro.html
@@ -1,7 +1,7 @@
 <!--<h3>Python-Future</h3>
 <p>-->
   <h4>Easy, clean, reliable Python 2/3 compatibility</h4>
-  <a href="http://python-future.org">Table of Contents</a>
+  <a href="https://python-future.org">Table of Contents</a>
 <!--
 </p>
 <h3>Other Formats</h3>
diff --git a/docs/compatible_idioms.rst b/docs/compatible_idioms.rst
index f7087699..ab478ed8 100644
--- a/docs/compatible_idioms.rst
+++ b/docs/compatible_idioms.rst
@@ -7,12 +7,12 @@ Cheat Sheet: Writing Python 2-3 compatible code
 -  **Author:** Ed Schofield.
 -  **Licence:** Creative Commons Attribution.
 
-A PDF version is here: http://python-future.org/compatible\_idioms.pdf
+A PDF version is here: https://python-future.org/compatible\_idioms.pdf
 
 This notebook shows you idioms for writing future-proof code that is
 compatible with both versions of Python: 2 and 3. It accompanies Ed
 Schofield's talk at PyCon AU 2014, "Writing 2/3 compatible code". (The
-video is here: http://www.youtube.com/watch?v=KOqk8j11aAI&t=10m14s.)
+video is here: https://www.youtube.com/watch?v=KOqk8j11aAI&t=10m14s.)
 
 Minimum versions:
 
@@ -38,7 +38,7 @@ The following scripts are also ``pip``-installable:
     futurize             # pip install future
     pasteurize           # pip install future
 
-See http://python-future.org and https://pythonhosted.org/six/ for more
+See https://python-future.org and https://pythonhosted.org/six/ for more
 information.
 
 Essential syntax differences
@@ -337,7 +337,7 @@ this idiom to make all string literals in a module unicode strings:
 
     s1 = 'The Zen of Python'
     s2 = 'きたないのよりきれいな方がいい\n'
-See http://python-future.org/unicode\_literals.html for more discussion
+See https://python-future.org/unicode\_literals.html for more discussion
 on which style to use.
 
 Byte-string literals
diff --git a/docs/faq.rst b/docs/faq.rst
index 046c0167..0ca39c2a 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -8,7 +8,7 @@ Who is this for?
 ongoing Python 2.7 support easily and with little maintenance burden.
 
 2. People who wish to ease and accelerate migration of their Python 2 codebases
-to Python 3.4+, module by module, without giving up Python 2 compatibility.
+to Python 3.3+, module by module, without giving up Python 2 compatibility.
 
 
 Why upgrade to Python 3?
@@ -92,7 +92,7 @@ What inspired this project?
 ---------------------------
 
 In our Python training courses, we at `Python Charmers
-<http://pythoncharmers.com>`_ faced a dilemma: teach people Python 3, which was
+<https://pythoncharmers.com>`_ faced a dilemma: teach people Python 3, which was
 future-proof but not as useful to them today because of weaker 3rd-party
 package support, or teach people Python 2, which was more useful today but
 would require them to change their code and unlearn various habits soon. We
diff --git a/docs/notebooks/Writing Python 2-3 compatible code.ipynb b/docs/notebooks/Writing Python 2-3 compatible code.ipynb
index e2af9c7e..663ede44 100644
--- a/docs/notebooks/Writing Python 2-3 compatible code.ipynb	
+++ b/docs/notebooks/Writing Python 2-3 compatible code.ipynb	
@@ -15,9 +15,9 @@
     "- **Author:** Ed Schofield.\n",
     "- **Licence:** Creative Commons Attribution.\n",
     "\n",
-    "A PDF version is here: http://python-future.org/compatible_idioms.pdf\n",
+    "A PDF version is here: https://python-future.org/compatible_idioms.pdf\n",
     "\n",
-    "This notebook shows you idioms for writing future-proof code that is compatible with both versions of Python: 2 and 3. It accompanies Ed Schofield's talk at PyCon AU 2014, \"Writing 2/3 compatible code\". (The video is here: <http://www.youtube.com/watch?v=KOqk8j11aAI&t=10m14s>.)\n",
+    "This notebook shows you idioms for writing future-proof code that is compatible with both versions of Python: 2 and 3. It accompanies Ed Schofield's talk at PyCon AU 2014, \"Writing 2/3 compatible code\". (The video is here: <https://www.youtube.com/watch?v=KOqk8j11aAI&t=10m14s>.)\n",
     "\n",
     "Minimum versions:\n",
     "\n",
@@ -48,7 +48,7 @@
     "    futurize             # pip install future\n",
     "    pasteurize           # pip install future\n",
     "\n",
-    "See http://python-future.org and https://pythonhosted.org/six/ for more information."
+    "See https://python-future.org and https://pythonhosted.org/six/ for more information."
    ]
   },
   {
@@ -756,7 +756,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "See http://python-future.org/unicode_literals.html for more discussion on which style to use."
+    "See https://python-future.org/unicode_literals.html for more discussion on which style to use."
    ]
   },
   {
@@ -2812,7 +2812,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "``urllib`` is the hardest module to use from Python 2/3 compatible code. You may like to use Requests (http://python-requests.org) instead."
+    "``urllib`` is the hardest module to use from Python 2/3 compatible code. You may like to use Requests (https://python-requests.org) instead."
    ]
   },
   {
diff --git a/src/future/__init__.py b/src/future/__init__.py
index ec2623f7..b097fd81 100644
--- a/src/future/__init__.py
+++ b/src/future/__init__.py
@@ -52,7 +52,7 @@
 --------------------
 
 An included script called `futurize
-<http://python-future.org/automatic_conversion.html>`_ aids in converting
+<https://python-future.org/automatic_conversion.html>`_ aids in converting
 code (from either Python 2 or Python 3) to code compatible with both
 platforms. It is similar to ``python-modernize`` but goes further in
 providing Python 3 compatibility through the use of the backported types
@@ -62,7 +62,7 @@
 Documentation
 -------------
 
-See: http://python-future.org
+See: https://python-future.org
 
 
 Credits
@@ -70,7 +70,7 @@
 
 :Author:  Ed Schofield, Jordan M. Adler, et al
 :Sponsor: Python Charmers: https://pythoncharmers.com
-:Others:  See docs/credits.rst or http://python-future.org/credits.html
+:Others:  See docs/credits.rst or https://python-future.org/credits.html
 
 
 Licensing
diff --git a/src/future/builtins/__init__.py b/src/future/builtins/__init__.py
index 8bc1649d..1734cd45 100644
--- a/src/future/builtins/__init__.py
+++ b/src/future/builtins/__init__.py
@@ -2,7 +2,7 @@
 A module that brings in equivalents of the new and modified Python 3
 builtins into Py2. Has no effect on Py3.
 
-See the docs `here <http://python-future.org/what-else.html>`_
+See the docs `here <https://python-future.org/what-else.html>`_
 (``docs/what-else.rst``) for more information.
 
 """

From 9f8763032b4ac1dbe043db363feae26fe6d0bacc Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 17:27:57 +1100
Subject: [PATCH 176/183] Update the FAQ entries on compatibility and
 contributing

---
 README.rst   |  2 +-
 docs/faq.rst | 19 +++++++++++--------
 2 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/README.rst b/README.rst
index ca533976..7c9ce67b 100644
--- a/README.rst
+++ b/README.rst
@@ -328,7 +328,7 @@ Licensing
 
 Docs
 ----
-See the docs `here <https://python-future.org>`.
+See the docs `here <https://python-future.org>`_.
 
 Next steps
 ----------
diff --git a/docs/faq.rst b/docs/faq.rst
index 0ca39c2a..2ac148d3 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -257,13 +257,14 @@ Platform and version support
 Which versions of Python does ``python-future`` support?
 --------------------------------------------------------
 
-Python 2.7, and 3.4+ only.
+Python 2.6 and 3.3+ only. Python 2.7 and Python 3.4+ are preferred.
 
-Python 2.7 introduced many important forward-compatibility
-features (such as import hooks, ``b'...'`` literals and ``__future__``
-definitions) that greatly reduce the maintenance burden for single-source
-Py2/3 compatible code. ``future`` leverages these features and aims to
-close the remaining gap between Python 3 and 2.7.
+You may be able to use Python 2.6 but writing Py2/3 compatible code is not as
+easy. Python 2.7 introduced many important forward-compatibility features (such
+as import hooks, ``b'...'`` literals and ``__future__`` definitions) that
+greatly reduce the maintenance burden for single-source Py2/3 compatible code.
+``future`` leverages these features and aims to close the remaining gap between
+Python 3 and 2.7.
 
 
 Do you support Pypy?
@@ -299,8 +300,10 @@ Can I help?
 -----------
 
 Yes please :) We welcome bug reports, additional tests, pull requests,
-and stories of either success or failure with using it. Help with the fixers
-for the ``futurize`` script is particularly welcome.
+and stories of either success or failure with using it.
+
+However, please note that the project is not very actively maintained. It
+should be considered done, like Python 2.
 
 
 Where is the repo?

From a10a78bbd2921a4deaa423bdf5712acf9eb8d5a4 Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 17:47:18 +1100
Subject: [PATCH 177/183] Update "What's New"

---
 README.rst        |  1 -
 docs/whatsnew.rst | 20 ++++++++++++++++----
 2 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/README.rst b/README.rst
index 7c9ce67b..6518b9e8 100644
--- a/README.rst
+++ b/README.rst
@@ -276,7 +276,6 @@ properly to a Python 2/3 compatible codebase using a tool like
 Note: the auto-translation feature is still in alpha; it needs more testing and
 development, and will likely never be perfect.
 
-For more info, see :ref:`translation`.
 
 Pre-commit hooks
 ----------------
diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index b92de81b..7adad9cc 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -4,13 +4,25 @@ What's New
 **********
 
 What's new in version 1.0.0 (2024-02-21)
-=========================================
-This is a minor bug-fix release containing a small number of fixes:
+========================================
+
+The new version number of 1.0.0 indicates that the python-future project, like
+Python 2, is now done.
+
+The most important change in this release is adding support for Python 3.12
+(ba1cc50 and a6222d2 and bcced95).
+
+This release also includes these fixes:
 
-- Fix for Python 3.12's removal of the imp module
 - Small updates to the docs
-- Add a note to the docs that the package, like Python 2, is done.
+- Add SECURITY.md describing security policy (0598d1b)
 - Fix pasteurize: NameError: name 'unicode' is not defined (de68c10)
+- Move CI to GitHub Actions (8cd11e8)
+- Add setuptools to requirements for building docs (0c347ff)
+- Fix typos in docs (350e87a)
+- Make the fix_unpacking fixer more robust (de68c10)
+- Small improvements to shell scripts according to shellcheck (6153844)
+
 
 What's new in version 0.18.3 (2023-01-13)
 =========================================

From b2ea4204fc269ede619526c7753c0fa6579d5d0d Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 17:53:54 +1100
Subject: [PATCH 178/183] Remove hacky logo from docs

---
 docs/_templates/navbar.html | 1 -
 1 file changed, 1 deletion(-)

diff --git a/docs/_templates/navbar.html b/docs/_templates/navbar.html
index b77fb767..fc96b5ca 100644
--- a/docs/_templates/navbar.html
+++ b/docs/_templates/navbar.html
@@ -12,7 +12,6 @@
           <span class="icon-bar"></span>
         </button>
 
-        <a class="logo"> <img height="32" width="32" src="_static/python-future-logo-textless-transparent.png" /></a>
         <a class="navbar-brand" href="{{ pathto(master_doc) }}">{% if theme_navbar_title -%}{{ theme_navbar_title|e }}{%- else -%}{{ project|e }}{%- endif -%}</a>
         <span class="navbar-text navbar-version pull-left"><b>{{ version|e }}</b></span>
       </div>

From dde60ad4c9857a11b9f54e8ae671aacde703e71a Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 17:53:17 +1100
Subject: [PATCH 179/183] Move old "What's New" entries to the change log

---
 docs/changelog.rst | 227 +++++++++++++++++++++++++++++++++++++++++++++
 docs/whatsnew.rst  | 222 --------------------------------------------
 2 files changed, 227 insertions(+), 222 deletions(-)

diff --git a/docs/changelog.rst b/docs/changelog.rst
index 059ad4f5..420e2bc4 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -5,6 +5,233 @@ Changes in previous versions
 
 Changes in the most recent major version are here: :ref:`whats-new`.
 
+Changes in version 0.18.3 (2023-01-13)
+======================================
+This is a minor bug-fix release containing a number of fixes:
+
+- Backport fix for bpo-38804 (c91d70b)
+- Fix bug in fix_print.py fixer (dffc579)
+- Fix bug in fix_raise.py fixer (3401099)
+- Fix newint bool in py3 (fe645ba)
+- Fix bug in super() with metaclasses (6e27aac)
+- docs: fix simple typo, reqest -> request (974eb1f)
+- Correct __eq__ (c780bf5)
+- Pass if lint fails (2abe00d)
+- Update docker image and parcel out to constant variable.  Add comment to update version constant (45cf382)
+- fix order (f96a219)
+- Add flake8 to image (046ff18)
+- Make lint.sh executable (58cc984)
+- Add docker push to optimize CI (01e8440)
+- Build System (42b3025)
+- Add docs build status badge to README.md (3f40bd7)
+- Use same docs requirements in tox (18ecc5a)
+- Add docs/requirements.txt (5f9893f)
+- Add PY37_PLUS, PY38_PLUS, and PY39_PLUS (bee0247)
+- fix 2.6 test, better comment (ddedcb9)
+- fix 2.6 test (3f1ff7e)
+- remove nan test (4dbded1)
+- include list test values (e3f1a12)
+- fix other python2 test issues (c051026)
+- fix missing subTest (f006cad)
+- import from old imp library on older python versions (fc84fa8)
+- replace fstrings with format for python 3.4,3.5 (4a687ea)
+- minor style/spelling fixes (8302d8c)
+- improve cmp function, add unittest (0d95a40)
+- Pin typing==3.7.4.1 for Python 3.3 compatiblity (1a48f1b)
+- Fix various py26 unit test failures (9ca5a14)
+- Add initial contributing guide with docs build instruction (e55f915)
+- Add docs building to tox.ini (3ee9e7f)
+- Support NumPy's specialized int types in builtins.round (b4b54f0)
+- Added r""" to the docstring to avoid warnings in python3 (5f94572)
+- Add __subclasscheck__ for past.types.basestring (c9bc0ff)
+- Correct example in README (681e78c)
+- Add simple documentation (6c6e3ae)
+- Add pre-commit hooks (a9c6a37)
+- Handling of __next__ and next by future.utils.get_next was reversed (52b0ff9)
+- Add a test for our fix (461d77e)
+- Compare headers to correct definition of str (3eaa8fd)
+- #322 Add support for negative ndigits in round; additionally, fixing a bug so that it handles passing in Decimal properly (a4911b9)
+- Add tkFileDialog to future.movers.tkinter (f6a6549)
+- Sort before comparing dicts in TestChainMap (6126997)
+- Fix typo (4dfa099)
+- Fix formatting in "What's new" (1663dfa)
+- Fix typo (4236061)
+- Avoid DeprecationWarning caused by invalid escape (e4b7fa1)
+- Fixup broken link to external django documentation re: porting to Python 3 and unicode_literals (d87713e)
+- Fixed newdict checking version every time (99030ec)
+- Add count from 2.7 to 2.6 (1b8ef51)
+
+Changes in version 0.18.2 (2019-10-30)
+======================================
+
+This is a minor bug-fix release containing a number of fixes:
+
+- Fix min/max functions with generators, and 'None' default (PR #514)
+- Use BaseException in raise_() (PR #515)
+- Fix builtins.round() for Decimals (Issue #501)
+- Fix raise_from() to prevent failures with immutable classes (PR #518)
+- Make FixInput idempotent (Issue #427)
+- Fix type in newround (PR #521)
+- Support mimetype guessing in urllib2 for Py3.8+ (Issue #508)
+
+Python 3.8 is not yet officially supported.
+
+Changes in version 0.18.1 (2019-10-09)
+======================================
+
+This is a minor bug-fix release containing a fix for raise_() 
+when passed an exception that's not an Exception (e.g. BaseException
+subclasses)
+
+Changes in version 0.18.0 (2019-10-09)
+======================================
+
+This is a major bug-fix and feature release, including:
+
+- Fix collections.abc import for py38+
+- Remove import for isnewbytes() function, reducing CPU cost significantly
+- Fix bug with importing past.translation when importing past which breaks zipped python installations
+- Fix an issue with copyreg import under Py3 that results in unexposed stdlib functionality
+- Export and document types in future.utils
+- Update behavior of newstr.__eq__() to match str.__eq__() as per reference docs
+- Fix raising and the raising fixer to handle cases where the syntax is ambiguous
+- Allow "default" parameter in min() and max() (Issue #334)
+- Implement __hash__() in newstr (Issue #454)
+- Future proof some version checks to handle the fact that Py4 won't be a major breaking release
+- Fix urllib.request imports for Python 3.8 compatibility (Issue #447)
+- Fix future import ordering (Issue #445)
+- Fixed bug in fix_division_safe fixture (Issue #434)
+- Do not globally destroy re.ASCII in PY3
+- Fix a bug in email.Message.set_boundary() (Issue #429)
+- Implement format_map() in str
+- Implement readinto() for socket.fp
+
+As well as a number of corrections to a variety of documentation, and updates to
+test infrastructure.
+
+Changes in version 0.17.1 (2018-10-30)
+======================================
+
+This release address a packaging error because of an erroneous declaration that
+any built wheels are universal.
+
+Changes in version 0.17.0 (2018-10-19)
+======================================
+
+This is a major bug-fix release, including:
+
+- Fix ``from collections import ChainMap`` after install_aliases() (issue #226)
+- Fix multiple import from ``__future__`` bug in futurize (issue #113)
+- Add support for proper %s formatting of newbytes
+- Properly implement iterator protocol for newrange object
+- Fix ``past.translation`` on read-only file systems
+- Fix Tkinter import bug introduced in Python 2.7.4 (issue #262)
+- Correct TypeError to ValueError in a specific edge case for newrange
+- Support inequality tests between newstrs and newbytes
+- Add type check to __get__ in newsuper
+- Fix fix_divsion_safe to support better conversion of complex expressions, and
+  skip obvious float division.
+
+As well as a number of corrections to a variety of documentation, and updates to
+test infrastructure.
+
+Changes in version 0.16.0 (2016-10-27)
+======================================
+
+This release removes the ``configparser`` package as an alias for
+``ConfigParser`` on Py2 to improve compatibility with the backported
+`configparser package <https://pypi.org/project/configparser/>`. Previously
+``python-future`` and the PyPI ``configparser`` backport clashed, causing
+various compatibility issues. (Issues #118, #181)
+
+If your code previously relied on ``configparser`` being supplied by
+``python-future``, the recommended upgrade path is to run ``pip install
+configparser`` or add ``configparser`` to your ``requirements.txt`` file.
+
+Note that, if you are upgrading ``future`` with ``pip``, you may need to
+uninstall the old version of future or manually remove the
+``site-packages/future-0.15.2-py2.7.egg`` folder for this change to take
+effect on your system.
+
+This releases also fixes these bugs:
+
+- Fix ``newbytes`` constructor bug. (Issue #171)
+- Fix semantics of ``bool()`` with ``newobject``. (Issue #211)
+- Fix ``standard_library.install_aliases()`` on PyPy. (Issue #205)
+- Fix assertRaises for ``pow`` and ``compile``` on Python 3.5. (Issue #183)
+- Fix return argument of ``future.utils.ensure_new_type`` if conversion to
+  new type does not exist. (Issue #185)
+- Add missing ``cmp_to_key`` for Py2.6. (Issue #189)
+- Allow the ``old_div`` fixer to be disabled. (Issue #190)
+- Improve compatibility with Google App Engine. (Issue #231)
+- Add some missing imports to the ``tkinter`` and ``tkinter.filedialog``
+  package namespaces. (Issues #212 and #233)
+- More complete implementation of ``raise_from`` on PY3. (Issues #141,
+  #213 and #235, fix provided by Varriount)
+
+
+Changes in version 0.15.2 (2015-09-11)
+======================================
+
+This is a minor bug-fix release:
+
+- Fix ``socket.create_connection()`` backport on Py2.6 (issue #162)
+- Add more tests of ``urllib.request`` etc.
+- Fix ``newsuper()`` calls from the ``__init__`` method of PyQt subclassses
+  (issue #160, thanks to Christopher Arndt)
+
+Changes in version 0.15.1 (2015-09-09)
+======================================
+
+This is a minor bug-fix release:
+
+- Use 3-argument ``socket.create_connection()`` backport to restore Py2.6
+  compatibility in ``urllib.request.urlopen()`` (issue #162)
+- Remove breakpoint in ``future.backports.http.client`` triggered on certain
+  data (issue #164)
+- Move ``exec`` fixer to stage 1 of ``futurize`` because the forward-compatible ``exec(a, b)``
+  idiom is supported in Python 2.6 and 2.7. See
+  https://docs.python.org/2/reference/simple_stmts.html#exec.
+
+
+Changes in version 0.15.0 (2015-07-25)
+======================================
+
+This release fixes compatibility bugs with CherryPy's Py2/3 compat layer and
+the latest version of the ``urllib3`` package. It also adds some additional
+backports for Py2.6 and Py2.7 from Py3.4's standard library.
+
+New features:
+
+- ``install_aliases()`` now exposes full backports of the Py3 urllib submodules
+  (``parse``, ``request`` etc.) from ``future.backports.urllib`` as submodules
+  of ``urllib`` on Py2.  This implies, for example, that
+  ``urllib.parse.unquote`` now takes an optional encoding argument as it does
+  on Py3. This improves compatibility with CherryPy's Py2/3 compat layer (issue
+  #158).
+- ``tkinter.ttk`` support (issue #151)
+- Backport of ``collections.ChainMap`` (issue #150)
+- Backport of ``itertools.count`` for Py2.6 (issue #152)
+- Enable and document support for the ``surrogateescape`` error handler for ``newstr`` and ``newbytes`` objects on Py2.x (issue #116). This feature is currently in alpha.
+- Add constants to ``http.client`` such as ``HTTP_PORT`` and ``BAD_REQUEST`` (issue #137)
+- Backport of ``reprlib.recursive_repr`` to Py2
+
+Bug fixes:
+
+- Add ``HTTPMessage`` to ``http.client``, which is missing from ``httplib.__all__`` on Python <= 2.7.10. This restores compatibility with the latest ``urllib3`` package (issue #159, thanks to Waldemar Kornewald)
+- Expand newint.__divmod__ and newint.__rdivmod__ to fall back to <type 'long'>
+  implementations where appropriate (issue #146 - thanks to Matt Bogosian)
+- Fix newrange slicing for some slice/range combos (issue #132, thanks to Brad Walker)
+- Small doc fixes (thanks to Michael Joseph and Tim Tröndle)
+- Improve robustness of test suite against opening .pyc files as text on Py2
+- Update backports of ``Counter`` and ``OrderedDict`` to use the newer
+  implementations from Py3.4. This fixes ``.copy()`` preserving subclasses etc.
+- ``futurize`` no longer breaks working Py2 code by changing ``basestring`` to
+  ``str``. Instead it imports the ``basestring`` forward-port from
+  ``past.builtins`` (issues #127 and #156)
+- ``future.utils``: add ``string_types`` etc. and update docs (issue #126)
+
+
 .. _whats-new-0.14.x:
 
 Changes in version 0.14.3 (2014-12-15)
diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index 7adad9cc..3c70ecad 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -24,228 +24,6 @@ This release also includes these fixes:
 - Small improvements to shell scripts according to shellcheck (6153844)
 
 
-What's new in version 0.18.3 (2023-01-13)
-=========================================
-This is a minor bug-fix release containing a number of fixes:
-
-- Backport fix for bpo-38804 (c91d70b)
-- Fix bug in fix_print.py fixer (dffc579)
-- Fix bug in fix_raise.py fixer (3401099)
-- Fix newint bool in py3 (fe645ba)
-- Fix bug in super() with metaclasses (6e27aac)
-- docs: fix simple typo, reqest -> request (974eb1f)
-- Correct __eq__ (c780bf5)
-- Pass if lint fails (2abe00d)
-- Update docker image and parcel out to constant variable.  Add comment to update version constant (45cf382)
-- fix order (f96a219)
-- Add flake8 to image (046ff18)
-- Make lint.sh executable (58cc984)
-- Add docker push to optimize CI (01e8440)
-- Build System (42b3025)
-- Add docs build status badge to README.md (3f40bd7)
-- Use same docs requirements in tox (18ecc5a)
-- Add docs/requirements.txt (5f9893f)
-- Add PY37_PLUS, PY38_PLUS, and PY39_PLUS (bee0247)
-- fix 2.6 test, better comment (ddedcb9)
-- fix 2.6 test (3f1ff7e)
-- remove nan test (4dbded1)
-- include list test values (e3f1a12)
-- fix other python2 test issues (c051026)
-- fix missing subTest (f006cad)
-- import from old imp library on older python versions (fc84fa8)
-- replace fstrings with format for python 3.4,3.5 (4a687ea)
-- minor style/spelling fixes (8302d8c)
-- improve cmp function, add unittest (0d95a40)
-- Pin typing==3.7.4.1 for Python 3.3 compatiblity (1a48f1b)
-- Fix various py26 unit test failures (9ca5a14)
-- Add initial contributing guide with docs build instruction (e55f915)
-- Add docs building to tox.ini (3ee9e7f)
-- Support NumPy's specialized int types in builtins.round (b4b54f0)
-- Added r""" to the docstring to avoid warnings in python3 (5f94572)
-- Add __subclasscheck__ for past.types.basestring (c9bc0ff)
-- Correct example in README (681e78c)
-- Add simple documentation (6c6e3ae)
-- Add pre-commit hooks (a9c6a37)
-- Handling of __next__ and next by future.utils.get_next was reversed (52b0ff9)
-- Add a test for our fix (461d77e)
-- Compare headers to correct definition of str (3eaa8fd)
-- #322 Add support for negative ndigits in round; additionally, fixing a bug so that it handles passing in Decimal properly (a4911b9)
-- Add tkFileDialog to future.movers.tkinter (f6a6549)
-- Sort before comparing dicts in TestChainMap (6126997)
-- Fix typo (4dfa099)
-- Fix formatting in "What's new" (1663dfa)
-- Fix typo (4236061)
-- Avoid DeprecationWarning caused by invalid escape (e4b7fa1)
-- Fixup broken link to external django documentation re: porting to Python 3 and unicode_literals (d87713e)
-- Fixed newdict checking version every time (99030ec)
-- Add count from 2.7 to 2.6 (1b8ef51)
-
-What's new in version 0.18.2 (2019-10-30)
-=========================================
-This is a minor bug-fix release containing a number of fixes:
-
-- Fix min/max functions with generators, and 'None' default (PR #514)
-- Use BaseException in raise_() (PR #515)
-- Fix builtins.round() for Decimals (Issue #501)
-- Fix raise_from() to prevent failures with immutable classes (PR #518)
-- Make FixInput idempotent (Issue #427)
-- Fix type in newround (PR #521)
-- Support mimetype guessing in urllib2 for Py3.8+ (Issue #508)
-
-Python 3.8 is not yet officially supported.
-
-What's new in version 0.18.1 (2019-10-09)
-=========================================
-This is a minor bug-fix release containing a fix for raise_() 
-when passed an exception that's not an Exception (e.g. BaseException
-subclasses)
-
-What's new in version 0.18.0 (2019-10-09)
-=========================================
-This is a major bug-fix and feature release, including:
-
-- Fix collections.abc import for py38+
-- Remove import for isnewbytes() function, reducing CPU cost significantly
-- Fix bug with importing past.translation when importing past which breaks zipped python installations
-- Fix an issue with copyreg import under Py3 that results in unexposed stdlib functionality
-- Export and document types in future.utils
-- Update behavior of newstr.__eq__() to match str.__eq__() as per reference docs
-- Fix raising and the raising fixer to handle cases where the syntax is ambiguous
-- Allow "default" parameter in min() and max() (Issue #334)
-- Implement __hash__() in newstr (Issue #454)
-- Future proof some version checks to handle the fact that Py4 won't be a major breaking release
-- Fix urllib.request imports for Python 3.8 compatibility (Issue #447)
-- Fix future import ordering (Issue #445)
-- Fixed bug in fix_division_safe fixture (Issue #434)
-- Do not globally destroy re.ASCII in PY3
-- Fix a bug in email.Message.set_boundary() (Issue #429)
-- Implement format_map() in str
-- Implement readinto() for socket.fp
-
-As well as a number of corrections to a variety of documentation, and updates to
-test infrastructure.
-
-What's new in version 0.17.1 (2018-10-30)
-=========================================
-This release address a packaging error because of an erroneous declaration that
-any built wheels are universal.
-
-What's new in version 0.17.0 (2018-10-19)
-=========================================
-
-This is a major bug-fix release, including:
-
-- Fix ``from collections import ChainMap`` after install_aliases() (issue #226)
-- Fix multiple import from ``__future__`` bug in futurize (issue #113)
-- Add support for proper %s formatting of newbytes
-- Properly implement iterator protocol for newrange object
-- Fix ``past.translation`` on read-only file systems
-- Fix Tkinter import bug introduced in Python 2.7.4 (issue #262)
-- Correct TypeError to ValueError in a specific edge case for newrange
-- Support inequality tests between newstrs and newbytes
-- Add type check to __get__ in newsuper
-- Fix fix_divsion_safe to support better conversion of complex expressions, and
-  skip obvious float division.
-
-As well as a number of corrections to a variety of documentation, and updates to
-test infrastructure.
-
-What's new in version 0.16.0 (2016-10-27)
-==========================================
-
-This release removes the ``configparser`` package as an alias for
-``ConfigParser`` on Py2 to improve compatibility with the backported
-`configparser package <https://pypi.org/project/configparser/>`. Previously
-``python-future`` and the PyPI ``configparser`` backport clashed, causing
-various compatibility issues. (Issues #118, #181)
-
-If your code previously relied on ``configparser`` being supplied by
-``python-future``, the recommended upgrade path is to run ``pip install
-configparser`` or add ``configparser`` to your ``requirements.txt`` file.
-
-Note that, if you are upgrading ``future`` with ``pip``, you may need to
-uninstall the old version of future or manually remove the
-``site-packages/future-0.15.2-py2.7.egg`` folder for this change to take
-effect on your system.
-
-This releases also fixes these bugs:
-
-- Fix ``newbytes`` constructor bug. (Issue #171)
-- Fix semantics of ``bool()`` with ``newobject``. (Issue #211)
-- Fix ``standard_library.install_aliases()`` on PyPy. (Issue #205)
-- Fix assertRaises for ``pow`` and ``compile``` on Python 3.5. (Issue #183)
-- Fix return argument of ``future.utils.ensure_new_type`` if conversion to
-  new type does not exist. (Issue #185)
-- Add missing ``cmp_to_key`` for Py2.6. (Issue #189)
-- Allow the ``old_div`` fixer to be disabled. (Issue #190)
-- Improve compatibility with Google App Engine. (Issue #231)
-- Add some missing imports to the ``tkinter`` and ``tkinter.filedialog``
-  package namespaces. (Issues #212 and #233)
-- More complete implementation of ``raise_from`` on PY3. (Issues #141,
-  #213 and #235, fix provided by Varriount)
-
-
-What's new in version 0.15.2 (2015-09-11)
-=========================================
-
-This is a minor bug-fix release:
-
-- Fix ``socket.create_connection()`` backport on Py2.6 (issue #162)
-- Add more tests of ``urllib.request`` etc.
-- Fix ``newsuper()`` calls from the ``__init__`` method of PyQt subclassses
-  (issue #160, thanks to Christopher Arndt)
-
-What's new in version 0.15.1 (2015-09-09)
-=========================================
-
-This is a minor bug-fix release:
-
-- Use 3-argument ``socket.create_connection()`` backport to restore Py2.6
-  compatibility in ``urllib.request.urlopen()`` (issue #162)
-- Remove breakpoint in ``future.backports.http.client`` triggered on certain
-  data (issue #164)
-- Move ``exec`` fixer to stage 1 of ``futurize`` because the forward-compatible ``exec(a, b)``
-  idiom is supported in Python 2.6 and 2.7. See
-  https://docs.python.org/2/reference/simple_stmts.html#exec.
-
-
-What's new in version 0.15.0 (2015-07-25)
-=========================================
-
-This release fixes compatibility bugs with CherryPy's Py2/3 compat layer and
-the latest version of the ``urllib3`` package. It also adds some additional
-backports for Py2.6 and Py2.7 from Py3.4's standard library.
-
-New features:
-
-- ``install_aliases()`` now exposes full backports of the Py3 urllib submodules
-  (``parse``, ``request`` etc.) from ``future.backports.urllib`` as submodules
-  of ``urllib`` on Py2.  This implies, for example, that
-  ``urllib.parse.unquote`` now takes an optional encoding argument as it does
-  on Py3. This improves compatibility with CherryPy's Py2/3 compat layer (issue
-  #158).
-- ``tkinter.ttk`` support (issue #151)
-- Backport of ``collections.ChainMap`` (issue #150)
-- Backport of ``itertools.count`` for Py2.6 (issue #152)
-- Enable and document support for the ``surrogateescape`` error handler for ``newstr`` and ``newbytes`` objects on Py2.x (issue #116). This feature is currently in alpha.
-- Add constants to ``http.client`` such as ``HTTP_PORT`` and ``BAD_REQUEST`` (issue #137)
-- Backport of ``reprlib.recursive_repr`` to Py2
-
-Bug fixes:
-
-- Add ``HTTPMessage`` to ``http.client``, which is missing from ``httplib.__all__`` on Python <= 2.7.10. This restores compatibility with the latest ``urllib3`` package (issue #159, thanks to Waldemar Kornewald)
-- Expand newint.__divmod__ and newint.__rdivmod__ to fall back to <type 'long'>
-  implementations where appropriate (issue #146 - thanks to Matt Bogosian)
-- Fix newrange slicing for some slice/range combos (issue #132, thanks to Brad Walker)
-- Small doc fixes (thanks to Michael Joseph and Tim Tröndle)
-- Improve robustness of test suite against opening .pyc files as text on Py2
-- Update backports of ``Counter`` and ``OrderedDict`` to use the newer
-  implementations from Py3.4. This fixes ``.copy()`` preserving subclasses etc.
-- ``futurize`` no longer breaks working Py2 code by changing ``basestring`` to
-  ``str``. Instead it imports the ``basestring`` forward-port from
-  ``past.builtins`` (issues #127 and #156)
-- ``future.utils``: add ``string_types`` etc. and update docs (issue #126)
-
 Previous versions
 =================
 

From e32250e67423aaf4fbccd332be8a40fdcdba2711 Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 17:56:40 +1100
Subject: [PATCH 180/183] Update docs pointing to changelog

---
 docs/whatsnew.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index 3c70ecad..d706b2e5 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -27,4 +27,4 @@ This release also includes these fixes:
 Previous versions
 =================
 
-See :ref:`whats-old` for versions prior to v0.15.
+See :ref:`whats-old`.

From a7097f804cd83fe1fd6a50ab088ae1d4ab303246 Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 22:33:43 +1100
Subject: [PATCH 181/183] Update docs

Also use a more modern theme (from pallets-sphinx-themes)
---
 docs/_templates/sidebarintro.html |  2 +-
 docs/_templates/sidebartoc.html   |  2 +-
 docs/conf.py                      |  7 ++++---
 docs/contents.rst.inc             |  8 ++++----
 docs/faq.rst                      | 13 ++++---------
 docs/other/upload_future_docs.sh  |  6 +++---
 docs/requirements.txt             |  2 +-
 7 files changed, 18 insertions(+), 22 deletions(-)

diff --git a/docs/_templates/sidebarintro.html b/docs/_templates/sidebarintro.html
index 1e1a512f..25325ec3 100644
--- a/docs/_templates/sidebarintro.html
+++ b/docs/_templates/sidebarintro.html
@@ -1,7 +1,7 @@
 <!--<h3>Python-Future</h3>
 <p>-->
   <h4>Easy, clean, reliable Python 2/3 compatibility</h4>
-  <a href="https://python-future.org">Table of Contents</a>
+  <a href="https://python-future.org/#contents">Table of Contents</a>
 <!--
 </p>
 <h3>Other Formats</h3>
diff --git a/docs/_templates/sidebartoc.html b/docs/_templates/sidebartoc.html
index 0d119afc..629fb6a1 100644
--- a/docs/_templates/sidebartoc.html
+++ b/docs/_templates/sidebartoc.html
@@ -1 +1 @@
-{{ toctree(maxdepth=theme_globaltoc_depth|toint, collapse=True, includehidden=theme_globaltoc_includehidden|tobool) }}
+{{ toctree(maxdepth=2, collapse=True, includehidden=True) }}
diff --git a/docs/conf.py b/docs/conf.py
index 6f00536b..cf4606c7 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -13,7 +13,7 @@
 
 from __future__ import absolute_import, print_function
 import sys, os
-import sphinx_bootstrap_theme
+# import sphinx_bootstrap_theme
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
@@ -32,6 +32,7 @@
               'sphinx.ext.intersphinx',
               'sphinx.ext.ifconfig',
               'sphinx.ext.viewcode',
+              'pallets_sphinx_themes',
               # 'sphinxcontrib.napoleon'    # see https://sphinxcontrib-napoleon.readthedocs.io/
               # 'sphinx.ext.napoleon'       # use this in Sphinx 1.3+
              ]
@@ -100,8 +101,8 @@
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'bootstrap'
-html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
+html_theme = 'jinja'
+# html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
diff --git a/docs/contents.rst.inc b/docs/contents.rst.inc
index 7c9bbf2f..869b3642 100644
--- a/docs/contents.rst.inc
+++ b/docs/contents.rst.inc
@@ -1,8 +1,8 @@
-Contents:
----------
+Contents
+========
 
 .. toctree::
-   :maxdepth: 2
+   :maxdepth: 3
 
    whatsnew
    overview
@@ -19,7 +19,7 @@ Contents:
    reference
 
 Indices and tables
-------------------
+******************
 
 * :ref:`genindex`
 * :ref:`modindex`
diff --git a/docs/faq.rst b/docs/faq.rst
index 2ac148d3..e49adf61 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -118,10 +118,7 @@ Maturity
 How well has it been tested?
 ----------------------------
 
-``future`` is used by several major projects, including `mezzanine
-<http://mezzanine.jupo.org>`_ and `ObsPy <http://www.obspy.org>`_. It is also
-currently being used to help with porting 800,000 lines of Python 2 code in
-`Sage <http://sagemath.org>`_ to Python 2/3.
+``future`` is used by thousands of projects and has been downloaded over 1.7 billion times. Some projects like Sage have used it to port 800,000+ lines of Python 2 code to Python 2/3.
 
 Currently ``python-future`` has over 1000 unit tests. Many of these are straight
 from the Python 3.3 and 3.4 test suites.
@@ -135,10 +132,8 @@ expanded.
 Is the API stable?
 ------------------
 
-Not yet; ``future`` is still in beta. Where possible, we will try not to break
-anything which was documented and used to work.  After version 1.0 is released,
-the API will not change in backward-incompatible ways until a hypothetical
-version 2.0.
+Yes; ``future`` is mature. We'll make very few changes from here, trying not to
+break anything which was documented and used to work.
 
 ..
     Are there any example of Python 2 packages ported to Python 3 using ``future`` and ``futurize``?
@@ -288,7 +283,7 @@ Support
 Is there a mailing list?
 ------------------------
 
-There was (`python-porting`), but it's now dead.
+There was a `python-porting` mailing list, but it's now dead.
 
 
 .. _contributing:
diff --git a/docs/other/upload_future_docs.sh b/docs/other/upload_future_docs.sh
index c5201f90..04470f3f 100644
--- a/docs/other/upload_future_docs.sh
+++ b/docs/other/upload_future_docs.sh
@@ -9,9 +9,9 @@ touch ~/shared/python-future-html-docs.zip
 rm ~/shared/python-future-html-docs.zip
 zip -r ~/shared/python-future-html-docs.zip *
 
-scp ~/shared/python-future-html-docs.zip python-future.org:
-scp ~/shared/cheatsheet.pdf python-future.org:
-ssh python-future.org
+scp ~/shared/python-future-html-docs.zip ubuntu@python-future.org:
+scp ~/shared/cheatsheet.pdf ubuntu@python-future.org:
+ssh ubuntu@python-future.org
 
 
 # On the remote machine:
diff --git a/docs/requirements.txt b/docs/requirements.txt
index de74d8b2..21e670ea 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,3 +1,3 @@
 sphinx==3.2.1
-sphinx_bootstrap_theme==0.7.1
+Pallets-Sphinx-Themes==2.2.1
 setuptools==65.5.1

From 2bdbfa5664241fee622b23590abd3492f973afc9 Mon Sep 17 00:00:00 2001
From: Ed Schofield <ed@pythoncharmers.com>
Date: Wed, 21 Feb 2024 22:36:09 +1100
Subject: [PATCH 182/183] Tidy up README

---
 README.rst | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.rst b/README.rst
index 6518b9e8..a3aceb7d 100644
--- a/README.rst
+++ b/README.rst
@@ -239,7 +239,7 @@ See :ref:`forwards-conversion` and :ref:`backwards-conversion` for more details.
 
 
 Automatic translation
----------------------
+~~~~~~~~~~~~~~~~~~~~~
 
 The ``past`` package can automatically translate some simple Python 2
 modules to Python 3 upon import. The goal is to support the "long tail" of
@@ -278,7 +278,7 @@ development, and will likely never be perfect.
 
 
 Pre-commit hooks
-----------------
+~~~~~~~~~~~~~~~~
 
 `Pre-commit <https://pre-commit.com/>`_ is a framework for managing and maintaining
 multi-language pre-commit hooks.

From cf21e675e9608287b7f74a38aa922d2f7dc6e87e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 15 Jul 2024 17:31:56 +0000
Subject: [PATCH 183/183] Bump setuptools from 65.5.1 to 70.0.0 in /docs

Bumps [setuptools](https://github.com/pypa/setuptools) from 65.5.1 to 70.0.0.
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/setuptools/compare/v65.5.1...v70.0.0)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 docs/requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/requirements.txt b/docs/requirements.txt
index 21e670ea..265642f4 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,3 +1,3 @@
 sphinx==3.2.1
 Pallets-Sphinx-Themes==2.2.1
-setuptools==65.5.1
+setuptools==70.0.0