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
+```
diff --git a/.gitignore b/.gitignore
index 8c52a551..01c37c9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,8 @@ develop-eggs
.installed.cfg
lib
lib64
+MANIFEST
+MANIFEST.in
# Backup files
*.bak
@@ -41,3 +43,9 @@ nosetests.xml
.mr.developer.cfg
.project
.pydevproject
+
+# PyCharm
+.idea
+
+# Generated test file
+mytempfile.py
diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml
new file mode 100644
index 00000000..dd8d0d65
--- /dev/null
+++ b/.pre-commit-hooks.yaml
@@ -0,0 +1,15 @@
+- 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
+ language_version: python3
+ types: [python]
+ entry: pasteurize -w -n --no-diffs
diff --git a/.travis.yml b/.travis.yml
index 4b74e8d2..3fe6a983 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,45 +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 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
+ - ./build.sh
+ - ./lint.sh
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..678de2e2
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,41 @@
+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
+WORKDIR /root/python-future
+ADD . /root/python-future
diff --git a/README.rst b/README.rst
index ea806538..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 `_ 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
@@ -57,6 +60,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 +266,39 @@ development, and will likely never be perfect.
For more info, see :ref:`translation`.
+Pre-commit hooks
+----------------
+
+`Pre-commit `_ 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: master
+ hooks:
+ - id: futurize
+ args: [--both-stages]
+
+The ``args`` part is optional, by default only stage1 is applied.
+
Licensing
---------
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..df1f00f7
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,17 @@
+# XXX: TODO: we should make this include -e once tests pass
+set -xuo pipefail
+
+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
+
+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)
+done
+
+python setup.py sdist
+python setup.py clean
+echo You may now run: "twine upload dist/*"
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/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
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::
diff --git a/docs/unicode_literals.rst b/docs/unicode_literals.rst
index 7252e4d6..f6eb2839 100644
--- a/docs/unicode_literals.rst
+++ b/docs/unicode_literals.rst
@@ -144,7 +144,7 @@ Others' perspectives
In favour of ``unicode_literals``
*********************************
-Django recommends importing ``unicode_literals`` as its top `porting tip `_ for
+Django recommends importing ``unicode_literals`` as its top `porting tip `_ for
migrating Django extension modules to Python 3. The following `quote
`_ is
from Aymeric Augustin on 23 August 2012 regarding why he chose
diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index e0b4603d..40f7191f 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -3,9 +3,66 @@
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:
+
- Fix min/max functions with generators, and 'None' default (PR #514)
- Use BaseException in raise_() (PR #515)
- Fix builtins.round() for Decimals (Issue #501)
diff --git a/lint.sh b/lint.sh
new file mode 100755
index 00000000..667b258f
--- /dev/null
+++ b/lint.sh
@@ -0,0 +1,3 @@
+# 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
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/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__)
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',
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:
diff --git a/src/future/backports/misc.py b/src/future/backports/misc.py
index 098a0667..992b978f 100644
--- a/src/future/backports/misc.py
+++ b/src/future/backports/misc.py
@@ -46,6 +46,16 @@ def ceil(x):
from itertools import islice
+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
+
+
if PY3:
try:
from _thread import get_ident
@@ -85,6 +95,10 @@ def wrapper(self):
return decorating_function
+# OrderedDict Shim from Raymond Hettinger, python core dev
+# http://code.activestate.com/recipes/576693-ordered-dictionary-for-py24/
+# here to support version 2.6.
+
################################################################################
### OrderedDict
################################################################################
diff --git a/src/future/builtins/newround.py b/src/future/builtins/newround.py
index 394a2c63..b06c1169 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,28 +30,30 @@ 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:
- # 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
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/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/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?')
diff --git a/src/future/types/newdict.py b/src/future/types/newdict.py
index 3f3a559d..d90316cb 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):
"""
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):
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
diff --git a/src/future/utils/__init__.py b/src/future/utils/__init__.py
index 46bd96de..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)
@@ -527,9 +530,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):
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/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/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/src/past/builtins/misc.py b/src/past/builtins/misc.py
index ba50aa9e..3600695c 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 xy.
+ 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('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('cannot compare {type_x}, float("nan") with cmp'.format(type_x=type(x)))
+ 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
@@ -42,7 +103,13 @@ def oct(number):
return '0' + builtins.oct(number)[2:]
raw_input = input
- from imp import reload
+
+ try:
+ from importlib import reload
+ except ImportError:
+ # for python2, python3 <= 3.4
+ from imp import reload
+
unicode = str
unichr = chr
xrange = range
@@ -82,7 +149,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/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/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:
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)
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)
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()
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==')
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()
diff --git a/tests/test_future/test_libfuturize_fixers.py b/tests/test_future/test_libfuturize_fixers.py
index 4ac0b7e1..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):
@@ -767,6 +798,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):
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):
"""
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
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()
diff --git a/tests/test_past/test_misc.py b/tests/test_past/test_misc.py
new file mode 100644
index 00000000..0367b3db
--- /dev/null
+++ b/tests/test_past/test_misc.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+"""
+Tests for the resurrected Py2-like cmp function
+"""
+
+from __future__ import absolute_import, unicode_literals, print_function
+
+import os.path
+import sys
+import traceback
+from contextlib import contextmanager
+
+from future.tests.base import unittest
+from future.utils import PY3, PY26
+
+if PY3:
+ from past.builtins import cmp
+
+_dir = os.path.dirname(os.path.abspath(__file__))
+sys.path.append(_dir)
+import test_values
+
+
+@contextmanager
+def empty_context_manager(*args, **kwargs):
+ yield 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:
+ if PY26:
+ # 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
+ with getattr(self, 'subTest', empty_context_manager)(x=x, y=y):
+ try:
+ past_cmp_value = cmp(x, y)
+ except Exception:
+ 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..11872084
--- /dev/null
+++ b/tests/test_past/test_values.py
@@ -0,0 +1,225 @@
+from math import pi
+
+inf, nan = float('inf'), float('nan')
+test_values = [
+ 0, 1, 2, -1, -9999999999, 9999999,
+ 0.0, inf, 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, 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, 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, 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, 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, '', -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, 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], [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, 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, 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, [], -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], [[], 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], 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], ['', 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],
+ ['', 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], [' ', 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],
+ ['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([]), 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]), 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}, 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],
+ [{'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}, 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}, 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, 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]]
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index f5c013f8..00000000
--- a/tox.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[tox]
-envlist = py26,py27,py33,py34,py35,py36,py37
-
-[testenv]
-deps =
- pytest
- unittest2
- py26: importlib
-commands = pytest {posargs}