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/.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.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..bcefff65
--- /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@v4
+ - 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
diff --git a/.gitignore b/.gitignore
index 52146838..1b8eaeb5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,12 @@ develop-eggs
.installed.cfg
lib
lib64
+MANIFEST
+MANIFEST.in
+
+# Backup files
+*.bak
+*.backup
# Installer logs
pip-log.txt
@@ -38,3 +44,8 @@ nosetests.xml
.project
.pydevproject
+# PyCharm / IntelliJ
+.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
deleted file mode 100644
index 7b0fa4ce..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-language: python
-python:
- - "3.3"
- - "2.7"
- - "2.6"
-# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors
-# These packages only exist on Ubuntu 13.04 and newer:
-# before_install:
-# - sudo apt-get install -qq libpython2.7-testsuite libpython3.3-testsuite
-# No dependencies currently unless using Python 2.6.
-install:
- - if [[ $TRAVIS_PYTHON_VERSION == 2.6* ]]; then pip install -r requirements_py26.txt --use-mirrors; fi
-# command to run tests, e.g. python setup.py test
-script: python setup.py test
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
new file mode 100644
index 00000000..c859757f
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,7 @@
+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/LICENSE.txt b/LICENSE.txt
index 65c70446..275cafd3 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2013-2014 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/MANIFEST.in b/MANIFEST.in
index 9cfb784c..d0e9f3d1 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,8 +1,27 @@
-include README.rst
-include futurize.py
-include discover_tests.py
-include LICENSE.txt
-include docs/*.py
-include docs/*.txt
-include docs/*.rst
-
+include *.sh
+include *.txt
+include *.rst
+include *.py
+include .travis.yml
+include pytest.ini
+recursive-include docs LICENSE
+recursive-include docs README
+recursive-include docs *.conf
+recursive-include docs *.css_t
+recursive-include docs *.html
+recursive-include docs *.ico
+recursive-include docs *.inc
+recursive-include docs *.ipynb
+recursive-include docs *.png
+recursive-include docs *.py
+recursive-include docs *.rst
+recursive-include docs *.sh
+recursive-include docs *.tiff
+recursive-include docs *.txt
+recursive-include docs Makefile
+recursive-include src *.py
+recursive-include src *.pem
+recursive-include tests *.au
+recursive-include tests *.gif
+recursive-include tests *.py
+recursive-include tests *.txt
diff --git a/README.rst b/README.rst
index fbff0c4c..a3aceb7d 100644
--- a/README.rst
+++ b/README.rst
@@ -1,40 +1,60 @@
.. _overview:
-Overview: an easier, safer, cleaner upgrade path to Python 3
-============================================================
+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
-``python-future`` is the missing compatibility layer between Python 3 and
-Python 2. It allows you to maintain a single, clean Python 3.x-compatible
-codebase with minimal cruft and run it easily on Python 2 mostly unchanged.
+``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.
-It provides ``future`` and ``past`` packages with backports and forward ports of
-features from Python 3 and 2. It also comes with ``futurize`` and
-``pasteurize``, customized 2to3-based scripts that helps you to convert either
-Py2 or Py3 code easily to support both Python 2 and 3 in a single clean
-Py3-style codebase, module by module.
+It provides ``future`` and ``past`` packages with backports and forward
+ports of features from Python 3 and 2. It also comes with ``futurize`` and
+``pasteurize``, customized 2to3-based scripts that helps you to convert
+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 ``future`` for Python 3/2 compatibility are `Mezzanine
-`_ and `ObsPy `_.
+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:
Features
--------
-- ``future.builtins`` package provides backports and remappings for 19
- builtins with different semantics on Py3 versus Py2
+- ``future.builtins`` package (also available as ``builtins`` on Py2) provides
+ backports and remappings for 20 builtins with different semantics on Py3
+ versus Py2
-- ``future.standard_library`` package provides backports and remappings from
- the Py3 standard library
+- support for directly importing 30 standard library modules under
+ their Python 3 names on Py2
-- ``past.builtins`` package provides forward-ports of Python 2 types and
- resurrects some Python 2 builtins (to aid with per-module code migrations)
+- support for importing the other 14 refactored standard library modules
+ under their Py3 names relatively cleanly via
+ ``future.standard_library`` and ``future.moves``
+
+- ``past.builtins`` package provides forward-ports of 19 Python 2 types and
+ builtin functions. These can aid with per-module code migrations.
- ``past.translation`` package supports transparent translation of Python 2
- modules to Python 3 upon import. [This feature is currently in alpha.]
+ modules to Python 3 upon import. [This feature is currently in alpha.]
-- 640+ unit tests, including many from the Py3.3 source tree.
+- 1000+ unit tests, including many from the Py3.3 source tree.
- ``futurize`` and ``pasteurize`` scripts based on ``2to3`` and parts of
``3to2`` and ``python-modernize``, for automatic conversion from either Py2
@@ -45,6 +65,11 @@ Features
``past.utils`` selected from Py2/3 compatibility interfaces from projects
like ``six``, ``IPython``, ``Jinja2``, ``Django``, and ``Pandas``.
+- support for the ``surrogateescape`` error handler when encoding and
+ decoding the backported ``str`` and ``bytes`` objects. [This feature is
+ currently in alpha.]
+
+- support for pre-commit hooks
.. _code-examples:
@@ -54,11 +79,13 @@ Code examples
Replacements for Py2's built-in functions and types are designed to be imported
at the top of each Python module together with Python's built-in ``__future__``
statements. For example, this code behaves identically on Python 2.6/2.7 after
-these imports as it does on Python 3.3+::
-
+these imports as it does on Python 3.3+:
+
+.. code-block:: python
+
from __future__ import absolute_import, division, print_function
- from future.builtins import (bytes, str, open, super, range,
- zip, round, input, int, pow, object)
+ from builtins import (bytes, str, open, super, range,
+ zip, round, input, int, pow, object)
# Backported Py3 bytes object
b = bytes(b'ABCD')
@@ -80,8 +107,8 @@ these imports as it does on Python 3.3+::
# Extra arguments for the open() function
f = open('japanese.txt', encoding='utf-8', errors='replace')
-
- # New simpler super() function:
+
+ # New zero-argument super() function:
class VerboseList(list):
def append(self, item):
print('Adding an item')
@@ -90,29 +117,29 @@ these imports as it does on Python 3.3+::
# New iterable range object with slicing support
for i in range(10**15)[:10]:
pass
-
+
# Other iterators: map, zip, filter
my_iter = zip(range(3), ['a', 'b', 'c'])
assert my_iter != list(my_iter)
-
+
# The round() function behaves as it does in Python 3, using
# "Banker's Rounding" to the nearest even last digit:
assert round(0.1250, 2) == 0.12
-
+
# input() replaces Py2's raw_input() (with no eval()):
name = input('What is your name? ')
print('Hello ' + name)
+ # pow() supports fractional exponents of negative numbers like in Py3:
+ z = pow(-1, 0.5)
+
# Compatible output from isinstance() across Py2/3:
assert isinstance(2**64, int) # long integers
assert isinstance(u'blah', str)
assert isinstance('blah', str) # only if unicode_literals is in effect
- # pow() supports fractional exponents of negative numbers like in Py3:
- z = pow(-1, 0.5)
-
# Py3-style iterators written as new-style classes (subclasses of
- # future.builtins.object) are backward compatibile with Py2:
+ # future.types.newobject) are automatically backward compatible with Py2:
class Upper(object):
def __init__(self, iterable):
self._iter = iter(iterable)
@@ -123,24 +150,37 @@ these imports as it does on Python 3.3+::
assert list(Upper('hello')) == list('HELLO')
-There is also support for renamed standard library modules in the form of import
-hooks. The context-manager form works like this::
+There is also support for renamed standard library modules. The recommended
+interface works like this:
+.. code-block:: python
+
+ # Many Py3 module names are supported directly on both Py2.x and 3.x:
+ from http.client import HttpConnection
+ import html.parser
+ import queue
+ import xmlrpc.client
+
+ # Refactored modules with clashing names on Py2 and Py3 are supported
+ # as follows:
from future import standard_library
+ standard_library.install_aliases()
- with standard_library.hooks():
- from http.client import HttpConnection
- from itertools import filterfalse
- import html.parser
- import queue
+ # Then, for example:
+ from itertools import filterfalse, zip_longest
+ from urllib.request import urlopen
+ from collections import ChainMap
+ from collections import UserDict, UserList, UserString
+ from subprocess import getoutput, getstatusoutput
+ from collections import Counter, OrderedDict # backported to Py2.6
-Automatic conversion to Py3/2-compatible code
-=============================================
+Automatic conversion to Py2/3-compatible code
+---------------------------------------------
-``future`` comes with two scripts called ``futurize`` and
+``python-future`` comes with two scripts called ``futurize`` and
``pasteurize`` to aid in making Python 2 code or Python 3 code compatible with
-both platforms (Py2&3). It is based on 2to3 and uses fixers from ``lib2to3``,
+both platforms (Py2/3). It is based on 2to3 and uses fixers from ``lib2to3``,
``lib3to2``, and ``python-modernize``, as well as custom fixers.
``futurize`` passes Python 2 code through all the appropriate fixers to turn it
@@ -155,55 +195,72 @@ the top of each module.
In both cases, the result should be relatively clean Py3-style code that runs
mostly unchanged on both Python 2 and Python 3.
-.. _forwards-conversion:
-
Futurize: 2 to both
---------------------
+~~~~~~~~~~~~~~~~~~~
-For example, running ``futurize -w mymodule.py`` turns this Python 2 code::
-
- import ConfigParser
+For example, running ``futurize -w mymodule.py`` turns this Python 2 code:
- class Blah(object):
- pass
- print 'Hello',
+.. code-block:: python
+
+ import Queue
+ from urllib2 import urlopen
+
+ def greet(name):
+ print 'Hello',
+ print name
+
+ print "What's your name?",
+ name = raw_input()
+ greet(name)
+
+into this code which runs on both Py2 and Py3:
+
+.. code-block:: python
-into this code which runs on both Py2 and Py3::
-
from __future__ import print_function
from future import standard_library
-
- import configparser
+ standard_library.install_aliases()
+ from builtins import input
+ import queue
+ from urllib.request import urlopen
- class Blah(object):
- pass
- print('Hello', end=' ')
+ def greet(name):
+ print('Hello', end=' ')
+ print(name)
-For complex projects, it may be better to divide the porting into two stages.
-``futurize`` supports a ``--stage1`` flag for safe changes that modernize the
-code but do not break Python 2.6 compatibility or introduce a depdendency on the
-``future`` package. Calling ``futurize --stage2`` then completes the process.
+ print("What's your name?", end=' ')
+ 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.
Automatic translation
----------------------
+~~~~~~~~~~~~~~~~~~~~~
-The ``past`` package can now automatically translate some simple Python 2
+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
real-world Python 2 modules (e.g. on PyPI) that have not been ported yet. For
example, here is how to use a Python 2-only package called ``plotrique`` on
-Python 3. First install it::
+Python 3. First install it:
+
+.. code-block:: bash
$ pip3 install plotrique==0.2.5-7 --no-compile # to ignore SyntaxErrors
-
+
(or use ``pip`` if this points to your Py3 environment.)
Then pass a whitelist of module name prefixes to the ``autotranslate()`` function.
-Example::
-
+Example:
+
+.. code-block:: bash
+
$ python3
- >>> from past import autotranslate
+ >>> from past.translation import autotranslate
>>> autotranslate(['plotrique'])
>>> import plotrique
@@ -216,24 +273,67 @@ last resort; ideally Python 2-only dependencies should be ported
properly to a Python 2/3 compatible codebase using a tool like
``futurize`` and the changes should be pushed to the upstream project.
-Note: the translation feature is still in alpha and needs more testing and
-development.
+Note: the auto-translation feature is still in alpha; it needs more testing and
+development, and will likely never be perfect.
-Next steps
-----------
-Check out the `Quickstart Guide `_.
+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:
-Credits and Licensing
----------------------
+.. code-block:: bash
-:Author: Ed Schofield
-:Sponsor: Python Charmers Pty Ltd, Australia, and Python Charmers Pte
- Ltd, Singapore. http://pythoncharmers.com
-:Others: See `Credits `_.
+ $ pre-commit install
-Copyright 2013-2014 Python Charmers Pty Ltd, Australia.
+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
+---------
+
+:Author: Ed Schofield, Jordan M. Adler, et al
+
+:Copyright: 2013-2024 Python Charmers, Australia.
+
+:Sponsors: Python Charmers: https://pythoncharmers.com
+
+ Pinterest https://opensource.pinterest.com
+
+:Licence: MIT. See ``LICENSE.txt`` or `here `_.
+
+:Other credits: See `here `_.
+
+Docs
+----
+See the docs `here `_.
+
+Next steps
+----------
-The software is distributed under an MIT licence. See LICENSE.txt or `Licensing
-`_.
+If you are new to Python-Future, check out the `Quickstart Guide
+`_.
+For an update on changes in the latest version, see the `What's New
+`_ page.
diff --git a/TESTING.txt b/TESTING.txt
index ff8d7002..b2ad5c65 100644
--- a/TESTING.txt
+++ b/TESTING.txt
@@ -1,10 +1,11 @@
-Currently three tests have errors on Py2.7 with module import errors (http.client
-and test.support) when the test suite is run with:
+A docker image, python-future-builder, is used to do testing and building. The test suite can be run with:
- $ python setup.py test
+ $ bash build.sh
-which uses the unittest module's test discovery mechanism.
-
-These same tests pass when running the test suite with:
+which tests the module under a number of different python versions, where available, or with:
$ py.test
+
+To execute a single test:
+
+ $ pytest -k test_chained_exceptions_stacktrace
diff --git a/discover_tests.py b/discover_tests.py
deleted file mode 100644
index 2617b1c9..00000000
--- a/discover_tests.py
+++ /dev/null
@@ -1,54 +0,0 @@
-"""
-Simple auto test discovery.
-
-From http://stackoverflow.com/a/17004409
-"""
-import os
-import sys
-import unittest
-
-if not hasattr(unittest.defaultTestLoader, 'discover'):
- import unittest2 as unittest
-
-def additional_tests():
- setup_file = sys.modules['__main__'].__file__
- setup_dir = os.path.abspath(os.path.dirname(setup_file))
- testsuite = unittest.defaultTestLoader.discover(setup_dir)
- blacklist = []
- if '/home/travis' in __file__:
- # Skip some tests that fail on travis-ci
- blacklist.append('test_command')
- return exclude_tests(testsuite, blacklist)
-
-
-class SkipCase(unittest.TestCase):
- def skeleton_run_test(self):
- raise unittest.SkipTest("Test fails spuriously on travis-ci")
-
-
-def exclude_tests(suite, blacklist):
- """
- Example:
-
- blacklist = [
- 'test_some_test_that_should_be_skipped',
- 'test_another_test_that_should_be_skipped'
- ]
- """
- new_suite = unittest.TestSuite()
-
- for test_group in suite._tests:
- for test in test_group:
- if not hasattr(test, '_tests'):
- # e.g. ModuleImportFailure
- new_suite.addTest(test)
- continue
- for subtest in test._tests:
- method = subtest._testMethodName
- if method in blacklist:
- setattr(test,
- method,
- getattr(SkipCase(), 'skeleton_run_test'))
- new_suite.addTest(test)
- return new_suite
-
diff --git a/docs/3rd-party-py3k-compat-code/ipython_py3compat.py b/docs/3rd-party-py3k-compat-code/ipython_py3compat.py
index f80a6963..c9fbb2c1 100755
--- a/docs/3rd-party-py3k-compat-code/ipython_py3compat.py
+++ b/docs/3rd-party-py3k-compat-code/ipython_py3compat.py
@@ -41,9 +41,9 @@ def wrapper(func_or_str):
else:
func = func_or_str
doc = func.__doc__
-
+
doc = str_change_func(doc)
-
+
if func:
func.__doc__ = doc
return func
@@ -52,97 +52,97 @@ def wrapper(func_or_str):
if sys.version_info[0] >= 3:
PY3 = True
-
+
input = input
builtin_mod_name = "builtins"
-
+
str_to_unicode = no_code
unicode_to_str = no_code
str_to_bytes = encode
bytes_to_str = decode
cast_bytes_py2 = no_code
-
+
def isidentifier(s, dotted=False):
if dotted:
return all(isidentifier(a) for a in s.split("."))
return s.isidentifier()
-
+
open = orig_open
-
+
MethodType = types.MethodType
-
+
def execfile(fname, glob, loc=None):
loc = loc if (loc is not None) else glob
exec compile(open(fname, 'rb').read(), fname, 'exec') in glob, loc
-
+
# Refactor print statements in doctests.
_print_statement_re = re.compile(r"\bprint (?P.*)$", re.MULTILINE)
def _print_statement_sub(match):
expr = match.groups('expr')
return "print(%s)" % expr
-
+
@_modify_str_or_docstring
def doctest_refactor_print(doc):
"""Refactor 'print x' statements in a doctest to print(x) style. 2to3
unfortunately doesn't pick up on our doctests.
-
+
Can accept a string or a function, so it can be used as a decorator."""
return _print_statement_re.sub(_print_statement_sub, doc)
-
+
# Abstract u'abc' syntax:
@_modify_str_or_docstring
def u_format(s):
""""{u}'abc'" --> "'abc'" (Python 3)
-
+
Accepts a string or a function, so it can be used as a decorator."""
return s.format(u='')
else:
PY3 = False
-
+
input = raw_input
builtin_mod_name = "__builtin__"
-
+
str_to_unicode = decode
unicode_to_str = encode
str_to_bytes = no_code
bytes_to_str = no_code
cast_bytes_py2 = cast_bytes
-
+
import re
_name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
def isidentifier(s, dotted=False):
if dotted:
return all(isidentifier(a) for a in s.split("."))
return bool(_name_re.match(s))
-
+
class open(object):
"""Wrapper providing key part of Python 3 open() interface."""
def __init__(self, fname, mode="r", encoding="utf-8"):
self.f = orig_open(fname, mode)
self.enc = encoding
-
+
def write(self, s):
return self.f.write(s.encode(self.enc))
-
+
def read(self, size=-1):
return self.f.read(size).decode(self.enc)
-
+
def close(self):
return self.f.close()
-
+
def __enter__(self):
return self
-
+
def __exit__(self, etype, value, traceback):
self.f.close()
-
+
def MethodType(func, instance):
return types.MethodType(func, instance, type(instance))
-
+
# don't override system execfile on 2.x:
execfile = execfile
-
+
def doctest_refactor_print(func_or_str):
return func_or_str
@@ -151,7 +151,7 @@ def doctest_refactor_print(func_or_str):
@_modify_str_or_docstring
def u_format(s):
""""{u}'abc'" --> "u'abc'" (Python 2)
-
+
Accepts a string or a function, so it can be used as a decorator."""
return s.format(u='u')
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/3rd-party-py3k-compat-code/pandas_py3k.py b/docs/3rd-party-py3k-compat-code/pandas_py3k.py
index 6070c0e9..2a8eb5ae 100755
--- a/docs/3rd-party-py3k-compat-code/pandas_py3k.py
+++ b/docs/3rd-party-py3k-compat-code/pandas_py3k.py
@@ -14,7 +14,7 @@
* Uses the original method if available, otherwise uses items, keys, values.
* types:
* text_type: unicode in Python 2, str in Python 3
- * binary_type: str in Python 2, bythes in Python 3
+ * binary_type: str in Python 2, bytes in Python 3
* string_types: basestring in Python 2, str in Python 3
* bind_method: binds functions to classes
diff --git a/docs/_static/python-future-icon-32.ico b/docs/_static/python-future-icon-32.ico
new file mode 100644
index 00000000..e3f2cf7f
Binary files /dev/null and b/docs/_static/python-future-icon-32.ico differ
diff --git a/docs/_static/python-future-icon-white-32.ico b/docs/_static/python-future-icon-white-32.ico
new file mode 100644
index 00000000..3fa3dab9
Binary files /dev/null and b/docs/_static/python-future-icon-white-32.ico differ
diff --git a/docs/_static/python-future-logo-textless-transparent.png b/docs/_static/python-future-logo-textless-transparent.png
new file mode 100644
index 00000000..95ba682d
Binary files /dev/null and b/docs/_static/python-future-logo-textless-transparent.png differ
diff --git a/docs/_templates/navbar.html b/docs/_templates/navbar.html
index 7edada2a..fc96b5ca 100644
--- a/docs/_templates/navbar.html
+++ b/docs/_templates/navbar.html
@@ -12,7 +12,6 @@
-
{% if theme_navbar_title -%}{{ theme_navbar_title|e }}{%- else -%}{{ project|e }}{%- endif -%}
{{ version|e }}
diff --git a/docs/_templates/sidebarintro.html b/docs/_templates/sidebarintro.html
index 296f74b1..25325ec3 100644
--- a/docs/_templates/sidebarintro.html
+++ b/docs/_templates/sidebarintro.html
@@ -1,6 +1,7 @@
- Tools and help for an easier, safer, cleaner upgrade path to Python 3.
+ Easy, clean, reliable Python 2/3 compatibility
+ Table of Contents
-Useful Links
+
diff --git a/docs/_templates/sidebartoc.html b/docs/_templates/sidebartoc.html
new file mode 100644
index 00000000..629fb6a1
--- /dev/null
+++ b/docs/_templates/sidebartoc.html
@@ -0,0 +1 @@
+{{ toctree(maxdepth=2, collapse=True, includehidden=True) }}
diff --git a/docs/_themes/future/static/future.css_t b/docs/_themes/future/static/future.css_t
index 6130f5c3..593da466 100644
--- a/docs/_themes/future/static/future.css_t
+++ b/docs/_themes/future/static/future.css_t
@@ -14,11 +14,11 @@
{% set sidebar_width = '220px' %}
{% set font_family = 'Geneva, sans serif' %}
{% set header_font_family = 'Oxygen, ' ~ font_family %}
-
+
@import url("basic.css");
-
+
/* -- page layout ----------------------------------------------------------- */
-
+
body {
font-family: {{ font_family }};
font-size: 17px;
@@ -49,7 +49,7 @@ div.sphinxsidebar {
hr {
border: 1px solid #B1B4B6;
}
-
+
div.body {
background-color: #ffffff;
color: #3E4349;
@@ -60,7 +60,7 @@ img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
-
+
div.footer {
width: {{ page_width }};
margin: 20px auto 30px auto;
@@ -76,7 +76,7 @@ div.footer a {
div.related {
display: none;
}
-
+
div.sphinxsidebar a {
color: #444;
text-decoration: none;
@@ -86,7 +86,7 @@ div.sphinxsidebar a {
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
-
+
div.sphinxsidebar {
font-size: 15px;
line-height: 1.5;
@@ -101,7 +101,7 @@ div.sphinxsidebarwrapper p.logo {
margin: 0;
text-align: center;
}
-
+
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: {{ font_family }};
@@ -115,7 +115,7 @@ div.sphinxsidebar h4 {
div.sphinxsidebar h4 {
font-size: 20px;
}
-
+
div.sphinxsidebar h3 a {
color: #444;
}
@@ -126,7 +126,7 @@ div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
-
+
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
@@ -137,7 +137,7 @@ div.sphinxsidebar ul {
padding: 0;
color: #000;
}
-
+
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: {{ font_family }};
@@ -147,19 +147,19 @@ div.sphinxsidebar input {
div.sphinxsidebar form.search input[name="q"] {
width: 130px;
}
-
+
/* -- body styles ----------------------------------------------------------- */
-
+
a {
color: #aa0000;
text-decoration: underline;
}
-
+
a:hover {
color: #dd0000;
text-decoration: underline;
}
-
+
div.body h1,
div.body h2,
div.body h3,
@@ -172,25 +172,25 @@ div.body h6 {
padding: 0;
color: black;
}
-
+
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
-
+
a.headerlink {
color: #ddd;
padding: 0 4px;
text-decoration: none;
}
-
+
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
-
+
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
@@ -237,20 +237,20 @@ div.note {
background-color: #eee;
border: 1px solid #ccc;
}
-
+
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
-
+
div.topic {
background-color: #eee;
}
-
+
p.admonition-title {
display: inline;
}
-
+
p.admonition-title:after {
content: ":";
}
@@ -344,7 +344,7 @@ ul, ol {
margin: 10px 0 10px 30px;
padding: 0;
}
-
+
pre {
background: #eee;
padding: 7px 30px;
@@ -361,7 +361,7 @@ dl dl pre {
margin-left: -90px;
padding-left: 90px;
}
-
+
tt {
background-color: #E8EFF0;
color: #222;
diff --git a/docs/automatic_conversion.rst b/docs/automatic_conversion.rst
index 8bb7c99b..5c718da5 100644
--- a/docs/automatic_conversion.rst
+++ b/docs/automatic_conversion.rst
@@ -1,12 +1,12 @@
.. _automatic-conversion:
-Automatic conversion to Py2/3 with ``futurize`` and ``pasteurize``
-==================================================================
+Automatic conversion to Py2/3
+=============================
The ``future`` source tree includes scripts called ``futurize`` and
``pasteurize`` to aid in making Python 2 code or Python 3 code compatible with
-both platforms (Py2&3) using the :mod:`future` module. It is based on 2to3 and
-uses fixers from ``lib2to3``, ``lib3to2``, and ``python-modernize``.
+both platforms (Py2/3) using the :mod:`future` module. These are based on
+``lib2to3`` and use fixers from ``2to3``, ``3to2``, and ``python-modernize``.
``futurize`` passes Python 2 code through all the appropriate fixers to turn it
into valid Python 3 code, and then adds ``__future__`` and ``future`` package
@@ -19,276 +19,11 @@ instead. This converts Py3-only constructs (e.g. new metaclass syntax) and adds
In both cases, the result should be relatively clean Py3-style code that runs
mostly unchanged on both Python 2 and Python 3.
-.. _forwards-conversion:
-Futurize: 2 to both
---------------------
+.. include:: futurize.rst
-For example, running ``futurize`` turns this Python 2 code::
-
- import ConfigParser
-
- class Blah(object):
- pass
- print 'Hello',
-
-into this code which runs on both Py2 and Py3::
-
- from __future__ import print_function
- from future import standard_library
-
- import configparser
-
- class Blah(object):
- pass
- print('Hello', end=' ')
-
-
-To write out all the changes to your Python files that ``futurize`` suggests,
-use the ``-w`` flag.
-
-For complex projects, it may be better to divide the porting into two stages.
-Stage 1 is for "safe" changes that modernize the code but do not break Python
-2.6 compatibility or introduce a depdendency on the ``future`` package. Stage 2
-is to complete the process.
-
-
-.. _forwards-conversion-stage1:
-
-Stage 1: "safe" fixes
-~~~~~~~~~~~~~~~~~~~~~
-
-Run with::
-
- futurize --stage1
-
-This applies fixes that modernize Python 2 code without changing the effect of
-the code. With luck, this will not introduce any bugs into the code, or will at
-least be trivial to fix. The changes are those that bring the Python code
-up-to-date without breaking Py2 compatibility. The resulting code will be
-modern Python 2.6-compatible code plus ``__future__`` imports from the
-following set::
-
- from __future__ import absolute_import
- from __future__ import division
- from __future__ import print_function
-
-Only those ``__future__`` imports deemed necessary will be added unless
-the ``--all-imports`` command-line option is passed to ``futurize``, in
-which case they are all added.
-
-The ``from __future__ import unicode_literals`` declaration is not added
-unless the ``--unicode-literals`` flag is passed to ``futurize``.
-
-The changes include::
-
- - except MyException, e:
- + except MyException as e:
-
- - print >>stderr, "Blah"
- + from __future__ import print_function
- + print("Blah", stderr)
-
-Implicit relative imports fixed, e.g.::
-
- - import mymodule
- + from __future__ import absolute_import
- + from . import mymodule
-
-.. and all unprefixed string literals '...' gain a b prefix to be b'...'.
-
-.. (This last step can be prevented using --no-bytes-literals if you already have b'...' markup in your code, whose meaning would otherwise be lost.)
-
-Stage 1 does not add any imports from the ``future`` package. The output of
-stage 1 will probably not (yet) run on Python 3.
-
-The goal for this stage is to create most of the ``diff`` for the entire
-porting process, but without introducing any bugs. It should be uncontroversial
-and safe to apply to every Python 2 package. The subsequent patches introducing
-Python 3 compatibility should then be shorter and easier to review.
-
-
-.. _forwards-conversion-stage2:
-
-Stage 2: Py3-style code with ``future`` wrappers for Py2
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Run with::
-
- futurize —-stage2 myfolder/*.py
-
-This stage adds a dependency on the ``future`` package. The goal for stage 2 is
-to make further mostly safe changes to the Python 2 code to use Python 3-style
-code that then still runs on Python 2 with the help of the appropriate builtins
-and utilities in ``future``.
-
-For example::
-
- name = raw_input('What is your name?\n')
-
- for k, v in d.iteritems():
- assert isinstance(v, basestring)
-
- class MyClass(object):
- def __unicode__(self):
- return u'My object'
- def __str__(self):
- return unicode(self).encode('utf-8')
-
-would be converted by Stage 2 to this code::
-
- from future.builtins import input
- from future.builtins import str
- from future.utils import iteritems, python_2_unicode_compatible
-
- name = input('What is your name?\n')
-
- for k, v in iteritems(d):
- assert isinstance(v, (str, bytes))
-
- @python_2_unicode_compatible
- class MyClass(object):
- def __str__(self):
- return u'My object'
-
-Stage 2 also renames standard-library imports to their Py3 names and adds these
-two lines::
-
- from future import standard_library
- standard_library.install_hooks()
-
-For example::
-
- import ConfigParser
-
-becomes::
-
- from future import standard_library
- standard_library.install_hooks()
- import ConfigParser
-
-
-Ideally the output of this stage should not be a ``SyntaxError`` on either
-Python 3 or Python 2.
-
-After this, you can run your tests on Python 3 and make further code changes
-until they pass on Python 3.
-
-The next step would be manually adding some decorators from ``future`` to
-e-enable Python 2 compatibility. See :ref:`what-else` for more info.
-
-
-
-.. _forwards-conversion-text:
-
-Separating text from bytes
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-After applying stage 2, the recommended step is to decide which of your Python
-2 strings represent text and which represent binary data and to prefix all
-string literals with either ``b`` or ``u`` accordingly. Furthermore, to ensure
-that these types behave similarly on Python 2 as on Python 3, also wrap
-byte-strings or text in the ``bytes`` and ``str`` types from ``future``. For
-example::
-
- from future.builtins import bytes, str
- b = bytes(b'\x00ABCD')
- s = str(u'This is normal text')
-
-Any unadorned string literals will then represent native platform strings
-(byte-strings on Py2, unicode strings on Py3).
-
-An alternative is to pass the ``--unicode_literals`` flag::
-
- $ futurize --unicode_literals mypython2script.py
-
-After runnign this, all string literals that were not explicitly marked up as
-``b''`` will mean text (Python 3 ``str`` or Python 2 ``unicode``).
-
-
-.. _forwards-conversion-stage3:
-
-Post-conversion
-~~~~~~~~~~~~~~~
-
-After running ``futurize``, we recommend first getting the tests passing on
-Py3, and then on Py2 again with the help of the ``future`` package.
-
-
-.. _backwards-conversion:
-
-Pasteurize: 3 to both
---------------------
-
-Running ``pasteurize -w mypy3module.py`` turns this Python 3 code::
-
- import configparser
-
- class Blah:
- pass
- print('Hello', end=None)
-
-into this code which runs on both Py2 and Py3::
-
- from __future__ import print_function
- from future import standard_library
- standard_library.install_hooks()
-
- import configparser
-
- class Blah(object):
- pass
- print('Hello', end=None)
-
-Notice that both ``futurize`` and ``pasteurize`` create explicit new-style
-classes that inherit from ``object`` on both Python versions, and both
-refer to stdlib modules (as well as builtins) under their Py3 names.
-
-``pasteurize`` also handles the following Python 3 features:
-
-- keyword-only arguments
-- metaclasses (using :func:`~future.utils.with_metaclass`)
-- extended tuple unpacking (PEP 3132)
-
-To handle function annotations (PEP 3107), see :ref:`func_annotations`.
-
-
-How well do ``futurize`` and ``pasteurize`` work?
--------------------------------------------------
-
-They are still incomplete and make some mistakes, like 2to3, on which they are
-based.
-
-Nevertheless, ``futurize`` and ``pasteurize`` are useful to automate much of the
-work of porting, particularly the boring repetitive text substitutions. They also
-help to flag which parts of the code require attention.
-
-Please report bugs on `GitHub
-`_.
-
-Contributions to the ``lib2to3``-based fixers for ``futurize`` and
-``pasteurize`` are particularly welcome! Please see :ref:`contributing`.
-
-
-.. _futurize-limitations
-
-Known limitations of ``futurize``
----------------------------------
-
-``futurize`` doesn't currently make any of these changes automatically::
-
-1. A source encoding declaration line like::
-
- # -*- coding:utf-8 -*-
-
- is not kept at the top of a file. It must be moved manually back to line 1 to take effect.
-
-2. Strings containing ``\U`` produce a ``SyntaxError`` on Python 3. An example is::
-
- s = 'C:\Users'.
-
- Python 2 expands this to ``s = 'C:\\Users'``, but Python 3 requires a raw
- prefix (``r'...'``). This also applies to multi-line strings (including
- multi-line docstrings).
+.. include:: futurize_cheatsheet.rst
+.. include:: pasteurize.rst
+.. include:: conversion_limitations.rst
diff --git a/docs/bind_method.rst b/docs/bind_method.rst
index 7eb91a43..d737384c 100644
--- a/docs/bind_method.rst
+++ b/docs/bind_method.rst
@@ -9,10 +9,10 @@ from the language. To bind a method to a class compatibly across Python
3 and Python 2, you can use the :func:`bind_method` helper function::
from future.utils import bind_method
-
+
class Greeter(object):
pass
-
+
def greet(self, message):
print(message)
@@ -24,6 +24,6 @@ from the language. To bind a method to a class compatibly across Python
On Python 3, calling ``bind_method(cls, name, func)`` is equivalent to
calling ``setattr(cls, name, func)``. On Python 2 it is equivalent to::
-
+
import types
setattr(cls, name, types.MethodType(func, None, cls))
diff --git a/docs/bytes_object.rst b/docs/bytes_object.rst
index f4567907..110280ad 100644
--- a/docs/bytes_object.rst
+++ b/docs/bytes_object.rst
@@ -11,11 +11,10 @@ Python 2's :class:`str`, rather than a true implementation of the Python
:mod:`future` contains a backport of the :mod:`bytes` object from Python 3
which passes most of the Python 3 tests for :mod:`bytes`. (See
-``future/tests/test_bytes.py`` in the source tree.) You can use it as
+``tests/test_future/test_bytes.py`` in the source tree.) You can use it as
follows::
- >>> from future.builtins import bytes
-
+ >>> from builtins import bytes
>>> b = bytes(b'ABCD')
On Py3, this is simply the builtin :class:`bytes` object. On Py2, this
@@ -27,7 +26,7 @@ strict separation of unicode strings and byte strings as Python 3's
Traceback (most recent call last):
File "", line 1, in
TypeError: argument can't be unicode string
-
+
>>> bytes(b',').join([u'Fred', u'Bill'])
Traceback (most recent call last):
File "", line 1, in
@@ -48,14 +47,14 @@ behaviours to Python 3's :class:`bytes`::
b = bytes(b'ABCD')
assert list(b) == [65, 66, 67, 68]
assert repr(b) == "b'ABCD'"
- assert b.split(b'b') == [b'A', b'CD']
+ assert b.split(b'B') == [b'A', b'CD']
Currently the easiest way to ensure identical behaviour of byte-strings
in a Py2/3 codebase is to wrap all byte-string literals ``b'...'`` in a
:func:`~bytes` call as follows::
-
- from future.builtins import bytes
-
+
+ from builtins import bytes
+
# ...
b = bytes(b'This is my bytestring')
@@ -67,26 +66,15 @@ code incompatibilities caused by the many differences between Py3 bytes
and Py2 strings.
-..
- .. _bytes-test-results:
-
- bytes test results
- ~~~~~~~~~~~~~~~~~~
-
- For reference, when using Py2's default :class:`bytes` (i.e.
- :class:`str`), running the ``bytes`` unit tests from Python 3.3's
- ``test_bytes.py`` on Py2 (after fixing imports) gives this::
-
- --------------------------------------------------------------
- Ran 203 tests in 0.209s
-
- FAILED (failures=31, errors=55, skipped=1)
- --------------------------------------------------------------
-
- Using :mod:`future`'s backported :class:`bytes` object passes most of
- the same Python 3.3 tests on Py2, except those requiring specific
- wording in exception messages.
-
- See ``future/tests/test_bytes.py`` in the source for the actual set
- of unit tests that are actually run.
+The :class:`bytes` type from :mod:`builtins` also provides support for the
+``surrogateescape`` error handler on Python 2.x. Here is an example that works
+identically on Python 2.x and 3.x::
+
+ >>> from builtins import bytes
+ >>> b = bytes(b'\xff')
+ >>> b.decode('utf-8', 'surrogateescape')
+ '\udcc3'
+This feature is in alpha. Please leave feedback `here
+`_ about whether this
+works for you.
diff --git a/docs/changelog.rst b/docs/changelog.rst
new file mode 100644
index 00000000..420e2bc4
--- /dev/null
+++ b/docs/changelog.rst
@@ -0,0 +1,1351 @@
+.. _whats-old:
+
+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 `. 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
+ 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)
+======================================
+
+This is a bug-fix release:
+
+- Expose contents of ``thread`` (not ``dummy_thread``) as ``_thread`` on Py2 (Issue #124)
+- Add signed support for ``newint.to_bytes()`` (Issue #128)
+- Fix ``OrderedDict.clear()`` on Py2.6 (Issue #125)
+- Improve ``newrange``: equality and slicing, start/stop/step properties, refactoring (Issues #129, #130)
+- Minor doc updates
+
+Changes in version 0.14.2 (2014-11-21)
+======================================
+
+This is a bug-fix release:
+
+- Speed up importing of ``past.translation`` (Issue #117)
+- ``html.escape()``: replace function with the more robust one from Py3.4
+- ``futurize``: avoid displacing encoding comments by ``__future__`` imports (Issues #97, #10, #121)
+- ``futurize``: don't swallow exit code (Issue #119)
+- Packaging: don't forcibly remove the old build dir in ``setup.py`` (Issue #108)
+- Docs: update further docs and tests to refer to ``install_aliases()`` instead of
+ ``install_hooks()``
+- Docs: fix ``iteritems`` import error in cheat sheet (Issue #120)
+- Tests: don't rely on presence of ``test.test_support`` on Py2 or ``test.support`` on Py3 (Issue #109)
+- Tests: don't override existing ``PYTHONPATH`` for tests (PR #111)
+
+Changes in version 0.14.1 (2014-10-02)
+======================================
+
+This is a minor bug-fix release:
+
+- Docs: add a missing template file for building docs (Issue #108)
+- Tests: fix a bug in error handling while reporting failed script runs (Issue #109)
+- ``install_aliases()``: don't assume that the ``test.test_support`` module always
+ exists on Py2 (Issue #109)
+
+
+Changes in version 0.14.0 (2014-10-02)
+======================================
+
+This is a major new release that offers a cleaner interface for most imports in
+Python 2/3 compatible code.
+
+Instead of this interface::
+
+ >>> from future.builtins import str, open, range, dict
+
+ >>> from future.standard_library import hooks
+ >>> with hooks():
+ ... import queue
+ ... import configparser
+ ... import tkinter.dialog
+ ... # etc.
+
+You can now use the following interface for much Python 2/3 compatible code::
+
+ >>> # Alias for future.builtins on Py2:
+ >>> from builtins import str, open, range, dict
+
+ >>> # Alias for future.moves.* on Py2:
+ >>> import queue
+ >>> import configparser
+ >>> import tkinter.dialog
+ >>> etc.
+
+Notice that the above code will run on Python 3 even without the presence of the
+``future`` package. Of the 44 standard library modules that were refactored with
+PEP 3108, 30 are supported with direct imports in this manner. (These are listed
+here: :ref:`direct-imports`.)
+
+The other 14 standard library modules that kept the same top-level names in
+Py3.x are not supported with this direct import interface on Py2. These include
+the 5 modules in the Py3 ``urllib`` package. These modules are accessible through
+the following interface (as well as the interfaces offered in previous versions
+of ``python-future``)::
+
+ from future.standard_library import install_aliases
+ install_aliases()
+
+ from collections import UserDict, UserList, UserString
+ import dbm.gnu
+ from itertools import filterfalse, zip_longest
+ from subprocess import getoutput, getstatusoutput
+ from sys import intern
+ import test.support
+ from urllib.request import urlopen
+ from urllib.parse import urlparse
+ # etc.
+ from collections import Counter, OrderedDict # backported to Py2.6
+
+The complete list of packages supported with this interface is here:
+:ref:`list-standard-library-refactored`.
+
+For more information on these and other interfaces to the standard library, see
+:ref:`standard-library-imports`.
+
+Bug fixes
+---------
+
+- This release expands the ``future.moves`` package to include most of the remaining
+ modules that were moved in the standard library reorganization (PEP 3108).
+ (Issue #104)
+
+- This release also removes the broken ``--doctests_only`` option from the ``futurize``
+ and ``pasteurize`` scripts for now. (Issue #103)
+
+Internal cleanups
+-----------------
+
+The project folder structure has changed. Top-level packages are now in a
+``src`` folder and the tests have been moved into a project-level ``tests``
+folder.
+
+The following deprecated internal modules have been removed (Issue #80):
+
+- ``future.utils.encoding`` and ``future.utils.six``.
+
+Deprecations
+------------
+
+The following internal functions have been deprecated and will be removed in a future release:
+
+- ``future.standard_library.scrub_py2_sys_modules``
+- ``future.standard_library.scrub_future_sys_modules``
+
+
+.. _whats-new-0.13.x:
+
+Changes in version 0.13.1 (2014-09-23)
+======================================
+
+This is a bug-fix release:
+
+- Fix (multiple) inheritance of ``future.builtins.object`` with metaclasses (Issues #91, #96)
+- Fix ``futurize``'s refactoring of ``urllib`` imports (Issue #94)
+- Fix ``futurize --all-imports`` (Issue #101)
+- Fix ``futurize --output-dir`` logging (Issue #102)
+- Doc formatting fix (Issues #98, #100)
+
+
+Changes in version 0.13.0 (2014-08-13)
+======================================
+
+This is mostly a clean-up release. It adds some small new compatibility features
+and fixes several bugs.
+
+Deprecations
+------------
+
+The following unused internal modules are now deprecated. They will be removed in a
+future release:
+
+- ``future.utils.encoding`` and ``future.utils.six``.
+
+(Issue #80). See `here `_
+for the rationale for unbundling them.
+
+
+New features
+------------
+
+- Docs: Add :ref:`compatible-idioms` from Ed Schofield's PyConAU 2014 talk.
+- Add ``newint.to_bytes()`` and ``newint.from_bytes()``. (Issue #85)
+- Add ``future.utils.raise_from`` as an equivalent to Py3's ``raise ... from
+ ...`` syntax. (Issue #86)
+- Add ``past.builtins.oct()`` function.
+- Add backports for Python 2.6 of ``subprocess.check_output()``,
+ ``itertools.combinations_with_replacement()``, and ``functools.cmp_to_key()``.
+
+Bug fixes
+---------
+
+- Use a private logger instead of the global logger in
+ ``future.standard_library`` (Issue #82). This restores compatibility of the
+ standard library hooks with ``flask``. (Issue #79)
+- Stage 1 of ``futurize`` no longer renames ``next`` methods to ``__next__``
+ (Issue #81). It still converts ``obj.next()`` method calls to
+ ``next(obj)`` correctly.
+- Prevent introduction of a second set of parentheses in ``print()`` calls in
+ some further cases.
+- Fix ``isinstance`` checks for subclasses of future types. (Issue #89)
+- Be explicit about encoding file contents as UTF-8 in unit tests. (Issue #63)
+ Useful for building RPMs and in other environments where ``LANG=C``.
+- Fix for 3-argument ``pow(x, y, z)`` with ``newint`` arguments. (Thanks to @str4d.)
+ (Issue #87)
+
+
+.. _whats-new-0.12.4:
+
+Changes in version 0.12.4 (2014-07-18)
+======================================
+
+- Fix upcasting behaviour of ``newint``. (Issue #76)
+
+
+.. _whats-new-0.12.3:
+
+Changes in version 0.12.3 (2014-06-19)
+======================================
+
+- Add "official Python 3.4 support": Py3.4 is now listed among the PyPI Trove
+ classifiers and the tests now run successfully on Py3.4. (Issue #67)
+
+- Add backports of ``collections.OrderedDict`` and
+ ``collections.Counter`` for Python 2.6. (Issue #52)
+
+- Add ``--version`` option for ``futurize`` and ``pasteurize`` scripts.
+ (Issue #57)
+
+- Fix ``future.utils.ensure_new_type`` with ``long`` input. (Issue #65)
+
+- Remove some false alarms on checks for ambiguous fixer names with
+ ``futurize -f ...``.
+
+- Testing fixes:
+ - Don't hard-code Python interpreter command in tests. (Issue #62)
+ - Fix deprecated ``unittest`` usage in Py3. (Issue #62)
+ - Be explicit about encoding temporary file contents as UTF-8 for
+ when ``LANG=C`` (e.g., when building an RPM). (Issue #63)
+ - All undecorated tests are now passing again on Python 2.6, 2.7, 3.3,
+ and 3.4 (thanks to Elliott Sales de Andrade).
+
+- Docs:
+ - Add list of fixers used by ``futurize``. (Issue #58)
+ - Add list of contributors to the Credits page.
+
+.. _whats-new-0.12.2:
+
+Changes in version 0.12.2 (2014-05-25)
+======================================
+
+- Add ``bytes.maketrans()`` method. (Issue #51)
+- Add support for Python versions between 2.7.0 and 2.7.3 (inclusive).
+ (Issue #53)
+- Bug fix for ``newlist(newlist([1, 2, 3]))``. (Issue #50)
+
+
+.. _whats-new-0.12.1:
+
+Changes in version 0.12.1 (2014-05-14)
+======================================
+
+- Python 2.6 support: ``future.standard_library`` now isolates the ``importlib``
+ dependency to one function (``import_``) so the ``importlib`` backport may
+ not be needed.
+
+- Doc updates
+
+
+.. _whats-new-0.12:
+
+Changes in version 0.12.0 (2014-05-06)
+======================================
+
+The major new feature in this version is improvements in the support for the
+reorganized standard library (PEP 3108) and compatibility of the import
+mechanism with 3rd-party modules.
+
+More robust standard-library import hooks
+-----------------------------------------
+
+**Note: backwards-incompatible change:** As previously announced (see
+:ref:`deprecated-auto-import-hooks`), the import hooks must now be enabled
+explicitly, as follows::
+
+ from future import standard_library
+ with standard_library.hooks():
+ import html.parser
+ import http.client
+ ...
+
+This now causes these modules to be imported from ``future.moves``, a new
+package that provides wrappers over the native Python 2 standard library with
+the new Python 3 organization. As a consequence, the import hooks provided in
+``future.standard_library`` are now fully compatible with the `Requests library
+`_.
+
+The functional interface with ``install_hooks()`` is still supported for
+backwards compatibility::
+
+ from future import standard_library
+ standard_library.install_hooks():
+
+ import html.parser
+ import http.client
+ ...
+ standard_library.remove_hooks()
+
+Explicit installation of import hooks allows finer-grained control
+over whether they are enabled for other imported modules that provide their own
+Python 2/3 compatibility layer. This also improves compatibility of ``future``
+with tools like ``py2exe``.
+
+
+``newobject`` base object defines fallback Py2-compatible special methods
+-------------------------------------------------------------------------
+
+There is a new ``future.types.newobject`` base class (available as
+``future.builtins.object``) that can streamline Py2/3 compatible code by
+providing fallback Py2-compatible special methods for its subclasses. It
+currently provides ``next()`` and ``__nonzero__()`` as fallback methods on Py2
+when its subclasses define the corresponding Py3-style ``__next__()`` and
+``__bool__()`` methods.
+
+This obviates the need to add certain compatibility hacks or decorators to the
+code such as the ``@implements_iterator`` decorator for classes that define a
+Py3-style ``__next__`` method.
+
+In this example, the code defines a Py3-style iterator with a ``__next__``
+method. The ``object`` class defines a ``next`` method for Python 2 that maps
+to ``__next__``::
+
+ from future.builtins import object
+
+ class Upper(object):
+ def __init__(self, iterable):
+ self._iter = iter(iterable)
+ def __next__(self): # note the Py3 interface
+ return next(self._iter).upper()
+ def __iter__(self):
+ return self
+
+ assert list(Upper('hello')) == list('HELLO')
+
+``newobject`` defines other Py2-compatible special methods similarly:
+currently these include ``__nonzero__`` (mapped to ``__bool__``) and
+``__long__`` (mapped to ``__int__``).
+
+Inheriting from ``newobject`` on Python 2 is safe even if your class defines
+its own Python 2-style ``__nonzero__`` and ``next`` and ``__long__`` methods.
+Your custom methods will simply override those on the base class.
+
+On Python 3, as usual, ``future.builtins.object`` simply refers to ``builtins.object``.
+
+
+``past.builtins`` module improved
+---------------------------------
+
+The ``past.builtins`` module is much more compatible with the corresponding
+builtins on Python 2; many more of the Py2 unit tests pass on Py3. For example,
+functions like ``map()`` and ``filter()`` now behave as they do on Py2 with with
+``None`` as the first argument.
+
+The ``past.builtins`` module has also been extended to add Py3 support for
+additional Py2 constructs that are not adequately handled by ``lib2to3`` (see
+Issue #37). This includes new ``execfile()`` and ``cmp()`` functions.
+``futurize`` now invokes imports of these functions from ``past.builtins``.
+
+
+``surrogateescape`` error handler
+---------------------------------
+
+The ``newstr`` type (``future.builtins.str``) now supports a backport of the
+Py3.x ``'surrogateescape'`` error handler for preserving high-bit
+characters when encoding and decoding strings with unknown encodings.
+
+
+``newlist`` type
+----------------
+
+There is a new ``list`` type in ``future.builtins`` that offers ``.copy()`` and
+``.clear()`` methods like the ``list`` type in Python 3.
+
+
+``listvalues`` and ``listitems``
+--------------------------------
+
+``future.utils`` now contains helper functions ``listvalues`` and
+``listitems``, which provide Python 2-style list snapshotting semantics for
+dictionaries in both Python 2 and Python 3.
+
+These came out of the discussion around Nick Coghlan's now-withdrawn PEP 469.
+
+There is no corresponding ``listkeys(d)`` function; use ``list(d)`` instead.
+
+
+Tests
+-----
+
+The number of unit tests has increased from 600 to over 800. Most of the new
+tests come from Python 3.3's test suite.
+
+
+Refactoring of ``future.standard_library.*`` -> ``future.backports``
+--------------------------------------------------------------------
+
+The backported standard library modules have been moved to ``future.backports``
+to make the distinction clearer between these and the new ``future.moves``
+package.
+
+
+Backported ``http.server`` and ``urllib`` modules
+-------------------------------------------------
+
+Alpha versions of backports of the ``http.server`` and ``urllib`` module from
+Python 3.3's standard library are now provided in ``future.backports``.
+
+Use them like this::
+
+ from future.backports.urllib.request import Request # etc.
+ from future.backports.http import server as http_server
+
+Or with this new interface::
+
+ from future.standard_library import import_, from_import
+
+ Request = from_import('urllib.request', 'Request', backport=True)
+ http = import_('http.server', backport=True)
+
+.. from future.standard_library.email import message_from_bytes # etc.
+.. from future.standard_library.xmlrpc import client, server
+
+
+Internal refactoring
+--------------------
+
+The ``future.builtins.types`` module has been moved to ``future.types``.
+Likewise, ``past.builtins.types`` has been moved to ``past.types``. The only
+user-visible effect of this is to change ``repr(type(obj))`` for instances
+of these types. For example::
+
+ >>> from future.builtins import bytes
+ >>> bytes(b'abc')
+ >>> type(b)
+ future.types.newbytes.newbytes
+
+Instead of::
+
+ >>> type(b) # prior to v0.12
+ future.builtins.types.newbytes.newbytes
+
+
+Bug fixes
+---------
+
+Many small improvements and fixes have been made across the project. Some highlights are:
+
+- Fixes and updates from Python 3.3.5 have been included in the backported
+ standard library modules.
+
+- Scrubbing of the ``sys.modules`` cache performed by ``remove_hooks()`` (also
+ called by the ``suspend_hooks`` and ``hooks`` context managers) is now more
+ conservative.
+
+.. Is this still true?
+.. It now removes only modules with Py3 names (such as
+.. ``urllib.parse``) and not the corresponding ``future.standard_library.*``
+.. modules (such as ``future.standard_library.urllib.parse``.
+
+- The ``fix_next`` and ``fix_reduce`` fixers have been moved to stage 1 of
+ ``futurize``.
+
+- ``futurize``: Shebang lines such as ``#!/usr/bin/env python`` and source code
+ file encoding declarations like ``# -*- coding=utf-8 -*-`` are no longer occasionally
+ displaced by ``from __future__ import ...`` statements. (Issue #10)
+
+- Improved compatibility with ``py2exe`` (`Issue #31 `_).
+
+- The ``future.utils.bytes_to_native_str`` function now returns a platform-native string
+ object and ``future.utils.native_str_to_bytes`` returns a ``newbytes`` object on Py2.
+ (`Issue #47 `_).
+
+- The backported ``http.client`` module and related modules use other new
+ backported modules such as ``email``. As a result they are more compliant
+ with the Python 3.3 equivalents.
+
+
+.. _whats-new-0.11.4:
+
+Changes in version 0.11.4 (2014-05-25)
+======================================
+
+This release contains various small improvements and fixes:
+
+- This release restores Python 2.6 compatibility. (Issue #42)
+
+- The ``fix_absolute_import`` fixer now supports Cython ``.pyx`` modules. (Issue
+ #35)
+
+- Right-division with ``newint`` objects is fixed. (Issue #38)
+
+- The ``fix_dict`` fixer has been moved to stage2 of ``futurize``.
+
+- Calls to ``bytes(string, encoding[, errors])`` now work with ``encoding`` and
+ ``errors`` passed as positional arguments. Previously this only worked if
+ ``encoding`` and ``errors`` were passed as keyword arguments.
+
+
+- The 0-argument ``super()`` function now works from inside static methods such
+ as ``__new__``. (Issue #36)
+
+- ``future.utils.native(d)`` calls now work for ``future.builtins.dict`` objects.
+
+
+.. _whats-new-0.11.3:
+
+Changes in version 0.11.3 (2014-02-27)
+======================================
+
+This release has improvements in the standard library import hooks mechanism and
+its compatibility with 3rd-party modules:
+
+
+Improved compatibility with ``requests``
+----------------------------------------
+
+The ``__exit__`` function of the ``hooks`` context manager and the
+``remove_hooks`` function both now remove submodules of
+``future.standard_library`` from the ``sys.modules`` cache. Therefore this code
+is now possible on Python 2 and 3::
+
+ from future import standard_library
+ standard_library.install_hooks()
+ import http.client
+ standard_library.remove_hooks()
+ import requests
+
+ data = requests.get('http://www.google.com')
+
+
+Previously, this required manually removing ``http`` and ``http.client`` from
+``sys.modules`` before importing ``requests`` on Python 2.x. (Issue #19)
+
+This change should also improve the compatibility of the standard library hooks
+with any other module that provides its own Python 2/3 compatibility code.
+
+Note that the situation will improve further in version 0.12; import hooks will
+require an explicit function call or the ``hooks`` context manager.
+
+
+Conversion scripts explicitly install import hooks
+--------------------------------------------------
+
+The ``futurize`` and ``pasteurize`` scripts now add an explicit call to
+``install_hooks()`` to install the standard library import hooks. These scripts
+now add these two lines::
+
+ from future import standard_library
+ standard_library.install_hooks()
+
+instead of just the first one. The next major version of ``future`` (0.12) will
+require the explicit call or use of the ``hooks`` context manager. This will
+allow finer-grained control over whether import hooks are enabled for other
+imported modules, such as ``requests``, which provide their own Python 2/3
+compatibility code.
+
+
+``futurize`` script no longer adds ``unicode_literals`` by default
+------------------------------------------------------------------
+
+There is a new ``--unicode-literals`` flag to ``futurize`` that adds the
+import::
+
+ from __future__ import unicode_literals
+
+to the top of each converted module. Without this flag, ``futurize`` now no
+longer adds this import. (Issue #22)
+
+The ``pasteurize`` script for converting from Py3 to Py2/3 still adds
+``unicode_literals``. (See the comments in Issue #22 for an explanation.)
+
+
+.. _whats-new-0.11:
+
+Changes in version 0.11 (2014-01-28)
+====================================
+
+There are several major new features in version 0.11.
+
+
+``past`` package
+----------------
+
+The python-future project now provides a ``past`` package in addition to the
+``future`` package. Whereas ``future`` provides improved compatibility with
+Python 3 code to Python 2, ``past`` provides support for using and interacting
+with Python 2 code from Python 3. The structure reflects that of ``future``,
+with ``past.builtins`` and ``past.utils``. There is also a new
+``past.translation`` package that provides transparent translation of Python 2
+code to Python 3. (See below.)
+
+One purpose of ``past`` is to ease module-by-module upgrades to
+codebases from Python 2. Another is to help with enabling Python 2 libraries to
+support Python 3 without breaking the API they currently provide. (For example,
+user code may expect these libraries to pass them Python 2's 8-bit strings,
+rather than Python 3's ``bytes`` object.) A third purpose is to help migrate
+projects to Python 3 even if one or more dependencies are still on Python 2.
+
+Currently ``past.builtins`` provides forward-ports of Python 2's ``str`` and
+``dict`` objects, ``basestring``, and list-producing iterator functions. In
+later releases, ``past.builtins`` will be used internally by the
+``past.translation`` package to help with importing and using old Python 2
+modules in a Python 3 environment.
+
+
+Auto-translation of Python 2 modules upon import
+------------------------------------------------
+
+``past`` provides an experimental ``translation`` package to help
+with importing and using old Python 2 modules in a Python 3 environment.
+
+This is implemented using import hooks that attempt to automatically
+translate Python 2 modules to Python 3 syntax and semantics upon import. Use
+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.translation.autotranslate()`` function. Example::
+
+ >>> from past.translation import autotranslate
+ >>> autotranslate(['plotrique'])
+ >>> import plotrique
+
+
+This is intended to help you migrate to Python 3 without the need for all
+your code's dependencies to support Python 3 yet. It should be used as a
+last resort; ideally Python 2-only dependencies should be ported
+properly to a Python 2/3 compatible codebase using a tool like
+``futurize`` and the changes should be pushed to the upstream project.
+
+For more information, see :ref:`translation`.
+
+
+Separate ``pasteurize`` script
+------------------------------
+
+The functionality from ``futurize --from3`` is now in a separate script called
+``pasteurize``. Use ``pasteurize`` when converting from Python 3 code to Python
+2/3 compatible source. For more information, see :ref:`backwards-conversion`.
+
+
+``pow()``
+---------
+
+There is now a ``pow()`` function in ``future.builtins.misc`` that behaves like
+the Python 3 ``pow()`` function when raising a negative number to a fractional
+power (returning a complex number).
+
+
+``input()`` no longer disabled globally on Py2
+----------------------------------------------
+
+Previous versions of ``future`` deleted the ``input()`` function from
+``__builtin__`` on Python 2 as a security measure. This was because
+Python 2's ``input()`` function allows arbitrary code execution and could
+present a security vulnerability on Python 2 if someone expects Python 3
+semantics but forgets to import ``input`` from ``future.builtins``. This
+behaviour has been reverted, in the interests of broadening the
+compatibility of ``future`` with other Python 2 modules.
+
+Please remember to import ``input`` from ``future.builtins`` if you use
+``input()`` in a Python 2/3 compatible codebase.
+
+
+.. _deprecated-auto-import-hooks:
+
+Deprecated feature: auto-installation of standard-library import hooks
+----------------------------------------------------------------------
+
+Previous versions of ``python-future`` installed import hooks automatically upon
+importing the ``standard_library`` module from ``future``. This has been
+deprecated in order to improve robustness and compatibility with modules like
+``requests`` that already perform their own single-source Python 2/3
+compatibility.
+
+As of v0.12, importing ``future.standard_library``
+will no longer install import hooks by default. Instead, please install the
+import hooks explicitly as follows::
+
+ from future import standard_library
+ standard_library.install_hooks()
+
+And uninstall them after your import statements using::
+
+ standard_library.remove_hooks()
+
+*Note*: This is a backward-incompatible change.
+
+
+
+Internal changes
+----------------
+
+The internal ``future.builtins.backports`` module has been renamed to
+``future.builtins.types``. This will change the ``repr`` of ``future``
+types but not their use.
+
+
+.. _whats-new-0.10.2:
+
+Changes in version 0.10.2 (2014-01-11)
+======================================
+
+New context-manager interface to ``standard_library.hooks``
+-----------------------------------------------------------
+
+There is a new context manager ``future.standard_library.hooks``. Use it like
+this::
+
+ from future import standard_library
+ with standard_library.hooks():
+ import queue
+ import configserver
+ from http.client import HTTPConnection
+ # etc.
+
+If not using this context manager, it is now encouraged to add an explicit call to
+``standard_library.install_hooks()`` as follows::
+
+ from future import standard_library
+ standard_library.install_hooks()
+
+ import queue
+ import html
+ import http.client
+ # etc.
+
+And to remove the hooks afterwards with::
+
+ standard_library.remove_hooks()
+
+The functions ``install_hooks()`` and ``remove_hooks()`` were previously
+called ``enable_hooks()`` and ``disable_hooks()``. The old names are
+deprecated (but are still available as aliases).
+
+As usual, this feature has no effect on Python 3.
+
+
+.. _whats-new-0.10:
+
+Changes in version 0.10.0 (2013-12-02)
+======================================
+
+Backported ``dict`` type
+------------------------
+
+``future.builtins`` now provides a Python 2 ``dict`` subclass whose
+:func:`keys`, :func:`values`, and :func:`items` methods produce
+memory-efficient iterators. On Python 2.7, these also have the same set-like
+view behaviour as on Python 3. This can streamline code needing to iterate
+over large dictionaries. For example::
+
+ from __future__ import print_function
+ from future.builtins import dict, range
+
+ squares = dict({i: i**2 for i in range(10**7)})
+
+ assert not isinstance(d.items(), list)
+ # Because items() is memory-efficient, so is this:
+ square_roots = dict((i_squared, i) for (i, i_squared) in squares.items())
+
+For more information, see :ref:`dict-object`.
+
+
+Utility functions ``raise_`` and ``exec_``
+------------------------------------------
+
+The functions ``raise_with_traceback()`` and ``raise_()`` were
+added to ``future.utils`` to offer either the Python 3.x or Python 2.x
+behaviour for raising exceptions. Thanks to Joel Tratner for the
+contribution of these. ``future.utils.reraise()`` is now deprecated.
+
+A portable ``exec_()`` function has been added to ``future.utils`` from
+``six``.
+
+
+Bugfixes
+--------
+- Fixed ``newint.__divmod__``
+- Improved robustness of installing and removing import hooks in :mod:`future.standard_library`
+- v0.10.1: Fixed broken ``pip install future`` on Py3
+
+
+.. _whats-new-0.9:
+
+Changes in version 0.9 (2013-11-06)
+===================================
+
+
+``isinstance`` checks are supported natively with backported types
+------------------------------------------------------------------
+
+The ``isinstance`` function is no longer redefined in ``future.builtins``
+to operate with the backported ``int``, ``bytes`` and ``str``.
+``isinstance`` checks with the backported types now work correctly by
+default; we achieve this through overriding the ``__instancecheck__``
+method of metaclasses of the backported types.
+
+For more information, see :ref:`isinstance-calls`.
+
+
+``futurize``: minimal imports by default
+----------------------------------------
+
+By default, the ``futurize`` script now only adds the minimal set of
+imports deemed necessary.
+
+There is now an ``--all-imports`` option to the ``futurize`` script which
+gives the previous behaviour, which is to add all ``__future__`` imports
+and ``from future.builtins import *`` imports to every module. (This even
+applies to an empty ``__init__.py`` file.)
+
+
+Looser type-checking for the backported ``str`` object
+------------------------------------------------------
+
+Now the ``future.builtins.str`` object behaves more like the Python 2
+``unicode`` object with regard to type-checking. This is to work around some
+bugs / sloppiness in the Python 2 standard library involving mixing of
+byte-strings and unicode strings, such as ``os.path.join`` in ``posixpath.py``.
+
+``future.builtins.str`` still raises the expected ``TypeError`` exceptions from
+Python 3 when attempting to mix it with ``future.builtins.bytes``.
+
+
+``suspend_hooks()`` context manager added to ``future.standard_library``
+------------------------------------------------------------------------
+
+Pychecker (as of v0.6.1)'s ``checker.py`` attempts to import the ``builtins``
+module as a way of determining whether Python 3 is running. Since this
+succeeds when ``from future import standard_library`` is in effect, this
+check does not work and pychecker sets the wrong value for its internal ``PY2``
+flag is set.
+
+To work around this, ``future`` now provides a context manager called
+``suspend_hooks`` that can be used as follows::
+
+ from future import standard_library
+ ...
+ with standard_library.suspend_hooks():
+ from pychecker.checker import Checker
+
+
+.. _whats-new-0.8:
+
+Changes in version 0.8 (2013-10-28)
+===================================
+
+Python 2.6 support
+------------------
+
+``future`` now includes support for Python 2.6.
+
+To run the ``future`` test suite on Python 2.6, this additional package is needed::
+
+ pip install unittest2
+
+``http.server`` also requires the ``argparse`` package::
+
+ pip install argparse
+
+
+Unused modules removed
+----------------------
+
+The ``future.six`` module has been removed. ``future`` doesn't require ``six``
+(and hasn't since version 0.3). If you need support for Python versions before
+2.6, ``six`` is the best option. ``future`` and ``six`` can be installed
+alongside each other easily if needed.
+
+The unused ``hacks`` module has also been removed from the source tree.
+
+
+``isinstance()`` added to :mod:`future.builtins` (v0.8.2)
+---------------------------------------------------------
+
+It is now possible to use ``isinstance()`` calls normally after importing ``isinstance`` from
+``future.builtins``. On Python 2, this is specially defined to be compatible with
+``future``'s backported ``int``, ``str``, and ``bytes`` types, as well as
+handling Python 2's ``int``/``long`` distinction.
+
+The result is that code that uses ``isinstance`` to perform type-checking of
+ints, strings, and bytes should now work identically on Python 2 as on Python 3.
+
+The utility functions ``isint``, ``istext``, and ``isbytes`` provided before for
+compatible type-checking across Python 2 and 3 in :mod:`future.utils` are now
+deprecated.
+
+
+.. _changelog:
+
+Summary of all changes
+======================
+
+v0.15.0:
+ * Full backports of ``urllib.parse`` and other ``urllib`` submodules are exposed by ``install_aliases()``.
+ * ``tkinter.ttk`` support
+ * Initial ``surrogateescape`` support
+ * Additional backports: ``collections``, ``http`` constants, etc.
+ * Bug fixes
+
+v0.14.3:
+ * Bug fixes
+
+v0.14.2:
+ * Bug fixes
+
+v0.14.1:
+ * Bug fixes
+
+v0.14.0:
+ * New top-level ``builtins`` package on Py2 for cleaner imports. Equivalent to
+ ``future.builtins``
+ * New top-level packages on Py2 with the same names as Py3 standard modules:
+ ``configparser``, ``copyreg``, ``html``, ``http``, ``xmlrpc``, ``winreg``
+
+v0.13.1:
+ * Bug fixes
+
+v0.13.0:
+ * Cheat sheet for writing Python 2/3 compatible code
+ * ``to_int`` and ``from_int`` methods for ``newbytes``
+ * Bug fixes
+
+v0.12.0:
+ * Add ``newobject`` and ``newlist`` types
+ * Improve compatibility of import hooks with ``Requests``, ``py2exe``
+ * No more auto-installation of import hooks by ``future.standard_library``
+ * New ``future.moves`` package
+ * ``past.builtins`` improved
+ * ``newstr.encode(..., errors='surrogateescape')`` supported
+ * Refactoring: ``future.standard_library`` submodules -> ``future.backports``
+ * Refactoring: ``future.builtins.types`` -> ``future.types``
+ * Refactoring: ``past.builtins.types`` -> ``past.types``
+ * New ``listvalues`` and ``listitems`` functions in ``future.utils``
+ * Many bug fixes to ``futurize``, ``future.builtins``, etc.
+
+v0.11.4:
+ * Restore Py2.6 compatibility
+
+v0.11.3:
+ * The ``futurize`` and ``pasteurize`` scripts add an explicit call to
+ ``future.standard_library.install_hooks()`` whenever modules affected by
+ PEP 3108 are imported.
+
+ * The ``future.builtins.bytes`` constructor now accepts ``frozenset``
+ objects as on Py3.
+
+v0.11.2:
+ * 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
+ docs and tests.
+
+v0.11:
+ * New ``past`` package with ``past.builtins`` and ``past.translation``
+ modules.
+
+v0.10.2:
+ * Improvements to stdlib hooks. New context manager:
+ ``future.standard_library.hooks()``.
+
+ * New ``raise_`` and ``raise_with_traceback`` functions in ``future.utils``.
+
+v0.10:
+ * New backported ``dict`` object with set-like ``keys``, ``values``, ``items``
+
+v0.9:
+ * :func:`isinstance` hack removed in favour of ``__instancecheck__`` on the
+ metaclasses of the backported types
+ * ``futurize`` now only adds necessary imports by default
+ * Looser type-checking by ``future.builtins.str`` when combining with Py2
+ native byte-strings.
+
+v0.8.3:
+ * New ``--all-imports`` option to ``futurize``
+ * Fix bug with ``str.encode()`` with encoding as a non-keyword arg
+
+v0.8.2:
+ * New ``isinstance`` function in :mod:`future.builtins`. This obviates
+ and deprecates the utility functions for type-checking in :mod:`future.utils`.
+
+v0.8.1:
+ * Backported ``socketserver.py``. Fixes sporadic test failures with
+ ``http.server`` (related to threading and old-style classes used in Py2.7's
+ ``SocketServer.py``).
+
+ * Move a few more safe ``futurize`` fixes from stage2 to stage1
+
+ * Bug fixes to :mod:`future.utils`
+
+v0.8:
+ * Added Python 2.6 support
+
+ * Removed unused modules: :mod:`future.six` and :mod:`future.hacks`
+
+ * Removed undocumented functions from :mod:`future.utils`
+
+v0.7:
+ * Added a backported Py3-like ``int`` object (inherits from ``long``).
+
+ * Added utility functions for type-checking and docs about
+ ``isinstance`` uses/alternatives.
+
+ * Fixes and stricter type-checking for ``bytes`` and ``str`` objects
+
+ * Added many more tests for the ``futurize`` script
+
+ * We no longer disable obsolete Py2 builtins by default with ``from
+ future.builtins import *``. Use ``from future.builtins.disabled
+ import *`` instead.
+
+v0.6:
+ * Added a backported Py3-like ``str`` object (inherits from Py2's ``unicode``)
+
+ * Removed support for the form ``from future import *``; use ``from future.builtins import *`` instead
+
+v0.5.3:
+ * Doc improvements
+
+v0.5.2:
+ * Add lots of docs and a Sphinx project
+
+v0.5.1:
+ * Upgraded included ``six`` module (included as ``future.utils.six``) to v1.4.1
+
+ * :mod:`http.server` module backported
+
+ * ``bytes.split()`` and ``.rsplit()`` bugfixes
+
+v0.5.0:
+ * Added backported Py3-like ``bytes`` object
+
+v0.4.2:
+ * Various fixes
+
+v0.4.1:
+ * Added :func:`open` (from :mod:`io` module on Py2)
+ * Improved docs
+
+v0.4.0:
+ * Added various useful compatibility functions to :mod:`future.utils`
+
+ * Reorganized package: moved all builtins to :mod:`future.builtins`; moved
+ all stdlib things to ``future.standard_library``
+
+ * Renamed ``python-futurize`` console script to ``futurize``
+
+ * Moved ``future.six`` to ``future.utils.six`` and pulled the most relevant
+ definitions to :mod:`future.utils`.
+
+ * More improvements to "Py3 to both" conversion (``futurize.py --from3``)
+
+v0.3.5:
+ * Fixed broken package setup ("package directory 'libfuturize/tests' does not exist")
+
+v0.3.4:
+ * Added ``itertools.zip_longest``
+
+ * Updated ``2to3_backcompat`` tests to use ``futurize.py``
+
+ * Improved ``libfuturize`` fixers: correct order of imports; add imports only when necessary (except ``absolute_import`` currently)
+
+v0.3.3:
+ * Added ``python-futurize`` console script
+
+ * Added ``itertools.filterfalse``
+
+ * Removed docs about unfinished backports (``urllib`` etc.)
+
+ * Removed old Py2 syntax in some files that breaks py3 ``setup.py install``
+
+v0.3.2:
+ * Added ``test.support`` module
+
+ * Added ``UserList``, ``UserString``, ``UserDict`` classes to ``collections`` module
+
+ * Removed ``int`` -> ``long`` mapping
+
+ * Added backported ``_markupbase.py`` etc. with new-style classes to fix travis-ci build problems
+
+ * Added working ``html`` and ``http.client`` backported modules
+v0.3.0:
+ * Generalized import hooks to allow dotted imports
+
+ * Added backports of ``urllib``, ``html``, ``http`` modules from Py3.3 stdlib using ``future``
+
+ * Added ``futurize`` script for automatically turning Py2 or Py3 modules into
+ cross-platform Py3 modules
+
+ * Renamed ``future.standard_library_renames`` to
+ ``future.standard_library``. (No longer just renames, but backports too.)
+
+v0.2.2.1:
+ * Small bug fixes to get tests passing on travis-ci.org
+
+v0.2.1:
+ * Small bug fixes
+
+v0.2.0:
+ * ``Features`` module renamed to ``modified_builtins``
+
+ * New functions added: :func:`round`, :func:`input`
+
+ * No more namespace pollution as a policy::
+
+ from future import *
+
+ should have no effect on Python 3. On Python 2, it only shadows the
+ builtins; it doesn't introduce any new names.
+
+ * End-to-end tests with Python 2 code and ``2to3`` now work
+
+v0.1.0:
+ * first version with tests!
+
+ * removed the inspect-module magic
+
+v0.0.x:
+ * initial releases. Use at your peril.
diff --git a/docs/compatible_idioms.rst b/docs/compatible_idioms.rst
new file mode 100644
index 00000000..ab478ed8
--- /dev/null
+++ b/docs/compatible_idioms.rst
@@ -0,0 +1,1457 @@
+.. _compatible-idioms:
+
+Cheat Sheet: Writing Python 2-3 compatible code
+===============================================
+
+- **Copyright (c):** 2013-2024 Python Charmers, Australia.
+- **Author:** Ed Schofield.
+- **Licence:** Creative Commons Attribution.
+
+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: https://www.youtube.com/watch?v=KOqk8j11aAI&t=10m14s.)
+
+Minimum versions:
+
+- Python 2: 2.7+
+- Python 3: 3.4+
+
+Setup
+-----
+
+The imports below refer to these ``pip``-installable packages on PyPI:
+
+::
+
+ import future # pip install future
+ import builtins # pip install future
+ import past # pip install future
+ import six # pip install six
+
+The following scripts are also ``pip``-installable:
+
+::
+
+ futurize # pip install future
+ pasteurize # pip install future
+
+See https://python-future.org and https://pythonhosted.org/six/ for more
+information.
+
+Essential syntax differences
+----------------------------
+
+print
+~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ print 'Hello'
+.. code:: python
+
+ # Python 2 and 3:
+ print('Hello')
+To print multiple strings, import ``print_function`` to prevent Py2 from
+interpreting it as a tuple:
+
+.. code:: python
+
+ # Python 2 only:
+ print 'Hello', 'Guido'
+.. code:: python
+
+ # Python 2 and 3:
+ from __future__ import print_function # (at top of module)
+
+ print('Hello', 'Guido')
+.. code:: python
+
+ # Python 2 only:
+ print >> sys.stderr, 'Hello'
+.. code:: python
+
+ # Python 2 and 3:
+ from __future__ import print_function
+
+ print('Hello', file=sys.stderr)
+.. code:: python
+
+ # Python 2 only:
+ print 'Hello',
+.. code:: python
+
+ # Python 2 and 3:
+ from __future__ import print_function
+
+ print('Hello', end='')
+Raising exceptions
+~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ raise ValueError, "dodgy value"
+.. code:: python
+
+ # Python 2 and 3:
+ raise ValueError("dodgy value")
+Raising exceptions with a traceback:
+
+.. code:: python
+
+ # Python 2 only:
+ traceback = sys.exc_info()[2]
+ raise ValueError, "dodgy value", traceback
+.. code:: python
+
+ # Python 3 only:
+ raise ValueError("dodgy value").with_traceback()
+.. code:: python
+
+ # Python 2 and 3: option 1
+ from six import reraise as raise_
+ # or
+ from future.utils import raise_
+
+ traceback = sys.exc_info()[2]
+ raise_(ValueError, "dodgy value", traceback)
+.. code:: python
+
+ # Python 2 and 3: option 2
+ from future.utils import raise_with_traceback
+
+ raise_with_traceback(ValueError("dodgy value"))
+Exception chaining (PEP 3134):
+
+.. code:: python
+
+ # Setup:
+ class DatabaseError(Exception):
+ pass
+.. code:: python
+
+ # Python 3 only
+ class FileDatabase:
+ def __init__(self, filename):
+ try:
+ self.file = open(filename)
+ except IOError as exc:
+ raise DatabaseError('failed to open') from exc
+.. code:: python
+
+ # Python 2 and 3:
+ from future.utils import raise_from
+
+ class FileDatabase:
+ def __init__(self, filename):
+ try:
+ self.file = open(filename)
+ except IOError as exc:
+ raise_from(DatabaseError('failed to open'), exc)
+.. code:: python
+
+ # Testing the above:
+ try:
+ fd = FileDatabase('non_existent_file.txt')
+ except Exception as e:
+ assert isinstance(e.__cause__, IOError) # FileNotFoundError on Py3.3+ inherits from IOError
+Catching exceptions
+~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ try:
+ ...
+ except ValueError, e:
+ ...
+.. code:: python
+
+ # Python 2 and 3:
+ try:
+ ...
+ except ValueError as e:
+ ...
+Division
+~~~~~~~~
+
+Integer division (rounding down):
+
+.. code:: python
+
+ # Python 2 only:
+ assert 2 / 3 == 0
+.. code:: python
+
+ # Python 2 and 3:
+ assert 2 // 3 == 0
+"True division" (float division):
+
+.. code:: python
+
+ # Python 3 only:
+ assert 3 / 2 == 1.5
+.. code:: python
+
+ # Python 2 and 3:
+ from __future__ import division # (at top of module)
+
+ assert 3 / 2 == 1.5
+"Old division" (i.e. compatible with Py2 behaviour):
+
+.. code:: python
+
+ # Python 2 only:
+ a = b / c # with any types
+.. code:: python
+
+ # Python 2 and 3:
+ from past.utils import old_div
+
+ a = old_div(b, c) # always same as / on Py2
+Long integers
+~~~~~~~~~~~~~
+
+Short integers are gone in Python 3 and ``long`` has become ``int``
+(without the trailing ``L`` in the ``repr``).
+
+.. code:: python
+
+ # Python 2 only
+ k = 9223372036854775808L
+
+ # Python 2 and 3:
+ k = 9223372036854775808
+.. code:: python
+
+ # Python 2 only
+ bigint = 1L
+
+ # Python 2 and 3
+ from builtins import int
+ bigint = int(1)
+To test whether a value is an integer (of any kind):
+
+.. code:: python
+
+ # Python 2 only:
+ if isinstance(x, (int, long)):
+ ...
+
+ # Python 3 only:
+ if isinstance(x, int):
+ ...
+
+ # Python 2 and 3: option 1
+ from builtins import int # subclass of long on Py2
+
+ if isinstance(x, int): # matches both int and long on Py2
+ ...
+
+ # Python 2 and 3: option 2
+ from past.builtins import long
+
+ if isinstance(x, (int, long)):
+ ...
+Octal constants
+~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ 0644 # Python 2 only
+.. code:: python
+
+ 0o644 # Python 2 and 3
+Backtick repr
+~~~~~~~~~~~~~
+
+.. code:: python
+
+ `x` # Python 2 only
+.. code:: python
+
+ repr(x) # Python 2 and 3
+Metaclasses
+~~~~~~~~~~~
+
+.. code:: python
+
+ class BaseForm(object):
+ pass
+
+ class FormType(type):
+ pass
+.. code:: python
+
+ # Python 2 only:
+ class Form(BaseForm):
+ __metaclass__ = FormType
+ pass
+.. code:: python
+
+ # Python 3 only:
+ class Form(BaseForm, metaclass=FormType):
+ pass
+.. code:: python
+
+ # Python 2 and 3:
+ from six import with_metaclass
+ # or
+ from future.utils import with_metaclass
+
+ class Form(with_metaclass(FormType, BaseForm)):
+ pass
+Strings and bytes
+-----------------
+
+Unicode (text) string literals
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you are upgrading an existing Python 2 codebase, it may be preferable
+to mark up all string literals as unicode explicitly with ``u``
+prefixes:
+
+.. code:: python
+
+ # Python 2 only
+ s1 = 'The Zen of Python'
+ s2 = u'きたないのよりきれいな方がいい\n'
+
+ # Python 2 and 3
+ s1 = u'The Zen of Python'
+ s2 = u'きたないのよりきれいな方がいい\n'
+The ``futurize`` and ``python-modernize`` tools do not currently offer
+an option to do this automatically.
+
+If you are writing code for a new project or new codebase, you can use
+this idiom to make all string literals in a module unicode strings:
+
+.. code:: python
+
+ # Python 2 and 3
+ from __future__ import unicode_literals # at top of module
+
+ s1 = 'The Zen of Python'
+ s2 = 'きたないのよりきれいな方がいい\n'
+See https://python-future.org/unicode\_literals.html for more discussion
+on which style to use.
+
+Byte-string literals
+~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only
+ s = 'This must be a byte-string'
+
+ # Python 2 and 3
+ s = b'This must be a byte-string'
+To loop over a byte-string with possible high-bit characters, obtaining
+each character as a byte-string of length 1:
+
+.. code:: python
+
+ # Python 2 only:
+ for bytechar in 'byte-string with high-bit chars like \xf9':
+ ...
+
+ # Python 3 only:
+ for myint in b'byte-string with high-bit chars like \xf9':
+ bytechar = bytes([myint])
+
+ # Python 2 and 3:
+ from builtins import bytes
+ for myint in bytes(b'byte-string with high-bit chars like \xf9'):
+ bytechar = bytes([myint])
+As an alternative, ``chr()`` and ``.encode('latin-1')`` can be used to
+convert an int into a 1-char byte string:
+
+.. code:: python
+
+ # Python 3 only:
+ for myint in b'byte-string with high-bit chars like \xf9':
+ char = chr(myint) # returns a unicode string
+ bytechar = char.encode('latin-1')
+
+ # Python 2 and 3:
+ from builtins import bytes, chr
+ for myint in bytes(b'byte-string with high-bit chars like \xf9'):
+ char = chr(myint) # returns a unicode string
+ bytechar = char.encode('latin-1') # forces returning a byte str
+basestring
+~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ a = u'abc'
+ b = 'def'
+ assert (isinstance(a, basestring) and isinstance(b, basestring))
+
+ # Python 2 and 3: alternative 1
+ from past.builtins import basestring # pip install future
+
+ a = u'abc'
+ b = b'def'
+ assert (isinstance(a, basestring) and isinstance(b, basestring))
+.. code:: python
+
+ # Python 2 and 3: alternative 2: refactor the code to avoid considering
+ # byte-strings as strings.
+
+ from builtins import str
+ a = u'abc'
+ b = b'def'
+ c = b.decode()
+ assert isinstance(a, str) and isinstance(c, str)
+ # ...
+unicode
+~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ templates = [u"blog/blog_post_detail_%s.html" % unicode(slug)]
+.. code:: python
+
+ # Python 2 and 3: alternative 1
+ from builtins import str
+ templates = [u"blog/blog_post_detail_%s.html" % str(slug)]
+.. code:: python
+
+ # Python 2 and 3: alternative 2
+ from builtins import str as text
+ templates = [u"blog/blog_post_detail_%s.html" % text(slug)]
+StringIO
+~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ from StringIO import StringIO
+ # or:
+ from cStringIO import StringIO
+
+ # Python 2 and 3:
+ from io import BytesIO # for handling byte strings
+ from io import StringIO # for handling unicode strings
+Imports relative to a package
+-----------------------------
+
+Suppose the package is:
+
+::
+
+ mypackage/
+ __init__.py
+ submodule1.py
+ submodule2.py
+
+
+and the code below is in ``submodule1.py``:
+
+.. code:: python
+
+ # Python 2 only:
+ import submodule2
+.. code:: python
+
+ # Python 2 and 3:
+ from . import submodule2
+.. code:: python
+
+ # Python 2 and 3:
+ # To make Py2 code safer (more like Py3) by preventing
+ # implicit relative imports, you can also add this to the top:
+ from __future__ import absolute_import
+Dictionaries
+------------
+
+.. code:: python
+
+ heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}
+Iterating through ``dict`` keys/values/items
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Iterable dict keys:
+
+.. code:: python
+
+ # Python 2 only:
+ for key in heights.iterkeys():
+ ...
+.. code:: python
+
+ # Python 2 and 3:
+ for key in heights:
+ ...
+Iterable dict values:
+
+.. code:: python
+
+ # Python 2 only:
+ for value in heights.itervalues():
+ ...
+.. code:: python
+
+ # Idiomatic Python 3
+ for value in heights.values(): # extra memory overhead on Py2
+ ...
+.. code:: python
+
+ # Python 2 and 3: option 1
+ from builtins import dict
+
+ heights = dict(Fred=175, Anne=166, Joe=192)
+ for key in heights.values(): # efficient on Py2 and Py3
+ ...
+.. code:: python
+
+ # Python 2 and 3: option 2
+ from future.utils import itervalues
+ # or
+ from six import itervalues
+
+ for key in itervalues(heights):
+ ...
+Iterable dict items:
+
+.. code:: python
+
+ # Python 2 only:
+ for (key, value) in heights.iteritems():
+ ...
+.. code:: python
+
+ # Python 2 and 3: option 1
+ for (key, value) in heights.items(): # inefficient on Py2
+ ...
+.. code:: python
+
+ # Python 2 and 3: option 2
+ from future.utils import viewitems
+
+ for (key, value) in viewitems(heights): # also behaves like a set
+ ...
+.. code:: python
+
+ # Python 2 and 3: option 3
+ from future.utils import iteritems
+ # or
+ from six import iteritems
+
+ for (key, value) in iteritems(heights):
+ ...
+dict keys/values/items as a list
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+dict keys as a list:
+
+.. code:: python
+
+ # Python 2 only:
+ keylist = heights.keys()
+ assert isinstance(keylist, list)
+.. code:: python
+
+ # Python 2 and 3:
+ keylist = list(heights)
+ assert isinstance(keylist, list)
+dict values as a list:
+
+.. code:: python
+
+ # Python 2 only:
+ heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}
+ valuelist = heights.values()
+ assert isinstance(valuelist, list)
+.. code:: python
+
+ # Python 2 and 3: option 1
+ valuelist = list(heights.values()) # inefficient on Py2
+.. code:: python
+
+ # Python 2 and 3: option 2
+ from builtins import dict
+
+ heights = dict(Fred=175, Anne=166, Joe=192)
+ valuelist = list(heights.values())
+.. code:: python
+
+ # Python 2 and 3: option 3
+ from future.utils import listvalues
+
+ valuelist = listvalues(heights)
+.. code:: python
+
+ # Python 2 and 3: option 4
+ from future.utils import itervalues
+ # or
+ from six import itervalues
+
+ valuelist = list(itervalues(heights))
+dict items as a list:
+
+.. code:: python
+
+ # Python 2 and 3: option 1
+ itemlist = list(heights.items()) # inefficient on Py2
+.. code:: python
+
+ # Python 2 and 3: option 2
+ from future.utils import listitems
+
+ itemlist = listitems(heights)
+.. code:: python
+
+ # Python 2 and 3: option 3
+ from future.utils import iteritems
+ # or
+ from six import iteritems
+
+ itemlist = list(iteritems(heights))
+Custom class behaviour
+----------------------
+
+Custom iterators
+~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only
+ class Upper(object):
+ def __init__(self, iterable):
+ self._iter = iter(iterable)
+ def next(self): # Py2-style
+ return self._iter.next().upper()
+ def __iter__(self):
+ return self
+
+ itr = Upper('hello')
+ assert itr.next() == 'H' # Py2-style
+ assert list(itr) == list('ELLO')
+.. code:: python
+
+ # Python 2 and 3: option 1
+ from builtins import object
+
+ class Upper(object):
+ def __init__(self, iterable):
+ self._iter = iter(iterable)
+ def __next__(self): # Py3-style iterator interface
+ return next(self._iter).upper() # builtin next() function calls
+ def __iter__(self):
+ return self
+
+ itr = Upper('hello')
+ assert next(itr) == 'H' # compatible style
+ assert list(itr) == list('ELLO')
+.. code:: python
+
+ # Python 2 and 3: option 2
+ from future.utils import implements_iterator
+
+ @implements_iterator
+ class Upper(object):
+ def __init__(self, iterable):
+ self._iter = iter(iterable)
+ def __next__(self): # Py3-style iterator interface
+ return next(self._iter).upper() # builtin next() function calls
+ def __iter__(self):
+ return self
+
+ itr = Upper('hello')
+ assert next(itr) == 'H'
+ assert list(itr) == list('ELLO')
+Custom ``__str__`` methods
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ class MyClass(object):
+ def __unicode__(self):
+ return 'Unicode string: \u5b54\u5b50'
+ def __str__(self):
+ return unicode(self).encode('utf-8')
+
+ a = MyClass()
+ print(a) # prints encoded string
+.. code:: python
+
+ # Python 2 and 3:
+ from future.utils import python_2_unicode_compatible
+
+ @python_2_unicode_compatible
+ class MyClass(object):
+ def __str__(self):
+ return u'Unicode string: \u5b54\u5b50'
+
+ a = MyClass()
+ print(a) # prints string encoded as utf-8 on Py2
+
+.. parsed-literal::
+
+ Unicode string: 孔子
+
+
+Custom ``__nonzero__`` vs ``__bool__`` method:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ class AllOrNothing(object):
+ def __init__(self, l):
+ self.l = l
+ def __nonzero__(self):
+ return all(self.l)
+
+ container = AllOrNothing([0, 100, 200])
+ assert not bool(container)
+.. code:: python
+
+ # Python 2 and 3:
+ from builtins import object
+
+ class AllOrNothing(object):
+ def __init__(self, l):
+ self.l = l
+ def __bool__(self):
+ return all(self.l)
+
+ container = AllOrNothing([0, 100, 200])
+ assert not bool(container)
+Lists versus iterators
+----------------------
+
+xrange
+~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ for i in xrange(10**8):
+ ...
+.. code:: python
+
+ # Python 2 and 3: forward-compatible
+ from builtins import range
+ for i in range(10**8):
+ ...
+.. code:: python
+
+ # Python 2 and 3: backward-compatible
+ from past.builtins import xrange
+ for i in xrange(10**8):
+ ...
+range
+~~~~~
+
+.. code:: python
+
+ # Python 2 only
+ mylist = range(5)
+ assert mylist == [0, 1, 2, 3, 4]
+.. code:: python
+
+ # Python 2 and 3: forward-compatible: option 1
+ mylist = list(range(5)) # copies memory on Py2
+ assert mylist == [0, 1, 2, 3, 4]
+.. code:: python
+
+ # Python 2 and 3: forward-compatible: option 2
+ from builtins import range
+
+ mylist = list(range(5))
+ assert mylist == [0, 1, 2, 3, 4]
+.. code:: python
+
+ # Python 2 and 3: option 3
+ from future.utils import lrange
+
+ mylist = lrange(5)
+ assert mylist == [0, 1, 2, 3, 4]
+.. code:: python
+
+ # Python 2 and 3: backward compatible
+ from past.builtins import range
+
+ mylist = range(5)
+ assert mylist == [0, 1, 2, 3, 4]
+map
+~~~
+
+.. code:: python
+
+ # Python 2 only:
+ mynewlist = map(f, myoldlist)
+ assert mynewlist == [f(x) for x in myoldlist]
+.. code:: python
+
+ # Python 2 and 3: option 1
+ # Idiomatic Py3, but inefficient on Py2
+ mynewlist = list(map(f, myoldlist))
+ assert mynewlist == [f(x) for x in myoldlist]
+.. code:: python
+
+ # Python 2 and 3: option 2
+ from builtins import map
+
+ mynewlist = list(map(f, myoldlist))
+ assert mynewlist == [f(x) for x in myoldlist]
+.. code:: python
+
+ # Python 2 and 3: option 3
+ try:
+ import itertools.imap as map
+ except ImportError:
+ pass
+
+ mynewlist = list(map(f, myoldlist)) # inefficient on Py2
+ assert mynewlist == [f(x) for x in myoldlist]
+.. code:: python
+
+ # Python 2 and 3: option 4
+ from future.utils import lmap
+
+ mynewlist = lmap(f, myoldlist)
+ assert mynewlist == [f(x) for x in myoldlist]
+.. code:: python
+
+ # Python 2 and 3: option 5
+ from past.builtins import map
+
+ mynewlist = map(f, myoldlist)
+ assert mynewlist == [f(x) for x in myoldlist]
+imap
+~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ from itertools import imap
+
+ myiter = imap(func, myoldlist)
+ assert isinstance(myiter, iter)
+.. code:: python
+
+ # Python 3 only:
+ myiter = map(func, myoldlist)
+ assert isinstance(myiter, iter)
+.. code:: python
+
+ # Python 2 and 3: option 1
+ from builtins import map
+
+ myiter = map(func, myoldlist)
+ assert isinstance(myiter, iter)
+.. code:: python
+
+ # Python 2 and 3: option 2
+ try:
+ import itertools.imap as map
+ except ImportError:
+ pass
+
+ myiter = map(func, myoldlist)
+ assert isinstance(myiter, iter)
+.. code:: python
+
+ # Python 2 and 3: option 3
+ from six.moves import map
+
+ myiter = map(func, myoldlist)
+ assert isinstance(myiter, iter)
+
+zip, izip
+~~~~~~~~~
+
+As above with ``zip`` and ``itertools.izip``.
+
+filter, ifilter
+~~~~~~~~~~~~~~~
+
+As above with ``filter`` and ``itertools.ifilter`` too.
+
+Other builtins
+--------------
+
+File IO with open()
+~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only
+ f = open('myfile.txt')
+ data = f.read() # as a byte string
+ text = data.decode('utf-8')
+
+ # Python 2 and 3: alternative 1
+ from io import open
+ f = open('myfile.txt', 'rb')
+ data = f.read() # as bytes
+ text = data.decode('utf-8') # unicode, not bytes
+
+ # Python 2 and 3: alternative 2
+ from io import open
+ f = open('myfile.txt', encoding='utf-8')
+ text = f.read() # unicode, not bytes
+reduce()
+~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ assert reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) == 1+2+3+4+5
+.. code:: python
+
+ # Python 2 and 3:
+ from functools import reduce
+
+ assert reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) == 1+2+3+4+5
+raw\_input()
+~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ name = raw_input('What is your name? ')
+ assert isinstance(name, str) # native str
+.. code:: python
+
+ # Python 2 and 3:
+ from builtins import input
+
+ name = input('What is your name? ')
+ assert isinstance(name, str) # native str on Py2 and Py3
+input()
+~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ input("Type something safe please: ")
+.. code:: python
+
+ # Python 2 and 3
+ from builtins import input
+ eval(input("Type something safe please: "))
+Warning: using either of these is **unsafe** with untrusted input.
+
+file()
+~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ f = file(pathname)
+.. code:: python
+
+ # Python 2 and 3:
+ f = open(pathname)
+
+ # But preferably, use this:
+ from io import open
+ f = open(pathname, 'rb') # if f.read() should return bytes
+ # or
+ f = open(pathname, 'rt') # if f.read() should return unicode text
+exec
+~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ exec 'x = 10'
+
+ # Python 2 and 3:
+ exec('x = 10')
+.. code:: python
+
+ # Python 2 only:
+ g = globals()
+ exec 'x = 10' in g
+
+ # Python 2 and 3:
+ g = globals()
+ exec('x = 10', g)
+.. code:: python
+
+ # Python 2 only:
+ l = locals()
+ exec 'x = 10' in g, l
+
+ # Python 2 and 3:
+ exec('x = 10', g, l)
+execfile()
+~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ execfile('myfile.py')
+.. code:: python
+
+ # Python 2 and 3: alternative 1
+ from past.builtins import execfile
+
+ execfile('myfile.py')
+.. code:: python
+
+ # Python 2 and 3: alternative 2
+ exec(compile(open('myfile.py').read()))
+
+ # This can sometimes cause this:
+ # SyntaxError: function ... uses import * and bare exec ...
+ # See https://github.com/PythonCharmers/python-future/issues/37
+unichr()
+~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ assert unichr(8364) == '€'
+.. code:: python
+
+ # Python 3 only:
+ assert chr(8364) == '€'
+.. code:: python
+
+ # Python 2 and 3:
+ from builtins import chr
+ assert chr(8364) == '€'
+intern()
+~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ intern('mystring')
+.. code:: python
+
+ # Python 3 only:
+ from sys import intern
+ intern('mystring')
+.. code:: python
+
+ # Python 2 and 3: alternative 1
+ from past.builtins import intern
+ intern('mystring')
+.. code:: python
+
+ # Python 2 and 3: alternative 2
+ from six.moves import intern
+ intern('mystring')
+.. code:: python
+
+ # Python 2 and 3: alternative 3
+ from future.standard_library import install_aliases
+ install_aliases()
+ from sys import intern
+ intern('mystring')
+.. code:: python
+
+ # Python 2 and 3: alternative 2
+ try:
+ from sys import intern
+ except ImportError:
+ pass
+ intern('mystring')
+apply()
+~~~~~~~
+
+.. code:: python
+
+ args = ('a', 'b')
+ kwargs = {'kwarg1': True}
+.. code:: python
+
+ # Python 2 only:
+ apply(f, args, kwargs)
+.. code:: python
+
+ # Python 2 and 3: alternative 1
+ f(*args, **kwargs)
+.. code:: python
+
+ # Python 2 and 3: alternative 2
+ from past.builtins import apply
+ apply(f, args, kwargs)
+chr()
+~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ assert chr(64) == b'@'
+ assert chr(200) == b'\xc8'
+.. code:: python
+
+ # Python 3 only: option 1
+ assert chr(64).encode('latin-1') == b'@'
+ assert chr(0xc8).encode('latin-1') == b'\xc8'
+.. code:: python
+
+ # Python 2 and 3: option 1
+ from builtins import chr
+
+ assert chr(64).encode('latin-1') == b'@'
+ assert chr(0xc8).encode('latin-1') == b'\xc8'
+.. code:: python
+
+ # Python 3 only: option 2
+ assert bytes([64]) == b'@'
+ assert bytes([0xc8]) == b'\xc8'
+.. code:: python
+
+ # Python 2 and 3: option 2
+ from builtins import bytes
+
+ assert bytes([64]) == b'@'
+ assert bytes([0xc8]) == b'\xc8'
+cmp()
+~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
+.. code:: python
+
+ # Python 2 and 3: alternative 1
+ from past.builtins import cmp
+ assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
+.. code:: python
+
+ # Python 2 and 3: alternative 2
+ cmp = lambda(x, y): (x > y) - (x < y)
+ assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
+reload()
+~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ reload(mymodule)
+.. code:: python
+
+ # Python 2 and 3
+ from imp import reload
+ reload(mymodule)
+Standard library
+----------------
+
+dbm modules
+~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only
+ import anydbm
+ import whichdb
+ import dbm
+ import dumbdbm
+ import gdbm
+
+ # Python 2 and 3: alternative 1
+ from future import standard_library
+ standard_library.install_aliases()
+
+ import dbm
+ import dbm.ndbm
+ import dbm.dumb
+ import dbm.gnu
+
+ # Python 2 and 3: alternative 2
+ from future.moves import dbm
+ from future.moves.dbm import dumb
+ from future.moves.dbm import ndbm
+ from future.moves.dbm import gnu
+
+ # Python 2 and 3: alternative 3
+ from six.moves import dbm_gnu
+ # (others not supported)
+commands / subprocess modules
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only
+ from commands import getoutput, getstatusoutput
+
+ # Python 2 and 3
+ from future import standard_library
+ standard_library.install_aliases()
+
+ from subprocess import getoutput, getstatusoutput
+StringIO module
+~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only
+ from StringIO import StringIO
+ from cStringIO import StringIO
+.. code:: python
+
+ # Python 2 and 3
+ from io import BytesIO
+ # and refactor StringIO() calls to BytesIO() if passing byte-strings
+http module
+~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ import httplib
+ import Cookie
+ import cookielib
+ import BaseHTTPServer
+ import SimpleHTTPServer
+ import CGIHttpServer
+
+ # Python 2 and 3 (after ``pip install future``):
+ import http.client
+ import http.cookies
+ import http.cookiejar
+ import http.server
+xmlrpc module
+~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ import DocXMLRPCServer
+ import SimpleXMLRPCServer
+
+ # Python 2 and 3 (after ``pip install future``):
+ import xmlrpc.server
+.. code:: python
+
+ # Python 2 only:
+ import xmlrpclib
+
+ # Python 2 and 3 (after ``pip install future``):
+ import xmlrpc.client
+html escaping and entities
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 and 3:
+ from cgi import escape
+
+ # Safer (Python 2 and 3, after ``pip install future``):
+ from html import escape
+
+ # Python 2 only:
+ from htmlentitydefs import codepoint2name, entitydefs, name2codepoint
+
+ # Python 2 and 3 (after ``pip install future``):
+ from html.entities import codepoint2name, entitydefs, name2codepoint
+html parsing
+~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ from HTMLParser import HTMLParser
+
+ # Python 2 and 3 (after ``pip install future``)
+ from html.parser import HTMLParser
+
+ # Python 2 and 3 (alternative 2):
+ from future.moves.html.parser import HTMLParser
+urllib module
+~~~~~~~~~~~~~
+
+``urllib`` is the hardest module to use from Python 2/3 compatible code.
+You might want to switch to Requests (http://python-requests.org) instead.
+
+.. code:: python
+
+ # Python 2 only:
+ from urlparse import urlparse
+ from urllib import urlencode
+ from urllib2 import urlopen, Request, HTTPError
+.. code:: python
+
+ # Python 3 only:
+ from urllib.parse import urlparse, urlencode
+ from urllib.request import urlopen, Request
+ from urllib.error import HTTPError
+.. code:: python
+
+ # Python 2 and 3: easiest option
+ from future.standard_library import install_aliases
+ install_aliases()
+
+ from urllib.parse import urlparse, urlencode
+ from urllib.request import urlopen, Request
+ from urllib.error import HTTPError
+.. code:: python
+
+ # Python 2 and 3: alternative 2
+ from future.standard_library import hooks
+
+ with hooks():
+ from urllib.parse import urlparse, urlencode
+ from urllib.request import urlopen, Request
+ from urllib.error import HTTPError
+.. code:: python
+
+ # Python 2 and 3: alternative 3
+ from future.moves.urllib.parse import urlparse, urlencode
+ from future.moves.urllib.request import urlopen, Request
+ from future.moves.urllib.error import HTTPError
+ # or
+ from six.moves.urllib.parse import urlparse, urlencode
+ from six.moves.urllib.request import urlopen
+ from six.moves.urllib.error import HTTPError
+.. code:: python
+
+ # Python 2 and 3: alternative 4
+ try:
+ from urllib.parse import urlparse, urlencode
+ from urllib.request import urlopen, Request
+ from urllib.error import HTTPError
+ except ImportError:
+ from urlparse import urlparse
+ from urllib import urlencode
+ from urllib2 import urlopen, Request, HTTPError
+Tkinter
+~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ import Tkinter
+ import Dialog
+ import FileDialog
+ import ScrolledText
+ import SimpleDialog
+ import Tix
+ import Tkconstants
+ import Tkdnd
+ import tkColorChooser
+ import tkCommonDialog
+ import tkFileDialog
+ import tkFont
+ import tkMessageBox
+ import tkSimpleDialog
+ import ttk
+
+ # Python 2 and 3 (after ``pip install future``):
+ import tkinter
+ import tkinter.dialog
+ import tkinter.filedialog
+ import tkinter.scrolledtext
+ import tkinter.simpledialog
+ import tkinter.tix
+ import tkinter.constants
+ import tkinter.dnd
+ import tkinter.colorchooser
+ import tkinter.commondialog
+ import tkinter.filedialog
+ import tkinter.font
+ import tkinter.messagebox
+ import tkinter.simpledialog
+ import tkinter.ttk
+socketserver
+~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ import SocketServer
+
+ # Python 2 and 3 (after ``pip install future``):
+ import socketserver
+copy\_reg, copyreg
+~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ import copy_reg
+
+ # Python 2 and 3 (after ``pip install future``):
+ import copyreg
+configparser
+~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ from ConfigParser import ConfigParser
+
+ # Python 2 and 3 (after ``pip install configparser``):
+ from configparser import ConfigParser
+queue
+~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ from Queue import Queue, heapq, deque
+
+ # Python 2 and 3 (after ``pip install future``):
+ from queue import Queue, heapq, deque
+repr, reprlib
+~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ from repr import aRepr, repr
+
+ # Python 2 and 3 (after ``pip install future``):
+ from reprlib import aRepr, repr
+UserDict, UserList, UserString
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ from UserDict import UserDict
+ from UserList import UserList
+ from UserString import UserString
+
+ # Python 3 only:
+ from collections import UserDict, UserList, UserString
+
+ # Python 2 and 3: alternative 1
+ from future.moves.collections import UserDict, UserList, UserString
+
+ # Python 2 and 3: alternative 2
+ from six.moves import UserDict, UserList, UserString
+
+ # Python 2 and 3: alternative 3
+ from future.standard_library import install_aliases
+ install_aliases()
+ from collections import UserDict, UserList, UserString
+itertools: filterfalse, zip\_longest
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code:: python
+
+ # Python 2 only:
+ from itertools import ifilterfalse, izip_longest
+
+ # Python 3 only:
+ from itertools import filterfalse, zip_longest
+
+ # Python 2 and 3: alternative 1
+ from future.moves.itertools import filterfalse, zip_longest
+
+ # Python 2 and 3: alternative 2
+ from six.moves import filterfalse, zip_longest
+
+ # Python 2 and 3: alternative 3
+ from future.standard_library import install_aliases
+ install_aliases()
+ from itertools import filterfalse, zip_longest
diff --git a/docs/conf.py b/docs/conf.py
index 6774e308..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
@@ -28,7 +28,14 @@
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode']
+extensions = ['sphinx.ext.autodoc',
+ '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+
+ ]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -44,7 +51,7 @@
# General information about the project.
project = u'Python-Future'
-copyright = u'2013-2014, 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
@@ -52,8 +59,8 @@
#
# if 'dev' in release:
# release = release.split('dev')[0] + 'dev'
-release = '0.12.0-dev'
-version = release # was: '.'.join(release.split('.')[:2])
+# release = '0.12.5-dev'
+# version = release # was: '.'.join(release.split('.')[:2])
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -94,18 +101,18 @@
# 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
# documentation.
html_theme_options = {
# Navigation bar title. (Default: ``project`` value)
- 'navbar_title': "Python-Future",
+ #'navbar_title': "Python-Future",
# Tab name for entire site. (Default: "Site")
- 'navbar_site_name': "All pages",
+ 'navbar_site_name': "Contents",
# A list of tuples containing pages or urls to link to.
# Valid tuples should be in the following forms:
@@ -115,20 +122,21 @@
# Note the "1" or "True" value above as the third argument to indicate
# an arbitrary url.
'navbar_links': [
- ("Overview", "overview.html"),
+ ("Overview", "overview"),
+ ("Cheat Sheet", "compatible_idioms.html", True),
("FAQ", "faq.html", True),
# ("Link", "http://example.com", True),
],
# Render the next and previous page links in navbar. (Default: true)
- 'navbar_sidebarrel': True,
+ 'navbar_sidebarrel': False,
# Render the current pages TOC in the navbar. (Default: true)
'navbar_pagenav': True,
# Global TOC depth for "site" navbar tab. (Default: 1)
# Switching to -1 shows all levels.
- 'globaltoc_depth': 2,
+ 'globaltoc_depth': 3,
# Include hidden TOCs in Site navbar?
#
@@ -175,12 +183,12 @@
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
-html_logo = '_static/python-future-logo.png'
+html_logo = '_static/python-future-logo-textless-transparent.png'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
-#html_favicon = None
+html_favicon = "_static/python-future-icon-32.ico"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
@@ -197,9 +205,12 @@
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
- 'index': ['sidebarlogo.html', 'sidebarintro.html',
- 'sourcelink.html', 'searchbox.html'],
- '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html']
+ '**': ['sidebarintro.html',
+ 'sidebartoc.html',
+ # 'sourcelink.html',
+ #'searchbox.html',
+ ]
+ # '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html']
}
# Additional templates that should be rendered to pages, maps page names to
@@ -216,10 +227,10 @@
#html_split_index = False
# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+html_show_sourcelink = False
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+html_show_sphinx = False
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
@@ -303,7 +314,7 @@
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Python-Future', u'Python-Future Documentation',
- u'Python Charmers', 'Python-Future', 'Easy support for Python 2 and 3',
+ u'Python Charmers', 'Python-Future', 'Easy compatibility for Python 2 and 3',
'Miscellaneous'),
]
diff --git a/docs/contents.rst.inc b/docs/contents.rst.inc
index b645a162..869b3642 100644
--- a/docs/contents.rst.inc
+++ b/docs/contents.rst.inc
@@ -1,25 +1,26 @@
-Contents:
----------
+Contents
+========
.. toctree::
- :maxdepth: 2
+ :maxdepth: 3
+ whatsnew
overview
quickstart
+ compatible_idioms
imports
what_else
automatic_conversion
- porting
- standard_library_incompatibilities
faq
- whatsnew
+ stdlib_incompatibilities
+ older_interfaces
+ changelog
credits
reference
Indices and tables
-------------------
+******************
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
-
diff --git a/docs/conversion_limitations.rst b/docs/conversion_limitations.rst
new file mode 100644
index 00000000..c2b15303
--- /dev/null
+++ b/docs/conversion_limitations.rst
@@ -0,0 +1,27 @@
+.. _futurize-limitations:
+
+Known limitations
+-----------------
+
+``futurize`` and ``pasteurize`` are useful to automate much of the
+work of porting, particularly the boring repetitive text substitutions. They also
+help to flag which parts of the code require attention.
+
+Nevertheless, ``futurize`` and ``pasteurize`` are still incomplete and make
+some mistakes, like 2to3, on which they are based. Please report bugs on
+`GitHub `_. Contributions to
+the ``lib2to3``-based fixers for ``futurize`` and ``pasteurize`` are
+particularly welcome! Please see :ref:`contributing`.
+
+``futurize`` doesn't currently make the following change automatically:
+
+1. Strings containing ``\U`` produce a ``SyntaxError`` on Python 3. An example is::
+
+ s = 'C:\Users'.
+
+ Python 2 expands this to ``s = 'C:\\Users'``, but Python 3 requires a raw
+ prefix (``r'...'``). This also applies to multi-line strings (including
+ multi-line docstrings).
+
+Also see the tests in ``future/tests/test_futurize.py`` marked
+``@expectedFailure`` or ``@skip`` for known limitations.
diff --git a/docs/credits.rst b/docs/credits.rst
index dc4936e3..4c029efd 100644
--- a/docs/credits.rst
+++ b/docs/credits.rst
@@ -1,46 +1,25 @@
-Credits
-=======
-
-:Author: Ed Schofield
-:Sponsor: Python Charmers Pty Ltd, Australia, and Python Charmers Pte
- Ltd, Singapore. http://pythoncharmers.com
-:Others: - The backported ``super()`` and ``range()`` functions are
- derived from Ryan Kelly's ``magicsuper`` module and Dan
- Crosta's ``xrange`` module.
- - The ``futurize`` and ``pasteurize`` scripts use ``lib2to3``,
- ``lib3to2``, and parts of Armin Ronacher's ``python-modernize``
- code.
- - The ``python_2_unicode_compatible`` decorator is from
- Django. The ``implements_iterator`` and ``with_metaclass``
- decorators are from Jinja2.
- - The ``exec_`` function and some others in ``future.utils``
- are from the ``six`` module by Benjamin Peterson.
- - The ``raise_`` and ``raise_with_traceback`` functions were
- contributed by Jeff Tratner.
- - Documentation is generated with ``sphinx`` using the
- ``sphinx-bootstrap`` theme.
- - ``past.translation`` is inspired by and borrows some code from
- Sanjay Vinip's ``uprefix`` module.
+Licensing and credits
+=====================
.. _licence:
-Licensing
----------
+Licence
+-------
The software is distributed under an MIT licence. The text is as follows
-(from LICENSE.txt)::
+(from ``LICENSE.txt``)::
+
+ Copyright (c) 2013-2024 Python Charmers, Australia
- Copyright (c) 2013-2014 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
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
-
+
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -49,3 +28,154 @@ The software is distributed under an MIT licence. The text is as follows
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+.. _sponsor:
+
+Sponsors
+--------
+
+Python Charmers: https://pythoncharmers.com
+
+.. _authors:
+
+Author
+-------
+
+Python-Future was largely written by Ed Schofield .
+
+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
+- Michael Joseph
+- Waldemar Kornewald
+- Alexey Kotlyarov
+- Steve Kowalik
+- Lion Krischer
+- Marcin Kuzminski
+- Joshua Landau
+- German Larrain
+- Chris Lasher
+- ghanshyam lele
+- Calum Lind
+- Tobias Megies
+- Anika Mukherji
+- 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
+- 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)
+- zihzihtw (GitHub user)
+
+Suggestions and Feedback
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Chris Adams
+- Martijn Faassen
+- Joe Gordon
+- Lion Krischer
+- Danielle Madeley
+- Val Markovic
+- wluebbe (GitHub user)
+
+
+Other Credits
+-------------
+
+- The backported ``super()`` and ``range()`` functions are derived from Ryan
+ Kelly's ``magicsuper`` module and Dan Crosta's ``xrange`` module.
+
+- The ``futurize`` and ``pasteurize`` scripts use ``lib2to3``, ``lib3to2``, and
+ parts of Armin Ronacher's ``python-modernize`` code.
+
+- The ``python_2_unicode_compatible`` decorator is from Django. The
+ ``implements_iterator`` and ``with_metaclass`` decorators are from Jinja2.
+
+- The ``exec_`` function and some others in ``future.utils`` are from the
+ ``six`` module by Benjamin Peterson.
+
+- The ``raise_`` and ``raise_with_traceback`` functions were contributed by
+ Jeff Tratner.
+
+- A working version of ``raise_from`` was contributed by Varriount (GitHub).
+
+- Documentation is generated with `Sphinx `_ using the
+ ``sphinx-bootstrap`` theme.
+
+- ``past.translation`` is inspired by and borrows some code from Sanjay Vinip's
+ ``uprefix`` module.
diff --git a/docs/custom_iterators.rst b/docs/custom_iterators.rst
index 32e55195..6ff389a4 100644
--- a/docs/custom_iterators.rst
+++ b/docs/custom_iterators.rst
@@ -3,16 +3,18 @@
Custom iterators
----------------
-If you define your own iterators, there is an incompatibility in the special method name
-across Py3 and Py2. On Python 3 it is ``__next__``, whereas on Python 2 it is
-``next``.
+If you define your own iterators, there is an incompatibility in the method name
+to retrieve the next item across Py3 and Py2. On Python 3 it is ``__next__``,
+whereas on Python 2 it is ``next``.
The most elegant solution to this is to derive your custom iterator class from
-``future.builtins.object``. This provides a special fallback ``next`` method
-that maps to ``__next__``. Use it as follows::
+``builtins.object`` and define a ``__next__`` method as you normally
+would on Python 3. On Python 2, ``object`` then refers to the
+``future.types.newobject`` base class, which provides a fallback ``next``
+method that calls your ``__next__``. Use it as follows::
+
+ from builtins import object
- from future.builtins import object
-
class Upper(object):
def __init__(self, iterable):
self._iter = iter(iterable)
@@ -27,11 +29,12 @@ that maps to ``__next__``. Use it as follows::
assert list(itr) == list('LLO')
-This works unless you are defining a subclass of a base class defined elsewhere
-that does not derive from ``future.builtins.object``.
+You can use this approach unless you are defining a custom iterator as a
+subclass of a base class defined elsewhere that does not derive from
+``newobject``. In that case, you can provide compatibility across
+Python 2 and Python 3 using the ``next`` function from ``future.builtins``::
-In this case, you can provide compatibility across Python 2 and Python 3 using the ``next``
-function in ``future.builtins``::
+ from builtins import next
from some_module import some_base_class
@@ -43,8 +46,6 @@ function in ``future.builtins``::
def __iter__(self):
return self
- from future.builtins import next
-
itr2 = Upper2('hello')
assert next(itr2) == 'H'
assert next(itr2) == 'E'
@@ -55,17 +56,17 @@ function in ``future.builtins``::
assert 'next' in dir(itr3)
assert next(itr3) == 'one'
-This works whenever your code calls the ``next()`` function explicitly. If you
-consume the iterator implicitly in a ``for`` loop or ``list()`` call or by some
-other method, the ``future.builtins.next`` function will not help; the third
-assertion below would fail on Python 2::
+This approach is feasible whenever your code calls the ``next()`` function
+explicitly. If you consume the iterator implicitly in a ``for`` loop or
+``list()`` call or by some other means, the ``future.builtins.next`` function
+will not help; the third assertion below would fail on Python 2::
itr2 = Upper2('hello')
assert next(itr2) == 'H'
assert next(itr2) == 'E'
assert list(itr2) == list('LLO') # fails because Py2 implicitly looks
- # for a ``__next__`` method.
+ # for a ``next`` method.
Instead, you can use a decorator called ``implements_iterator`` from
``future.utils`` to allow Py3-style iterators to work identically on Py2, even
@@ -91,4 +92,3 @@ the iterator as follows::
return self
On Python 3, as usual, this decorator does nothing.
-
diff --git a/docs/dev_notes.rst b/docs/dev_notes.rst
new file mode 100644
index 00000000..6985bca4
--- /dev/null
+++ b/docs/dev_notes.rst
@@ -0,0 +1,16 @@
+Notes
+-----
+This module only supports Python 2.7, and Python 3.4+.
+
+The following renames are already supported on Python 2.7 without any
+additional work from us::
+
+ reload() -> imp.reload()
+ reduce() -> functools.reduce()
+ StringIO.StringIO -> io.StringIO
+ Bytes.BytesIO -> io.BytesIO
+
+Old things that can one day be fixed automatically by futurize.py::
+
+ string.uppercase -> string.ascii_uppercase # works on either Py2.7 or Py3+
+ sys.maxint -> sys.maxsize # but this isn't identical
diff --git a/docs/development.rst b/docs/development.rst
new file mode 100644
index 00000000..a12f2ca5
--- /dev/null
+++ b/docs/development.rst
@@ -0,0 +1,19 @@
+.. developer-docs
+
+Developer docs
+==============
+
+The easiest way to start developing ``python-future`` is as follows:
+
+1. Install Anaconda Python distribution
+
+2. Run::
+
+ conda install -n future2 python=2.7 pip
+ conda install -n future3 python=3.4 pip
+
+ git clone https://github.com/PythonCharmers/python-future
+
+3. If you are using Anaconda Python distribution, this comes without a ``test``
+module on Python 2.x. Copy ``Python-2.7.6/Lib/test`` from the Python source tree
+to ``~/anaconda/envs/yourenvname/lib/python2.7/site-packages/`.
diff --git a/docs/dict_object.rst b/docs/dict_object.rst
index b4c54e16..165cf763 100644
--- a/docs/dict_object.rst
+++ b/docs/dict_object.rst
@@ -10,55 +10,51 @@ methods which return memory-efficient set-like iterator objects, not lists.
If your dictionaries are small, performance is not critical, and you don't need
the set-like behaviour of iterator objects from Python 3, you can of course
stick with standard Python 3 code in your Py2/3 compatible codebase::
-
+
# Assuming d is a native dict ...
- for item in d:
+ for key in d:
# code here
for item in d.items():
# code here
-
+
for value in d.values():
# code here
-In this case there will be memory overhead of list creation for each call to
-``items``, ``values`` or ``keys``.
+In this case there will be memory overhead of list creation on Py2 for each
+call to ``items``, ``values`` or ``keys``.
-For improved efficiency, ``future.builtins`` provides a Python 2 ``dict``
-subclass whose :func:`keys`, :func:`values`, and :func:`items` methods return
-iterators on all versions of Python >= 2.6. On Python 2.7, these iterators also
-have the same set-like view behaviour as dictionaries in Python 3. This can
-streamline code that iterates over large dictionaries. For example::
+For improved efficiency, ``future.builtins`` (aliased to ``builtins``) provides
+a Python 2 ``dict`` subclass whose :func:`keys`, :func:`values`, and
+:func:`items` methods return iterators on all versions of Python >= 2.7. On
+Python 2.7, these iterators also have the same set-like view behaviour as
+dictionaries in Python 3. This can streamline code that iterates over large
+dictionaries. For example::
from __future__ import print_function
- from future.builtins import dict, range
-
+ from builtins import dict, range
+
# Memory-efficient construction:
d = dict((i, i**2) for i in range(10**7))
-
+
assert not isinstance(d.items(), list)
-
+
# Because items() is memory-efficient, so is this:
d2 = dict((v, k) for (k, v) in d.items())
-
-On Python 2.6, these methods currently return iterators but do not support the
-new Py3 set-like behaviour.
-
-As usual, on Python 3 ``future.builtins.dict`` is just the built-in ``dict``
-class.
+As usual, on Python 3 ``dict`` imported from either ``builtins`` or
+``future.builtins`` is just the built-in ``dict`` class.
Memory-efficiency and alternatives
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you already have large native dictionaries, the downside to wrapping them in
-a ``dict`` call is that memory is copied (on both Py3 and with
-``future.builtins.dict``). For example::
+a ``dict`` call is that memory is copied (on both Py3 and on Py2). For
+example::
- # Currently, this allocates and then frees a large amount of temporary
- # memory:
+ # This allocates and then frees a large amount of temporary memory:
d = dict({i: i**2 for i in range(10**7)})
If dictionary methods like ``values`` and ``items`` are called only once, this
@@ -70,10 +66,10 @@ The memory-efficient (and CPU-efficient) alternatives are:
- to construct a dictionary from an iterator. The above line could use a
generator like this::
- d = dict((i, i**2) for i in range(10**7)
+ d = dict((i, i**2) for i in range(10**7))
- to construct an empty dictionary with a ``dict()`` call using
- ``future.builtins.dict`` (rather than ``{}``) and then update it;
+ ``builtins.dict`` (rather than ``{}``) and then update it;
- to use the ``viewitems`` etc. functions from :mod:`future.utils`, passing in
regular dictionaries::
@@ -82,16 +78,15 @@ The memory-efficient (and CPU-efficient) alternatives are:
for (key, value) in viewitems(hugedictionary):
# some code here
-
+
# Set intersection:
d = {i**2: i for i in range(1000)}
both = viewkeys(d) & set(range(0, 1000, 7))
-
+
# Set union:
both = viewvalues(d1) | viewvalues(d2)
-For Python 2.6 compatibility, the functions ``iteritems`` etc. are also
-available in :mod:`future.utils`. These are equivalent to the functions of the
-same names in ``six``, which is equivalent to calling the ``iteritems`` etc.
-methods on Python 2, or to calling ``items`` etc. on Python 3.
-
+For compatibility, the functions ``iteritems`` etc. are also available in
+:mod:`future.utils`. These are equivalent to the functions of the same names in
+``six``, which is equivalent to calling the ``iteritems`` etc. methods on
+Python 2, or to calling ``items`` etc. on Python 3.
diff --git a/docs/faq.rst b/docs/faq.rst
index 94c234ab..e49adf61 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -5,14 +5,10 @@ Who is this for?
================
1. People with existing or new Python 3 codebases who wish to provide
-ongoing Python 2.6 / 2.7 support easily and with little maintenance burden.
+ongoing Python 2.7 support easily and with little maintenance burden.
-2. People who wish to simplify migration of their codebases to Python
-3.3+, module by module, without giving up Python 2 compatibility.
-
-.. 3. People who would prefer to write clean, future-proof Python
-.. 3-compatible code, but who are required to write code that still runs
-.. on a Python 2 stack.
+2. People who wish to ease and accelerate migration of their Python 2 codebases
+to Python 3.3+, module by module, without giving up Python 2 compatibility.
Why upgrade to Python 3?
@@ -29,22 +25,22 @@ Python 2.7 is the end of the Python 2 line. (See `PEP 404
libraries are improving only in Python 3.x.
Python 3.x is a better language and better set of standard libraries than
-Python 2.x in almost every way. Python 3.x is cleaner, less warty, and easier to
+Python 2.x in many ways. Python 3.x is cleaner, less warty, and easier to
learn than Python 2. It has better memory efficiency, easier Unicode handling,
-and powerful new features like function annotations and the `asyncio
-`_ module.
+and powerful new features like the `asyncio
+`_ module.
.. Unicode handling is also much easier. For example, see `this page
.. `_
.. describing some of the problems with handling Unicode on Python 2 that
-.. Python 3 mostly solves.
+.. Python 3 mostly solves.
Porting philosophy
==================
-Why use this approach?
-----------------------
+Why write Python 3-style code?
+------------------------------
Here are some quotes:
@@ -72,8 +68,8 @@ Here are some quotes:
Can't I just roll my own Py2/3 compatibility layer?
---------------------------------------------------
-Yes, but using ``future`` will probably lead to cleaner code with fewer
-bugs.
+Yes, but using ``python-future`` will probably be easier and lead to cleaner
+code with fewer bugs.
Consider this quote:
@@ -88,65 +84,73 @@ Consider this quote:
``future`` also includes various Py2/3 compatibility tools in
:mod:`future.utils` picked from large projects (including IPython,
-Django, Jinja2, Pandas), which should hopefully reduce the burden on
-every project to roll its own py3k compatibility wrapper module.
+Django, Jinja2, Pandas), which should reduce the burden on every project to
+roll its own py3k compatibility wrapper module.
+
+What inspired this project?
+---------------------------
-How did the original need for this arise?
------------------------------------------
+In our Python training courses, we at `Python Charmers
+`_ 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
+searched for ways to avoid polluting the world with more deprecated code, but
+didn't find a good way.
-In teaching Python, we at Python Charmers 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 searched for ways to avoid polluting the world
-with more deprecated code, but didn't find a good way.
+Also, in attempting to help with porting packages such as `scikit-learn
+`_ to Python 3, I (Ed) was dissatisfied with how much
+code cruft was necessary to introduce to support Python 2 and 3 from a single
+codebase (the preferred porting option). Since backward-compatibility with
+Python 2 may be necessary for at least the next 5 years, one of the promised
+benefits of Python 3 -- cleaner code with fewer of Python 2's warts -- was
+difficult to realize before in practice in a single codebase that supported
+both platforms.
-Also, in attempting to help with porting packages such as
-``scikit-learn`` to Python 3, I was dissatisfied with how much code cruft
-was necessary to introduce to support Python 2 and 3 from a single
-codebase (the preferred porting option). Since backward-compatibility
-with Python 2 may be necessary for at least the next 5 years, one of the
-promised benefits of Python 3 -- cleaner code with fewer of Python 2's
-warts -- was difficult to realize before in practice in a single codebase
-that supported both platforms.
+The goal is to accelerate the uptake of Python 3 and help the strong Python
+community to remain united around a single version of the language.
Maturity
========
-Is it tested?
--------------
+How well has it been tested?
+----------------------------
+
+``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.
+
+In general, the ``future`` package itself is in good shape, whereas the
+``futurize`` script for automatic porting is imperfect; chances are it will
+require some manual cleanup afterwards. The ``past`` package also needs to be
+expanded.
+
-``future`` is used by ``mezzanine``, among other projects. Currently
-``future`` has 640+ unit tests. Many of these are straight from the Python 3.3
-test suite. In general, the ``future`` package itself is in good shape, whereas
-the ``futurize`` script for automatic porting is incomplete and imperfect.
-(Chances are it will require some manual cleanup afterwards.)
-
Is the API stable?
------------------
-Not yet; ``future`` is still in beta. 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``?
------------------------------------------------------------------------------------------------
-
+
Yes, an example is the port of ``xlwt``, available `here
`_.
-
+
The code also contains backports for several Py3 standard library
modules under ``future/standard_library/``.
-Relationship between ``future`` and other compatibility tools
-=============================================================
+Relationship between python-future and other compatibility tools
+================================================================
-How does this relate to ``2to3`` and ``lib2to3``?
--------------------------------------------------
+How does this relate to ``2to3``?
+---------------------------------
``2to3`` is a powerful and flexible tool that can produce different
styles of Python 3 code. It is, however, primarily designed for one-way
@@ -168,13 +172,11 @@ most inputs; worse, it allows arbitrary code execution by the user
for specially crafted inputs because of the ``eval()`` executed by Python
2's ``input()`` function.
-This is not an isolated example; almost every output of ``2to3`` will
-need modification to provide backward compatibility with Python 2.
-``future`` is designed for just this purpose.
-
-The ``future`` source tree contains a script called ``futurize`` that is
-based on ``lib2to3``. It is designed to turn either Python 2-only or
-Python 3-only code into code that is compatible with both platforms.
+This is not an isolated example; almost every output of ``2to3`` will need
+modification to provide backward compatibility with Python 2. As an
+alternative, the ``python-future`` project provides a script called
+``futurize`` that is based on ``lib2to3`` but will produce code that is
+compatible with both platforms (Py2 and Py3).
Can I maintain a Python 2 codebase and use 2to3 to automatically convert to Python 3 in the setup script?
@@ -182,12 +184,12 @@ Can I maintain a Python 2 codebase and use 2to3 to automatically convert to Pyth
This was originally the approach recommended by Python's core developers,
but it has some large drawbacks:
-
+
1. First, your actual working codebase will be stuck with Python 2's
warts and smaller feature set for as long as you need to retain Python 2
compatibility. This may be at least 5 years for many projects, possibly
much longer.
-
+
2. Second, this approach carries the significant disadvantage that you
cannot apply patches submitted by Python 3 users against the
auto-generated Python 3 code. (See `this talk
@@ -197,41 +199,44 @@ auto-generated Python 3 code. (See `this talk
What is the relationship between ``future`` and ``six``?
--------------------------------------------------------
-``future`` is a higher-level compatibility layer than ``six`` that
-includes more backported functionality from Python 3 and supports cleaner
-code but requires more modern Python versions to run.
+``python-future`` is a higher-level compatibility layer than ``six`` that
+includes more backported functionality from Python 3, more forward-ported
+functionality from Python 2, and supports cleaner code, but requires more
+modern Python versions to run.
-``future`` and ``six`` share the same goal of making it possible to write
+``python-future`` and ``six`` share the same goal of making it possible to write
a single-source codebase that works on both Python 2 and Python 3.
-``future`` has the further goal of allowing standard Py3 code to run with
+``python-future`` has the further goal of allowing standard Py3 code to run with
almost no modification on both Py3 and Py2. ``future`` provides a more
complete set of support for Python 3's features, including backports of
Python 3 builtins such as the ``bytes`` object (which is very different
to Python 2's ``str`` object) and several standard library modules.
-``future`` supports only Python 2.6+ and Python 3.3+, whereas ``six``
+``python-future`` supports only Python 2.7+ and Python 3.4+, whereas ``six``
supports all versions of Python from 2.4 onwards. (See
:ref:`supported-versions`.) If you must support older Python versions,
-``six`` will be esssential for you. However, beware that maintaining
+``six`` will be essential for you. However, beware that maintaining
single-source compatibility with older Python versions is ugly and `not
fun `_.
-If you can drop support for older Python versions, ``future`` leverages
-some important features introduced into Python 2.6 and 2.7, such as
-import hooks, to allow you to write more idiomatic, maintainable code.
+If you can drop support for older Python versions, ``python-future`` leverages
+some important features introduced into Python 2.7, such as
+import hooks, and a comprehensive and well-tested set of backported
+functionality, to allow you to write more idiomatic, maintainable code with
+fewer compatibility hacks.
-What is the relationship between this project and ``python-modernize``?
------------------------------------------------------------------------
+What is the relationship between ``python-future`` and ``python-modernize``?
+----------------------------------------------------------------------------
``python-future`` contains, in addition to the ``future`` compatibility
package, a ``futurize`` script that is similar to ``python-modernize.py``
in intent and design. Both are based heavily on ``2to3``.
-
+
Whereas ``python-modernize`` converts Py2 code into a common subset of
Python 2 and 3, with ``six`` as a run-time dependency, ``futurize``
converts either Py2 or Py3 code into (almost) standard Python 3 code,
-with ``future`` as a run-time dependency.
+with ``future`` as a run-time dependency.
Because ``future`` provides more backported Py3 behaviours from ``six``,
the code resulting from ``futurize`` is more likely to work
@@ -244,22 +249,17 @@ Platform and version support
.. _supported-versions:
-Which versions of Python does ``future`` support?
--------------------------------------------------
-
-Python 2.6, 2.7, and 3.3+ only.
+Which versions of Python does ``python-future`` support?
+--------------------------------------------------------
-Python 2.6 and 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.6 / 2.7.
+Python 2.6 and 3.3+ only. Python 2.7 and Python 3.4+ are preferred.
-Python 3.2 could perhaps be supported too, although the illegal unicode
-literal ``u'...'`` syntax may be inconvenient to work around. The Py3.2
-userbase is very small, however. Please let us know via GitHub `issue #29
-`_ if you
-would like to see Py3.2 support.
+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?
@@ -270,7 +270,7 @@ and pull requests are welcome!
Do you support IronPython and/or Jython?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Not sure. This would be nice...
@@ -283,8 +283,7 @@ Support
Is there a mailing list?
------------------------
-Yes, please ask any questions on the `python-porting
-`_ mailing list.
+There was a `python-porting` mailing list, but it's now dead.
.. _contributing:
@@ -296,12 +295,13 @@ 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?
------------------
``_.
-
diff --git a/docs/features_incomplete.rst b/docs/features_incomplete.rst
deleted file mode 100644
index 7ad88bb0..00000000
--- a/docs/features_incomplete.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-.. _future-builtins:
-
-``future.builtins``
-===================
-
-- ``pow()`` supports fractional exponents of negative numbers like in Py3::
-
- >>> from future.builtins import pow
- >>> pow(-1, 0.5)
- (6.123233995736766e-17+1j)
-
-- ``round()`` uses Banker's Rounding as in Py3 to the nearest even last digit::
-
- >>> from future.builtins import round
- >>> assert round(0.1250, 2) == 0.12
-
diff --git a/docs/future-builtins.rst b/docs/future-builtins.rst
index 7ad88bb0..df8ff79d 100644
--- a/docs/future-builtins.rst
+++ b/docs/future-builtins.rst
@@ -3,14 +3,15 @@
``future.builtins``
===================
+The ``future.builtins`` module is also accessible as ``builtins`` on Py2.
+
- ``pow()`` supports fractional exponents of negative numbers like in Py3::
- >>> from future.builtins import pow
+ >>> from builtins import pow
>>> pow(-1, 0.5)
(6.123233995736766e-17+1j)
- ``round()`` uses Banker's Rounding as in Py3 to the nearest even last digit::
- >>> from future.builtins import round
+ >>> from builtins import round
>>> assert round(0.1250, 2) == 0.12
-
diff --git a/docs/futurize.rst b/docs/futurize.rst
new file mode 100644
index 00000000..11520a6c
--- /dev/null
+++ b/docs/futurize.rst
@@ -0,0 +1,314 @@
+.. _forwards-conversion:
+
+``futurize``: Py2 to Py2/3
+--------------------------
+
+.. include:: futurize_overview.rst
+
+
+.. _forwards-conversion-stage1:
+
+Stage 1: "safe" fixes
+~~~~~~~~~~~~~~~~~~~~~
+
+Run the first stage of the conversion process with::
+
+ futurize --stage1 mypackage/*.py
+
+or, if you are using zsh, recursively::
+
+ futurize --stage1 mypackage/**/*.py
+
+This applies fixes that modernize Python 2 code without changing the effect of
+the code. With luck, this will not introduce any bugs into the code, or will at
+least be trivial to fix. The changes are those that bring the Python code
+up-to-date without breaking Py2 compatibility. The resulting code will be
+modern Python 2.7-compatible code plus ``__future__`` imports from the
+following set:
+
+.. code-block:: python
+
+ from __future__ import absolute_import
+ from __future__ import division
+ from __future__ import print_function
+
+Only those ``__future__`` imports deemed necessary will be added unless
+the ``--all-imports`` command-line option is passed to ``futurize``, in
+which case they are all added.
+
+The ``from __future__ import unicode_literals`` declaration is not added
+unless the ``--unicode-literals`` flag is passed to ``futurize``.
+
+The changes include::
+
+ - except MyException, e:
+ + except MyException as e:
+
+ - print >>stderr, "Blah"
+ + from __future__ import print_function
+ + print("Blah", stderr)
+
+ - class MyClass:
+ + class MyClass(object):
+
+ - def next(self):
+ + def __next__(self):
+
+ - if d.has_key(key):
+ + if key in d:
+
+Implicit relative imports fixed, e.g.::
+
+ - import mymodule
+ + from __future__ import absolute_import
+ + from . import mymodule
+
+.. and all unprefixed string literals '...' gain a b prefix to be b'...'.
+
+.. (This last step can be prevented using --no-bytes-literals if you already have b'...' markup in your code, whose meaning would otherwise be lost.)
+
+Stage 1 does not add any imports from the ``future`` package. The output of
+stage 1 will probably not (yet) run on Python 3.
+
+The goal for this stage is to create most of the ``diff`` for the entire
+porting process, but without introducing any bugs. It should be uncontroversial
+and safe to apply to every Python 2 package. The subsequent patches introducing
+Python 3 compatibility should then be shorter and easier to review.
+
+The complete set of fixers applied by ``futurize --stage1`` is:
+
+.. code-block:: python
+
+ 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
+ lib2to3.fixes.fix_idioms
+ lib2to3.fixes.fix_intern
+ lib2to3.fixes.fix_isinstance
+ lib2to3.fixes.fix_methodattrs
+ lib2to3.fixes.fix_ne
+ lib2to3.fixes.fix_numliterals
+ lib2to3.fixes.fix_paren
+ lib2to3.fixes.fix_reduce
+ lib2to3.fixes.fix_renames
+ lib2to3.fixes.fix_repr
+ lib2to3.fixes.fix_standarderror
+ lib2to3.fixes.fix_sys_exc
+ lib2to3.fixes.fix_throw
+ lib2to3.fixes.fix_tuple_params
+ lib2to3.fixes.fix_types
+ lib2to3.fixes.fix_ws_comma
+ lib2to3.fixes.fix_xreadlines
+ libfuturize.fixes.fix_absolute_import
+ libfuturize.fixes.fix_next_call
+ libfuturize.fixes.fix_print_with_import
+ libfuturize.fixes.fix_raise
+
+The following fixers from ``lib2to3`` are not applied:
+
+.. code-block:: python
+
+ lib2to3.fixes.fix_import
+
+The ``fix_absolute_import`` fixer in ``libfuturize.fixes`` is applied instead of
+``lib2to3.fixes.fix_import``. The new fixer both makes implicit relative
+imports explicit and adds the declaration ``from __future__ import
+absolute_import`` at the top of each relevant module.
+
+.. code-block:: python
+
+ lib2to3.fixes.fix_next
+
+The ``fix_next_call`` fixer in ``libfuturize.fixes`` is applied instead of
+``fix_next`` in stage 1. The new fixer changes any ``obj.next()`` calls to
+``next(obj)``, which is Py2/3 compatible, but doesn't change any ``next`` method
+names to ``__next__``, which would break Py2 compatibility.
+
+``fix_next`` is applied in stage 2.
+
+.. code-block:: python
+
+ lib2to3.fixes.fix_print
+
+The ``fix_print_with_import`` fixer in ``libfuturize.fixes`` changes the code to
+use print as a function and also adds ``from __future__ import
+print_function`` to the top of modules using ``print()``.
+
+In addition, it avoids adding an extra set of parentheses if these already
+exist. So ``print(x)`` does not become ``print((x))``.
+
+.. code-block:: python
+
+ lib2to3.fixes.fix_raise
+
+This fixer translates code to use the Python 3-only ``with_traceback()``
+method on exceptions.
+
+.. code-block:: python
+
+ lib2to3.fixes.fix_set_literal
+
+This converts ``set([1, 2, 3]``) to ``{1, 2, 3}``.
+
+.. code-block:: python
+
+ lib2to3.fixes.fix_ws_comma
+
+This performs cosmetic changes. This is not applied by default because it
+does not serve to improve Python 2/3 compatibility. (In some cases it may
+also reduce readability: see issue #58.)
+
+
+
+.. _forwards-conversion-stage2:
+
+Stage 2: Py3-style code with wrappers for Py2
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Run stage 2 of the conversion process with::
+
+ futurize --stage2 myfolder/*.py
+
+This stage adds a dependency on the ``future`` package. The goal for stage 2 is
+to make further mostly safe changes to the Python 2 code to use Python 3-style
+code that then still runs on Python 2 with the help of the appropriate builtins
+and utilities in ``future``.
+
+For example::
+
+ name = raw_input('What is your name?\n')
+
+ for k, v in d.iteritems():
+ assert isinstance(v, basestring)
+
+ class MyClass(object):
+ def __unicode__(self):
+ return u'My object'
+ def __str__(self):
+ return unicode(self).encode('utf-8')
+
+would be converted by Stage 2 to this code::
+
+ from builtins import input
+ from builtins import str
+ from future.utils import iteritems, python_2_unicode_compatible
+
+ name = input('What is your name?\n')
+
+ for k, v in iteritems(d):
+ assert isinstance(v, (str, bytes))
+
+ @python_2_unicode_compatible
+ class MyClass(object):
+ def __str__(self):
+ return u'My object'
+
+Stage 2 also renames standard-library imports to their Py3 names and adds these
+two lines::
+
+ from future import standard_library
+ standard_library.install_aliases()
+
+For example::
+
+ import ConfigParser
+
+becomes::
+
+ from future import standard_library
+ standard_library.install_aliases()
+ import configparser
+
+The complete list of fixers applied in Stage 2 is::
+
+ lib2to3.fixes.fix_dict
+ lib2to3.fixes.fix_filter
+ lib2to3.fixes.fix_getcwdu
+ lib2to3.fixes.fix_input
+ lib2to3.fixes.fix_itertools
+ lib2to3.fixes.fix_itertools_imports
+ 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_safe
+ libfuturize.fixes.fix_execfile
+ libfuturize.fixes.fix_future_builtins
+ libfuturize.fixes.fix_future_standard_library
+ libfuturize.fixes.fix_future_standard_library_urllib
+ libfuturize.fixes.fix_metaclass
+ libpasteurize.fixes.fix_newstyle
+ libfuturize.fixes.fix_object
+ libfuturize.fixes.fix_unicode_keep_u
+ libfuturize.fixes.fix_xrange_with_import
+
+
+Not applied::
+
+ lib2to3.fixes.fix_buffer # Perhaps not safe. Test this.
+ lib2to3.fixes.fix_callable # Not needed in Py3.2+
+ lib2to3.fixes.fix_execfile # Some problems: see issue #37.
+ # We use the custom libfuturize.fixes.fix_execfile instead.
+ lib2to3.fixes.fix_future # Removing __future__ imports is bad for Py2 compatibility!
+ 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_metaclass # Causes SyntaxError in Py2! Use the one from ``six`` instead
+ lib2to3.fixes.fix_unicode # Strips off the u'' prefix, which removes a potentially
+ # helpful source of information for disambiguating
+ # unicode/byte strings.
+ lib2to3.fixes.fix_urllib # Included in libfuturize.fix_future_standard_library_urllib
+ lib2to3.fixes.fix_xrange # Custom one because of a bug with Py3.3's lib2to3
+
+
+
+.. Ideally the output of this stage should not be a ``SyntaxError`` on either
+.. Python 3 or Python 2.
+
+.. _forwards-conversion-text:
+
+Separating text from bytes
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After applying stage 2, the recommended step is to decide which of your Python
+2 strings represent text and which represent binary data and to prefix all
+string literals with either ``b`` or ``u`` accordingly. Furthermore, to ensure
+that these types behave similarly on Python 2 as on Python 3, also wrap
+byte-strings or text in the ``bytes`` and ``str`` types from ``future``. For
+example::
+
+ from builtins import bytes, str
+ b = bytes(b'\x00ABCD')
+ s = str(u'This is normal text')
+
+Any unadorned string literals will then represent native platform strings
+(byte-strings on Py2, unicode strings on Py3).
+
+An alternative is to pass the ``--unicode-literals`` flag::
+
+ $ futurize --unicode-literals mypython2script.py
+
+After running this, all string literals that were not explicitly marked up as
+``b''`` will mean text (Python 3 ``str`` or Python 2 ``unicode``).
+
+
+
+.. _forwards-conversion-stage3:
+
+Post-conversion
+~~~~~~~~~~~~~~~
+
+After running ``futurize``, we recommend first running your tests on Python 3 and making further code changes until they pass on Python 3.
+
+The next step would be manually tweaking the code to re-enable Python 2
+compatibility with the help of the ``future`` package. For example, you can add
+the ``@python_2_unicode_compatible`` decorator to any classes that define custom
+``__str__`` methods. See :ref:`what-else` for more info.
diff --git a/docs/futurize_cheatsheet.rst b/docs/futurize_cheatsheet.rst
new file mode 100644
index 00000000..82f211c6
--- /dev/null
+++ b/docs/futurize_cheatsheet.rst
@@ -0,0 +1,124 @@
+.. _futurize_cheatsheet:
+
+``futurize`` quick-start guide
+------------------------------
+
+How to convert Py2 code to Py2/3 code using ``futurize``:
+
+.. _porting-setup:
+
+Step 0: setup
+~~~~~~~~~~~~~
+
+Step 0 goal: set up and see the tests passing on Python 2 and failing on Python 3.
+
+a. Clone the package from github/bitbucket. Optionally rename your repo to ``package-future``. Examples: ``reportlab-future``, ``paramiko-future``, ``mezzanine-future``.
+b. Create and activate a Python 2 conda environment or virtualenv. Install the package with ``python setup.py install`` and run its test suite on Py2.7 (e.g. ``python setup.py test`` or ``py.test``)
+c. Optionally: if there is a ``.travis.yml`` file, add Python version 3.6 and remove any versions < 2.6.
+d. Install Python 3 with e.g. ``sudo apt-get install python3``. On other platforms, an easy way is to use `Miniconda `_. Then e.g.::
+
+ conda create -n py36 python=3.6 pip
+
+.. _porting-step1:
+
+Step 1: modern Py2 code
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The goal for this step is to modernize the Python 2 code without introducing any dependencies (on ``future`` or e.g. ``six``) at this stage.
+
+**1a**. Install ``future`` into the virtualenv using::
+
+ 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 source files
+recursively with::
+
+ futurize --stage1 -w .
+
+**1c**. Commit all changes
+
+**1d**. Re-run the test suite on Py2 and fix any errors.
+
+See :ref:`forwards-conversion-stage1` for more info.
+
+
+Example error
+*************
+
+One relatively common error after conversion is::
+
+ Traceback (most recent call last):
+ ...
+ File "/home/user/Install/BleedingEdge/reportlab/tests/test_encrypt.py", line 19, in
+ from .test_pdfencryption import parsedoc
+ ValueError: Attempted relative import in non-package
+
+If you get this error, try adding an empty ``__init__.py`` file in the package
+directory. (In this example, in the tests/ directory.) If this doesn’t help,
+and if this message appears for all tests, they must be invoked differently
+(from the cmd line or e.g. ``setup.py``). The way to run a module inside a
+package on Python 3, or on Python 2 with ``absolute_import`` in effect, is::
+
+ python -m tests.test_platypus_xref
+
+(For more info, see `PEP 328 `_ and
+the `PEP 8 `_ section on absolute
+imports.)
+
+
+.. _porting-step2:
+
+Step 2: working Py3 code that still supports Py2
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The goal for this step is to get the tests passing first on Py3 and then on Py2
+again with the help of the ``future`` package.
+
+**2a**. Run::
+
+ futurize --stage2 myfolder1/*.py myfolder2/*.py
+
+You can view the stage 2 changes to all Python source files recursively with::
+
+ futurize --stage2 .
+
+To apply the changes, add the ``-w`` argument.
+
+This stage makes further conversions needed to support both Python 2 and 3.
+These will likely require imports from ``future`` on Py2 (and sometimes on Py3),
+such as::
+
+ from future import standard_library
+ standard_library.install_aliases()
+ # ...
+ from builtins import bytes
+ from builtins import open
+ from future.utils import with_metaclass
+
+Optionally, you can use the ``--unicode-literals`` flag to add this import to
+the top of each module::
+
+ from __future__ import unicode_literals
+
+All strings in the module would then be unicode on Py2 (as on Py3) unless
+explicitly marked with a ``b''`` prefix.
+
+If you would like ``futurize`` to import all the changed builtins to have their
+Python 3 semantics on Python 2, invoke it like this::
+
+ futurize --stage2 --all-imports myfolder/*.py
+
+
+**2b**. Re-run your tests on Py3 now. Make changes until your tests pass on Python 3.
+
+**2c**. Commit your changes! :)
+
+**2d**. Now run your tests on Python 2 and notice the errors. Add wrappers from
+``future`` to re-enable Python 2 compatibility. See the
+:ref:`compatible-idioms` cheat sheet and :ref:`what-else` for more info.
+
+After each change, re-run the tests on Py3 and Py2 to ensure they pass on both.
+
+**2e**. You're done! Celebrate! Push your code and announce to the world! Hashtags
+#python3 #python-future.
diff --git a/docs/futurize_overview.rst b/docs/futurize_overview.rst
new file mode 100644
index 00000000..769b65c7
--- /dev/null
+++ b/docs/futurize_overview.rst
@@ -0,0 +1,55 @@
+The ``futurize`` script passes Python 2 code through all the appropriate fixers
+to turn it into valid Python 3 code, and then adds ``__future__`` and
+``future`` package imports to re-enable compatibility with Python 2.
+
+For example, running ``futurize`` turns this Python 2 code:
+
+.. code-block:: python
+
+ import ConfigParser # Py2 module name
+
+ class Upper(object):
+ def __init__(self, iterable):
+ self._iter = iter(iterable)
+ def next(self): # Py2-style iterator interface
+ return next(self._iter).upper()
+ def __iter__(self):
+ return self
+
+ itr = Upper('hello')
+ print next(itr),
+ for letter in itr:
+ print letter, # Py2-style print statement
+
+into this code which runs on both Py2 and Py3:
+
+.. code-block:: python
+
+ from __future__ import print_function
+ from future import standard_library
+ standard_library.install_aliases()
+ from future.builtins import next
+ from future.builtins import object
+ import configparser # Py3-style import
+
+ class Upper(object):
+ def __init__(self, iterable):
+ self._iter = iter(iterable)
+ def __next__(self): # Py3-style iterator interface
+ return next(self._iter).upper()
+ def __iter__(self):
+ return self
+
+ itr = Upper('hello')
+ print(next(itr), end=' ') # Py3-style print function
+ for letter in itr:
+ print(letter, end=' ')
+
+
+To write out all the changes to your Python files that ``futurize`` suggests,
+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 dependency on the ``future`` package. Stage 2
+is to complete the process.
diff --git a/docs/hindsight.rst b/docs/hindsight.rst
index a7b283a1..b4654c6a 100644
--- a/docs/hindsight.rst
+++ b/docs/hindsight.rst
@@ -1,4 +1,3 @@
In a perfect world, the new metaclass syntax should ideally be available in
Python 2 as a `__future__`` import like ``from __future__ import
new_metaclass_syntax``.
-
diff --git a/docs/imports.rst b/docs/imports.rst
index 6293a55a..f7dcd9fc 100644
--- a/docs/imports.rst
+++ b/docs/imports.rst
@@ -3,7 +3,7 @@
Imports
=======
-.. ___future__-imports:
+.. _-__future__-imports:
__future__ imports
------------------
@@ -24,31 +24,29 @@ standard feature of Python, see the following docs:
- print_function: `PEP 3105: Make print a function `_
- unicode_literals: `PEP 3112: Bytes literals in Python 3000 `_
-These are all available in Python 2.6 and up, and enabled by default in Python 3.x.
+These are all available in Python 2.7 and up, and enabled by default in Python 3.x.
-.. _star-imports:
+.. _builtins-imports:
+
+Imports of builtins
+-------------------
-future imports
---------------
+.. _star-imports:
Implicit imports
~~~~~~~~~~~~~~~~
-If you don't mind namespace pollution on Python 2, the easiest way to provide
-Py2/3 compatibility for new code using ``future`` is to include the following
-imports at the top of every module::
+If you don't mind namespace pollution, the easiest way to provide Py2/3
+compatibility for new code using ``future`` is to include the following imports
+at the top of every module::
- from future.builtins import *
+ from builtins import *
-together with these module imports when necessary::
-
- from future import standard_library, utils
+On Python 3, this has no effect. (It shadows builtins with globals of the same
+names.)
-On Python 3, ``from future.builtins import *`` line has zero effect and zero
-namespace pollution.
-
-On Python 2, this import line shadows 16 builtins (listed below) to
+On Python 2, this import line shadows 18 builtins (listed below) to
provide their Python 3 semantics.
@@ -60,136 +58,43 @@ Explicit imports
Explicit forms of the imports are often preferred and are necessary for using
certain automated code-analysis tools.
-The complete set of imports from ``future`` is::
-
- from future import standard_library, utils
- from future.builtins import (ascii, bytes, chr, dict, filter, hex, input,
- int, map, next, oct, open, pow, range, round,
- str, super, zip)
+The complete set of imports of builtins from ``future`` is::
+
+ from builtins import (ascii, bytes, chr, dict, filter, hex, input,
+ int, map, next, oct, open, pow, range, round,
+ str, super, zip)
+These are also available under the ``future.builtins`` namespace for backward compatibility.
-The disadvantage of importing only some of the builtins is that it
-increases the risk of introducing Py2/3 portability bugs as your code
-evolves over time. Be especially aware of not importing ``input``, which could
-expose a security vulnerability on Python 2 if Python 3's semantics are
-expected.
+Importing only some of the builtins is cleaner but increases the risk of
+introducing Py2/3 portability bugs as your code evolves over time. For example,
+be aware of forgetting to import ``input``, which could expose a security
+vulnerability on Python 2 if Python 3's semantics are expected.
-One further technical distinction is that unlike the ``import *`` form above,
-these explicit imports do actually modify ``locals()`` on Py3; this is
-equivalent to typing ``bytes = bytes; int = int`` etc. for each builtin.
+.. One further technical distinction is that unlike the ``import *`` form above,
+.. these explicit imports do actually modify ``locals()`` on Py3; this is
+.. equivalent to typing ``bytes = bytes; int = int`` etc. for each builtin.
The internal API is currently as follows::
- from future.builtins.types import bytes, dict, int, range, str
- from future.builtins.misc import ascii, chr, hex, input, next, oct, open, round, super
+ from future.types import bytes, dict, int, range, str
+ from future.builtins.misc import (ascii, chr, hex, input, next,
+ oct, open, pow, round, super)
from future.builtins.iterators import filter, map, zip
-To understand the details of the backported builtins on Python 2, see the
-docs for these modules. Please note that this internal API is evolving and may
-not be stable between different versions of ``future``.
+Please note that this internal API is evolving and may not be stable between
+different versions of ``future``. To understand the details of the backported
+builtins on Python 2, see the docs for these modules.
+For more information on what the backported types provide, see :ref:`what-else`.
.. < Section about past.translation is included here >
-.. include:: translation.rst
-
-
-.. _standard-library-imports:
-
-Standard library imports
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-:mod:`future` supports the standard library reorganization (PEP 3108)
-via import hooks, allowing almost all moved standard library modules to
-be accessed under their Python 3 names and locations in Python 2.
-
-There are three interfaces to the backported standard library modules. The first
-is via a context-manager called ``hooks``::
-
-The second interface to the standard library modules is via an explicit call to
-``install_hooks``::
-
- from future import standard_library
- standard_library.install_hooks()
-
- import urllib
- f = urllib.request.urlopen('http://www.python.org/')
-
- standard_library.remove_hooks()
-
-It is a good idea to disable the import hooks again after use by calling
-``remove_hooks()``, in order to prevent the futurized modules from being invoked
-inadvertently by other modules. (Python does not automatically disable import
-hooks at the end of a module, but keeps them active indefinitely.)
-
-The third interface avoids import hooks entirely. It may therefore be more
-robust, at the cost of less idiomatic code. Use it as follows::
-
- from future.standard_library import queue
- from future.standard_library import socketserver
- from future.standard_library.http.client import HTTPConnection
- # etc.
-
-If you wish to achieve the effect of a two-level import such as this::
-
- import http.client
-
-portably on both Python 2 and Python 3, you can use this idiom::
-
- from future.standard_library import http
- from future.standard_library.http import client as _client
- http.client = client
-
-This is ugly, but it has the advantage that it can be used by automatic
-translation scripts such as ``futurize`` and ``pasteurize``.
-
-
-List of standard library modules
-________________________________
-
-The modules available are::
-
- import socketserver
- import queue
- import configparser
- import test.support
- import html.parser
- from collections import UserList
- from itertools import filterfalse, zip_longest
- from http.client import HttpConnection
- # and other moved modules and definitions
-
-:mod:`future` also includes backports for these stdlib modules from Py3
-that were heavily refactored versus Py2::
-
- import html
- import html.entities
- import html.parser
-
- import http
- import http.client
- import http.server
-
-The following modules are currently not supported, but we aim to support them in
-the future::
-
- import http.cookies
- import http.cookiejar
-
- import urllib
- import urllib.parse
- import urllib.request
- import urllib.error
-
-If you need one of these, please open an issue `here
-`_.
-
-
.. _obsolete-builtins:
Obsolete Python 2 builtins
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+__________________________
Twelve Python 2 builtins have been removed from Python 3. To aid with
porting code to Python 3 module by module, you can use the following
@@ -210,207 +115,12 @@ equivalent Python 3 forms and then adds ``future`` imports to resurrect
Python 2 support, as described in :ref:`forwards-conversion-stage2`.
-.. _unicode-literals:
-
-Should I import unicode_literals?
----------------------------------
-
-The ``future`` package can be used with or without ``unicode_literals``
-imports.
-
-There is some contention in the community about whether it is advisable
-to import ``unicode_literals`` from ``__future__`` in a Python 2/3
-compatible codebase.
+.. include:: standard_library_imports.rst
-In general, it is more compelling to use ``unicode_literals`` when back-porting
-new or existing Python 3 code to Python 2/3. For porting existing Python 2
-code to 2/3, explicitly marking up all unicode string literals with ``u''``
-prefixes helps to avoid unintentionally changing an existing Python 2 API.
-
-If you use ``unicode_literals``, testing and debugging your code with
-*Python 3* first is probably the easiest way to fix your code. After this,
-fixing Python 2 support will be easier.
-
-To avoid confusion, we recommend using ``unicode_literals`` everywhere
-across a code-base or not at all, instead of turning on for only some
-modules.
-
-This section summarizes the benefits and drawbacks of using
-``unicode_literals``.
-
-Benefits
-~~~~~~~~
-
-1. String literals are unicode on Python 3. Making them unicode on Python 2
- leads to more consistency of your string types across the two
- runtimes. This can make it easier to understand and debug your code.
-
-2. Code without ``u''`` prefixes is cleaner, one of the claimed advantages
- of Python 3. Even though some unicode strings would require a function
- call to invert them to native strings for some Python 2 APIs (see
- :ref:`stdlib-incompatibilities`), the incidence of these function calls
- would usually be much lower than the incidence of ``u''`` prefixes for text
- strings in the absence of ``unicode_literals``.
-
-3. The diff for port to a Python 2/3-compatible codebase may be smaller,
- less noisy, and easier to review with ``unicode_literals`` than if an
- explicit ``u''`` prefix is added to every unadorned string literal.
-
-4. If support for Python 3.2 is required (e.g. for Ubuntu 12.04 LTS or
- Debian wheezy), ``u''`` prefixes are a ``SyntaxError``, making
- ``unicode_literals`` the only option for a Python 2/3 compatible
- codebase. [However, ``future`` doesn't support Python 3.0-3.2 anyway.]
-
-
-Drawbacks
-~~~~~~~~~
-
-1. Adding ``unicode_literals`` to a module amounts to a "global flag day" for
- that module, changing the data types of all strings in the module at once.
- Cautious developers may prefer an incremental approach. (See
- `here `_ for an excellent article
- describing the superiority of an incremental patch-set in the the case
- of the Linux kernel.)
-
-.. This is a larger-scale change than adding explicit ``u''`` prefixes to
-.. all strings that should be Unicode.
-
-2. Changing to ``unicode_literals`` will likely introduce regressions on
- Python 2 that require an initial investment of time to find and fix. The
- APIs may be changed in subtle ways that are not immediately obvious.
-
- An example on Python 2::
-
- ### Module: mypaths.py
-
- ...
- def unix_style_path(path):
- return path.replace('\\', '/')
- ...
-
- ### User code:
-
- >>> path1 = '\\Users\\Ed'
- >>> unix_style_path(path1)
- '/Users/ed'
-
- On Python 2, adding a ``unicode_literals`` import to ``mypaths.py`` would
- change the return type of the ``unix_style_path`` function from ``str`` to
- ``unicode`` in the user code, which is difficult to anticipate and probably
- unintended.
-
- The counter-argument is that this code is broken, in a portability
- sense; we see this from Python 3 raising a ``TypeError`` upon passing the
- function a byte-string. The code needs to be changed to make explicit
- whether the ``path`` argument is to be a byte string or a unicode string.
-
-3. With ``unicode_literals`` in effect, there is no way to specify a native
- string literal (``str`` type on both platforms). This can be worked around as follows::
-
- >>> from __future__ import unicode_literals
- >>> ...
- >>> from future.utils import bytes_to_native_str as n
-
- >>> s = n(b'ABCD')
- >>> s
- 'ABCD' # on both Py2 and Py3
-
- although this incurs a performance penalty (a function call and, on Py3,
- a ``decode`` method call.)
-
- This is a little awkward because various Python library APIs (standard
- and non-standard) require a native string to be passed on both Py2
- and Py3. (See :ref:`stdlib-incompatibilities` for some examples. WSGI
- dictionaries are another.)
-
-3. If a codebase already explicitly marks up all text with ``u''`` prefixes,
- and if support for Python versions 3.0-3.2 can be dropped, then
- removing the existing ``u''`` prefixes and replacing these with
- ``unicode_literals`` imports (the porting approach Django used) would
- introduce more noise into the patch and make it more difficult to review.
- However, note that the ``futurize`` script takes advantage of PEP 414 and
- does not remove explicit ``u''`` prefixes that already exist.
-
-4. Turning on ``unicode_literals`` converts even docstrings to unicode, but
- Pydoc breaks with unicode docstrings containing non-ASCII characters for
- Python versions < 2.7.7. (`Fix
- committed `_ in Jan 2014.)::
-
- >>> def f():
- ... u"Author: Martin von Löwis"
-
- >>> help(f)
-
- /Users/schofield/Install/anaconda/python.app/Contents/lib/python2.7/pydoc.pyc in pipepager(text, cmd)
- 1376 pipe = os.popen(cmd, 'w')
- 1377 try:
- -> 1378 pipe.write(text)
- 1379 pipe.close()
- 1380 except IOError:
-
- UnicodeEncodeError: 'ascii' codec can't encode character u'\xf6' in position 71: ordinal not in range(128)
-
-See `this Stack Overflow thread
-`_
-for other gotchas.
-
-
-Others' perspectives
-~~~~~~~~~~~~~~~~~~~~
-
-In favour of ``unicode_literals``
-*********************************
-
-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
-``unicode_literals`` for the port of Django to a Python 2/3-compatible
-codebase.:
-
- "... I'd like to explain why this PEP [PEP 414, which allows explicit
- ``u''`` prefixes for unicode literals on Python 3.3+] is at odds with
- the porting philosophy I've applied to Django, and why I would have
- vetoed taking advantage of it.
-
- "I believe that aiming for a Python 2 codebase with Python 3
- compatibility hacks is a counter-productive way to port a project. You
- end up with all the drawbacks of Python 2 (including the legacy `u`
- prefixes) and none of the advantages Python 3 (especially the sane
- string handling).
-
- "Working to write Python 3 code, with legacy compatibility for Python
- 2, is much more rewarding. Of course it takes more effort, but the
- results are much cleaner and much more maintainable. It's really about
- looking towards the future or towards the past.
-
- "I understand the reasons why PEP 414 was proposed and why it was
- accepted. It makes sense for legacy software that is minimally
- maintained. I hope nobody puts Django in this category!"
-
-
-Against ``unicode_literals``
-****************************
-
- "There are so many subtle problems that ``unicode_literals`` causes.
- For instance lots of people accidentally introduce unicode into
- filenames and that seems to work, until they are using it on a system
- where there are unicode characters in the filesystem path."
-
- -- Armin Ronacher
-
- "+1 from me for avoiding the unicode_literals future, as it can have
- very strange side effects in Python 2.... This is one of the key
- reasons I backed Armin's PEP 414."
-
- -- Nick Coghlan
-
- "Yeah, one of the nuisances of the WSGI spec is that the header values
- IIRC are the str or StringType on both py2 and py3. With
- unicode_literals this causes hard-to-spot bugs, as some WSGI servers
- might be more tolerant than others, but usually using unicode in python
- 2 for WSGI headers will cause the response to fail."
-
- -- Antti Haapala
+.. include:: translation.rst
+.. include:: unicode_literals.rst
+Next steps
+----------
+See :ref:`what-else`.
diff --git a/docs/index.rst b/docs/index.rst
index 9f8f6e2e..cc84c9b7 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,10 +1,9 @@
-future: clean single-source support for Python 2/3
-======================================================
+Easy, clean, reliable Python 2/3 compatibility
+==============================================
-``future`` is the missing compatibility layer between Python 2 and Python
-3. It allows you to use a single, clean Python 3.x-compatible
+``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.
.. include:: contents.rst.inc
-
diff --git a/docs/int_object.rst b/docs/int_object.rst
index 4ff0b8b5..f774784b 100644
--- a/docs/int_object.rst
+++ b/docs/int_object.rst
@@ -23,7 +23,7 @@ is a subclass of Python 2's ``long`` with the same representation
behaviour as Python 3's ``int``. To ensure an integer is long compatibly with
both Py3 and Py2, cast it like this::
- >>> from future.builtins import int
+ >>> from builtins import int
>>> must_be_a_long_integer = int(1234)
The backported ``int`` object helps with writing doctests and simplifies code
@@ -31,6 +31,8 @@ that deals with ``long`` and ``int`` as special cases on Py2. An example is the
following code from ``xlwt-future`` (called by the ``xlwt.antlr.BitSet`` class)
for writing out Excel ``.xls`` spreadsheets. With ``future``, the code is::
+ from builtins import int
+
def longify(data):
"""
Turns data (an int or long, or a list of ints or longs) into a
@@ -64,4 +66,3 @@ Without ``future`` (or with ``future`` < 0.7), this might be::
return list(map(int, data)) # same as returning data, but with up-front typechecking
else:
return list(map(long, data))
-
diff --git a/docs/isinstance.rst b/docs/isinstance.rst
index 771928aa..2bb5084a 100644
--- a/docs/isinstance.rst
+++ b/docs/isinstance.rst
@@ -4,7 +4,7 @@ isinstance
----------
The following tests all pass on Python 3::
-
+
>>> assert isinstance(2**62, int)
>>> assert isinstance(2**63, int)
>>> assert isinstance(b'my byte-string', bytes)
@@ -39,7 +39,7 @@ then the fifth test fails too::
After importing the builtins from ``future``, all these tests pass on
Python 2 as on Python 3::
- >>> from future.builtins import bytes, int, str
+ >>> from builtins import bytes, int, str
>>> assert isinstance(10, int)
>>> assert isinstance(10**100, int)
@@ -52,7 +52,7 @@ However, note that the last test requires that ``unicode_literals`` be imported
>>> assert isinstance('unicode string 2', str)
This works because the backported types ``int``, ``bytes`` and ``str``
-have metaclasses that override ``__instancecheck__``. See `PEP 3119
+(and others) have metaclasses that override ``__instancecheck__``. See `PEP 3119
`_
for details.
@@ -60,8 +60,8 @@ for details.
Passing data to/from Python 2 libraries
---------------------------------------
-If you are passing any of the backported types (``bytes``, ``str``,
-``int``) into brittle library code that performs type-checks using ``type()``,
+If you are passing any of the backported types (``bytes``, ``int``, ``dict,
+``str``) into brittle library code that performs type-checks using ``type()``,
rather than ``isinstance()``, or requires that you pass Python 2's native types
(rather than subclasses) for some other reason, it may be necessary to upcast
the types from ``future`` to their native superclasses on Py2.
@@ -69,30 +69,30 @@ the types from ``future`` to their native superclasses on Py2.
The ``native`` function in ``future.utils`` is provided for this. Here is how
to use it. (The output showing is from Py2)::
- >>> from future.builtins import *
+ >>> from builtins import int, bytes, str
>>> from future.utils import native
>>> a = int(10**20) # Py3-like long int
>>> a
100000000000000000000
>>> type(a)
- future.builtins.types.newint.newint
+ future.types.newint.newint
>>> native(a)
100000000000000000000L
>>> type(native(a))
long
-
+
>>> b = bytes(b'ABC')
>>> type(b)
- future.builtins.types.newbytes.newbytes
+ future.types.newbytes.newbytes
>>> native(b)
'ABC'
>>> type(native(b))
str
-
+
>>> s = str(u'ABC')
>>> type(s)
- future.builtins.types.newstr.newstr
+ future.types.newstr.newstr
>>> native(s)
u'ABC'
>>> type(native(s))
@@ -115,4 +115,3 @@ The objects ``native_str`` and ``native_bytes`` are available in
The functions ``native_str_to_bytes`` and ``bytes_to_native_str`` are also
available for more explicit conversions.
-
diff --git a/docs/limitations.rst b/docs/limitations.rst
index 5b13ad81..0d13805d 100644
--- a/docs/limitations.rst
+++ b/docs/limitations.rst
@@ -1,4 +1,3 @@
-
limitations of the ``future`` module and differences between Py2 and Py3 that are not (yet) handled
===================================================================================================
@@ -39,7 +38,7 @@ Also:
b'\x00'[0] != 0
b'\x01'[0] != 1
-
+
``futurize`` does not yet wrap all byte-string literals in a ``bytes()``
call. This is on the to-do list. See :ref:`bytes-object` for more information.
@@ -51,5 +50,3 @@ Notes
adds this back in automatically, but ensure you do this too
when writing your classes, otherwise weird breakage when e.g. calling
``super()`` may occur.
-
-
diff --git a/docs/metaclasses.rst b/docs/metaclasses.rst
index c4bcdd00..d40c5a46 100644
--- a/docs/metaclasses.rst
+++ b/docs/metaclasses.rst
@@ -5,16 +5,14 @@ Python 3 and Python 2 syntax for metaclasses are incompatible.
``future`` provides a function (from ``jinja2/_compat.py``) called
:func:`with_metaclass` that can assist with specifying metaclasses
portably across Py3 and Py2. Use it like this::
-
+
from future.utils import with_metaclass
class BaseForm(object):
pass
-
+
class FormType(type):
pass
-
+
class Form(with_metaclass(FormType, BaseForm)):
pass
-
-
diff --git a/docs/notebooks/Writing Python 2-3 compatible code.ipynb b/docs/notebooks/Writing Python 2-3 compatible code.ipynb
new file mode 100644
index 00000000..663ede44
--- /dev/null
+++ b/docs/notebooks/Writing Python 2-3 compatible code.ipynb
@@ -0,0 +1,3167 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Cheat Sheet: Writing Python 2-3 compatible code"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "- **Copyright (c):** 2013-2024 Python Charmers, Australia.\n",
+ "- **Author:** Ed Schofield.\n",
+ "- **Licence:** Creative Commons Attribution.\n",
+ "\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: .)\n",
+ "\n",
+ "Minimum versions:\n",
+ "\n",
+ " - Python 2: 2.6+\n",
+ " - Python 3: 3.3+"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Setup"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The imports below refer to these ``pip``-installable packages on PyPI:\n",
+ "\n",
+ " import future # pip install future\n",
+ " import builtins # pip install future\n",
+ " import past # pip install future\n",
+ " import six # pip install six\n",
+ "\n",
+ "The following scripts are also ``pip``-installable:\n",
+ "\n",
+ " futurize # pip install future\n",
+ " pasteurize # pip install future\n",
+ "\n",
+ "See https://python-future.org and https://pythonhosted.org/six/ for more information."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Essential syntax differences"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### print"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "print 'Hello'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "print('Hello')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To print multiple strings, import ``print_function`` to prevent Py2 from interpreting it as a tuple:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "print 'Hello', 'Guido'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from __future__ import print_function # (at top of module)\n",
+ "\n",
+ "print('Hello', 'Guido')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "print >> sys.stderr, 'Hello'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from __future__ import print_function\n",
+ "\n",
+ "print('Hello', file=sys.stderr)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "print 'Hello',"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from __future__ import print_function\n",
+ "\n",
+ "print('Hello', end='')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Raising exceptions"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "raise ValueError, \"dodgy value\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "raise ValueError(\"dodgy value\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Raising exceptions with a traceback:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "traceback = sys.exc_info()[2]\n",
+ "raise ValueError, \"dodgy value\", traceback"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 3 only:\n",
+ "raise ValueError(\"dodgy value\").with_traceback()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 1\n",
+ "from six import reraise as raise_\n",
+ "# or\n",
+ "from future.utils import raise_\n",
+ "\n",
+ "traceback = sys.exc_info()[2]\n",
+ "raise_(ValueError, \"dodgy value\", traceback)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 2\n",
+ "from future.utils import raise_with_traceback\n",
+ "\n",
+ "raise_with_traceback(ValueError(\"dodgy value\"))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Exception chaining (PEP 3134):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Setup:\n",
+ "class DatabaseError(Exception):\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 3 only\n",
+ "class FileDatabase:\n",
+ " def __init__(self, filename):\n",
+ " try:\n",
+ " self.file = open(filename)\n",
+ " except IOError as exc:\n",
+ " raise DatabaseError('failed to open') from exc"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from future.utils import raise_from\n",
+ "\n",
+ "class FileDatabase:\n",
+ " def __init__(self, filename):\n",
+ " try:\n",
+ " self.file = open(filename)\n",
+ " except IOError as exc:\n",
+ " raise_from(DatabaseError('failed to open'), exc)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Testing the above:\n",
+ "try:\n",
+ " fd = FileDatabase('non_existent_file.txt')\n",
+ "except Exception as e:\n",
+ " assert isinstance(e.__cause__, IOError) # FileNotFoundError on Py3.3+ inherits from IOError"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Catching exceptions"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "try:\n",
+ " ...\n",
+ "except ValueError, e:\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "try:\n",
+ " ...\n",
+ "except ValueError as e:\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Division"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Integer division (rounding down):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "assert 2 / 3 == 0"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "assert 2 // 3 == 0"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\"True division\" (float division):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 3 only:\n",
+ "assert 3 / 2 == 1.5"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from __future__ import division # (at top of module)\n",
+ "\n",
+ "assert 3 / 2 == 1.5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\"Old division\" (i.e. compatible with Py2 behaviour):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "a = b / c # with any types"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from past.utils import old_div\n",
+ "\n",
+ "a = old_div(b, c) # always same as / on Py2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Long integers"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Short integers are gone in Python 3 and ``long`` has become ``int`` (without the trailing ``L`` in the ``repr``)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only\n",
+ "k = 9223372036854775808L\n",
+ "\n",
+ "# Python 2 and 3:\n",
+ "k = 9223372036854775808"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only\n",
+ "bigint = 1L\n",
+ "\n",
+ "# Python 2 and 3\n",
+ "from builtins import int\n",
+ "bigint = int(1)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To test whether a value is an integer (of any kind):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "if isinstance(x, (int, long)):\n",
+ " ...\n",
+ "\n",
+ "# Python 3 only:\n",
+ "if isinstance(x, int):\n",
+ " ...\n",
+ "\n",
+ "# Python 2 and 3: option 1\n",
+ "from builtins import int # subclass of long on Py2\n",
+ "\n",
+ "if isinstance(x, int): # matches both int and long on Py2\n",
+ " ...\n",
+ "\n",
+ "# Python 2 and 3: option 2\n",
+ "from past.builtins import long\n",
+ "\n",
+ "if isinstance(x, (int, long)):\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Octal constants"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "0644 # Python 2 only"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "0o644 # Python 2 and 3"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Backtick repr"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "`x` # Python 2 only"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "repr(x) # Python 2 and 3"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Metaclasses"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "class BaseForm(object):\n",
+ " pass\n",
+ "\n",
+ "class FormType(type):\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "class Form(BaseForm):\n",
+ " __metaclass__ = FormType\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 3 only:\n",
+ "class Form(BaseForm, metaclass=FormType):\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from six import with_metaclass\n",
+ "# or\n",
+ "from future.utils import with_metaclass\n",
+ "\n",
+ "class Form(with_metaclass(FormType, BaseForm)):\n",
+ " pass"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Strings and bytes"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Unicode (text) string literals"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you are upgrading an existing Python 2 codebase, it may be preferable to mark up all string literals as unicode explicitly with ``u`` prefixes:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only\n",
+ "s1 = 'The Zen of Python'\n",
+ "s2 = u'きたないのよりきれいな方がいい\\n'\n",
+ "\n",
+ "# Python 2 and 3\n",
+ "s1 = u'The Zen of Python'\n",
+ "s2 = u'きたないのよりきれいな方がいい\\n'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The ``futurize`` and ``python-modernize`` tools do not currently offer an option to do this automatically."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If you are writing code for a new project or new codebase, you can use this idiom to make all string literals in a module unicode strings:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3\n",
+ "from __future__ import unicode_literals # at top of module\n",
+ "\n",
+ "s1 = 'The Zen of Python'\n",
+ "s2 = 'きたないのよりきれいな方がいい\\n'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "See https://python-future.org/unicode_literals.html for more discussion on which style to use."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Byte-string literals"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only\n",
+ "s = 'This must be a byte-string'\n",
+ "\n",
+ "# Python 2 and 3\n",
+ "s = b'This must be a byte-string'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To loop over a byte-string with possible high-bit characters, obtaining each character as a byte-string of length 1:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "for bytechar in 'byte-string with high-bit chars like \\xf9':\n",
+ " ...\n",
+ "\n",
+ "# Python 3 only:\n",
+ "for myint in b'byte-string with high-bit chars like \\xf9':\n",
+ " bytechar = bytes([myint])\n",
+ "\n",
+ "# Python 2 and 3:\n",
+ "from builtins import bytes\n",
+ "for myint in bytes(b'byte-string with high-bit chars like \\xf9'):\n",
+ " bytechar = bytes([myint])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As an alternative, ``chr()`` and ``.encode('latin-1')`` can be used to convert an int into a 1-char byte string:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 3 only:\n",
+ "for myint in b'byte-string with high-bit chars like \\xf9':\n",
+ " char = chr(myint) # returns a unicode string\n",
+ " bytechar = char.encode('latin-1')\n",
+ "\n",
+ "# Python 2 and 3:\n",
+ "from builtins import bytes, chr\n",
+ "for myint in bytes(b'byte-string with high-bit chars like \\xf9'):\n",
+ " char = chr(myint) # returns a unicode string\n",
+ " bytechar = char.encode('latin-1') # forces returning a byte str"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### basestring"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "a = u'abc'\n",
+ "b = 'def'\n",
+ "assert (isinstance(a, basestring) and isinstance(b, basestring))\n",
+ "\n",
+ "# Python 2 and 3: alternative 1\n",
+ "from past.builtins import basestring # pip install future\n",
+ "\n",
+ "a = u'abc'\n",
+ "b = b'def'\n",
+ "assert (isinstance(a, basestring) and isinstance(b, basestring))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 2: refactor the code to avoid considering\n",
+ "# byte-strings as strings.\n",
+ "\n",
+ "from builtins import str\n",
+ "a = u'abc'\n",
+ "b = b'def'\n",
+ "c = b.decode()\n",
+ "assert isinstance(a, str) and isinstance(c, str)\n",
+ "# ..."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### unicode"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "templates = [u\"blog/blog_post_detail_%s.html\" % unicode(slug)]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 1\n",
+ "from builtins import str\n",
+ "templates = [u\"blog/blog_post_detail_%s.html\" % str(slug)]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 2\n",
+ "from builtins import str as text\n",
+ "templates = [u\"blog/blog_post_detail_%s.html\" % text(slug)]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### StringIO"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "from StringIO import StringIO\n",
+ "# or:\n",
+ "from cStringIO import StringIO\n",
+ "\n",
+ "# Python 2 and 3:\n",
+ "from io import BytesIO # for handling byte strings\n",
+ "from io import StringIO # for handling unicode strings"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Imports relative to a package"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Suppose the package is:\n",
+ "\n",
+ " mypackage/\n",
+ " __init__.py\n",
+ " submodule1.py\n",
+ " submodule2.py\n",
+ " \n",
+ "and the code below is in ``submodule1.py``:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only: \n",
+ "import submodule2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from . import submodule2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "# To make Py2 code safer (more like Py3) by preventing\n",
+ "# implicit relative imports, you can also add this to the top:\n",
+ "from __future__ import absolute_import"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Dictionaries"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Iterating through ``dict`` keys/values/items"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Iterable dict keys:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "for key in heights.iterkeys():\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "for key in heights:\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Iterable dict values:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "for value in heights.itervalues():\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Idiomatic Python 3\n",
+ "for value in heights.values(): # extra memory overhead on Py2\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 1\n",
+ "from builtins import dict\n",
+ "\n",
+ "heights = dict(Fred=175, Anne=166, Joe=192)\n",
+ "for key in heights.values(): # efficient on Py2 and Py3\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 2\n",
+ "from future.utils import itervalues\n",
+ "# or\n",
+ "from six import itervalues\n",
+ "\n",
+ "for key in itervalues(heights):\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Iterable dict items:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "for (key, value) in heights.iteritems():\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 1\n",
+ "for (key, value) in heights.items(): # inefficient on Py2 \n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 2\n",
+ "from future.utils import viewitems\n",
+ "\n",
+ "for (key, value) in viewitems(heights): # also behaves like a set\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 3\n",
+ "from future.utils import iteritems\n",
+ "# or\n",
+ "from six import iteritems\n",
+ "\n",
+ "for (key, value) in iteritems(heights):\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### dict keys/values/items as a list"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "dict keys as a list:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "keylist = heights.keys()\n",
+ "assert isinstance(keylist, list)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "keylist = list(heights)\n",
+ "assert isinstance(keylist, list)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "dict values as a list:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}\n",
+ "valuelist = heights.values()\n",
+ "assert isinstance(valuelist, list)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 1\n",
+ "valuelist = list(heights.values()) # inefficient on Py2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 2\n",
+ "from builtins import dict\n",
+ "\n",
+ "heights = dict(Fred=175, Anne=166, Joe=192)\n",
+ "valuelist = list(heights.values())"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 3\n",
+ "from future.utils import listvalues\n",
+ "\n",
+ "valuelist = listvalues(heights)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 4\n",
+ "from future.utils import itervalues\n",
+ "# or\n",
+ "from six import itervalues\n",
+ "\n",
+ "valuelist = list(itervalues(heights))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "dict items as a list:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 1\n",
+ "itemlist = list(heights.items()) # inefficient on Py2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 2\n",
+ "from future.utils import listitems\n",
+ "\n",
+ "itemlist = listitems(heights)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 3\n",
+ "from future.utils import iteritems\n",
+ "# or\n",
+ "from six import iteritems\n",
+ "\n",
+ "itemlist = list(iteritems(heights))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Custom class behaviour"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Custom iterators"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only\n",
+ "class Upper(object):\n",
+ " def __init__(self, iterable):\n",
+ " self._iter = iter(iterable)\n",
+ " def next(self): # Py2-style\n",
+ " return self._iter.next().upper()\n",
+ " def __iter__(self):\n",
+ " return self\n",
+ "\n",
+ "itr = Upper('hello')\n",
+ "assert itr.next() == 'H' # Py2-style\n",
+ "assert list(itr) == list('ELLO')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 1\n",
+ "from builtins import object\n",
+ "\n",
+ "class Upper(object):\n",
+ " def __init__(self, iterable):\n",
+ " self._iter = iter(iterable)\n",
+ " def __next__(self): # Py3-style iterator interface\n",
+ " return next(self._iter).upper() # builtin next() function calls\n",
+ " def __iter__(self):\n",
+ " return self\n",
+ "\n",
+ "itr = Upper('hello')\n",
+ "assert next(itr) == 'H' # compatible style\n",
+ "assert list(itr) == list('ELLO')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 2\n",
+ "from future.utils import implements_iterator\n",
+ "\n",
+ "@implements_iterator\n",
+ "class Upper(object):\n",
+ " def __init__(self, iterable):\n",
+ " self._iter = iter(iterable)\n",
+ " def __next__(self): # Py3-style iterator interface\n",
+ " return next(self._iter).upper() # builtin next() function calls\n",
+ " def __iter__(self):\n",
+ " return self\n",
+ "\n",
+ "itr = Upper('hello')\n",
+ "assert next(itr) == 'H'\n",
+ "assert list(itr) == list('ELLO')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Custom ``__str__`` methods"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "class MyClass(object):\n",
+ " def __unicode__(self):\n",
+ " return 'Unicode string: \\u5b54\\u5b50'\n",
+ " def __str__(self):\n",
+ " return unicode(self).encode('utf-8')\n",
+ "\n",
+ "a = MyClass()\n",
+ "print(a) # prints encoded string"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Unicode string: 孔子\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from future.utils import python_2_unicode_compatible\n",
+ "\n",
+ "@python_2_unicode_compatible\n",
+ "class MyClass(object):\n",
+ " def __str__(self):\n",
+ " return u'Unicode string: \\u5b54\\u5b50'\n",
+ "\n",
+ "a = MyClass()\n",
+ "print(a) # prints string encoded as utf-8 on Py2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Custom ``__nonzero__`` vs ``__bool__`` method:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "class AllOrNothing(object):\n",
+ " def __init__(self, l):\n",
+ " self.l = l\n",
+ " def __nonzero__(self):\n",
+ " return all(self.l)\n",
+ "\n",
+ "container = AllOrNothing([0, 100, 200])\n",
+ "assert not bool(container)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from builtins import object\n",
+ "\n",
+ "class AllOrNothing(object):\n",
+ " def __init__(self, l):\n",
+ " self.l = l\n",
+ " def __bool__(self):\n",
+ " return all(self.l)\n",
+ "\n",
+ "container = AllOrNothing([0, 100, 200])\n",
+ "assert not bool(container)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Lists versus iterators"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### xrange"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "for i in xrange(10**8):\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: forward-compatible\n",
+ "from builtins import range\n",
+ "for i in range(10**8):\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: backward-compatible\n",
+ "from past.builtins import xrange\n",
+ "for i in xrange(10**8):\n",
+ " ..."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### range"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only\n",
+ "mylist = range(5)\n",
+ "assert mylist == [0, 1, 2, 3, 4]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: forward-compatible: option 1\n",
+ "mylist = list(range(5)) # copies memory on Py2\n",
+ "assert mylist == [0, 1, 2, 3, 4]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: forward-compatible: option 2\n",
+ "from builtins import range\n",
+ "\n",
+ "mylist = list(range(5))\n",
+ "assert mylist == [0, 1, 2, 3, 4]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 3\n",
+ "from future.utils import lrange\n",
+ "\n",
+ "mylist = lrange(5)\n",
+ "assert mylist == [0, 1, 2, 3, 4]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: backward compatible\n",
+ "from past.builtins import range\n",
+ "\n",
+ "mylist = range(5)\n",
+ "assert mylist == [0, 1, 2, 3, 4]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### map"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "mynewlist = map(f, myoldlist)\n",
+ "assert mynewlist == [f(x) for x in myoldlist]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 1\n",
+ "# Idiomatic Py3, but inefficient on Py2\n",
+ "mynewlist = list(map(f, myoldlist))\n",
+ "assert mynewlist == [f(x) for x in myoldlist]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 2\n",
+ "from builtins import map\n",
+ "\n",
+ "mynewlist = list(map(f, myoldlist))\n",
+ "assert mynewlist == [f(x) for x in myoldlist]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 3\n",
+ "try:\n",
+ " from itertools import imap as map\n",
+ "except ImportError:\n",
+ " pass\n",
+ "\n",
+ "mynewlist = list(map(f, myoldlist)) # inefficient on Py2\n",
+ "assert mynewlist == [f(x) for x in myoldlist]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 4\n",
+ "from future.utils import lmap\n",
+ "\n",
+ "mynewlist = lmap(f, myoldlist)\n",
+ "assert mynewlist == [f(x) for x in myoldlist]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 5\n",
+ "from past.builtins import map\n",
+ "\n",
+ "mynewlist = map(f, myoldlist)\n",
+ "assert mynewlist == [f(x) for x in myoldlist]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### imap"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "from itertools import imap\n",
+ "\n",
+ "myiter = imap(func, myoldlist)\n",
+ "assert isinstance(myiter, iter)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 3 only:\n",
+ "myiter = map(func, myoldlist)\n",
+ "assert isinstance(myiter, iter)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 1\n",
+ "from builtins import map\n",
+ "\n",
+ "myiter = map(func, myoldlist)\n",
+ "assert isinstance(myiter, iter)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 2\n",
+ "try:\n",
+ " from itertools import imap as map\n",
+ "except ImportError:\n",
+ " pass\n",
+ "\n",
+ "myiter = map(func, myoldlist)\n",
+ "assert isinstance(myiter, iter)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### zip, izip"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As above with ``zip`` and ``itertools.izip``."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### filter, ifilter"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As above with ``filter`` and ``itertools.ifilter`` too."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Other builtins"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### File IO with open()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only\n",
+ "f = open('myfile.txt')\n",
+ "data = f.read() # as a byte string\n",
+ "text = data.decode('utf-8')\n",
+ "\n",
+ "# Python 2 and 3: alternative 1\n",
+ "from io import open\n",
+ "f = open('myfile.txt', 'rb')\n",
+ "data = f.read() # as bytes\n",
+ "text = data.decode('utf-8') # unicode, not bytes\n",
+ "\n",
+ "# Python 2 and 3: alternative 2\n",
+ "from io import open\n",
+ "f = open('myfile.txt', encoding='utf-8')\n",
+ "text = f.read() # unicode, not bytes"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### reduce()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "assert reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) == 1+2+3+4+5"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from functools import reduce\n",
+ "\n",
+ "assert reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) == 1+2+3+4+5"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### raw_input()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "name = raw_input('What is your name? ')\n",
+ "assert isinstance(name, str) # native str"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from builtins import input\n",
+ "\n",
+ "name = input('What is your name? ')\n",
+ "assert isinstance(name, str) # native str on Py2 and Py3"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### input()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "input(\"Type something safe please: \")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3\n",
+ "from builtins import input\n",
+ "eval(input(\"Type something safe please: \"))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Warning: using either of these is **unsafe** with untrusted input."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### file()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "f = file(pathname)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "f = open(pathname)\n",
+ "\n",
+ "# But preferably, use this:\n",
+ "from io import open\n",
+ "f = open(pathname, 'rb') # if f.read() should return bytes\n",
+ "# or\n",
+ "f = open(pathname, 'rt') # if f.read() should return unicode text"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### exec"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "exec 'x = 10'\n",
+ "\n",
+ "# Python 2 and 3:\n",
+ "exec('x = 10')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "g = globals()\n",
+ "exec 'x = 10' in g\n",
+ "\n",
+ "# Python 2 and 3:\n",
+ "g = globals()\n",
+ "exec('x = 10', g)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "l = locals()\n",
+ "exec 'x = 10' in g, l\n",
+ "\n",
+ "# Python 2 and 3:\n",
+ "exec('x = 10', g, l)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But note that Py3's `exec()` is less powerful (and less dangerous) than Py2's `exec` statement."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### execfile()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "execfile('myfile.py')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 1\n",
+ "from past.builtins import execfile\n",
+ "\n",
+ "execfile('myfile.py')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 2\n",
+ "exec(compile(open('myfile.py').read()))\n",
+ "\n",
+ "# This can sometimes cause this:\n",
+ "# SyntaxError: function ... uses import * and bare exec ...\n",
+ "# See https://github.com/PythonCharmers/python-future/issues/37"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### unichr()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "assert unichr(8364) == '€'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 3 only:\n",
+ "assert chr(8364) == '€'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from builtins import chr\n",
+ "assert chr(8364) == '€'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### intern()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "intern('mystring')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 3 only:\n",
+ "from sys import intern\n",
+ "intern('mystring')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 1\n",
+ "from past.builtins import intern\n",
+ "intern('mystring')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 2\n",
+ "from six.moves import intern\n",
+ "intern('mystring')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 3\n",
+ "from future.standard_library import install_aliases\n",
+ "install_aliases()\n",
+ "from sys import intern\n",
+ "intern('mystring')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 2\n",
+ "try:\n",
+ " from sys import intern\n",
+ "except ImportError:\n",
+ " pass\n",
+ "intern('mystring')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### apply()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "args = ('a', 'b')\n",
+ "kwargs = {'kwarg1': True}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "apply(f, args, kwargs)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 1\n",
+ "f(*args, **kwargs)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 2\n",
+ "from past.builtins import apply\n",
+ "apply(f, args, kwargs)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### chr()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "assert chr(64) == b'@'\n",
+ "assert chr(200) == b'\\xc8'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 3 only: option 1\n",
+ "assert chr(64).encode('latin-1') == b'@'\n",
+ "assert chr(0xc8).encode('latin-1') == b'\\xc8'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 1\n",
+ "from builtins import chr\n",
+ "\n",
+ "assert chr(64).encode('latin-1') == b'@'\n",
+ "assert chr(0xc8).encode('latin-1') == b'\\xc8'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 3 only: option 2\n",
+ "assert bytes([64]) == b'@'\n",
+ "assert bytes([0xc8]) == b'\\xc8'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: option 2\n",
+ "from builtins import bytes\n",
+ "\n",
+ "assert bytes([64]) == b'@'\n",
+ "assert bytes([0xc8]) == b'\\xc8'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### cmp()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 1\n",
+ "from past.builtins import cmp\n",
+ "assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 2\n",
+ "cmp = lambda(x, y): (x > y) - (x < y)\n",
+ "assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### reload()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "reload(mymodule)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3\n",
+ "from imp import reload\n",
+ "reload(mymodule)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Standard library"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### dbm modules"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only\n",
+ "import anydbm\n",
+ "import whichdb\n",
+ "import dbm\n",
+ "import dumbdbm\n",
+ "import gdbm\n",
+ "\n",
+ "# Python 2 and 3: alternative 1\n",
+ "from future import standard_library\n",
+ "standard_library.install_aliases()\n",
+ "\n",
+ "import dbm\n",
+ "import dbm.ndbm\n",
+ "import dbm.dumb\n",
+ "import dbm.gnu\n",
+ "\n",
+ "# Python 2 and 3: alternative 2\n",
+ "from future.moves import dbm\n",
+ "from future.moves.dbm import dumb\n",
+ "from future.moves.dbm import ndbm\n",
+ "from future.moves.dbm import gnu\n",
+ "\n",
+ "# Python 2 and 3: alternative 3\n",
+ "from six.moves import dbm_gnu\n",
+ "# (others not supported)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### commands / subprocess modules"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only\n",
+ "from commands import getoutput, getstatusoutput\n",
+ "\n",
+ "# Python 2 and 3\n",
+ "from future import standard_library\n",
+ "standard_library.install_aliases()\n",
+ "\n",
+ "from subprocess import getoutput, getstatusoutput"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### subprocess.check_output()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2.7 and above\n",
+ "from subprocess import check_output\n",
+ "\n",
+ "# Python 2.6 and above: alternative 1\n",
+ "from future.moves.subprocess import check_output\n",
+ "\n",
+ "# Python 2.6 and above: alternative 2\n",
+ "from future import standard_library\n",
+ "standard_library.install_aliases()\n",
+ "\n",
+ "from subprocess import check_output"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### collections: Counter, OrderedDict, ChainMap"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2.7 and above\n",
+ "from collections import Counter, OrderedDict, ChainMap\n",
+ "\n",
+ "# Python 2.6 and above: alternative 1\n",
+ "from future.backports import Counter, OrderedDict, ChainMap\n",
+ "\n",
+ "# Python 2.6 and above: alternative 2\n",
+ "from future import standard_library\n",
+ "standard_library.install_aliases()\n",
+ "\n",
+ "from collections import Counter, OrderedDict, ChainMap"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### StringIO module"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only\n",
+ "from StringIO import StringIO\n",
+ "from cStringIO import StringIO"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3\n",
+ "from io import BytesIO\n",
+ "# and refactor StringIO() calls to BytesIO() if passing byte-strings"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### http module"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "import httplib\n",
+ "import Cookie\n",
+ "import cookielib\n",
+ "import BaseHTTPServer\n",
+ "import SimpleHTTPServer\n",
+ "import CGIHttpServer\n",
+ "\n",
+ "# Python 2 and 3 (after ``pip install future``):\n",
+ "import http.client\n",
+ "import http.cookies\n",
+ "import http.cookiejar\n",
+ "import http.server"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### xmlrpc module"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "import DocXMLRPCServer\n",
+ "import SimpleXMLRPCServer\n",
+ "\n",
+ "# Python 2 and 3 (after ``pip install future``):\n",
+ "import xmlrpc.server"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "import xmlrpclib\n",
+ "\n",
+ "# Python 2 and 3 (after ``pip install future``):\n",
+ "import xmlrpc.client"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### html escaping and entities"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3:\n",
+ "from cgi import escape\n",
+ "\n",
+ "# Safer (Python 2 and 3, after ``pip install future``):\n",
+ "from html import escape\n",
+ "\n",
+ "# Python 2 only:\n",
+ "from htmlentitydefs import codepoint2name, entitydefs, name2codepoint\n",
+ "\n",
+ "# Python 2 and 3 (after ``pip install future``):\n",
+ "from html.entities import codepoint2name, entitydefs, name2codepoint"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### html parsing"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "from HTMLParser import HTMLParser\n",
+ "\n",
+ "# Python 2 and 3 (after ``pip install future``)\n",
+ "from html.parser import HTMLParser\n",
+ "\n",
+ "# Python 2 and 3 (alternative 2):\n",
+ "from future.moves.html.parser import HTMLParser"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### urllib module"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "``urllib`` is the hardest module to use from Python 2/3 compatible code. You may like to use Requests (https://python-requests.org) instead."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "from urlparse import urlparse\n",
+ "from urllib import urlencode\n",
+ "from urllib2 import urlopen, Request, HTTPError"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 3 only:\n",
+ "from urllib.parse import urlparse, urlencode\n",
+ "from urllib.request import urlopen, Request\n",
+ "from urllib.error import HTTPError"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: easiest option\n",
+ "from future.standard_library import install_aliases\n",
+ "install_aliases()\n",
+ "\n",
+ "from urllib.parse import urlparse, urlencode\n",
+ "from urllib.request import urlopen, Request\n",
+ "from urllib.error import HTTPError"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 2\n",
+ "from future.standard_library import hooks\n",
+ "\n",
+ "with hooks():\n",
+ " from urllib.parse import urlparse, urlencode\n",
+ " from urllib.request import urlopen, Request\n",
+ " from urllib.error import HTTPError"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 3\n",
+ "from future.moves.urllib.parse import urlparse, urlencode\n",
+ "from future.moves.urllib.request import urlopen, Request\n",
+ "from future.moves.urllib.error import HTTPError\n",
+ "# or\n",
+ "from six.moves.urllib.parse import urlparse, urlencode\n",
+ "from six.moves.urllib.request import urlopen\n",
+ "from six.moves.urllib.error import HTTPError"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 and 3: alternative 4\n",
+ "try:\n",
+ " from urllib.parse import urlparse, urlencode\n",
+ " from urllib.request import urlopen, Request\n",
+ " from urllib.error import HTTPError\n",
+ "except ImportError:\n",
+ " from urlparse import urlparse\n",
+ " from urllib import urlencode\n",
+ " from urllib2 import urlopen, Request, HTTPError"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Tkinter"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "import Tkinter\n",
+ "import Dialog\n",
+ "import FileDialog\n",
+ "import ScrolledText\n",
+ "import SimpleDialog\n",
+ "import Tix \n",
+ "import Tkconstants\n",
+ "import Tkdnd \n",
+ "import tkColorChooser\n",
+ "import tkCommonDialog\n",
+ "import tkFileDialog\n",
+ "import tkFont\n",
+ "import tkMessageBox\n",
+ "import tkSimpleDialog\n",
+ "import ttk\n",
+ "\n",
+ "# Python 2 and 3 (after ``pip install future``):\n",
+ "import tkinter\n",
+ "import tkinter.dialog\n",
+ "import tkinter.filedialog\n",
+ "import tkinter.scrolledtext\n",
+ "import tkinter.simpledialog\n",
+ "import tkinter.tix\n",
+ "import tkinter.constants\n",
+ "import tkinter.dnd\n",
+ "import tkinter.colorchooser\n",
+ "import tkinter.commondialog\n",
+ "import tkinter.filedialog\n",
+ "import tkinter.font\n",
+ "import tkinter.messagebox\n",
+ "import tkinter.simpledialog\n",
+ "import tkinter.ttk"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### socketserver"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "import SocketServer\n",
+ "\n",
+ "# Python 2 and 3 (after ``pip install future``):\n",
+ "import socketserver"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### copy_reg, copyreg"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "import copy_reg\n",
+ "\n",
+ "# Python 2 and 3 (after ``pip install future``):\n",
+ "import copyreg"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### configparser"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "from ConfigParser import ConfigParser\n",
+ "\n",
+ "# Python 2 and 3 (after ``pip install future``):\n",
+ "from configparser import ConfigParser"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### queue"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "from Queue import Queue, heapq, deque\n",
+ "\n",
+ "# Python 2 and 3 (after ``pip install future``):\n",
+ "from queue import Queue, heapq, deque"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### repr, reprlib"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "from repr import aRepr, repr\n",
+ "\n",
+ "# Python 2 and 3 (after ``pip install future``):\n",
+ "from reprlib import aRepr, repr"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### UserDict, UserList, UserString"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "from UserDict import UserDict\n",
+ "from UserList import UserList\n",
+ "from UserString import UserString\n",
+ "\n",
+ "# Python 3 only:\n",
+ "from collections import UserDict, UserList, UserString\n",
+ "\n",
+ "# Python 2 and 3: alternative 1\n",
+ "from future.moves.collections import UserDict, UserList, UserString\n",
+ "\n",
+ "# Python 2 and 3: alternative 2\n",
+ "from six.moves import UserDict, UserList, UserString\n",
+ "\n",
+ "# Python 2 and 3: alternative 3\n",
+ "from future.standard_library import install_aliases\n",
+ "install_aliases()\n",
+ "from collections import UserDict, UserList, UserString"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### itertools: filterfalse, zip_longest"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "# Python 2 only:\n",
+ "from itertools import ifilterfalse, izip_longest\n",
+ "\n",
+ "# Python 3 only:\n",
+ "from itertools import filterfalse, zip_longest\n",
+ "\n",
+ "# Python 2 and 3: alternative 1\n",
+ "from future.moves.itertools import filterfalse, zip_longest\n",
+ "\n",
+ "# Python 2 and 3: alternative 2\n",
+ "from six.moves import filterfalse, zip_longest\n",
+ "\n",
+ "# Python 2 and 3: alternative 3\n",
+ "from future.standard_library import install_aliases\n",
+ "install_aliases()\n",
+ "from itertools import filterfalse, zip_longest"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.4.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/docs/notebooks/bytes object.ipynb b/docs/notebooks/bytes object.ipynb
new file mode 100644
index 00000000..57921442
--- /dev/null
+++ b/docs/notebooks/bytes object.ipynb
@@ -0,0 +1,161 @@
+{
+ "metadata": {
+ "name": ""
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+ {
+ "cells": [
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "import sys\n",
+ "sys.version"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [
+ {
+ "metadata": {},
+ "output_type": "pyout",
+ "prompt_number": 6,
+ "text": [
+ "'2.7.6 (default, Mar 22 2014, 22:59:56) \\n[GCC 4.8.2]'"
+ ]
+ }
+ ],
+ "prompt_number": 6
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "import future\n",
+ "future.__version__"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [
+ {
+ "metadata": {},
+ "output_type": "pyout",
+ "prompt_number": 1,
+ "text": [
+ "'0.12.0-dev'"
+ ]
+ }
+ ],
+ "prompt_number": 1
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "from builtins import bytes"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [],
+ "prompt_number": 2
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "# Backported Py3 bytes object\n",
+ "b = bytes(b'ABCD')"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [],
+ "prompt_number": 3
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "list(b)"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [
+ {
+ "metadata": {},
+ "output_type": "pyout",
+ "prompt_number": 4,
+ "text": [
+ "[65, 66, 67, 68]"
+ ]
+ }
+ ],
+ "prompt_number": 4
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "repr(b)"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [
+ {
+ "metadata": {},
+ "output_type": "pyout",
+ "prompt_number": 5,
+ "text": [
+ "\"b'ABCD'\""
+ ]
+ }
+ ],
+ "prompt_number": 5
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "# These raise TypeErrors:\n",
+ "# b + u'EFGH'\n",
+ "# bytes(b',').join([u'Fred', u'Bill'])\n",
+ "# b < u'abcd'"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [],
+ "prompt_number": 10
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "b == u'ABCD'"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [
+ {
+ "metadata": {},
+ "output_type": "pyout",
+ "prompt_number": 9,
+ "text": [
+ "False"
+ ]
+ }
+ ],
+ "prompt_number": 9
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [],
+ "language": "python",
+ "metadata": {},
+ "outputs": []
+ }
+ ],
+ "metadata": {}
+ }
+ ]
+}
diff --git a/docs/notebooks/object special methods (next, bool, ...).ipynb b/docs/notebooks/object special methods (next, bool, ...).ipynb
index 69b3dbe7..7da31856 100644
--- a/docs/notebooks/object special methods (next, bool, ...).ipynb
+++ b/docs/notebooks/object special methods (next, bool, ...).ipynb
@@ -28,24 +28,24 @@
{
"metadata": {},
"output_type": "pyout",
- "prompt_number": 4,
+ "prompt_number": 1,
"text": [
- "'2.7.6 |Continuum Analytics, Inc.| (default, Jan 17 2014, 10:13:17) \\n[GCC 4.1.2 20080704 (Red Hat 4.1.2-54)]'"
+ "'2.7.6 (default, Mar 22 2014, 22:59:56) \\n[GCC 4.8.2]'"
]
}
],
- "prompt_number": 4
+ "prompt_number": 1
},
{
"cell_type": "code",
"collapsed": false,
"input": [
- "from future.builtins import object"
+ "from builtins import object"
],
"language": "python",
"metadata": {},
"outputs": [],
- "prompt_number": 1
+ "prompt_number": 2
},
{
"cell_type": "code",
@@ -63,22 +63,174 @@
"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",
" def __next__(self): # note the Py3 interface\n",
" return next(self._iter).upper()\n",
" def __iter__(self):\n",
- " return self\n",
- "\n",
- "assert list(Upper('hello')) == list('HELLO')"
+ " return self"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 3
},
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "assert list(Upper('hello')) == list('HELLO')"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [],
+ "prompt_number": 5
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "class AllOrNothing(object):\n",
+ " def __init__(self, l):\n",
+ " self.l = l\n",
+ " def __bool__(self):\n",
+ " return all(self.l)"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [],
+ "prompt_number": 6
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "container = AllOrNothing([0, 100, 200])\n",
+ "bool(container)"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [
+ {
+ "metadata": {},
+ "output_type": "pyout",
+ "prompt_number": 8,
+ "text": [
+ "False"
+ ]
+ }
+ ],
+ "prompt_number": 8
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "container2 = AllOrNothing([-100, 100, 200])\n",
+ "bool(container2)"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [
+ {
+ "metadata": {},
+ "output_type": "pyout",
+ "prompt_number": 9,
+ "text": [
+ "True"
+ ]
+ }
+ ],
+ "prompt_number": 9
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Classes derived from Python builtins don't have this behaviour:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "class AllOrNothingBroken(list):\n",
+ " def __bool__(self):\n",
+ " print('Called!')\n",
+ " return all(self)"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [],
+ "prompt_number": 13
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "container3 = AllOrNothingBroken([0, 1, 2])\n",
+ "bool(container3)"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [
+ {
+ "metadata": {},
+ "output_type": "pyout",
+ "prompt_number": 14,
+ "text": [
+ "True"
+ ]
+ }
+ ],
+ "prompt_number": 14
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "But subclasses of ``future`` types do:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "from builtins import list\n",
+ "\n",
+ "class AllOrNothingFixed(list):\n",
+ " def __bool__(self):\n",
+ " print('Called!')\n",
+ " return all(self)"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [],
+ "prompt_number": 15
+ },
+ {
+ "cell_type": "code",
+ "collapsed": false,
+ "input": [
+ "container4 = AllOrNothingFixed([0, 1, 2])\n",
+ "bool(container4)"
+ ],
+ "language": "python",
+ "metadata": {},
+ "outputs": [
+ {
+ "metadata": {},
+ "output_type": "pyout",
+ "prompt_number": 17,
+ "text": [
+ "True"
+ ]
+ }
+ ],
+ "prompt_number": 17
+ },
{
"cell_type": "code",
"collapsed": false,
@@ -91,4 +243,4 @@
"metadata": {}
}
]
-}
\ No newline at end of file
+}
diff --git a/docs/older_interfaces.rst b/docs/older_interfaces.rst
new file mode 100644
index 00000000..546f92b9
--- /dev/null
+++ b/docs/older_interfaces.rst
@@ -0,0 +1,141 @@
+.. _older-standard-library-interfaces:
+
+Older interfaces
+~~~~~~~~~~~~~~~~
+
+In addition to the direct and ``install_aliases()`` interfaces (described in
+:ref:`standard-library-imports`), ``future`` supports four other interfaces to
+the reorganized standard library. This is largely for historical reasons (for
+versions prior to 0.14).
+
+
+``future.moves`` interface
+__________________________
+
+The ``future.moves`` interface avoids import hooks. It may therefore be more
+robust, at the cost of less idiomatic code. Use it as follows::
+
+ from future.moves import queue
+ from future.moves import socketserver
+ from future.moves.http.client import HTTPConnection
+ # etc.
+
+If you wish to achieve the effect of a two-level import such as this::
+
+ import http.client
+
+portably on both Python 2 and Python 3, note that Python currently does not
+support syntax like this::
+
+ from future.moves import http.client
+
+One workaround is to replace the dot with an underscore::
+
+ import future.moves.http.client as http_client
+
+
+Comparing future.moves and six.moves
+++++++++++++++++++++++++++++++++++++
+
+``future.moves`` and ``six.moves`` provide a similar Python 3-style
+interface to the native standard library module definitions.
+
+The major difference is that the ``future.moves`` package is a real Python package
+(``future/moves/__init__.py``) with real modules provided as ``.py`` files, whereas
+``six.moves`` constructs fake ``_LazyModule`` module objects within the Python
+code and injects them into the ``sys.modules`` cache.
+
+The advantage of ``six.moves`` is that the code fits in a single module that can be
+copied into a project that seeks to eliminate external dependencies.
+
+The advantage of ``future.moves`` is that it is likely to be more robust in the
+face of magic like Django's auto-reloader and tools like ``py2exe`` and
+``cx_freeze``. See issues #51, #53, #56, and #63 in the ``six`` project for
+more detail of bugs related to the ``six.moves`` approach.
+
+
+``import_`` and ``from_import`` functions
+_________________________________________
+
+The functional interface is to use the ``import_`` and ``from_import``
+functions from ``future.standard_library`` as follows::
+
+ from future.standard_library import import_, from_import
+
+ http = import_('http.client')
+ urllib = import_('urllib.request')
+
+ urlopen, urlsplit = from_import('urllib.request', 'urlopen', 'urlsplit')
+
+This interface also works with two-level imports.
+
+
+Context-manager for import hooks
+________________________________
+
+The context-manager interface is via a context-manager called ``hooks``::
+
+ from future.standard_library import hooks
+ with hooks():
+ import socketserver
+ import queue
+ import configparser
+ import test.support
+ import html.parser
+ from collections import UserList
+ from itertools import filterfalse, zip_longest
+ from http.client import HttpConnection
+ import urllib.request
+ # and other moved modules and definitions
+
+This interface is straightforward and effective, using PEP 302 import
+hooks. However, there are reports that this sometimes leads to problems
+(see issue #238). Until this is resolved, it is probably safer to use direct
+imports or one of the other import mechanisms listed above.
+
+
+install_hooks() call (deprecated)
+_________________________________
+
+The last interface to the reorganized standard library is via a call to
+``install_hooks()``::
+
+ from future import standard_library
+ standard_library.install_hooks()
+
+ import urllib
+ f = urllib.request.urlopen('http://www.python.org/')
+
+ standard_library.remove_hooks()
+
+If you use this interface, it is recommended to disable the import hooks again
+after use by calling ``remove_hooks()``, in order to prevent the futurized
+modules from being invoked inadvertently by other modules. (Python does not
+automatically disable import hooks at the end of a module, but keeps them
+active for the life of a process unless removed.)
+
+.. The call to ``scrub_future_sys_modules()`` removes any modules from the
+.. ``sys.modules`` cache (on Py2 only) that have Py3-style names, like ``http.client``.
+.. This can prevent libraries that have their own Py2/3 compatibility code from
+.. importing the ``future.moves`` or ``future.backports`` modules unintentionally.
+.. Code such as this will then fall through to using the Py2 standard library
+.. modules on Py2::
+..
+.. try:
+.. from http.client import HTTPConnection
+.. except ImportError:
+.. from httplib import HTTPConnection
+..
+.. **Requests**: The above snippet is from the `requests
+.. `_ library. As of v0.12, the
+.. ``future.standard_library`` import hooks are compatible with Requests.
+
+
+.. If you wish to avoid changing every reference of ``http.client`` to
+.. ``http_client`` in your code, an alternative is this::
+..
+.. from future.standard_library import http
+.. from future.standard_library.http import client as _client
+.. http.client = client
+
+.. but it has the advantage that it can be used by automatic translation scripts such as ``futurize`` and ``pasteurize``.
diff --git a/docs/open_function.rst b/docs/open_function.rst
index f409bcaa..7915d8a8 100644
--- a/docs/open_function.rst
+++ b/docs/open_function.rst
@@ -5,15 +5,15 @@ open()
The Python 3 builtin :func:`open` function for opening files returns file
contents as (unicode) strings unless the binary (``b``) flag is passed, as in::
-
+
open(filename, 'rb')
in which case its methods like :func:`read` return Py3 :class:`bytes` objects.
-``future.builtins`` provides an ``open`` function on Py2 that is mostly
-compatible with that on Python 3 (e.g. it offers keyword arguments like
-``encoding``). This maps to the ``open`` backport available in the standard
-library :mod:`io` module on Py2.6 and Py2.7.
+On Py2 with ``future`` installed, the :mod:`builtins` module provides an
+``open`` function that is mostly compatible with that on Python 3 (e.g. it
+offers keyword arguments like ``encoding``). This maps to the ``open`` backport
+available in the standard library :mod:`io` module on Py2.7.
One difference to be aware of between the Python 3 ``open`` and
``future.builtins.open`` on Python 2 is that the return types of methods such
@@ -24,7 +24,7 @@ need the returned data to behave the exactly same way on Py2 as on Py3, you can
cast it explicitly as follows::
from __future__ import unicode_literals
- from future.builtins import *
+ from builtins import open, bytes
data = open('image.png', 'rb').read()
# On Py2, data is a standard 8-bit str with loose Unicode coercion.
@@ -37,4 +37,3 @@ cast it explicitly as follows::
assert data[4] == 13 # integer
# Raises TypeError:
# data + u''
-
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/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 "
@@ -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):
diff --git a/docs/other/fix_notebook_html_colour.py b/docs/other/fix_notebook_html_colour.py
new file mode 100755
index 00000000..36c2205f
--- /dev/null
+++ b/docs/other/fix_notebook_html_colour.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+"""
+A script to re-enable colour in .html files produced from IPython notebooks.
+
+Based on a script in a GitHub gist with this copyright notice:
+
+#----------------------------------------------------------------------------
+# Copyright (c) 2013 - Damián Avila
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# A little snippet to fix @media print issue printing slides from IPython
+#-----------------------------------------------------------------------------
+"""
+
+import io
+import sys
+
+notebook = sys.argv[1]
+assert notebook.endswith('.html')
+# notebook = 'jevans.ipynb'
+path = notebook[:-5] + '.html'
+flag = u'@media print{*{text-shadow:none !important;color:#000 !important'
+
+with io.open(path, 'r') as in_file:
+ data = in_file.readlines()
+ for i, line in enumerate(data):
+ if line[:64] == flag:
+ data[i] = data[i].replace('color:#000 !important;', '')
+
+with io.open(path, 'w') as out_file:
+ out_file.writelines(data)
+
+print("You can now print your slides")
diff --git a/docs/other/lessons.txt b/docs/other/lessons.txt
index 5794f496..ede523cb 100644
--- a/docs/other/lessons.txt
+++ b/docs/other/lessons.txt
@@ -30,7 +30,7 @@ Python 2:
Python 3:
>>> array.array(b'b')
TypeError: must be a unicode character, not bytes
-
+
>>> array.array(u'b')
array('b')
@@ -47,5 +47,3 @@ Running test_bytes.py from Py3 on Py2 (after fixing imports) gives this:
Ran 203 tests in 0.209s
FAILED (failures=31, errors=55, skipped=1)
-
-
diff --git a/docs/other/upload_future_docs.sh b/docs/other/upload_future_docs.sh
index cbfeb26d..04470f3f 100644
--- a/docs/other/upload_future_docs.sh
+++ b/docs/other/upload_future_docs.sh
@@ -1,23 +1,23 @@
-On the local machine
---------------------
+# On the local machine
-git checkout v0.7.0
+git checkout v0.16.0 # or whatever
rm -Rf docs/build/
cd docs; make html
+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 *
-cd /shared
-scp python-future-html-docs.zip 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:
-----------------------
+# On the remote machine:
-cd /var/www/python-future/
-unzip ~/python-future-html-docs.zip
+cd /var/www/python-future.org/
+unzip -o ~/python-future-html-docs.zip
chmod a+r * html/* html/_static/*
-
+cp ~/cheatsheet.pdf ./html/compatible_idioms.pdf
+cp ~/cheatsheet.pdf ./html/cheatsheet.pdf
diff --git a/docs/other/useful_links.txt b/docs/other/useful_links.txt
index 8dec2f9b..abb96849 100644
--- a/docs/other/useful_links.txt
+++ b/docs/other/useful_links.txt
@@ -23,7 +23,7 @@ http://lucumr.pocoo.org/2011/12/7/thoughts-on-python3/
http://python3porting.com/fixers.html
http://washort.twistedmatrix.com/2010/11/unicode-in-python-and-how-to-prevent-it.html
http://docs.python.org/release/3.0.1/whatsnew/3.0.html
-https://pypi.python.org/pypi/unicode-nazi
+https://pypi.org/project/unicode-nazi/
http://www.rmi.net/~lutz/strings30.html
"Porting your code to Python 3": Alexandre Vassalotti: peadrop.com/slides/mp5.pdf
@@ -43,7 +43,7 @@ python-modernize: https://github.com/mitsuhiko/python-modernize
2to3 docs describing the different fixers: http://docs.python.org/2/library/2to3.html
-Injecting code into running Python processes (hopefully not needed): https://pypi.python.org/pypi/pyrasite/2.0
+Injecting code into running Python processes (hopefully not needed): https://pypi.org/project/pyrasite/2.0/
Withdrawn PEP to help with the Py3k standard library transition: http://www.peps.io/364/
@@ -52,7 +52,7 @@ Import hooks
http://www.peps.io/302/
"Hacking Python imports ... for fun and profit": blog post from 2012-05: http://xion.org.pl/2012/05/06/hacking-python-imports/
-Full importlib backport to Py2: https://pypi.python.org/pypi/backport_importlib/0...1
+Full importlib backport to Py2: https://pypi.org/project/backport_importlib/0...1/
Python 2.7 importlib subset: http://docs.python.org/2/whatsnew/2.7.html#importlib-section
@@ -78,7 +78,7 @@ PEPs: 358, 3112, 3137, 3138
http://python3porting.com/noconv.html#unicode-section
Unicode literals u'...' back in Python 3.3: http://www.python.org/dev/peps/pep-0414/
https://github.com/django/django/blob/master/django/utils/encoding.py
-https://pypi.python.org/pypi/unicode-nazi
+https://pypi.org/project/unicode-nazi/
http://docs.python.org/3/library/stdtypes.html#bytes-methods
http://wolfprojects.altervista.org/talks/unicode-and-python-3/
Buffer protocol (which bytes and bytes-like objects obey): http://docs.python.org/3.3/c-api/buffer.html#bufferobjects
@@ -86,7 +86,7 @@ Buffer protocol (which bytes and bytes-like objects obey): http://docs.python.or
Python's future
----------------
-https://ncoghlan_devs-python-notes.readthedocs.org/en/latest/python3/questions_and_answers.html
+https://ncoghlan-devs-python-notes.readthedocs.io/en/latest/python3/questions_and_answers.html
http://www.ironfroggy.com/software/i-am-worried-about-the-future-of-python
@@ -104,8 +104,7 @@ Also: typecheck module on PyPI
To categorize
-------------
-https://pypi.python.org/pypi/awkwardduet/1.1a4
+https://pypi.org/project/awkwardduet/1.1a4/
https://github.com/campadrenalin/persei/blob/master/persei.py
http://slideshare.net/dabeaz/mastering-python-3-io
http://rmi.net/~lutz/strings30.html
-
diff --git a/docs/overview.rst b/docs/overview.rst
index 0aa732a1..72a33558 100644
--- a/docs/overview.rst
+++ b/docs/overview.rst
@@ -1,2 +1 @@
.. include:: ../README.rst
-
diff --git a/docs/pasteurize.rst b/docs/pasteurize.rst
new file mode 100644
index 00000000..070b5d1a
--- /dev/null
+++ b/docs/pasteurize.rst
@@ -0,0 +1,45 @@
+.. _backwards-conversion:
+
+``pasteurize``: Py3 to Py2/3
+----------------------------
+
+Running ``pasteurize -w mypy3module.py`` turns this Python 3 code::
+
+ import configparser
+ import copyreg
+
+ class Blah:
+ pass
+ print('Hello', end=None)
+
+into this code which runs on both Py2 and Py3::
+
+ from __future__ import print_function
+ from future import standard_library
+ standard_library.install_hooks()
+
+ import configparser
+ import copyreg
+
+ class Blah(object):
+ pass
+ print('Hello', end=None)
+
+Notice that both ``futurize`` and ``pasteurize`` create explicit new-style
+classes that inherit from ``object`` on both Python versions, and both
+refer to stdlib modules (as well as builtins) under their Py3 names.
+
+Note also that the ``configparser`` module is a special case; there is a full
+backport available on PyPI (https://pypi.org/project/configparser/), so, as
+of v0.16.0, ``python-future`` no longer provides a ``configparser`` package
+alias. To use the resulting code on Py2, install the ``configparser`` backport
+with ``pip install configparser`` or by adding it to your ``requirements.txt``
+file.
+
+``pasteurize`` also handles the following Python 3 features:
+
+- keyword-only arguments
+- metaclasses (using :func:`~future.utils.with_metaclass`)
+- extended tuple unpacking (PEP 3132)
+
+To handle function annotations (PEP 3107), see :ref:`func_annotations`.
diff --git a/docs/porting.rst b/docs/porting.rst
deleted file mode 100644
index 9d500c4c..00000000
--- a/docs/porting.rst
+++ /dev/null
@@ -1,131 +0,0 @@
-.. _porting:
-
-Python 2 to 2&3 porting cheat-sheet
-===================================
-
-Instructions and notes on porting code from Python 2 to both Python 3 and 2 using ``future``:
-
-.. _porting-setup:
-
-Step 0: setup
--------------
-
-Step 0 goal: set up and see the tests passing on Python 2 and failing on Python 3.
-
-a. Clone the package from github/bitbucket. Optionally rename your repo to ``package-future``. Examples: ``reportlab-future``, ``paramiko-future``, ``mezzanine-future``.
-b. Create and activate a Python 2 conda environment or virtualenv. Install the package with ``python setup.py install`` and run its test suite on Py2.7 or Py2.6 (e.g. ``python setup.py test`` or ``py.test`` or ``nosetests``)
-c. Optionally: if there’s a ``.travis.yml`` file, add Python version 3.3 and remove any versions < 2.6.
-d. Install Python 3.3 with e.g. ``sudo apt-get install python3``. On other platforms, an easy way is to use Miniconda3. See `Miniconda3 `_. Then e.g.::
-
- conda create -n py33 python=3.3
-
-.. _porting-step1:
-
-Step 1: modern Py2 code
------------------------
-
-The goal for this step is to modernize the Python 2 code without introducing any dependencies (on ``future`` or e.g. ``six``) at this stage.
-
-1a. Install ``future`` into the virtualenv using::
-
- pip install future
-
-1b. Run ``futurize --stage1 -w *.py subdir1/*.py subdir2/*.py``
-
- Note that with ``zsh``, you can apply stage1 to all Python source files
- recursively with::
-
- futurize --stage1 -w **/*.py
-
-1c. Commit all changes
-
-1d. Re-run the test suite on Py2 and fix any errors.
-
-See :ref:`forwards-conversion-stage1` for more info.
-
-
-Example error
-~~~~~~~~~~~~~
-
-One relatively common error after conversion is::
-
- Traceback (most recent call last):
- ...
- File "/home/user/Install/BleedingEdge/reportlab/tests/test_encrypt.py", line 19, in
- from .test_pdfencryption import parsedoc
- ValueError: Attempted relative import in non-package
-
-If you get this error, try adding an empty ``__init__.py`` file in the package
-directory. (In this example, in the tests/ directory.) If this doesn’t help,
-and if this message appears for all tests, they must be invoked differently
-(from the cmd line or e.g. ``setup.py``). The way to run a module inside a
-package on Python 3, or on Python 2 with ``absolute_import`` in effect, is::
-
- python -m tests.test_platypus_xref
-
-(For more info, see `PEP 328 `_ and
-the `PEP 8 `_ section on absolute
-imports.)
-
-
-.. _porting-step2:
-
-Step 2: working Py3 code that still supports Py2
-------------------------------------------------
-
-The goal for this step is to get the tests passing first on Py3 and then on Py2
-again with the help of the ``future`` package.
-
-2a. Run::
-
- futurize —-stage2 myfolder1/*.py myfolder2/*.py
-
- Alternatively, with ``zsh``, you can view the stage 2 changes to all Python source files
- recursively with::
-
- futurize --stage2 **/*.py
-
- To apply the changes, add the ``-w`` argument.
-
- This stage makes further conversions needed to support both Python 2 and 3.
- These will likely require imports from ``future``, such as::
-
- from future import standard_library
- standard_library.install_hooks()
- from future.builtins import bytes
- from future.builtins import open
-
-Optionally, you can use the ``--unicode-literals`` flag to adds this further
-import to the top of each module::
-
- from __future__ import unicode_literals
-
-All strings would then be unicode (on Py2 as on Py3) unless explicitly marked
-with a ``b''`` prefix.
-
-If you would like ``futurize`` to import all the changed builtins to have their
-Python 3 semantics on Python 2, invoke it like this::
-
- futurize --stage2 --all-imports myfolder/*.py
-
-
-2b. Re-run your tests on Py3 now. Make changes until your tests pass on Python 3.
-
-2c. Commit your changes! :)
-
-2d. Now run your tests on Python 2 and notice the errors. Add wrappers from ``future`` to re-enable Python 2 compatibility:
-
- - :func:`utils.reraise()` function for raising exceptions compatibly
- - ``bytes(b'blah')`` instead of ``b'blah'``
- - ``str('my string')`` instead of ``'my string'`` if you need to enforce Py3’s strict type-checking on Py2
- - ``int(1234)`` instead of ``1234`` if you want to enforce a Py3-like long integer
- - :func:`@utils.implements_iterator` decorator for any custom iterator class with a ``.__next__()`` method (which used to be ``.next()``)
- - :func:`@utils.python_2_unicode_compatible` decorator for any class with a ``__str__`` method (which used to be ``__unicode__``).
- - :func:`utils.with_metaclass` to define any metaclasses.
-
-See :ref:`what-else` for more info.
-
-After each change, re-run the tests on Py3 and Py2 to ensure they pass on both.
-
-2e. You’re done! Celebrate! Push your code and announce to the world! Hashtags
-#python3 #python-future.
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index 299822d9..8461a1a2 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -16,7 +16,8 @@ To install the latest stable version, type::
pip install future
-If you would prefer the latest development version, it is available `here `_.
+If you would prefer the latest development version, it is available `here
+`_.
If you are writing code from scratch
@@ -26,14 +27,15 @@ The easiest way is to start each new module with these lines::
from __future__ import (absolute_import, division,
print_function, unicode_literals)
- from future.builtins import *
+ from builtins import *
Then write standard Python 3 code. The :mod:`future` package will
-provide support for running your code on Python 2.6 and 2.7 mostly unchanged.
+provide support for running your code on Python 2.7, and 3.4+ mostly
+unchanged.
-- For more examples, see :ref:`overview`.
-- For explicit import forms, see :ref:`imports`.
+- For explicit import forms, see :ref:`explicit-imports`.
- For more details, see :ref:`what-else`.
+- For a cheat sheet, see :ref:`compatible-idioms`.
To convert existing Python 3 code
@@ -48,13 +50,13 @@ module::
from __future__ import print_function
from __future__ import unicode_literals
- from future.builtins import open
- from future.builtins import str
+ from builtins import open
+ from builtins import str
# etc., as needed
from future import standard_library
- standard_library.install_hooks()
-
+ standard_library.install_aliases()
+
and converts several Python 3-only constructs (like keyword-only arguments) to a
form compatible with both Py3 and Py2. Most remaining Python 3 code should
simply work on Python 2.
@@ -65,7 +67,9 @@ See :ref:`backwards-conversion` for more details.
To convert existing Python 2 code
---------------------------------
-Start with the :ref:`automatic-conversion` page.
+.. include:: futurize_overview.rst
+
+See :ref:`forwards-conversion-stage1` and :ref:`forwards-conversion-stage2` for more details.
.. If you already know Python 3, start with the :ref:`automatic-conversion` page.
.. If you don't know Python 3 yet, start with :ref:`python3-essentials`.
@@ -76,25 +80,20 @@ Start with the :ref:`automatic-conversion` page.
Standard library reorganization
-------------------------------
-:mod:`future` supports the standard library reorganization (PEP 3108)
-via import hooks, allowing almost all moved standard library modules to
-be accessed under their Python 3 names and locations in Python 2::
-
+:mod:`future` supports the standard library reorganization (PEP 3108) via
+one of several mechanisms, allowing most moved standard library modules
+to be accessed under their Python 3 names and locations in Python 2::
+
from future import standard_library
- with standard_library.hooks():
- import socketserver
- import queue
- import configparser
- import test.support
- import html.parser
- from collections import UserList
- from itertools import filterfalse, zip_longest
- from http.client import HttpConnection
- # and other moved modules and definitions
-
-:mod:`future` also includes backports for these stdlib modules from Py3
-that were heavily refactored versus Py2::
-
+ standard_library.install_aliases()
+
+ # Then these Py3-style imports work on both Python 2 and Python 3:
+ import socketserver
+ import queue
+ from collections import UserDict, UserList, UserString
+ from collections import ChainMap # even on Py2.7
+ from itertools import filterfalse, zip_longest
+
import html
import html.entities
import html.parser
@@ -105,27 +104,16 @@ that were heavily refactored versus Py2::
import http.cookies
import http.cookiejar
- import urllib
- import urllib.parse
import urllib.request
+ import urllib.parse
+ import urllib.response
import urllib.error
+ import urllib.robotparser
import xmlrpc.client
import xmlrpc.server
-
-The following modules are currently not supported, but we aim to support them in
-the future::
-
-If you need one of these, please open an issue `here
-`_.
-
-For other forms of imports from the standard library, see
-:ref:`standard-library-imports`.
-
-For more information on interfaces that have changed in the standard library
-between Python 2 and Python 3, see :ref:`stdlib-incompatibilities`.
-
+and others. For a complete list, see :ref:`direct-imports`.
.. _py2-dependencies:
@@ -138,25 +126,26 @@ upon import. First, install the Python 2-only package into your Python 3
environment::
$ pip3 install mypackagename --no-compile # to ignore SyntaxErrors
-
+
(or use ``pip`` if this points to your Py3 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
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
-`_ or post to the
-`python-porting `_
-mailing list.
+`_.
For more information on the automatic translation feature, see :ref:`translation`.
+
Next steps
----------
-For more information about writing Py3/2-compatible code, see :ref:`what-else`.
+For more information about writing Py2/3-compatible code, see:
+- :ref:`compatible-idioms`
+- :ref:`what-else`.
diff --git a/docs/reference.rst b/docs/reference.rst
index b974db96..d9ac5e12 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -1,11 +1,10 @@
-#############
-API Reference
-#############
+API Reference (in progress)
+***************************
-*NOTE: This page is still a work in progress... We need to go through our
+**NOTE: This page is still a work in progress... We need to go through our
docstrings and make them sphinx-compliant, and figure out how to improve
formatting with the sphinx-bootstrap-theme plugin. Pull requests would be
-very welcome.*
+very welcome.**
.. contents::
@@ -13,7 +12,7 @@ very welcome.*
:depth: 2
future.builtins Interface
-============================
+=========================
.. automodule:: future.builtins
:members:
@@ -22,6 +21,13 @@ future.builtins Interface
.. relevant docstrings.
+Backported types from Python 3
+==============================
+
+.. automodule:: future.types
+ :members:
+
+
future.standard_library Interface
=================================
@@ -36,26 +42,40 @@ future.utils Interface
:members:
-Backported types
-================
+past.builtins Interface
+=========================
+
+.. automodule:: past.builtins
+ :members:
-bytes
------
-.. automodule:: future.builtins.types.newbytes
+.. Docs are also in future-builtins.rst. Extract these and put them into the
+.. relevant docstrings.
-dict
------
-.. automodule:: future.builtins.types.newdict
-int
----
-.. automodule:: future.builtins.backports.newint
+Forward-ported types from Python 2
+==================================
+
+.. automodule:: past.types
+ :members:
-range
------
-.. automodule:: future.builtins.types.newrange
-str
----
-.. automodule:: future.builtins.types.newstr
+.. bytes
+.. -----
+.. .. automodule:: future.types.newbytes
+..
+.. dict
+.. -----
+.. .. automodule:: future.types.newdict
+..
+.. int
+.. ---
+.. .. automodule:: future.builtins.backports.newint
+..
+.. range
+.. -----
+.. .. automodule:: future.types.newrange
+..
+.. str
+.. ---
+.. .. automodule:: future.types.newstr
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 00000000..265642f4
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,3 @@
+sphinx==3.2.1
+Pallets-Sphinx-Themes==2.2.1
+setuptools==70.0.0
diff --git a/docs/roadmap.rst b/docs/roadmap.rst
index a759796f..c5020d5e 100644
--- a/docs/roadmap.rst
+++ b/docs/roadmap.rst
@@ -5,14 +5,17 @@ futurize script
---------------
1. "Safe" mode -- from Py2 to modern Py2 or Py3 to more-compatible Py3
+
- Split the fixers into two categories: safe and bold
- Safe is highly unlikely to break existing Py2 or Py3 support. The
output of this still requires :mod:`future` imports. Examples:
- - Compatible metaclass syntax on Py3
- - Explicit inheritance from object on Py3
-
+
+ - Compatible metaclass syntax on Py3
+ - Explicit inheritance from object on Py3
+
- Bold might make assumptions about which strings on Py2 should be
unicode strings and which should be bytestrings.
+
- We should also build up a database of which standard library
interfaces on Py2 and Py3 accept unicode strings versus
byte-strings, which have changed, and which haven't.
@@ -34,11 +37,10 @@ Experimental:
from future import new_metaclass_syntax
from future import new_style_classes
-- Maybe::
+- [Done] Maybe::
from future.builtins import str
should import a custom str is a Py3 str-like object which inherits from unicode and
removes the decode() method and has any other Py3-like behaviours
(possibly stricter casting?)
-
diff --git a/docs/standard_library_imports.rst b/docs/standard_library_imports.rst
new file mode 100644
index 00000000..c09e9e30
--- /dev/null
+++ b/docs/standard_library_imports.rst
@@ -0,0 +1,181 @@
+.. _standard-library-imports:
+
+Standard library imports
+------------------------
+
+:mod:`future` supports the standard library reorganization (PEP 3108) through
+several mechanisms.
+
+.. _direct-imports:
+
+Direct imports
+~~~~~~~~~~~~~~
+
+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 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``::
+
+ >>> # Alias for future.builtins on Py2:
+ >>> from builtins import str, open, range, dict
+
+ >>> # Top-level packages with Py3 names provided on Py2:
+ >>> import queue
+ >>> import tkinter.dialog
+ >>> etc.
+
+Notice that this code actually runs on Python 3 without the presence of the
+``future`` package.
+
+Of the 44 modules that were refactored with PEP 3108 (standard library
+reorganization), 29 are supported with direct imports in the above manner. The
+complete list is here::
+
+ ### Renamed modules:
+
+ import builtins
+
+ import copyreg
+
+ import html
+ import html.entities
+ import html.parser
+
+ import http.client
+ import http.cookies
+ import http.cookiejar
+ import http.server
+
+ import queue
+
+ import reprlib
+
+ import socketserver
+
+ from tkinter import colorchooser
+ from tkinter import commondialog
+ from tkinter import constants
+ from tkinter import dialog
+ from tkinter import dnd
+ from tkinter import filedialog
+ from tkinter import font
+ from tkinter import messagebox
+ from tkinter import scrolledtext
+ from tkinter import simpledialog
+ from tkinter import tix
+ from tkinter import ttk
+
+ import winreg # Windows only
+
+ import xmlrpc.client
+ import xmlrpc.server
+
+ import _dummy_thread
+ import _markupbase
+ import _thread
+
+Note that, as of v0.16.0, ``python-future`` no longer includes an alias for the
+``configparser`` module because a full backport exists (see https://pypi.org/project/configparser/).
+
+.. _list-standard-library-refactored:
+
+Aliased imports
+~~~~~~~~~~~~~~~
+
+The following 14 modules were refactored or extended from Python 2.7 to 3.x
+but were neither renamed in Py3.x nor were the new APIs backported to Py2.x.
+This precludes compatibility interfaces that work out-of-the-box. Instead, the
+``future`` package makes the Python 3.x APIs available on Python 2.x as
+follows::
+
+ from future.standard_library import install_aliases
+ install_aliases()
+
+ from collections import UserDict, UserList, UserString
+
+ import urllib.parse
+ import urllib.request
+ import urllib.response
+ import urllib.robotparser
+ import urllib.error
+
+ import dbm
+ import dbm.dumb
+ import dbm.gnu # requires Python dbm support
+ import dbm.ndbm # requires Python dbm support
+
+ from itertools import filterfalse, zip_longest
+
+ from subprocess import getoutput, getstatusoutput
+
+ from sys import intern
+
+ import test.support
+
+
+The newly exposed ``urllib`` submodules are backports of those from Py3.x.
+This means, for example, that ``urllib.parse.unquote()`` now exists and takes
+an optional ``encoding`` argument on Py2.x as it does on Py3.x.
+
+**Limitation:** Note that the ``http``-based backports do not currently support
+HTTPS (as of 2015-09-11) because the SSL support changed considerably in Python
+3.x. If you need HTTPS support, please use this idiom for now::
+
+ from future.moves.urllib.request import urlopen
+
+Backports also exist of the following features from Python 3.4:
+
+- ``math.ceil`` returns an int on Py3
+- ``collections.ChainMap`` (for 2.7)
+- ``reprlib.recursive_repr`` (for 2.7)
+
+These can then be imported on Python 2.7+ as follows::
+
+ from future.standard_library import install_aliases
+ install_aliases()
+
+ from math import ceil # now returns an int
+ from collections import ChainMap
+ from reprlib import recursive_repr
+
+
+External standard-library backports
+-----------------------------------
+
+Backports of the following modules from the Python 3.x standard library are
+available independently of the python-future project::
+
+ import enum # pip install enum34
+ import singledispatch # pip install singledispatch
+ import pathlib # pip install pathlib
+
+A few modules from Python 3.4 are also available in the ``backports``
+package namespace after ``pip install backports.lzma`` etc.::
+
+ from backports import lzma
+ from backports import functools_lru_cache as lru_cache
+
+
+Included full backports
+-----------------------
+
+Alpha-quality full backports of the following modules from Python 3.3's
+standard library to Python 2.x are also available in ``future.backports``::
+
+ http.client
+ http.server
+ html.entities
+ html.parser
+ urllib
+ xmlrpc.client
+ xmlrpc.server
+
+The goal for these modules, unlike the modules in the ``future.moves`` package
+or top-level namespace, is to backport new functionality introduced in Python
+3.3.
+
+If you need the full backport of one of these packages, please open an issue `here
+`_.
diff --git a/docs/stdlib_incompatibilities.rst b/docs/stdlib_incompatibilities.rst
index 131f9bef..e93f96ba 100644
--- a/docs/stdlib_incompatibilities.rst
+++ b/docs/stdlib_incompatibilities.rst
@@ -1,12 +1,5 @@
.. _stdlib-incompatibilities:
-Use of strings and bytes in standard library interfaces
-=======================================================
-
-- io.StringIO: takes a unicode on both Py2 and Py3
-- io.BytesIO: takes a byte-string on both Py2 and Py3
-
-
Standard library incompatibilities
==================================
@@ -25,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:
@@ -40,14 +32,14 @@ platform string: unicode string on Python 3, byte string on Python 2.
Python 2::
>>> array.array(b'b')
array.array(b'b')
-
+
>>> array.array(u'u')
TypeError: must be char, not unicode
Python 3::
>>> array.array(b'b')
TypeError: must be a unicode character, not bytes
-
+
>>> array.array(u'b')
array('b')
@@ -61,9 +53,12 @@ You can use the following code on both Python 3 and Python 2::
import array
# ...
-
+
a = array.array(bytes_to_native_str(b'b'))
+This was `fixed in Python 2.7.11
+`_.
+Since then, ``array.array()`` now also accepts unicode format typecode.
.. _stdlib-array-read:
@@ -95,14 +90,16 @@ This enables 'ASCII mode' for regular expressions (see the docs `here
struct.pack()
-------------
-The :func:`struct.pack` function must take a native string as its format argument. For example::
+Before Python version 2.7.7, the :func:`struct.pack` function
+required a native string as its format argument. For example::
>>> from __future__ import unicode_literals
>>> from struct import pack
- >>> pack('<4H2I', version, rec_type, build, year, file_hist_flags, ver_can_read)
-
-raises ``TypeError: Struct() argument 1 must be string, not unicode`` on Python
-2. To work around this, pass the format string argument as e.g.
-``future.utils.native('<4H2I')``.
+ >>> pack('<4H2I', version, rec_type, build, year, file_hist_flags, ver_can_read)
+raised ``TypeError: Struct() argument 1 must be string, not unicode``.
+This was `fixed in Python 2.7.7
+`_.
+Since then, ``struct.pack()`` now also accepts unicode format
+strings.
diff --git a/docs/str_object.rst b/docs/str_object.rst
index 8c35b471..568b897a 100644
--- a/docs/str_object.rst
+++ b/docs/str_object.rst
@@ -14,15 +14,20 @@ 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 backport of the :mod:`str` object from Python 3 which
-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::
+: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 __future__ import unicode_literals
- >>> from future.builtins import str
+ >>> from builtins import str
-(On Py3, this simply imports the builtin :class:`str` object.)
+On Py2, this gives us::
+
+ >>> str
+ future.types.newstr.newstr
+
+(On Py3, it is simply the usual builtin :class:`str` object.)
Then, for example, the following code has the same effect on Py2 as on Py3::
@@ -44,14 +49,14 @@ Then, for example, the following code has the same effect on Py2 as on Py3::
TypeError: argument can't be
Various other operations that mix strings and bytes or other types are
-permitted on Py2 with the :class:`future.builtins.str` class even though they
+permitted on Py2 with the :class:`newstr` class even though they
are illegal with Python 3. For example::
>>> s2 = b'/' + str('ABCD')
>>> s2
'/ABCD'
>>> type(s2)
- future.builtins.types.newstr.newstr
+ future.types.newstr.newstr
This is allowed for compatibility with parts of the Python 2 standard
library and various third-party libraries that mix byte-strings and unicode
@@ -62,16 +67,16 @@ they are unicode. (See ``posixpath.py``.) Another example is the
.. For example, this is permissible on Py2::
-..
+..
.. >>> u'u' > 10
.. True
-..
+..
.. >>> u'u' <= b'u'
.. True
-..
+..
.. On Py3, these raise TypeErrors.
-In most other ways, these :class:`future.builtins.str` objects on Py2 have the
+In most other ways, these :class:`builtins.str` objects on Py2 have the
same behaviours as Python 3's :class:`str`::
>>> s = str('ABCD')
@@ -79,21 +84,16 @@ same behaviours as Python 3's :class:`str`::
>>> assert list(s) == ['A', 'B', 'C', 'D']
>>> assert s.split('B') == ['A', 'CD']
-.. If you must ensure identical use of (unicode) strings across Py3 and Py2 in a
-.. single-source codebase, you can wrap string literals in a :func:`~str` call, as
-.. follows::
-..
-.. from __future__ import unicode_literals
-.. from future.builtins import *
-..
-.. # ...
-..
-.. s = str('This absolutely must behave like a Py3 string')
-..
-.. # ...
-..
-.. Most of the time this is unnecessary, but the stricter type-checking of the
-.. ``future.builtins.str`` object is useful for ensuring the same consistent
-.. separation between unicode and byte strings on Py2 as on Py3. This is
-.. important when writing protocol handlers, for example.
+The :class:`str` type from :mod:`builtins` also provides support for the
+``surrogateescape`` error handler on Python 2.x. Here is an example that works
+identically on Python 2.x and 3.x::
+
+ >>> from builtins import str
+ >>> s = str(u'\udcff')
+ >>> s.encode('utf-8', 'surrogateescape')
+ b'\xff'
+
+This feature is in alpha. Please leave feedback `here
+`_ about whether this
+works for you.
diff --git a/docs/translation.rst b/docs/translation.rst
index a839d5c0..632c46b1 100644
--- a/docs/translation.rst
+++ b/docs/translation.rst
@@ -1,7 +1,7 @@
.. _translation:
-Importing Python 2-only dependencies on Python 3
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Using Python 2-only dependencies on Python 3
+--------------------------------------------
The ``past`` module provides an experimental ``translation`` package to help
with importing and using old Python 2 modules in a Python 3 environment.
@@ -19,11 +19,11 @@ Here is how to use it::
$ 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::
-
- >>> from past import autotranslate
+``past.translation.autotranslate()`` function. Example::
+
+ >>> from past.translation import autotranslate
>>> autotranslate(['plotrique'])
>>> import plotrique
@@ -40,19 +40,19 @@ This will translate, import and run Python 2 code such as the following::
# Print statements are translated transparently to functions:
print 'Hello from a print statement'
-
+
# xrange() is translated to Py3's range():
total = 0
for i in xrange(10):
total += i
print 'Total is: %d' % total
-
+
# Dictionary methods like .keys() and .items() are supported and
# return lists as on Python 2:
d = {'a': 1, 'b': 2}
assert d.keys() == ['a', 'b']
assert isinstance(d.items(), list)
-
+
# Functions like range, reduce, map, filter also return lists:
assert isinstance(range(10), list)
@@ -72,7 +72,7 @@ This will translate, import and run Python 2 code such as the following::
The attributes of the module are then accessible normally from Python 3.
For example::
-
+
# This Python 3 code works
>>> type(mypy2module.d)
builtins.dict
@@ -84,10 +84,10 @@ This is a standard Python 3 data type, so, when called from Python 3 code,
builtins.dict_keys
-.. _translation-limitations
+.. _translation-limitations:
Known limitations of ``past.translation``
-*******************************************
+*****************************************
- It currently requires a newline at the end of the module or it throws a
``ParseError``.
@@ -110,5 +110,3 @@ Known limitations of ``past.translation``
Please report any bugs you find on the ``python-future`` `bug tracker
`_.
-
-
diff --git a/docs/unicode_literals.rst b/docs/unicode_literals.rst
new file mode 100644
index 00000000..f6eb2839
--- /dev/null
+++ b/docs/unicode_literals.rst
@@ -0,0 +1,197 @@
+.. _unicode-literals:
+
+Should I import unicode_literals?
+---------------------------------
+
+The ``future`` package can be used with or without ``unicode_literals``
+imports.
+
+In general, it is more compelling to use ``unicode_literals`` when
+back-porting new or existing Python 3 code to Python 2/3 than when porting
+existing Python 2 code to 2/3. In the latter case, explicitly marking up all
+unicode string literals with ``u''`` prefixes would help to avoid
+unintentionally changing the existing Python 2 API. However, if changing the
+existing Python 2 API is not a concern, using ``unicode_literals`` may speed up
+the porting process.
+
+This section summarizes the benefits and drawbacks of using
+``unicode_literals``. To avoid confusion, we recommend using
+``unicode_literals`` everywhere across a code-base or not at all, instead of
+turning on for only some modules.
+
+
+
+Benefits
+~~~~~~~~
+
+1. String literals are unicode on Python 3. Making them unicode on Python 2
+ leads to more consistency of your string types across the two
+ runtimes. This can make it easier to understand and debug your code.
+
+2. Code without ``u''`` prefixes is cleaner, one of the claimed advantages
+ of Python 3. Even though some unicode strings would require a function
+ call to invert them to native strings for some Python 2 APIs (see
+ :ref:`stdlib-incompatibilities`), the incidence of these function calls
+ would usually be much lower than the incidence of ``u''`` prefixes for text
+ strings in the absence of ``unicode_literals``.
+
+3. The diff when porting to a Python 2/3-compatible codebase may be smaller,
+ less noisy, and easier to review with ``unicode_literals`` than if an
+ explicit ``u''`` prefix is added to every unadorned string literal.
+
+4. If support for Python 3.2 is required (e.g. for Ubuntu 12.04 LTS or
+ Debian wheezy), ``u''`` prefixes are a ``SyntaxError``, making
+ ``unicode_literals`` the only option for a Python 2/3 compatible
+ codebase. [However, note that ``future`` doesn't support Python 3.0-3.2.]
+
+
+Drawbacks
+~~~~~~~~~
+
+1. Adding ``unicode_literals`` to a module amounts to a "global flag day" for
+ that module, changing the data types of all strings in the module at once.
+ Cautious developers may prefer an incremental approach. (See
+ `here `_ for an excellent article
+ describing the superiority of an incremental patch-set in the the case
+ of the Linux kernel.)
+
+.. This is a larger-scale change than adding explicit ``u''`` prefixes to
+.. all strings that should be Unicode.
+
+2. Changing to ``unicode_literals`` will likely introduce regressions on
+ Python 2 that require an initial investment of time to find and fix. The
+ APIs may be changed in subtle ways that are not immediately obvious.
+
+ An example on Python 2::
+
+ ### Module: mypaths.py
+
+ ...
+ def unix_style_path(path):
+ return path.replace('\\', '/')
+ ...
+
+ ### User code:
+
+ >>> path1 = '\\Users\\Ed'
+ >>> unix_style_path(path1)
+ '/Users/ed'
+
+ On Python 2, adding a ``unicode_literals`` import to ``mypaths.py`` would
+ change the return type of the ``unix_style_path`` function from ``str`` to
+ ``unicode`` in the user code, which is difficult to anticipate and probably
+ unintended.
+
+ The counter-argument is that this code is broken, in a portability
+ sense; we see this from Python 3 raising a ``TypeError`` upon passing the
+ function a byte-string. The code needs to be changed to make explicit
+ whether the ``path`` argument is to be a byte string or a unicode string.
+
+3. With ``unicode_literals`` in effect, there is no way to specify a native
+ string literal (``str`` type on both platforms). This can be worked around as follows::
+
+ >>> from __future__ import unicode_literals
+ >>> ...
+ >>> from future.utils import bytes_to_native_str as n
+
+ >>> s = n(b'ABCD')
+ >>> s
+ 'ABCD' # on both Py2 and Py3
+
+ although this incurs a performance penalty (a function call and, on Py3,
+ a ``decode`` method call.)
+
+ This is a little awkward because various Python library APIs (standard
+ and non-standard) require a native string to be passed on both Py2
+ and Py3. (See :ref:`stdlib-incompatibilities` for some examples. WSGI
+ dictionaries are another.)
+
+3. If a codebase already explicitly marks up all text with ``u''`` prefixes,
+ and if support for Python versions 3.0-3.2 can be dropped, then
+ removing the existing ``u''`` prefixes and replacing these with
+ ``unicode_literals`` imports (the porting approach Django used) would
+ introduce more noise into the patch and make it more difficult to review.
+ However, note that the ``futurize`` script takes advantage of PEP 414 and
+ does not remove explicit ``u''`` prefixes that already exist.
+
+4. Turning on ``unicode_literals`` converts even docstrings to unicode, but
+ Pydoc breaks with unicode docstrings containing non-ASCII characters for
+ Python versions < 2.7.7. (`Fix
+ committed `_ in Jan 2014.)::
+
+ >>> def f():
+ ... u"Author: Martin von Löwis"
+
+ >>> help(f)
+
+ /Users/schofield/Install/anaconda/python.app/Contents/lib/python2.7/pydoc.pyc in pipepager(text, cmd)
+ 1376 pipe = os.popen(cmd, 'w')
+ 1377 try:
+ -> 1378 pipe.write(text)
+ 1379 pipe.close()
+ 1380 except IOError:
+
+ UnicodeEncodeError: 'ascii' codec can't encode character u'\xf6' in position 71: ordinal not in range(128)
+
+See `this Stack Overflow thread
+`_
+for other gotchas.
+
+
+Others' perspectives
+~~~~~~~~~~~~~~~~~~~~
+
+In favour of ``unicode_literals``
+*********************************
+
+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
+``unicode_literals`` for the port of Django to a Python 2/3-compatible
+codebase.:
+
+ "... I'd like to explain why this PEP [PEP 414, which allows explicit
+ ``u''`` prefixes for unicode literals on Python 3.3+] is at odds with
+ the porting philosophy I've applied to Django, and why I would have
+ vetoed taking advantage of it.
+
+ "I believe that aiming for a Python 2 codebase with Python 3
+ compatibility hacks is a counter-productive way to port a project. You
+ end up with all the drawbacks of Python 2 (including the legacy `u`
+ prefixes) and none of the advantages Python 3 (especially the sane
+ string handling).
+
+ "Working to write Python 3 code, with legacy compatibility for Python
+ 2, is much more rewarding. Of course it takes more effort, but the
+ results are much cleaner and much more maintainable. It's really about
+ looking towards the future or towards the past.
+
+ "I understand the reasons why PEP 414 was proposed and why it was
+ accepted. It makes sense for legacy software that is minimally
+ maintained. I hope nobody puts Django in this category!"
+
+
+Against ``unicode_literals``
+****************************
+
+ "There are so many subtle problems that ``unicode_literals`` causes.
+ For instance lots of people accidentally introduce unicode into
+ filenames and that seems to work, until they are using it on a system
+ where there are unicode characters in the filesystem path."
+
+ -- Armin Ronacher
+
+ "+1 from me for avoiding the unicode_literals future, as it can have
+ very strange side effects in Python 2.... This is one of the key
+ reasons I backed Armin's PEP 414."
+
+ -- Nick Coghlan
+
+ "Yeah, one of the nuisances of the WSGI spec is that the header values
+ IIRC are the str or StringType on both py2 and py3. With
+ unicode_literals this causes hard-to-spot bugs, as some WSGI servers
+ might be more tolerant than others, but usually using unicode in python
+ 2 for WSGI headers will cause the response to fail."
+
+ -- Antti Haapala
diff --git a/docs/upgrading.rst b/docs/upgrading.rst
new file mode 100644
index 00000000..0d8afca6
--- /dev/null
+++ b/docs/upgrading.rst
@@ -0,0 +1,12 @@
+.. upgrading
+
+Upgrading
+*********
+
+We strive to support compatibility between versions of ``python-future``. Part of this involves keeping around old interfaces and marking them as deprecated for a period to allow projects to transition in a straightforward manner to using the new interfaces.
+
+
+.. upgrading-to-v0.12
+
+Upgrading to v0.12
+==================
diff --git a/docs/utilities.rst b/docs/utilities.rst
index 24170e12..e3f1e9c6 100644
--- a/docs/utilities.rst
+++ b/docs/utilities.rst
@@ -46,4 +46,3 @@ Examples::
# prints ['H', 'E', 'L', 'L', 'O']
On Python 3 these decorators are no-ops.
-
diff --git a/docs/what_else.rst b/docs/what_else.rst
index 1afb03fe..51f19869 100644
--- a/docs/what_else.rst
+++ b/docs/what_else.rst
@@ -10,8 +10,8 @@ compatible code.
.. include:: bytes_object.rst
.. include:: str_object.rst
-.. include:: int_object.rst
.. include:: dict_object.rst
+.. include:: int_object.rst
.. include:: isinstance.rst
.. include:: open_function.rst
.. include:: custom_str_methods.rst
@@ -23,4 +23,3 @@ compatible code.
.. include:: metaclasses.rst
..
-
diff --git a/docs/whatsnew.rst b/docs/whatsnew.rst
index 1cd37b1f..d706b2e5 100644
--- a/docs/whatsnew.rst
+++ b/docs/whatsnew.rst
@@ -1,789 +1,30 @@
-What's new
-**********
-
-
-.. whats-new-0.12:
-
-What's new in version 0.12
-==========================
-
-The major new feature in version is improvements in the standard library module
-and its compatibility with 3rd-party modules.
-
-Standard-library import hooks now require explicit installation
----------------------------------------------------------------
-
-*Note: backwards-incompatible change:* As previously announced (see
-:ref:`deprecated-auto-import-hooks`), the import hooks must now be installed
-explicitly, as follows::
-
- from future import standard_library
- with standard_library.hooks():
- import html.parser
- import http.client
-
-or with the functional interface::
-
- from future import standard_library
- standard_library.install_hooks():
-
- import html.parser
- import http.client
- ...
- standard_library.remove_hooks()
-
-This allows finer-grained control over whether import hooks are enabled for
-other imported modules, such as ``requests``, which provide their own Python
-2/3 compatibility code. This also improves compatibility of ``future`` with
-tools like ``py2exe`` (see `issue #31
-`).
-
-
-.. Versioned standard library imports
-.. ----------------------------------
-..
-.. ``future`` now offers a choice of either backported versions of the standard library modules from Python 3.3 or renamed Python 2.7 versions. Use it as follows::
-..
-.. from future import standard_library
-.. standard_library.install_hooks(version='3.3')
-.. import html.parser
-.. ...
-.. standard_library.remove_hooks()
-..
-.. or as follows::
-..
-.. from future import standard_library
-.. with standard_library.hooks(version='2.7'):
-.. import html.parser
-.. ...
-..
-.. If ``version='2.7'`` is selected, on Python 2.7 the import hooks provide an interface to the
-.. Python 2.7 standard library modules remapped to their equivalent Python 3.x names. For example, the above code is equivalent to this on Python 2.7 (more or less)::
-..
-.. import htmllib
-.. module = type(htmllib)
-.. html = module('html')
-.. html.parser = module('html.parser')
-.. html.parser.HTMLParser = htmllib.HTMLParser
-.. html.parser.HTMLParseError = htmllib.htmlParseError
-..
-.. but the dozen or so other functions in Python 3.3's ``html.parser`` module are not available on Python 2.7.
-..
-..
-.. If ``version=='3.3'`` is selected,
-..
-.. These are not (yet) full backports of
-.. the Python 3.3
-.. modules but remappings to the corresponding
-.. functionality in the Python 2.x standard library.
-
-
-New ``urllib``, ``email``, and ``xmlrpc`` modules
--------------------------------------------------
-
-Backports of the ``urllib``, ``email``, and ``xmlrpc`` modules from Python
-3.3's standard library are now provided.
-
-Use them like this::
-
- with standard_library.hooks():
- from urllib.request import Request # etc.
- from email import message_from_bytes # etc.
-
-
-``past.builtins`` module improved
----------------------------------
-
-The ``past.builtins`` module is much more compatible with the
-corresponding builtins on Python 2; many more of the Py2 unit tests pass
-on Py3. For example, functions like ``map()`` and ``filter()``
-now behave as they do on Py2 with with ``None`` as the first argument.
-
-
-``newobject`` base object defines Py2-compatible special methods
------------------------------------------------------------------
-
-There is a new ``future.bytes.object`` class that can streamline Py3/2
-compatible code by providing fallback special methods for its subclasses for
-Py2 compatibility. This obviates the need to add compatibility hacks or
-decorators to the code such as the ``@implements_iterator`` for classes that
-define a Py3-style ``__next__`` method.
-
-In this example, the code defines a Py3-style iterator with a ``__next__``
-method. The ``object`` class defines a ``next`` method for Python 2 that maps
-to ``__next__``::
-
- from future.builtins import object
-
- class Upper(object):
- def __init__(self, iterable):
- self._iter = iter(iterable)
- def __next__(self): # note the Py3 interface
- return next(self._iter).upper()
- def __iter__(self):
- return self
-
- assert list(Upper('hello')) == list('HELLO')
-
-``future.builtins.object`` defines other Py2-compatible special methods similarly:
-currently these include ``__nonzero__`` (mapped to ``__bool__``) and
-``__long__`` (mapped to ``__int__``).
-
-On Python 3, as usual, ``object`` simply points to ``builtins.object``.
-
-
-past.builtins
--------------
-The ``past.builtins`` package has been extended to add Py3 support for
-additional Py2 constructs that are not adequately handled by ``lib2to3`` (see
-upstream bug #). This includes custom ``execfile()`` and ``cmp()`` functions.
-``futurize`` now invokes imports of these functions from ``past.builtins``.
-
-
-Relative imports from Cython modules
-------------------------------------
-
-...
-
-
-Bug fixes
----------
-
-Many small improvements and fixes have been made across the project. Some highlights are:
-
-- Fixes and updates from Python 3.3.5 have been included in the backported
- standard library modules.
-
-- ``http.client`` module and related modules use the new backported modules
- such as ``email``. As a result they are more compliant with the Python 3.3
- equivalents.
-
-- Scrubbing of the ``sys.modules`` cache performed by ``remove_hooks()`` (also
- called by the ``suspend_hooks`` and ``hooks`` context managers) is now more
- conservative. It now removes only modules with Py3 names (such as
- ``urllib.parse``) and not the corresponding ``future.standard_library.*``
- modules (such as ``future.standard_library.urllib.parse``.
-
-- The zero-argument ``super()`` function now works from within
- ``staticmethod``s such as those called ``__new__``.
-
-- Calls to ``bytes(string, encoding[, errors])`` now work with ``encoding`` and
- ``errors`` passed as positional arguments. Previously this only worked if
- ``encoding`` and ``errors`` were specified as keyword arguments.
-
-- ``future.utils.native(d)`` calls now work for ``future.builtins.dict`` objects.
-
-- Right-division with ``newint`` objects is fixed. (Issue #38).
-
-
-.. whats-new-0.11.3:
-
-What's new in version 0.11.3
-============================
-
-This release has improvements in the standard library import hooks mechanism and
-its compatibility with 3rd-party modules:
-
-
-Improved compatibility with ``requests``
-----------------------------------------
-
-The ``__exit__`` function of the ``hooks`` context manager and the
-``remove_hooks`` function both now remove submodules of
-``future.standard_library`` from the ``sys.modules`` cache. Therefore this code
-is now possible on Python 2 and 3::
-
- from future import standard_library
- import http.client
- standard_library.remove_hooks()
- import requests
-
- data = requests.get('http://www.google.com')
-
-
-Previously, this required manually removing ``http`` and ``http.client`` from
-``sys.modules`` before importing ``requests`` on Python 2.x. (Issue #19).
-
-This change should also improve the compatibility of the standard library hooks
-with any other module that provides its own Python 2/3 compatibility code.
-
-Note that the situation will improve further in version 0.12; import hooks will
-require an explicit function call or the ``hooks`` context manager.
-
-
-Conversion scripts explicitly install import hooks
---------------------------------------------------
-
-The ``futurize`` and ``pasteurize`` scripts now add an explicit call to
-``install_hooks()`` to install the standard library import hooks. These scripts
-now add these two lines::
-
- from future import standard_library
- standard_library.install_hooks()
-
-instead of just the first one. The next major version of ``future`` (0.12) will
-require the explicit call or use of the ``hooks`` context manager. This will
-allow finer-grained control over whether import hooks are enabled for other
-imported modules, such as ``requests``, which provide their own Python 2/3
-compatibility code.
-
-
-``futurize`` script no longer adds ``unicode_literals`` by default
-------------------------------------------------------------------
-
-There is a new ``--unicode-literals`` flag to ``futurize`` that adds the
-import::
-
- from __future__ import unicode_literals
-
-to the top of each converted module. Without this flag, ``futurize`` now no
-longer adds this import. (Issue #22).
-
-The ``pasteurize`` script for converting from Py3 to Py2/3 still adds
-``unicode_literals``. (See the comments in issue #22 for an explanation.)
-
-
-.. whats-new-0.11:
-
-What's new in version 0.11
-==========================
-
-There are several major new features in version 0.11.
-
-
-``past`` package
-----------------
-
-The python-future project now provides a ``past`` package in addition to the
-``future`` package. Whereas ``future`` provides improved compatibility with
-Python 3 code to Python 2, ``past`` provides support for using and interacting
-with Python 2 code from Python 3. The structure reflects that of ``future``,
-with ``past.builtins`` and ``past.utils``. There is also a new
-``past.translation`` package that provides transparent translation of Python 2
-code to Python 3. (See below.)
-
-One purpose of ``past`` is to ease module-by-module upgrades to
-codebases from Python 2. Another is to help with enabling Python 2 libraries to
-support Python 3 without breaking the API they currently provide. (For example,
-user code may expect these libraries to pass them Python 2's 8-bit strings,
-rather than Python 3's ``bytes`` object.) A third purpose is to help migrate
-projects to Python 3 even if one or more dependencies are still on Python 2.
-
-Currently ``past.builtins`` provides forward-ports of Python 2's ``str`` and
-``dict`` objects, ``basestring``, and list-producing iterator functions. In
-later releases, ``past.builtins`` will be used internally by the
-``past.translation`` package to help with importing and using old Python 2
-modules in a Python 3 environment.
-
-
-Auto-translation of Python 2 modules upon import
-------------------------------------------------
-
-``past`` provides an experimental ``translation`` package to help
-with importing and using old Python 2 modules in a Python 3 environment.
-
-This is implemented using import hooks that attempt to automatically
-translate Python 2 modules to Python 3 syntax and semantics upon import. Use
-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::
-
- >>> from past import autotranslate
- >>> autotranslate(['plotrique'])
- >>> import plotrique
-
-
-This is intended to help you migrate to Python 3 without the need for all
-your code's dependencies to support Python 3 yet. It should be used as a
-last resort; ideally Python 2-only dependencies should be ported
-properly to a Python 2/3 compatible codebase using a tool like
-``futurize`` and the changes should be pushed to the upstream project.
-
-For more information, see :ref:`translation`.
-
-
-Separate ``pasteurize`` script
-------------------------------
-
-The functionality from ``futurize --from3`` is now in a separate script called
-``pasteurize``. Use ``pasteurize`` when converting from Python 3 code to Python
-2/3 compatible source. For more information, see :ref:`backwards-conversion`.
-
-
-pow()
------
-
-There is now a ``pow()`` function in ``future.builtins.misc`` that behaves like
-the Python 3 ``pow()`` function when raising a negative number to a fractional
-power (returning a complex number).
-
-
-input() no longer disabled globally on Py2
-------------------------------------------
-
-Previous versions of ``future`` deleted the ``input()`` function from
-``__builtin__`` on Python 2 as a security measure. This was because
-Python 2's ``input()`` function allows arbitrary code execution and could
-present a security vulnerability on Python 2 if someone expects Python 3
-semantics but forgets to import ``input`` from ``future.builtins``. This
-behaviour has been reverted, in the interests of broadening the
-compatibility of ``future`` with other Python 2 modules.
-
-Please remember to import ``input`` from ``future.builtins`` if you use
-``input()`` in a Python 2/3 compatible codebase.
-
-
-.. deprecated-auto-import-hooks
-
-Deprecated feature: auto-installation of standard-library import hooks
-----------------------------------------------------------------------
-
-Previous versions of ``python-future`` installed import hooks automatically upon
-``from future import standard_library``. This has been deprecated in order to
-improve robustness and compatibility with modules like ``requests`` that already
-perform their own single-source Python 2/3 compatibility.
-
-.. (Previously, the import hooks were
-.. bleeding into surrounding code, causing incompatibilities with modules like
-.. ``requests`` (issue #19).
-
-In the next version of ``python-future``, importing ``future.standard_library``
-will no longer install import hooks by default. Instead, please install the
-import hooks explicitly as follows::
-
- from future import standard_library
- standard_library.install_hooks()
-
-and uninstall them after your import statements using::
-
- standard_library.remove_hooks()
-
-.. For more fine-grained use of import hooks, the names can be passed explicitly as
-.. follows::
-..
-.. from future import standard_library
-.. standard_library.install_hooks()
-
-
-*Note*: this will be a backward-incompatible change.
-
-.. This feature may be resurrected in a later version if a safe implementation can be found.
-
-
-Internal changes
-----------------
-
-The internal ``future.builtins.backports`` module has been renamed to
-``future.builtins.types``. This will change the ``repr`` of ``future``
-types but not their use.
-
-
-.. whats-new-0.10.2:
-
-What's new in version 0.10.2
-============================
-
-
-.. Simpler imports
-.. ---------------
-..
-.. It is now possible to import builtins directly from the ``future``
-.. namespace as follows::
-..
-.. >>> from future import *
-..
-.. or just those you need::
-..
-.. >>> from future import open, str
-
-
-Utility functions for raising exceptions with a traceback portably
-------------------------------------------------------------------
-
-The functions ``raise_with_traceback()`` and ``raise_`` were added to
-``future.utils`` to offer either the Python 3.x or Python 2.x behaviour
-for raising exceptions. Thanks to Joel Tratner for the contribution of
-these.
-
-
-.. whats-new-0.10:
-
-What's new in version 0.10
-==========================
+.. _whats-new:
-Backported ``dict`` type
-------------------------
-
-``future.builtins`` now provides a Python 2 ``dict`` subclass whose
-:func:`keys`, :func:`values`, and :func:`items` methods produce
-memory-efficient iterators. On Python 2.7, these also have the same set-like
-view behaviour as on Python 3. This can streamline code needing to iterate
-over large dictionaries. For example::
-
- from __future__ import print_function
- from future.builtins import dict, range
-
- squares = dict({i: i**2 for i in range(10**7)})
-
- assert not isinstance(d.items(), list)
- # Because items() is memory-efficient, so is this:
- square_roots = dict((i_squared, i) for (i, i_squared) in squares.items())
-
-For more information, see :ref:`dict-object`.
-
-
-Refactoring of standard_library hooks (v0.10.2)
------------------------------------------------
-
-There is a new context manager ``future.standard_library.hooks``. Use it like
-this::
-
- from future import standard_library
- with standard_library.hooks():
- import queue
- import configserver
- from http.client import HTTPConnection
- # etc.
-
-If not using this decorator, it is now encouraged to add an explicit call to
-``standard_library.install_hooks()`` as follows::
-
- from future import standard_library
- standard_library.install_hooks()
-
- import queue
- import html
- import http.client
- # etc.
-
-and to remove the hooks afterwards with::
-
- standard_library.remove_hooks()
-
-The functions ``install_hooks()`` and ``remove_hooks()`` were previously
-called ``enable_hooks()`` and ``disable_hooks()``. The old names are
-still available as aliases, but are deprecated.
-
-As usual, this feature has no effect on Python 3.
-
-
-
-Utility functions raise_ and exec_
-----------------------------------
-
-The functions ``raise_with_traceback()`` and ``raise_()`` were
-added to ``future.utils`` to offer either the Python 3.x or Python 2.x
-behaviour for raising exceptions. Thanks to Joel Tratner for the
-contribution of these. ``future.utils.reraise()`` is now deprecated.
-
-A portable ``exec_()`` function has been added to ``future.utils`` from
-``six``.
-
-
-Bugfixes
---------
-- Fixed newint.__divmod__
-- Improved robustness of installing and removing import hooks in :mod:`future.standard_library`
-- v0.10.1: Fixed broken ``pip install future`` on Py3
-
-
-.. whats-new-0.9:
-
-What's new in version 0.9
-=========================
-
-
-``isinstance`` checks supported natively with backported types
---------------------------------------------------------------
-
-The ``isinstance`` function is no longer redefined in ``future.builtins``
-to operate with the backported ``int``, ``bytes`` and ``str``.
-``isinstance`` checks with the backported types now work correctly by
-default; we achieve this through overriding the ``__instancecheck__``
-method of metaclasses of the backported types.
-
-For more information, see :ref:`isinstance-calls`.
-
-
-``futurize``: minimal imports by default
-----------------------------------------
-
-By default, the ``futurize`` script now only adds the minimal set of
-imports deemed necessary.
-
-There is now an ``--all-imports`` option to the ``futurize`` script which
-gives the previous behaviour, which is to add all ``__future__`` imports
-and ``from future.builtins import *`` imports to every module. (This even
-applies to an empty ``__init__.py`` file.
-
-
-Looser type-checking for the backported ``str`` object
-------------------------------------------------------
-
-Now the ``future.builtins.str`` object behaves more like the Python 2
-``unicode`` object with regard to type-checking. This is to work around some
-bugs / sloppiness in the Python 2 standard library involving mixing of
-byte-strings and unicode strings, such as ``os.path.join`` in ``posixpath.py``.
-
-``future.builtins.str`` still raises the expected ``TypeError`` exceptions from
-Python 3 when attempting to mix it with ``future.builtins.bytes``.
-
-
-suspend_hooks() context manager added to ``future.standard_library``
---------------------------------------------------------------------
-
-Pychecker (as of v0.6.1)'s ``checker.py`` attempts to import the ``builtins``
-module as a way of determining whether Python 3 is running. Since this
-succeeds when ``from future import standard_library`` is in effect, this
-check does not work and pychecker sets the wrong value for its internal ``PY2``
-flag is set.
-
-To work around this, ``future`` now provides a context manager called
-``suspend_hooks`` that can be used as follows::
-
- from future import standard_library
- ...
- with standard_library.suspend_hooks():
- from pychecker.checker import Checker
-
-
-.. whats-new-0.8:
-
-What's new in version 0.8
-=========================
-
-Python 2.6 support
-------------------
-
-``future`` now includes support for Python 2.6.
-
-To run the ``future`` test suite on Python 2.6, this additional package is needed::
-
- pip install unittest2
-
-``http.server`` also requires the ``argparse`` package::
-
- pip install argparse
-
-
-Unused modules removed
-----------------------
-
-The ``future.six`` module has been removed. ``future`` doesn't require ``six``
-(and hasn't since version 0.3). If you need support for Python versions before
-2.6, ``six`` is the best option. ``future`` and ``six`` can be installed
-alongside each other easily if needed.
-
-The unused ``hacks`` module has also been removed from the source tree.
-
-
-isinstance() added to :mod:`future.builtins` (v0.8.2)
------------------------------------------------------
-
-It is now possible to use ``isinstance()`` calls normally after importing ``isinstance`` from
-``future.builtins``. On Python 2, this is specially defined to be compatible with
-``future``'s backported ``int``, ``str``, and ``bytes`` types, as well as
-handling Python 2's int/long distinction.
-
-The result is that code that uses ``isinstance`` to perform type-checking of
-ints, strings, and bytes should now work identically on Python 2 as on Python 3.
-
-The utility functions ``isint``, ``istext``, and ``isbytes`` provided before for
-compatible type-checking across Python 2 and 3 in :mod:`future.utils` are now
-deprecated.
-
-
-.. changelog:
-
-Summary of all changes
-======================
-
-What's new in version 0.11.x
-============================
-
-v0.11.3:
- * The ``futurize`` and ``pasteurize`` scripts add an explicit call to
- ``future.standard_library.install_hooks()`` whenever modules affected by PEP
- 3108 are imported.
-
- * The ``future.builtins.bytes`` constructor now accepts ``frozenset``
- objects as on Py3.
-
-v0.11.2:
- * The ``past.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
- docs and tests.
-
-v0.11:
- * New ``past`` package with ``past.builtins`` and ``past.translation``
- modules.
-
-v0.10.2:
- * Improvements to stdlib hooks. New context manager:
- ``future.standard_library.hooks()``.
-
- * New ``raise_`` and ``raise_with_traceback`` functions in ``future.utils``.
-
-v0.10:
- * New backported ``dict`` object with set-like ``keys``, ``values``, ``items``
-
-v0.9:
- * :func:`isinstance` hack removed in favour of ``__instancecheck__`` on the
- metaclasses of the backported types
- * ``futurize`` now only adds necessary imports by default
- * Looser type-checking by ``future.builtins.str`` when combining with Py2
- native byte-strings.
-
-v0.8.3:
- * New ``--all-imports`` option to ``futurize``
- * Fix bug with ``str.encode()`` with encoding as a non-keyword arg
-
-v0.8.2:
- * New ``isinstance`` function in :mod:`future.builtins`. This obviates
- and deprecates the utility functions for type-checking in :mod:`future.utils`.
-
-v0.8.1:
- * Backported ``socketserver.py``. Fixes sporadic test failures with
- ``http.server`` (related to threading and old-style classes used in Py2.7's
- ``SocketServer.py``).
-
- * Move a few more safe ``futurize`` fixes from stage2 to stage1
-
- * Bug fixes to :mod:`future.utils`
-
-v0.8:
- * Added Python 2.6 support
-
- * Removed unused modules: :mod:`future.six` and :mod:`future.hacks`
-
- * Removed undocumented functions from :mod:`future.utils`
-
-v0.7:
- * Added a backported Py3-like ``int`` object (inherits from long).
-
- * Added utility functions for type-checking and docs about
- ``isinstance`` uses/alternatives.
-
- * Fixes and stricter type-checking for bytes and str objects
-
- * Added many more tests for the ``futurize`` script
-
- * We no longer disable obsolete Py2 builtins by default with ``from
- future.builtins import *``. Use ``from future.builtins.disabled
- import *`` instead.
-
-v0.6:
- * Added a backported Py3-like ``str`` object (inherits from Py2's ``unicode``)
-
- * Removed support for the form ``from future import *``: use ``from future.builtins import *`` instead
-
-v0.5.3:
- * Doc improvements
-
-v0.5.2:
- * Add lots of docs and a Sphinx project
-
-v0.5.1:
- * Upgraded included ``six`` module (included as ``future.utils.six``) to v1.4.1
-
- * :mod:`http.server` module backported
-
- * bytes.split() and .rsplit() bugfixes
-
-v0.5.0:
- * Added backported Py3-like ``bytes`` object
-
-v0.4.2:
- * Various fixes
-
-v0.4.1:
- * Added :func:`open` (from :mod:`io` module on Py2)
- * Improved docs
-
-v0.4.0:
- * Added various useful compatibility functions to :mod:`future.utils`
-
- * Reorganized package: moved all builtins to :mod:`future.builtins`; moved
- all stdlib things to ``future.standard_library``
-
- * Renamed ``python-futurize`` console script to ``futurize``
-
- * Moved ``future.six`` to ``future.utils.six`` and pulled the most relevant
- definitions to :mod:`future.utils`.
-
- * More improvements to "Py3 to both" conversion (``futurize.py --from3``)
-
-v0.3.5:
- * Fixed broken package setup ("package directory 'libfuturize/tests' does not exist")
-
-v0.3.4:
- * Added ``itertools.zip_longest``
-
- * Updated 2to3_backcompat tests to use futurize.py
-
- * Improved libfuturize fixers: correct order of imports; add imports only when necessary (except absolute_import currently)
-
-v0.3.3:
- * Added ``python-futurize`` console script
-
- * Added ``itertools.filterfalse``
-
- * Removed docs about unfinished backports (urllib etc.)
-
- * Removed old Py2 syntax in some files that breaks py3 setup.py install
-
-v0.3.2:
- * Added test.support module
-
- * Added UserList, UserString, UserDict classes to collections module
-
- * Removed ``int`` -> ``long`` mapping
-
- * Added backported ``_markupbase.py`` etc. with new-style classes to fix travis-ci build problems
-
- * Added working ``html`` and ``http.client`` backported modules
-v0.3.0:
- * Generalized import hooks to allow dotted imports
-
- * Added backports of ``urllib``, ``html``, ``http`` modules from Py3.3 stdlib using ``future``
-
- * Added ``futurize`` script for automatically turning Py2 or Py3 modules into
- cross-platform Py3 modules
-
- * Renamed ``future.standard_library_renames`` to
- ``future.standard_library``. (No longer just renames, but backports too.)
-
-v0.2.2.1:
- * Small bug fixes to get tests passing on travis-ci.org
-
-v0.2.1:
- * Small bug fixes
-
-v0.2.0:
- * Features module renamed to modified_builtins
+What's New
+**********
- * New functions added: :func:`round`, :func:`input`
+What's new in version 1.0.0 (2024-02-21)
+========================================
- * No more namespace pollution as a policy::
+The new version number of 1.0.0 indicates that the python-future project, like
+Python 2, is now done.
- from future import *
+The most important change in this release is adding support for Python 3.12
+(ba1cc50 and a6222d2 and bcced95).
- should have no effect on Python 3. On Python 2, it only shadows the
- builtins; it doesn't introduce any new names.
+This release also includes these fixes:
- * End-to-end tests with Python 2 code and 2to3 now work
+- Small updates to the docs
+- 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)
-v0.1.0:
- * first version with tests!
- * removed the inspect-module magic
+Previous versions
+=================
-v0.0.x:
- * initial releases. Use at your peril.
+See :ref:`whats-old`.
diff --git a/docs/why_python3.rst b/docs/why_python3.rst
index 90199d64..a4b535f4 100644
--- a/docs/why_python3.rst
+++ b/docs/why_python3.rst
@@ -19,7 +19,7 @@ Why are Unicode strings better on Python 3?
-------------------------------------------
- it is not the default string type (you have to prefix the string
-with a u to get Unicode);
+ with a u to get Unicode);
- it is missing some functionality, e.g. casefold;
@@ -30,7 +30,7 @@ with a u to get Unicode);
- narrow builds take up to two times more memory per string as needed;
- worse, narrow builds have very naive (possibly even "broken")
-handling of code points in the Supplementary Multilingual Planes.
+ handling of code points in the Supplementary Multilingual Planes.
The unicode string type in Python 3 is better because:
@@ -39,10 +39,10 @@ The unicode string type in Python 3 is better because:
- it includes more functionality;
- starting in Python 3.3, it gets rid of the distinction between
-narrow and wide builds;
+ narrow and wide builds;
- which reduces the memory overhead of strings by up to a factor
-of four in many cases;
+ of four in many cases;
- and fixes the issue of SMP code points.
@@ -56,11 +56,11 @@ Standard library:
~~~~~~~~~~~~~~~~~
- SSL contexts in http.client
--
+-
Non-arguments for Python 3
==========================
--
+-
diff --git a/future/__init__.py b/future/__init__.py
deleted file mode 100644
index 72433aec..00000000
--- a/future/__init__.py
+++ /dev/null
@@ -1,83 +0,0 @@
-"""
-future: Easy, safe support for Python 3/2 compatibility
-=======================================================
-
-``future`` is the missing compatibility layer between Python 3 and Python
-2. It allows you to use a single, clean Python 3.x-compatible codebase to
-support both Python 3 and Python 2 with minimal overhead.
-
-Notable projects that use ``future`` for Python 2/3 compatibility are `Mezzanine `_ and `xlwt-future `_.
-
-It is designed to be used as follows::
-
- from __future__ import (absolute_import, division,
- print_function, unicode_literals)
- from future.builtins import (bytes, dict, int, range, str,
- ascii, chr, hex, input, next,
- oct, open, pow, round, super,
- filter, map, zip)
-
-followed by predominantly standard, idiomatic Python 3 code that then runs
-similarly on Python 2.6/2.7 and Python 3.3+.
-
-The imports have no effect on Python 3. On Python 2, they shadow the
-corresponding builtins, which normally have different semantics on Python 3
-versus 2, to provide their Python 3 semantics.
-
-
-Standard library reorganization
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-``from future import standard_library`` provides a context-manager called
-``hooks`` that installs import hooks (PEP 3108) to allow renamed and
-moved standard library modules to be imported from their new Py3 locations.
-
-
-Automatic conversion
---------------------
-An included script called `futurize
-`_ 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
-and builtin functions in ``future``.
-
-
-Documentation
--------------
-
-See: http://python-future.org
-
-Also see the docstrings for each of these modules for more info::
-
-- future.standard_library
-- future.builtins
-- future.utils
-
-
-Credits
--------
-
-:Author: Ed Schofield
-: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
-
-
-Licensing
----------
-Copyright 2013-2014 Python Charmers Pty Ltd, Australia.
-The software is distributed under an MIT licence. See LICENSE.txt.
-
-"""
-
-__title__ = 'future'
-__author__ = 'Ed Schofield'
-__license__ = 'MIT'
-__copyright__ = 'Copyright 2014 Python Charmers Pty Ltd'
-__ver_major__ = 0
-__ver_minor__ = 12
-__ver_patch__ = 0
-__ver_sub__ = '-dev'
-__version__ = "%d.%d.%d%s" % (__ver_major__, __ver_minor__,
- __ver_patch__, __ver_sub__)
diff --git a/future/builtins/__init__.py b/future/builtins/__init__.py
deleted file mode 100644
index 42509c2a..00000000
--- a/future/builtins/__init__.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""
-A module that brings in equivalents of the new and modified Python 3
-builtins into Py2. Has no effect on Py3.
-
-See the docs for these modules for more information::
-
-- future.builtins.types
-- future.builtins.iterators
-- future.builtins.newnext
-- future.builtins.newround
-- future.builtins.newsuper
-- future.builtins.misc
-- future.builtins.disabled
-
-"""
-
-from future.builtins.iterators import (filter, map, zip)
-# 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)
-from future.builtins.types import (bytes, dict, int, object, range, str)
-from future import utils
-
-
-if not utils.PY3:
- # We only import names that shadow the builtins on Py2. No other namespace
- # pollution on Py2.
-
- # Only shadow builtins on Py2; no new names
- __all__ = ['filter', 'map', 'zip',
- 'ascii', 'chr', 'hex', 'input', 'next', 'oct', 'open', 'pow',
- 'round', 'super',
- 'bytes', 'dict', 'int', 'object', 'range', 'str',
- ]
-
-else:
- # No namespace pollution on Py3
- __all__ = []
-
- # TODO: add 'callable' for Py3.0 and Py3.1?
diff --git a/future/builtins/newround.py b/future/builtins/newround.py
deleted file mode 100644
index 4287afff..00000000
--- a/future/builtins/newround.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""
-``python-future``: pure Python implementation of Python 3 round().
-"""
-
-from future.utils import PYPY
-
-
-def newround(number, ndigits=None):
- """
- See Python 3 documentation: uses Banker's Rounding.
-
- Delegates to the __round__ method if for some reason this exists.
-
- If not, rounds a number to a given precision in decimal digits (default
- 0 digits). This returns an int when called with one argument,
- otherwise the same type as the number. ndigits may be negative.
-
- See the test_round method in future/tests/test_builtins.py for
- examples.
- """
- return_int = False
- if ndigits is None:
- return_int = True
- ndigits = 0
- if hasattr(number, '__round__'):
- return number.__round__(ndigits)
-
- # Use the decimal module for simplicity of implementation (and
- # hopefully correctness).
- from decimal import Decimal, ROUND_HALF_EVEN
-
- 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)
- d = Decimal.from_float(number).quantize(exponent,
- rounding=ROUND_HALF_EVEN)
- if return_int:
- return int(d)
- else:
- return float(d)
-
-
-__all__ = ['newround']
diff --git a/future/builtins/types/newdict.py b/future/builtins/types/newdict.py
deleted file mode 100644
index 70cb609e..00000000
--- a/future/builtins/types/newdict.py
+++ /dev/null
@@ -1,106 +0,0 @@
-"""
-A dict subclass for Python 2 that behaves like Python 3's dict
-
-Example use:
-
->>> from future.builtins import dict
->>> d1 = dict() # instead of {} for an empty dict
->>> d2 = dict(key1='value1', key2='value2')
-
-The keys, values and items methods now return iterators on Python 2.x
-(with set-like behaviour on Python 2.7).
-
->>> for d in (d1, d2):
-... assert not isinstance(d.keys(), list)
-... assert not isinstance(d.values(), list)
-... assert not isinstance(d.items(), list)
-"""
-
-import sys
-
-from future.utils import with_metaclass
-
-
-_builtin_dict = dict
-ver = sys.version_info[:2]
-
-
-class BaseNewDict(type):
- def __instancecheck__(cls, instance):
- return isinstance(instance, _builtin_dict)
-
-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()
-
- def __new__(cls, *args, **kwargs):
- """
- dict() -> new empty dictionary
- dict(mapping) -> new dictionary initialized from a mapping object's
- (key, value) pairs
- dict(iterable) -> new dictionary initialized as if via:
- d = {}
- for k, v in iterable:
- d[k] = v
- dict(**kwargs) -> new dictionary initialized with the name=value pairs
- 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:
- return args[0]
- else:
- value = args[0]
- return super(newdict, cls).__new__(cls, value)
-
- def __native__(self):
- """
- Hook for the future.utils.native() function
- """
- return dict(self)
-
-
-__all__ = ['newdict']
diff --git a/future/builtins/types/newint.py b/future/builtins/types/newint.py
deleted file mode 100644
index 97dc5ac1..00000000
--- a/future/builtins/types/newint.py
+++ /dev/null
@@ -1,250 +0,0 @@
-"""
-Backport of Python 3's int, based on Py2's long.
-
-They are very similar. The most notable difference is:
-
-- representation: trailing L in Python 2 removed in Python 3
-
-"""
-
-from __future__ import division
-
-from numbers import Integral
-
-from future.builtins.types.newbytes import newbytes
-from future.utils import PY3, isint, istext, isbytes, with_metaclass
-
-
-if PY3:
- long = int
-
-
-class BaseNewInt(type):
- def __instancecheck__(cls, instance):
- # Special case for Py2 short or long int
- return isinstance(instance, (int, long))
-
-
-class newint(with_metaclass(BaseNewInt, long)):
- """
- A backport of the Python 3 int object to Py2
- """
- def __new__(cls, x=0, base=10):
- """
- From the Py3 int docstring:
-
- | int(x=0) -> integer
- | int(x, base=10) -> integer
- |
- | Convert a number or string to an integer, or return 0 if no arguments
- | are given. If x is a number, return x.__int__(). For floating point
- | numbers, this truncates towards zero.
- |
- | If x is not a number or if base is given, then x must be a string,
- | bytes, or bytearray instance representing an integer literal in the
- | given base. The literal can be preceded by '+' or '-' and be surrounded
- | by whitespace. The base defaults to 10. Valid bases are 0 and 2-36.
- | Base 0 means to interpret the base from the string as an integer literal.
- | >>> int('0b100', base=0)
- | 4
-
- """
- try:
- val = x.__int__()
- except AttributeError:
- val = x
- else:
- if not isint(val):
- raise TypeError('__int__ returned non-int ({0})'.format(type(val)))
-
- if base != 10:
- # Explicit base
- if not (istext(val) or isbytes(val) or isinstance(val, bytearray)):
- raise TypeError("int() can't convert non-string with explicit base")
- try:
- return super(newint, cls).__new__(cls, val, base)
- except TypeError:
- return super(newint, cls).__new__(cls, newbytes(val), base)
- # After here, base is 10
- try:
- return super(newint, cls).__new__(cls, val)
- except TypeError:
- # Py2 long doesn't handle bytearray input with an explicit base, so
- # handle this here.
- # Py3: int(bytearray(b'10'), 2) == 2
- # Py2: int(bytearray(b'10'), 2) == 2 raises TypeError
- # Py2: long(bytearray(b'10'), 2) == 2 raises TypeError
- try:
- return super(newint, cls).__new__(cls, newbytes(val))
- except:
- raise TypeError("newint argument must be a string or a number, not '{0}'".format(
- type(val)))
-
-
- def __repr__(self):
- """
- Without the L suffix
- """
- value = super(newint, self).__repr__()
- assert value[-1] == 'L'
- return value[:-1]
-
- def __add__(self, other):
- return newint(super(newint, self).__add__(other))
-
- def __radd__(self, other):
- return newint(super(newint, self).__radd__(other))
-
- def __sub__(self, other):
- return newint(super(newint, self).__sub__(other))
-
- def __rsub__(self, other):
- return newint(super(newint, self).__rsub__(other))
-
- def __mul__(self, other):
- value = super(newint, self).__mul__(other)
- if isint(value):
- return newint(value)
- return value
-
- def __rmul__(self, other):
- value = super(newint, self).__rmul__(other)
- if isint(value):
- return newint(value)
- return value
-
- def __div__(self, other):
- # We override this rather than e.g. relying on object.__div__ or
- # long.__div__ because we want to wrap the result in a newint()
- # call if other is another int
- result = long(self) / other
- if isinstance(other, (int, long)):
- return newint(result)
- else:
- return result
-
- def __rdiv__(self, other):
- result = other / long(self)
- if isinstance(other, (int, long)):
- return newint(result)
- else:
- return result
-
- def __idiv__(self, other):
- # long has no __idiv__ method. Use __itruediv__ and cast back to newint:
- result = self.__itruediv__(other)
- if isinstance(other, (int, long)):
- return newint(result)
- else:
- return result
-
- def __truediv__(self, other):
- result = super(newint, self).__truediv__(other)
- if result is NotImplemented:
- result = long(self) / other
- return result
-
- def __rtruediv__(self, other):
- return super(newint, self).__rtruediv__(other)
-
- def __itruediv__(self, other):
- # long has no __itruediv__ method
- mylong = long(self)
- mylong /= other
- return mylong
-
- def __floordiv__(self, other):
- return newint(super(newint, self).__floordiv__(other))
-
- def __rfloordiv__(self, other):
- return newint(super(newint, self).__rfloordiv__(other))
-
- def __ifloordiv__(self, other):
- # long has no __ifloordiv__ method
- mylong = long(self)
- mylong //= other
- return newint(mylong)
-
- def __mod__(self, other):
- return newint(super(newint, self).__mod__(other))
-
- def __rmod__(self, other):
- return newint(super(newint, self).__rmod__(other))
-
- def __divmod__(self, other):
- result = super(newint, self).__divmod__(other)
- return (newint(result[0]), newint(result[1]))
-
- def __rdivmod__(self, other):
- result = super(newint, self).__rdivmod__(other)
- return (newint(result[0]), newint(result[1]))
-
- def __pow__(self, other):
- return newint(super(newint, self).__pow__(other))
-
- def __rpow__(self, other):
- return newint(super(newint, self).__rpow__(other))
-
- def __lshift__(self, other):
- return newint(super(newint, self).__lshift__(other))
-
- def __rlshift__(self, other):
- return newint(super(newint, self).__lshift__(other))
-
- def __rshift__(self, other):
- return newint(super(newint, self).__rshift__(other))
-
- def __rrshift__(self, other):
- return newint(super(newint, self).__rshift__(other))
-
- def __and__(self, other):
- return newint(super(newint, self).__and__(other))
-
- def __rand__(self, other):
- return newint(super(newint, self).__rand__(other))
-
- def __or__(self, other):
- return newint(super(newint, self).__or__(other))
-
- def __ror__(self, other):
- return newint(super(newint, self).__ror__(other))
-
- def __xor__(self, other):
- return newint(super(newint, self).__xor__(other))
-
- def __rxor__(self, other):
- return newint(super(newint, self).__rxor__(other))
-
- # __radd__(self, other) __rsub__(self, other) __rmul__(self, other) __rdiv__(self, other) __rtruediv__(self, other) __rfloordiv__(self, other) __rmod__(self, other) __rdivmod__(self, other) __rpow__(self, other) __rlshift__(self, other) __rrshift__(self, other) __rand__(self, other) __rxor__(self, other) __ror__(self, other)
-
- # __iadd__(self, other) __isub__(self, other) __imul__(self, other) __idiv__(self, other) __itruediv__(self, other) __ifloordiv__(self, other) __imod__(self, other) __ipow__(self, other, [modulo]) __ilshift__(self, other) __irshift__(self, other) __iand__(self, other) __ixor__(self, other) __ior__(self, other)
-
- def __neg__(self):
- return newint(super(newint, self).__neg__())
-
- def __pos__(self):
- return newint(super(newint, self).__pos__())
-
- def __abs__(self):
- return newint(super(newint, self).__abs__())
-
- def __invert__(self):
- return newint(super(newint, self).__invert__())
-
- def __int__(self):
- return self
-
- def __nonzero__(self):
- return self.__bool__()
-
- def __bool__(self):
- """
- So subclasses can override this, Py3-style
- """
- return super(newint, self).__nonzero__()
-
- def __native__(self):
- return long(self)
-
-
-__all__ = ['newint']
diff --git a/future/standard_library/__init__.py b/future/standard_library/__init__.py
deleted file mode 100644
index 4f7361fb..00000000
--- a/future/standard_library/__init__.py
+++ /dev/null
@@ -1,931 +0,0 @@
-"""
-Python 3 reorganized the standard library (PEP 3108). This module exposes
-several standard library modules to Python 2 under their new Python 3
-names.
-
-It is designed to be used as follows::
-
- from future import standard_library
- standard_library.install_hooks()
-
-And then these normal Py3 imports work on both Py3 and Py2::
-
- import builtins
- import configparser
- import copyreg
- import queue
- import reprlib
- import socketserver
- import winreg # on Windows only
- import test.support
- import html, html.parser, html.entites
- import http, http.client, http.server
- import http.cookies, http.cookiejar
- import xmlrpc.client, xmlrpc.server
- import urllib.request, urllib.parse
- import urllib.error, urllib.robotparser
-
- import _thread
- import _dummythread
- import _markupbase
-
- from itertools import filterfalse, zip_longest
- from sys import intern
-
-(The renamed modules and functions are still available under their old
-names on Python 2.)
-
-To turn off the import hooks, use::
-
- standard_library.remove_hooks()
-
-and to turn it on again, use::
-
- standard_library.install_hooks()
-
-This is a cleaner alternative to this idiom (see
-http://docs.pythonsprints.com/python3_porting/py-porting.html)::
-
- try:
- import queue
- except ImportError:
- import Queue as queue
-
-
-Limitations
------------
-We don't currently support these modules, but would like to::
-
- import dbm
- import dbm.dumb
- import dbm.gnu
- import collections.abc # on Py33
- import tkinter
- import pickle # should (optionally) bring in cPickle on Python 2
-
-
-Notes
------
-This module only supports Python 2.6, Python 2.7, and Python 3.1+.
-
-The following renames are already supported on Python 2.7 without any
-additional work from us::
-
- reload() -> imp.reload()
- reduce() -> functools.reduce()
- StringIO.StringIO -> io.StringIO
- Bytes.BytesIO -> io.BytesIO
-
-Old things that can one day be fixed automatically by futurize.py::
-
- string.uppercase -> string.ascii_uppercase # works on either Py2.7 or Py3+
- sys.maxint -> sys.maxsize # but this isn't identical
-
-TODO: Check out these:
-Not available on Py2.6:
- unittest2 -> unittest?
- buffer -> memoryview?
-
-"""
-
-from __future__ import absolute_import, division, print_function
-
-import sys
-import logging
-import imp
-import contextlib
-import types
-import copy
-import os
-
-from future import utils
-
-# 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)
-# dbm
-# urllib
-# test
-# email
-
-REPLACED_MODULES = set(['test', 'urllib', 'pickle', 'email']) # add dbm when we support it
-
-# The following module names are not present in Python 2.x, so they cause no
-# potential clashes:
-# http
-# html
-# tkinter
-# xmlrpc
-
-# These modules need names from elsewhere being added to them:
-# subprocess: should provide getoutput and other fns from commands
-# module but these fns are missing: getstatus, mk2arg,
-# mkarg
-# re: needs an ASCII constant that works compatibly with Py3
-
-
-# Old to new
-# etc: see lib2to3/fixes/fix_imports.py
-RENAMES = {
- # 'cStringIO': 'io', # there's a new io module in Python 2.6
- # that provides StringIO and BytesIO
- # 'StringIO': 'io', # ditto
- # 'cPickle': 'pickle',
- '__builtin__': 'builtins',
- 'copy_reg': 'copyreg',
- 'Queue': 'queue',
- 'future.standard_library.socketserver': 'socketserver',
- 'ConfigParser': 'configparser',
- 'repr': 'reprlib',
- # 'FileDialog': 'tkinter.filedialog',
- # 'tkFileDialog': 'tkinter.filedialog',
- # 'SimpleDialog': 'tkinter.simpledialog',
- # 'tkSimpleDialog': 'tkinter.simpledialog',
- # 'tkColorChooser': 'tkinter.colorchooser',
- # 'tkCommonDialog': 'tkinter.commondialog',
- # 'Dialog': 'tkinter.dialog',
- # 'Tkdnd': 'tkinter.dnd',
- # 'tkFont': 'tkinter.font',
- # 'tkMessageBox': 'tkinter.messagebox',
- # 'ScrolledText': 'tkinter.scrolledtext',
- # 'Tkconstants': 'tkinter.constants',
- # 'Tix': 'tkinter.tix',
- # 'ttk': 'tkinter.ttk',
- # 'Tkinter': 'tkinter',
- '_winreg': 'winreg',
- 'thread': '_thread',
- 'dummy_thread': '_dummy_thread',
- # 'anydbm': 'dbm', # causes infinite import loop
- # 'whichdb': 'dbm', # causes infinite import loop
- # anydbm and whichdb are handled by fix_imports2
- # 'dbhash': 'dbm.bsd',
- # 'dumbdbm': 'dbm.dumb',
- # 'dbm': 'dbm.ndbm',
- # 'gdbm': 'dbm.gnu',
- 'future.standard_library.xmlrpc': 'xmlrpc',
- 'future.standard_library.email': 'email', # for use by urllib
- # 'DocXMLRPCServer': 'xmlrpc.server',
- # 'SimpleXMLRPCServer': 'xmlrpc.server',
- # 'httplib': 'http.client',
- # 'htmlentitydefs' : 'html.entities',
- # 'HTMLParser' : 'html.parser',
- # 'Cookie': 'http.cookies',
- # 'cookielib': 'http.cookiejar',
- # 'BaseHTTPServer': 'http.server',
- # 'SimpleHTTPServer': 'http.server',
- # 'CGIHTTPServer': 'http.server',
- 'future.standard_library.test': 'test', # primarily for renaming test_support to support
- # 'commands': 'subprocess',
- # 'urlparse' : 'urllib.parse',
- # 'robotparser' : 'urllib.robotparser',
- # 'abc': 'collections.abc', # for Py33
- 'future.standard_library.html': 'html',
- 'future.standard_library.http': 'http',
- # 'future.standard_library.moves.urllib': 'urllib',
- 'future.standard_library.urllib': 'urllib',
- 'future.standard_library._markupbase': '_markupbase',
- }
-
-
-class WarnOnImport(object):
- def __init__(self, *args):
- self.module_names = args
-
- def find_module(self, fullname, path=None):
- if fullname in self.module_names:
- self.path = path
- return self
- return None
-
- def load_module(self, name):
- if name in sys.modules:
- return sys.modules[name]
- module_info = imp.find_module(name, self.path)
- module = imp.load_module(name, *module_info)
- sys.modules[name] = module
-
- logging.warning("Imported deprecated module %s", name)
- return module
-
-
-class RenameImport(object):
- """
- A class for import hooks mapping Py3 module names etc. to the Py2 equivalents.
- """
- # Different RenameImport classes are created when importing this module from
- # different source files. This causes isinstance(hook, RenameImport) checks
- # to produce inconsistent results. We add this RENAMER attribute here so
- # remove_hooks() and install_hooks() can find instances of these classes
- # easily:
- RENAMER = True
-
- def __init__(self, old_to_new):
- '''
- Pass in a dictionary-like object mapping from old names to new
- names. E.g. {'ConfigParser': 'configparser', 'cPickle': 'pickle'}
- '''
- self.old_to_new = old_to_new
- both = set(old_to_new.keys()) & set(old_to_new.values())
- assert (len(both) == 0 and
- len(set(old_to_new.values())) == len(old_to_new.values())), \
- 'Ambiguity in renaming (handler not implemented)'
- self.new_to_old = dict((new, old) for (old, new) in old_to_new.items())
-
- def find_module(self, fullname, path=None):
- # Handles hierarchical importing: package.module.module2
- new_base_names = set([s.split('.')[0] for s in self.new_to_old])
- # Before v0.12: Was: if fullname in set(self.old_to_new) | new_base_names:
- if fullname in new_base_names:
- return self
- return None
-
- def load_module(self, name):
- path = None
- if name in sys.modules:
- return sys.modules[name]
- elif name in self.new_to_old:
- # New name. Look up the corresponding old (Py2) name:
- oldname = self.new_to_old[name]
- module = self._find_and_load_module(oldname)
- else:
- module = self._find_and_load_module(name)
- # In any case, make it available under the requested (Py3) name
- sys.modules[name] = module
- return module
-
- def _find_and_load_module(self, name, path=None):
- """
- Finds and loads it. But if there's a . in the name, handles it
- properly.
- """
- bits = name.split('.')
- while len(bits) > 1:
- # Treat the first bit as a package
- packagename = bits.pop(0)
- package = self._find_and_load_module(packagename, path)
- try:
- path = package.__path__
- except AttributeError:
- logging.debug('Debug me: no __path__. '
- 'Should anything special be done here?')
- pass
- # if packagename == 'future':
- # path = FIXME
- name = bits[0]
- if name == 'moves':
- # imp.find_module doesn't find this fake module
- return moves
- else:
- module_info = imp.find_module(name, path)
- return imp.load_module(name, *module_info)
-
-
-# (New module name, new object name, old module name, old object name)
-MOVES = [('collections', 'UserList', 'UserList', 'UserList'),
- ('collections', 'UserDict', 'UserDict', 'UserDict'),
- ('collections', 'UserString','UserString', 'UserString'),
- ('itertools', 'filterfalse','itertools', 'ifilterfalse'),
- ('itertools', 'zip_longest','itertools', 'izip_longest'),
- ('sys', 'intern','__builtin__', 'intern'),
- # The email module has no ASCII flag in Py2, but this is the default.
- # Set re.ASCII to a zero constant. io.SEEK_SET just happens to be one.
- ('re', 'ASCII','io', 'SEEK_SET'),
- ('base64', 'encodebytes','base64', 'encodestring'),
- ('base64', 'decodebytes','base64', 'decodestring'),
- # urllib._urlopener urllib.request
- # urllib.ContentTooShortError urllib.error
- # urllib.FancyURLOpener urllib.request
- # urllib.pathname2url urllib.request
- # urllib.quote urllib.parse
- # urllib.quote_plus urllib.parse
- # urllib.splitattr urllib.parse
- # urllib.splithost urllib.parse
- # urllib.splitnport urllib.parse
- # urllib.splitpasswd urllib.parse
- # urllib.splitport urllib.parse
- # urllib.splitquery urllib.parse
- # urllib.splittag urllib.parse
- # urllib.splittype urllib.parse
- # urllib.splituser urllib.parse
- # urllib.splitvalue urllib.parse
- # urllib.unquote urllib.parse
- # urllib.unquote_plus urllib.parse
- # urllib.urlcleanup urllib.request
- # urllib.urlencode urllib.parse
- # urllib.urlopen urllib.request
- # urllib.URLOpener urllib.request
- # urllib.urlretrieve urllib.request
- # urllib2.AbstractBasicAuthHandler urllib.request
- # urllib2.AbstractDigestAuthHandler urllib.request
- # urllib2.BaseHandler urllib.request
- # urllib2.build_opener urllib.request
- # urllib2.CacheFTPHandler urllib.request
- # urllib2.FileHandler urllib.request
- # urllib2.FTPHandler urllib.request
- # urllib2.HTTPBasicAuthHandler urllib.request
- # urllib2.HTTPCookieProcessor urllib.request
- # urllib2.HTTPDefaultErrorHandler urllib.request
- # urllib2.HTTPDigestAuthHandler urllib.request
- # urllib2.HTTPError urllib.request
- # urllib2.HTTPHandler urllib.request
- # urllib2.HTTPPasswordMgr urllib.request
- # urllib2.HTTPPasswordMgrWithDefaultRealm urllib.request
- # urllib2.HTTPRedirectHandler urllib.request
- # urllib2.HTTPSHandler urllib.request
- # urllib2.install_opener urllib.request
- # urllib2.OpenerDirector urllib.request
- # urllib2.ProxyBasicAuthHandler urllib.request
- # urllib2.ProxyDigestAuthHandler urllib.request
- # urllib2.ProxyHandler urllib.request
- # urllib2.Request urllib.request
- # urllib2.UnknownHandler urllib.request
- # urllib2.URLError urllib.request
- # urllib2.urlopen urllib.request
- # urlparse.parse_qs urllib.parse
- # urlparse.parse_qsl urllib.parse
- # urlparse.urldefrag urllib.parse
- # urlparse.urljoin urllib.parse
- # urlparse.urlparse urllib.parse
- # urlparse.urlsplit urllib.parse
- # urlparse.urlunparse urllib.parse
- # urlparse.urlunsplit urllib.parse
- ]
-
-
-class hooks(object):
- """
- Acts as a context manager. Saves the state of sys.modules and restores it
- after the 'with' block.
-
- Use like this:
-
- >>> from future import standard_library
- >>> with standard_library.hooks():
- ... import http.client
- >>> import requests # incompatible with ``future``'s standard library hooks
-
- For this to work, http.client will be scrubbed from sys.modules after the
- 'with' block. That way the modules imported in the 'with' block will
- continue to be accessible in the current namespace but not from any
- imported modules (like requests).
- """
- def __enter__(self):
- logging.debug('Entering hooks context manager')
- self.old_sys_modules = copy.copy(sys.modules)
- self.hooks_were_installed = detect_hooks()
- scrub_py2_sys_modules() # in case they interfere ... e.g. urllib
- install_hooks(keep_sys_modules=True)
- return self
-
- def __exit__(self, *args):
- logging.debug('Exiting hooks context manager')
- if not self.hooks_were_installed:
- remove_hooks(keep_sys_modules=True)
- scrub_future_sys_modules()
-
-
-# Sanity check for is_py2_stdlib_module(): We aren't replacing any
-# builtin modules names:
-if utils.PY2:
- assert len(set(RENAMES.values()) & set(sys.builtin_module_names)) == 0
-
-def is_py2_stdlib_module(m):
- """
- Tries to infer whether the module m is from the Python 2 standard library.
- This may not be reliable on all systems.
- """
- if utils.PY3:
- return False
- if not 'stdlib_path' in is_py2_stdlib_module.__dict__:
- stdlib_files = [contextlib.__file__, os.__file__, copy.__file__]
- stdlib_paths = [os.path.split(f)[0] for f in stdlib_files]
- if not len(set(stdlib_paths)) == 1:
- raise RuntimeError('Could not determine the location of the Python '
- 'standard library')
- # They are identical, so choose one and add / so we don't match urllib2
- is_py2_stdlib_module.stdlib_path = stdlib_paths[0]
-
- if m.__name__ in sys.builtin_module_names:
- return True
-
- if hasattr(m, '__file__'):
- modpath = os.path.split(m.__file__)
- if (modpath[0].startswith(is_py2_stdlib_module.stdlib_path) and
- 'site-packages' not in modpath[0]):
- return True
-
- return False
-
-
-def scrub_py2_sys_modules():
- """
- Removes any Python 2 standard library modules from ``sys.modules`` that
- would interfere with Py3-style imports using ``future.standard_library``
- import hooks.
- """
- if utils.PY3:
- return
- for modulename in REPLACED_MODULES:
- if not modulename in sys.modules:
- continue
-
- module = sys.modules[modulename]
-
- if is_py2_stdlib_module(module):
- logging.debug('Deleting (Py2) {} from sys.modules'.format(modulename))
- del sys.modules[modulename]
-
-
-def scrub_future_sys_modules():
- """
- Removes any submodules of ``future.standard_library`` and Python 3 names of
- any PEP 3108 renamed modules from the ``sys.modules`` cache.
- """
- if utils.PY3:
- return
- future_stdlib = os.path.join('future', 'standard_library')
- for modulename, module in sys.modules.items():
- if modulename.startswith('future'):
- logging.debug('Not removing', modulename)
- continue
- # We look for builtins, configparser, urllib, email, http, etc., and
- # their submodules
- if (modulename in RENAMES.values() or
- any(modulename.startswith(m + '.') for m in RENAMES.values())):
-
- if module is None:
- # This shouldn't have happened. Delete it.
- logging.debug('Deleting EMPTY module {} from sys.modules'.format(modulename))
- del sys.modules[modulename]
- continue
-
- # We don't want to remove Python 2.x urllib if this is cached
- if is_py2_stdlib_module(module):
- continue
-
- logging.debug('Deleting (future) {} from sys.modules'.format(modulename))
-
- # builtins has no __file__:
- if hasattr(module, '__file__'):
- if not os.path.join('future', 'standard_library') in module.__file__:
- # Why would this occur?
- s = ('Please report this unknown condition as an issue on '
- 'https://github.com/PythonCharmers/python-future: {}, {}'
- ).format(modulename, module.__file__)
- logging.warn(s)
- continue
- del sys.modules[modulename]
-
-
-class suspend_hooks(object):
- """
- Acts as a context manager. Use like this:
-
- >>> from future import standard_library
- >>> standard_library.install_hooks()
- >>> import http.client
- >>> # ...
- >>> with standard_library.suspend_hooks():
- >>> import requests # incompatible with ``future``'s standard library hooks
-
- If the hooks were disabled before the context, they are not installed when
- the context is left.
- """
- def __enter__(self):
- self.hooks_were_installed = detect_hooks()
- remove_hooks(keep_sys_modules=True)
- scrub_future_sys_modules()
- return self
- def __exit__(self, *args):
- if self.hooks_were_installed:
- scrub_py2_sys_modules() # in case they interfere ... e.g. urllib
- install_hooks(keep_sys_modules=True)
- # TODO: add any previously scrubbed modules back to the sys.modules
- # cache?
-
-
-def install_hooks(keep_sys_modules=False):
- """
- This function installs the future.standard_library import hook into
- sys.meta_path. By default it also removes any Python 2 standard library
- modules from the ``sys.modules`` cache that would interfere the Py3-style
- ``future`` imports using the import hooks.
-
- To leave ``sys.modules`` cache alone, pass keep_sys_modules=True.
- """
- if utils.PY3:
- return
- if not keep_sys_modules:
- scrub_py2_sys_modules() # in case they interfere ... e.g. urllib
- logging.debug('sys.meta_path was: {}'.format(sys.meta_path))
- logging.debug('Installing hooks ...')
-
- for (newmodname, newobjname, oldmodname, oldobjname) in MOVES:
- newmod = __import__(newmodname)
- oldmod = __import__(oldmodname)
- obj = getattr(oldmod, oldobjname)
- setattr(newmod, newobjname, obj)
-
- # Add it unless it's there already
- newhook = RenameImport(RENAMES)
- if not detect_hooks():
- sys.meta_path.append(newhook)
- logging.debug('sys.meta_path is now: {}'.format(sys.meta_path))
-
-
-def enable_hooks():
- """
- Deprecated. Use install_hooks() instead. This will be removed by
- ``future`` v1.0.
- """
- install_hooks()
-
-
-def remove_hooks(keep_sys_modules=False):
- """
- This function removes the import hook from sys.meta_path. By default it also removes
- any submodules of ``future.standard_library`` from the ``sys.modules``
- cache.
-
- To leave the ``sys.modules`` cache alone, pass keep_sys_modules=True.
- """
- if utils.PY3:
- return
- logging.debug('Uninstalling hooks ...')
- # Loop backwards, so deleting items keeps the ordering:
- for i, hook in list(enumerate(sys.meta_path))[::-1]:
- if hasattr(hook, 'RENAMER'):
- del sys.meta_path[i]
- if not keep_sys_modules:
- scrub_future_sys_modules()
-
-
-def disable_hooks():
- """
- Deprecated. Use remove_hooks() instead. This will be removed by
- ``future`` v1.0.
- """
- remove_hooks()
-
-
-def detect_hooks():
- """
- Returns True if the import hooks are installed, False if not.
- """
- logging.debug('Detecting hooks ...')
- present = any([hasattr(hook, 'RENAMER') for hook in sys.meta_path])
- if present:
- logging.debug('Detected.')
- else:
- logging.debug('Not detected.')
- return present
-
-
-# Now import the modules:
-# with hooks():
-# for (oldname, newname) in RENAMES.items():
-# if newname == 'winreg' and sys.platform not in ['win32', 'win64']:
-# continue
-# if newname in REPLACED_MODULES:
-# # Skip this check for e.g. the stdlib's ``test`` module,
-# # which we have replaced completely.
-# continue
-# newmod = __import__(newname)
-# globals()[newname] = newmod
-
-
-### Pasted from six.py v1.5.2 by Benjamin Peterson ###
-# def _add_doc(func, doc):
-# """Add documentation to a function."""
-# func.__doc__ = doc
-#
-#
-# def _import_module(name):
-# """Import module, returning the module after the last dot."""
-# __import__(name)
-# return sys.modules[name]
-#
-#
-# class _LazyDescr(object):
-#
-# def __init__(self, name):
-# self.name = name
-#
-# def __get__(self, obj, tp):
-# result = self._resolve()
-# setattr(obj, self.name, result) # Invokes __set__.
-# # This is a bit ugly, but it avoids running this again.
-# delattr(obj.__class__, self.name)
-# return result
-#
-#
-# class MovedModule(_LazyDescr):
-#
-# def __init__(self, name, old, new=None):
-# super(MovedModule, self).__init__(name)
-# if utils.PY3:
-# if new is None:
-# new = name
-# self.mod = new
-# else:
-# self.mod = old
-#
-# def _resolve(self):
-# return _import_module(self.mod)
-#
-# def __getattr__(self, attr):
-# # Hack around the Django autoreloader. The reloader tries to get
-# # __file__ or __name__ of every module in sys.modules. This doesn't work
-# # well if this MovedModule is for an module that is unavailable on this
-# # machine (like winreg on Unix systems). Thus, we pretend __file__ and
-# # __name__ don't exist if the module hasn't been loaded yet. See issues
-# # #51 and #53.
-# if attr in ("__file__", "__name__") and self.mod not in sys.modules:
-# raise AttributeError
-# _module = self._resolve()
-# value = getattr(_module, attr)
-# setattr(self, attr, value)
-# return value
-#
-#
-# class _LazyModule(types.ModuleType):
-#
-# def __init__(self, name):
-# super(_LazyModule, self).__init__(name)
-# self.__doc__ = self.__class__.__doc__
-#
-# def __dir__(self):
-# attrs = ["__doc__", "__name__"]
-# attrs += [attr.name for attr in self._moved_attributes]
-# return attrs
-#
-# # Subclasses should override this
-# _moved_attributes = []
-#
-#
-# class MovedAttribute(_LazyDescr):
-#
-# def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
-# super(MovedAttribute, self).__init__(name)
-# if utils.PY3:
-# if new_mod is None:
-# new_mod = name
-# self.mod = new_mod
-# if new_attr is None:
-# if old_attr is None:
-# new_attr = name
-# else:
-# new_attr = old_attr
-# self.attr = new_attr
-# else:
-# self.mod = old_mod
-# if old_attr is None:
-# old_attr = name
-# self.attr = old_attr
-#
-# def _resolve(self):
-# module = _import_module(self.mod)
-# return getattr(module, self.attr)
-#
-#
-#
-# class _MovedItems(_LazyModule):
-# """Lazy loading of moved objects"""
-#
-#
-# _moved_attributes = [
-# MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
-# MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
-# MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
-# MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
-# MovedAttribute("map", "itertools", "builtins", "imap", "map"),
-# MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
-# MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
-# MovedAttribute("reduce", "__builtin__", "functools"),
-# MovedAttribute("StringIO", "StringIO", "io"),
-# MovedAttribute("UserString", "UserString", "collections"),
-# MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
-# MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
-# MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
-#
-# MovedModule("builtins", "__builtin__"),
-# MovedModule("configparser", "ConfigParser"),
-# MovedModule("copyreg", "copy_reg"),
-# MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
-# MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
-# MovedModule("http_cookies", "Cookie", "http.cookies"),
-# MovedModule("html_entities", "htmlentitydefs", "html.entities"),
-# MovedModule("html_parser", "HTMLParser", "html.parser"),
-# MovedModule("http_client", "httplib", "http.client"),
-# MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
-# MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
-# MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
-# MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
-# MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
-# MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
-# MovedModule("cPickle", "cPickle", "pickle"),
-# MovedModule("queue", "Queue"),
-# MovedModule("reprlib", "repr"),
-# MovedModule("socketserver", "SocketServer"),
-# MovedModule("_thread", "thread", "_thread"),
-# MovedModule("tkinter", "Tkinter"),
-# MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
-# MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
-# MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
-# MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
-# MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
-# MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
-# MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
-# MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
-# MovedModule("tkinter_colorchooser", "tkColorChooser",
-# "tkinter.colorchooser"),
-# MovedModule("tkinter_commondialog", "tkCommonDialog",
-# "tkinter.commondialog"),
-# MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
-# MovedModule("tkinter_font", "tkFont", "tkinter.font"),
-# MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
-# MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
-# "tkinter.simpledialog"),
-# MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
-# MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
-# MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
-# MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
-# MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
-# MovedModule("winreg", "_winreg"),
-# ]
-# for attr in _moved_attributes:
-# setattr(_MovedItems, attr.name, attr)
-# if isinstance(attr, MovedModule):
-# sys.modules[__name__ + ".moves." + attr.name] = attr
-# del attr
-#
-# _MovedItems._moved_attributes = _moved_attributes
-#
-# moves = sys.modules[__name__ + ".moves"] = _MovedItems(__name__ + ".moves")
-#
-#
-# class Module_six_moves_urllib_parse(_LazyModule):
-# """Lazy loading of moved objects in future.standard_library.moves.urllib_parse"""
-#
-#
-# _urllib_parse_moved_attributes = [
-# MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
-# MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
-# MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
-# MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
-# MovedAttribute("urljoin", "urlparse", "urllib.parse"),
-# MovedAttribute("urlparse", "urlparse", "urllib.parse"),
-# MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
-# MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
-# MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
-# MovedAttribute("quote", "urllib", "urllib.parse"),
-# MovedAttribute("quote_plus", "urllib", "urllib.parse"),
-# MovedAttribute("unquote", "urllib", "urllib.parse"),
-# MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
-# MovedAttribute("urlencode", "urllib", "urllib.parse"),
-# ]
-# for attr in _urllib_parse_moved_attributes:
-# setattr(Module_six_moves_urllib_parse, attr.name, attr)
-# del attr
-#
-# Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
-#
-# sys.modules[__name__ + ".moves.urllib_parse"] = sys.modules[__name__ + ".moves.urllib.parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse")
-#
-#
-# class Module_six_moves_urllib_error(_LazyModule):
-# """Lazy loading of moved objects in future.standard_library.moves.urllib_error"""
-#
-#
-# _urllib_error_moved_attributes = [
-# MovedAttribute("URLError", "urllib2", "urllib.error"),
-# MovedAttribute("HTTPError", "urllib2", "urllib.error"),
-# MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
-# ]
-# for attr in _urllib_error_moved_attributes:
-# setattr(Module_six_moves_urllib_error, attr.name, attr)
-# del attr
-#
-# Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
-#
-# sys.modules[__name__ + ".moves.urllib_error"] = sys.modules[__name__ + ".moves.urllib.error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib.error")
-#
-#
-# class Module_six_moves_urllib_request(_LazyModule):
-# """Lazy loading of moved objects in future.standard_library.moves.urllib_request"""
-#
-#
-# _urllib_request_moved_attributes = [
-# MovedAttribute("urlopen", "urllib2", "urllib.request"),
-# MovedAttribute("install_opener", "urllib2", "urllib.request"),
-# MovedAttribute("build_opener", "urllib2", "urllib.request"),
-# MovedAttribute("pathname2url", "urllib", "urllib.request"),
-# MovedAttribute("url2pathname", "urllib", "urllib.request"),
-# MovedAttribute("getproxies", "urllib", "urllib.request"),
-# MovedAttribute("Request", "urllib2", "urllib.request"),
-# MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
-# MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
-# MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
-# MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
-# MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
-# MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
-# MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
-# MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
-# MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
-# MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
-# MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
-# MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
-# MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
-# MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
-# MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
-# MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
-# MovedAttribute("FileHandler", "urllib2", "urllib.request"),
-# MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
-# MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
-# MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
-# MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
-# MovedAttribute("urlretrieve", "urllib", "urllib.request"),
-# MovedAttribute("urlcleanup", "urllib", "urllib.request"),
-# MovedAttribute("URLopener", "urllib", "urllib.request"),
-# MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
-# MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
-# ]
-# for attr in _urllib_request_moved_attributes:
-# setattr(Module_six_moves_urllib_request, attr.name, attr)
-# del attr
-#
-# Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
-#
-# sys.modules[__name__ + ".moves.urllib_request"] = sys.modules[__name__ + ".moves.urllib.request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib.request")
-#
-#
-# class Module_six_moves_urllib_response(_LazyModule):
-# """Lazy loading of moved objects in future.standard_library.moves.urllib_response"""
-#
-#
-# _urllib_response_moved_attributes = [
-# MovedAttribute("addbase", "urllib", "urllib.response"),
-# MovedAttribute("addclosehook", "urllib", "urllib.response"),
-# MovedAttribute("addinfo", "urllib", "urllib.response"),
-# MovedAttribute("addinfourl", "urllib", "urllib.response"),
-# ]
-# for attr in _urllib_response_moved_attributes:
-# setattr(Module_six_moves_urllib_response, attr.name, attr)
-# del attr
-#
-# Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
-#
-# sys.modules[__name__ + ".moves.urllib_response"] = sys.modules[__name__ + ".moves.urllib.response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib.response")
-#
-#
-# class Module_six_moves_urllib_robotparser(_LazyModule):
-# """Lazy loading of moved objects in future.standard_library.moves.urllib_robotparser"""
-#
-#
-# _urllib_robotparser_moved_attributes = [
-# MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
-# ]
-# for attr in _urllib_robotparser_moved_attributes:
-# setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
-# del attr
-#
-# Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
-#
-# sys.modules[__name__ + ".moves.urllib_robotparser"] = sys.modules[__name__ + ".moves.urllib.robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser")
-#
-#
-# class Module_six_moves_urllib(types.ModuleType):
-# """Create a future.standard_library.moves.urllib namespace that resembles the Python 3 namespace"""
-# parse = sys.modules[__name__ + ".moves.urllib_parse"]
-# error = sys.modules[__name__ + ".moves.urllib_error"]
-# request = sys.modules[__name__ + ".moves.urllib_request"]
-# response = sys.modules[__name__ + ".moves.urllib_response"]
-# robotparser = sys.modules[__name__ + ".moves.urllib_robotparser"]
-#
-# def __dir__(self):
-# return ['parse', 'error', 'request', 'response', 'robotparser']
-#
-#
-# sys.modules[__name__ + ".moves.urllib"] = Module_six_moves_urllib(__name__ + ".moves.urllib")
-#
-#
-# def add_move(move):
-# """Add an item to future.standard_library.moves."""
-# setattr(_MovedItems, move.name, move)
-#
-#
-# def remove_move(name):
-# """Remove item from future.standard_library.moves."""
-# try:
-# delattr(_MovedItems, name)
-# except AttributeError:
-# try:
-# del moves.__dict__[name]
-# except KeyError:
-# raise AttributeError("no such move, %r" % (name,))
-### End of code pasted from six.py v1.5.2 by Benjamin Peterson ###
-
-
-# As of v0.12, this no longer happens implicitly:
-# if not utils.PY3:
-# install_hooks()
diff --git a/future/standard_library/test/regrtest.py b/future/standard_library/test/regrtest.py
deleted file mode 100755
index 26f27ff3..00000000
--- a/future/standard_library/test/regrtest.py
+++ /dev/null
@@ -1,1564 +0,0 @@
-#! /usr/bin/python2.7
-
-"""
-Usage:
-
-python -m test.regrtest [options] [test_name1 [test_name2 ...]]
-python path/to/Lib/test/regrtest.py [options] [test_name1 [test_name2 ...]]
-
-
-If no arguments or options are provided, finds all files matching
-the pattern "test_*" in the Lib/test subdirectory and runs
-them in alphabetical order (but see -M and -u, below, for exceptions).
-
-For more rigorous testing, it is useful to use the following
-command line:
-
-python -E -tt -Wd -3 -m test.regrtest [options] [test_name1 ...]
-
-
-Options:
-
--h/--help -- print this text and exit
-
-Verbosity
-
--v/--verbose -- run tests in verbose mode with output to stdout
--w/--verbose2 -- re-run failed tests in verbose mode
--W/--verbose3 -- re-run failed tests in verbose mode immediately
--q/--quiet -- no output unless one or more tests fail
--S/--slow -- print the slowest 10 tests
- --header -- print header with interpreter info
-
-Selecting tests
-
--r/--randomize -- randomize test execution order (see below)
- --randseed -- pass a random seed to reproduce a previous random run
--f/--fromfile -- read names of tests to run from a file (see below)
--x/--exclude -- arguments are tests to *exclude*
--s/--single -- single step through a set of tests (see below)
--u/--use RES1,RES2,...
- -- specify which special resource intensive tests to run
--M/--memlimit LIMIT
- -- run very large memory-consuming tests
-
-Special runs
-
--l/--findleaks -- if GC is available detect tests that leak memory
--L/--runleaks -- run the leaks(1) command just before exit
--R/--huntrleaks RUNCOUNTS
- -- search for reference leaks (needs debug build, v. slow)
--j/--multiprocess PROCESSES
- -- run PROCESSES processes at once
--T/--coverage -- turn on code coverage tracing using the trace module
--D/--coverdir DIRECTORY
- -- Directory where coverage files are put
--N/--nocoverdir -- Put coverage files alongside modules
--t/--threshold THRESHOLD
- -- call gc.set_threshold(THRESHOLD)
--F/--forever -- run the specified tests in a loop, until an error happens
-
-
-Additional Option Details:
-
--r randomizes test execution order. You can use --randseed=int to provide a
-int seed value for the randomizer; this is useful for reproducing troublesome
-test orders.
-
--s On the first invocation of regrtest using -s, the first test file found
-or the first test file given on the command line is run, and the name of
-the next test is recorded in a file named pynexttest. If run from the
-Python build directory, pynexttest is located in the 'build' subdirectory,
-otherwise it is located in tempfile.gettempdir(). On subsequent runs,
-the test in pynexttest is run, and the next test is written to pynexttest.
-When the last test has been run, pynexttest is deleted. In this way it
-is possible to single step through the test files. This is useful when
-doing memory analysis on the Python interpreter, which process tends to
-consume too many resources to run the full regression test non-stop.
-
--f reads the names of tests from the file given as f's argument, one
-or more test names per line. Whitespace is ignored. Blank lines and
-lines beginning with '#' are ignored. This is especially useful for
-whittling down failures involving interactions among tests.
-
--L causes the leaks(1) command to be run just before exit if it exists.
-leaks(1) is available on Mac OS X and presumably on some other
-FreeBSD-derived systems.
-
--R runs each test several times and examines sys.gettotalrefcount() to
-see if the test appears to be leaking references. The argument should
-be of the form stab:run:fname where 'stab' is the number of times the
-test is run to let gettotalrefcount settle down, 'run' is the number
-of times further it is run and 'fname' is the name of the file the
-reports are written to. These parameters all have defaults (5, 4 and
-"reflog.txt" respectively), and the minimal invocation is '-R :'.
-
--M runs tests that require an exorbitant amount of memory. These tests
-typically try to ascertain containers keep working when containing more than
-2 billion objects, which only works on 64-bit systems. There are also some
-tests that try to exhaust the address space of the process, which only makes
-sense on 32-bit systems with at least 2Gb of memory. The passed-in memlimit,
-which is a string in the form of '2.5Gb', determines howmuch memory the
-tests will limit themselves to (but they may go slightly over.) The number
-shouldn't be more memory than the machine has (including swap memory). You
-should also keep in mind that swap memory is generally much, much slower
-than RAM, and setting memlimit to all available RAM or higher will heavily
-tax the machine. On the other hand, it is no use running these tests with a
-limit of less than 2.5Gb, and many require more than 20Gb. Tests that expect
-to use more than memlimit memory will be skipped. The big-memory tests
-generally run very, very long.
-
--u is used to specify which special resource intensive tests to run,
-such as those requiring large file support or network connectivity.
-The argument is a comma-separated list of words indicating the
-resources to test. Currently only the following are defined:
-
- all - Enable all special resources.
-
- audio - Tests that use the audio device. (There are known
- cases of broken audio drivers that can crash Python or
- even the Linux kernel.)
-
- curses - Tests that use curses and will modify the terminal's
- state and output modes.
-
- largefile - It is okay to run some test that may create huge
- files. These tests can take a long time and may
- consume >2GB of disk space temporarily.
-
- network - It is okay to run tests that use external network
- resource, e.g. testing SSL support for sockets.
-
- bsddb - It is okay to run the bsddb testsuite, which takes
- a long time to complete.
-
- decimal - Test the decimal module against a large suite that
- verifies compliance with standards.
-
- cpu - Used for certain CPU-heavy tests.
-
- subprocess Run all tests for the subprocess module.
-
- urlfetch - It is okay to download files required on testing.
-
- gui - Run tests that require a running GUI.
-
- xpickle - Test pickle and cPickle against Python 2.4, 2.5 and 2.6 to
- test backwards compatibility. These tests take a long time
- to run.
-
-To enable all resources except one, use '-uall,-'. For
-example, to run all the tests except for the bsddb tests, give the
-option '-uall,-bsddb'.
-"""
-
-from __future__ import print_function
-
-import StringIO
-import getopt
-import json
-import os
-import random
-import re
-import shutil
-import sys
-import time
-import traceback
-import warnings
-import unittest
-import tempfile
-import imp
-import platform
-import sysconfig
-
-
-# Some times __path__ and __file__ are not absolute (e.g. while running from
-# Lib/) and, if we change the CWD to run the tests in a temporary dir, some
-# imports might fail. This affects only the modules imported before os.chdir().
-# These modules are searched first in sys.path[0] (so '' -- the CWD) and if
-# they are found in the CWD their __file__ and __path__ will be relative (this
-# happens before the chdir). All the modules imported after the chdir, are
-# not found in the CWD, and since the other paths in sys.path[1:] are absolute
-# (site.py absolutize them), the __file__ and __path__ will be absolute too.
-# Therefore it is necessary to absolutize manually the __file__ and __path__ of
-# the packages to prevent later imports to fail when the CWD is different.
-for module in sys.modules.itervalues():
- if hasattr(module, '__path__'):
- module.__path__ = [os.path.abspath(path) for path in module.__path__]
- if hasattr(module, '__file__'):
- module.__file__ = os.path.abspath(module.__file__)
-
-
-# MacOSX (a.k.a. Darwin) has a default stack size that is too small
-# for deeply recursive regular expressions. We see this as crashes in
-# the Python test suite when running test_re.py and test_sre.py. The
-# fix is to set the stack limit to 2048.
-# This approach may also be useful for other Unixy platforms that
-# suffer from small default stack limits.
-if sys.platform == 'darwin':
- try:
- import resource
- except ImportError:
- pass
- else:
- soft, hard = resource.getrlimit(resource.RLIMIT_STACK)
- newsoft = min(hard, max(soft, 1024*2048))
- resource.setrlimit(resource.RLIMIT_STACK, (newsoft, hard))
-
-# Test result constants.
-PASSED = 1
-FAILED = 0
-ENV_CHANGED = -1
-SKIPPED = -2
-RESOURCE_DENIED = -3
-INTERRUPTED = -4
-
-from test import test_support
-
-RESOURCE_NAMES = ('audio', 'curses', 'largefile', 'network', 'bsddb',
- 'decimal', 'cpu', 'subprocess', 'urlfetch', 'gui',
- 'xpickle')
-
-TEMPDIR = os.path.abspath(tempfile.gettempdir())
-
-
-def usage(code, msg=''):
- print(__doc__)
- if msg: print(msg)
- sys.exit(code)
-
-
-def main(tests=None, testdir=None, verbose=0, quiet=False,
- exclude=False, single=False, randomize=False, fromfile=None,
- findleaks=False, use_resources=None, trace=False, coverdir='coverage',
- runleaks=False, huntrleaks=False, verbose2=False, print_slow=False,
- random_seed=None, use_mp=None, verbose3=False, forever=False,
- header=False):
- """Execute a test suite.
-
- This also parses command-line options and modifies its behavior
- accordingly.
-
- tests -- a list of strings containing test names (optional)
- testdir -- the directory in which to look for tests (optional)
-
- Users other than the Python test suite will certainly want to
- specify testdir; if it's omitted, the directory containing the
- Python test suite is searched for.
-
- If the tests argument is omitted, the tests listed on the
- command-line will be used. If that's empty, too, then all *.py
- files beginning with test_ will be used.
-
- The other default arguments (verbose, quiet, exclude,
- single, randomize, findleaks, use_resources, trace, coverdir,
- print_slow, and random_seed) allow programmers calling main()
- directly to set the values that would normally be set by flags
- on the command line.
- """
-
- test_support.record_original_stdout(sys.stdout)
- try:
- opts, args = getopt.getopt(sys.argv[1:], 'hvqxsSrf:lu:t:TD:NLR:FwWM:j:',
- ['help', 'verbose', 'verbose2', 'verbose3', 'quiet',
- 'exclude', 'single', 'slow', 'randomize', 'fromfile=', 'findleaks',
- 'use=', 'threshold=', 'trace', 'coverdir=', 'nocoverdir',
- 'runleaks', 'huntrleaks=', 'memlimit=', 'randseed=',
- 'multiprocess=', 'slaveargs=', 'forever', 'header'])
- except getopt.error as msg:
- usage(2, msg)
-
- # Defaults
- if random_seed is None:
- random_seed = random.randrange(10000000)
- if use_resources is None:
- use_resources = []
- for o, a in opts:
- if o in ('-h', '--help'):
- usage(0)
- elif o in ('-v', '--verbose'):
- verbose += 1
- elif o in ('-w', '--verbose2'):
- verbose2 = True
- elif o in ('-W', '--verbose3'):
- verbose3 = True
- elif o in ('-q', '--quiet'):
- quiet = True;
- verbose = 0
- elif o in ('-x', '--exclude'):
- exclude = True
- elif o in ('-s', '--single'):
- single = True
- elif o in ('-S', '--slow'):
- print_slow = True
- elif o in ('-r', '--randomize'):
- randomize = True
- elif o == '--randseed':
- random_seed = int(a)
- elif o in ('-f', '--fromfile'):
- fromfile = a
- elif o in ('-l', '--findleaks'):
- findleaks = True
- elif o in ('-L', '--runleaks'):
- runleaks = True
- elif o in ('-t', '--threshold'):
- import gc
- gc.set_threshold(int(a))
- elif o in ('-T', '--coverage'):
- trace = True
- elif o in ('-D', '--coverdir'):
- coverdir = os.path.join(os.getcwd(), a)
- elif o in ('-N', '--nocoverdir'):
- coverdir = None
- elif o in ('-R', '--huntrleaks'):
- huntrleaks = a.split(':')
- if len(huntrleaks) not in (2, 3):
- print(a, huntrleaks)
- usage(2, '-R takes 2 or 3 colon-separated arguments')
- if not huntrleaks[0]:
- huntrleaks[0] = 5
- else:
- huntrleaks[0] = int(huntrleaks[0])
- if not huntrleaks[1]:
- huntrleaks[1] = 4
- else:
- huntrleaks[1] = int(huntrleaks[1])
- if len(huntrleaks) == 2 or not huntrleaks[2]:
- huntrleaks[2:] = ["reflog.txt"]
- elif o in ('-M', '--memlimit'):
- test_support.set_memlimit(a)
- elif o in ('-u', '--use'):
- u = [x.lower() for x in a.split(',')]
- for r in u:
- if r == 'all':
- use_resources[:] = RESOURCE_NAMES
- continue
- remove = False
- if r[0] == '-':
- remove = True
- r = r[1:]
- if r not in RESOURCE_NAMES:
- usage(1, 'Invalid -u/--use option: ' + a)
- if remove:
- if r in use_resources:
- use_resources.remove(r)
- elif r not in use_resources:
- use_resources.append(r)
- elif o in ('-F', '--forever'):
- forever = True
- elif o in ('-j', '--multiprocess'):
- use_mp = int(a)
- elif o == '--header':
- header = True
- elif o == '--slaveargs':
- args, kwargs = json.loads(a)
- try:
- result = runtest(*args, **kwargs)
- except BaseException as e:
- result = INTERRUPTED, e.__class__.__name__
- print() # Force a newline (just in case)
- print(json.dumps(result))
- sys.exit(0)
- else:
- print(("No handler for option {0}. Please "
- "report this as a bug at http://bugs.python.org.").format(o), file=sys.stderr)
- sys.exit(1)
- if single and fromfile:
- usage(2, "-s and -f don't go together!")
- if use_mp and trace:
- usage(2, "-T and -j don't go together!")
- if use_mp and findleaks:
- usage(2, "-l and -j don't go together!")
-
- good = []
- bad = []
- skipped = []
- resource_denieds = []
- environment_changed = []
- interrupted = False
-
- if findleaks:
- try:
- import gc
- except ImportError:
- print('No GC available, disabling findleaks.')
- findleaks = False
- else:
- # Uncomment the line below to report garbage that is not
- # freeable by reference counting alone. By default only
- # garbage that is not collectable by the GC is reported.
- #gc.set_debug(gc.DEBUG_SAVEALL)
- found_garbage = []
-
- if single:
- filename = os.path.join(TEMPDIR, 'pynexttest')
- try:
- fp = open(filename, 'r')
- next_test = fp.read().strip()
- tests = [next_test]
- fp.close()
- except IOError:
- pass
-
- if fromfile:
- tests = []
- fp = open(os.path.join(test_support.SAVEDCWD, fromfile))
- for line in fp:
- guts = line.split() # assuming no test has whitespace in its name
- if guts and not guts[0].startswith('#'):
- tests.extend(guts)
- fp.close()
-
- # Strip .py extensions.
- removepy(args)
- removepy(tests)
-
- stdtests = STDTESTS[:]
- nottests = NOTTESTS.copy()
- if exclude:
- for arg in args:
- if arg in stdtests:
- stdtests.remove(arg)
- nottests.add(arg)
- args = []
-
- # For a partial run, we do not need to clutter the output.
- if verbose or header or not (quiet or single or tests or args):
- # Print basic platform information
- print("==", platform.python_implementation(), \
- " ".join(sys.version.split()))
- print("== ", platform.platform(aliased=True), \
- "%s-endian" % sys.byteorder)
- print("== ", os.getcwd())
- print("Testing with flags:", sys.flags)
-
- alltests = findtests(testdir, stdtests, nottests)
- selected = tests or args or alltests
- if single:
- selected = selected[:1]
- try:
- next_single_test = alltests[alltests.index(selected[0])+1]
- except IndexError:
- next_single_test = None
- if randomize:
- random.seed(random_seed)
- print("Using random seed", random_seed)
- random.shuffle(selected)
- if trace:
- import trace
- tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix],
- trace=False, count=True)
-
- test_times = []
- test_support.use_resources = use_resources
- save_modules = sys.modules.keys()
-
- def accumulate_result(test, result):
- ok, test_time = result
- test_times.append((test_time, test))
- if ok == PASSED:
- good.append(test)
- elif ok == FAILED:
- bad.append(test)
- elif ok == ENV_CHANGED:
- bad.append(test)
- environment_changed.append(test)
- elif ok == SKIPPED:
- skipped.append(test)
- elif ok == RESOURCE_DENIED:
- skipped.append(test)
- resource_denieds.append(test)
-
- if forever:
- def test_forever(tests=list(selected)):
- while True:
- for test in tests:
- yield test
- if bad:
- return
- tests = test_forever()
- else:
- tests = iter(selected)
-
- if use_mp:
- try:
- from threading import Thread
- except ImportError:
- print("Multiprocess option requires thread support")
- sys.exit(2)
- from Queue import Queue
- from subprocess import Popen, PIPE
- debug_output_pat = re.compile(r"\[\d+ refs\]$")
- output = Queue()
- def tests_and_args():
- for test in tests:
- args_tuple = (
- (test, verbose, quiet),
- dict(huntrleaks=huntrleaks, use_resources=use_resources)
- )
- yield (test, args_tuple)
- pending = tests_and_args()
- opt_args = test_support.args_from_interpreter_flags()
- base_cmd = [sys.executable] + opt_args + ['-m', 'test.regrtest']
- def work():
- # A worker thread.
- try:
- while True:
- try:
- test, args_tuple = next(pending)
- except StopIteration:
- output.put((None, None, None, None))
- return
- # -E is needed by some tests, e.g. test_import
- popen = Popen(base_cmd + ['--slaveargs', json.dumps(args_tuple)],
- stdout=PIPE, stderr=PIPE,
- universal_newlines=True,
- close_fds=(os.name != 'nt'))
- stdout, stderr = popen.communicate()
- # Strip last refcount output line if it exists, since it
- # comes from the shutdown of the interpreter in the subcommand.
- stderr = debug_output_pat.sub("", stderr)
- stdout, _, result = stdout.strip().rpartition("\n")
- if not result:
- output.put((None, None, None, None))
- return
- result = json.loads(result)
- if not quiet:
- stdout = test+'\n'+stdout
- output.put((test, stdout.rstrip(), stderr.rstrip(), result))
- except BaseException:
- output.put((None, None, None, None))
- raise
- workers = [Thread(target=work) for i in range(use_mp)]
- for worker in workers:
- worker.start()
- finished = 0
- try:
- while finished < use_mp:
- test, stdout, stderr, result = output.get()
- if test is None:
- finished += 1
- continue
- if stdout:
- print(stdout)
- if stderr:
- print(stderr, file=sys.stderr)
- sys.stdout.flush()
- sys.stderr.flush()
- if result[0] == INTERRUPTED:
- assert result[1] == 'KeyboardInterrupt'
- raise KeyboardInterrupt # What else?
- accumulate_result(test, result)
- except KeyboardInterrupt:
- interrupted = True
- pending.close()
- for worker in workers:
- worker.join()
- else:
- for test in tests:
- if not quiet:
- print(test)
- sys.stdout.flush()
- if trace:
- # If we're tracing code coverage, then we don't exit with status
- # if on a false return value from main.
- tracer.runctx('runtest(test, verbose, quiet)',
- globals=globals(), locals=vars())
- else:
- try:
- result = runtest(test, verbose, quiet, huntrleaks)
- accumulate_result(test, result)
- if verbose3 and result[0] == FAILED:
- print("Re-running test %r in verbose mode" % test)
- runtest(test, True, quiet, huntrleaks)
- except KeyboardInterrupt:
- interrupted = True
- break
- except:
- raise
- if findleaks:
- gc.collect()
- if gc.garbage:
- print("Warning: test created", len(gc.garbage), end=' ')
- print("uncollectable object(s).")
- # move the uncollectable objects somewhere so we don't see
- # them again
- found_garbage.extend(gc.garbage)
- del gc.garbage[:]
- # Unload the newly imported modules (best effort finalization)
- for module in sys.modules.keys():
- if module not in save_modules and module.startswith("test."):
- test_support.unload(module)
-
- if interrupted:
- # print a newline after ^C
- print()
- print("Test suite interrupted by signal SIGINT.")
- omitted = set(selected) - set(good) - set(bad) - set(skipped)
- print(count(len(omitted), "test"), "omitted:")
- printlist(omitted)
- if good and not quiet:
- if not bad and not skipped and not interrupted and len(good) > 1:
- print("All", end=' ')
- print(count(len(good), "test"), "OK.")
- if print_slow:
- test_times.sort(reverse=True)
- print("10 slowest tests:")
- for time, test in test_times[:10]:
- print("%s: %.1fs" % (test, time))
- if bad:
- bad = set(bad) - set(environment_changed)
- if bad:
- print(count(len(bad), "test"), "failed:")
- printlist(bad)
- if environment_changed:
- print("{0} altered the execution environment:".format(
- count(len(environment_changed), "test")))
- printlist(environment_changed)
- if skipped and not quiet:
- print(count(len(skipped), "test"), "skipped:")
- printlist(skipped)
-
- e = _ExpectedSkips()
- plat = sys.platform
- if e.isvalid():
- surprise = set(skipped) - e.getexpected() - set(resource_denieds)
- if surprise:
- print(count(len(surprise), "skip"), \
- "unexpected on", plat + ":")
- printlist(surprise)
- else:
- print("Those skips are all expected on", plat + ".")
- else:
- print("Ask someone to teach regrtest.py about which tests are")
- print("expected to get skipped on", plat + ".")
-
- if verbose2 and bad:
- print("Re-running failed tests in verbose mode")
- for test in bad:
- print("Re-running test %r in verbose mode" % test)
- sys.stdout.flush()
- try:
- test_support.verbose = True
- ok = runtest(test, True, quiet, huntrleaks)
- except KeyboardInterrupt:
- # print a newline separate from the ^C
- print()
- break
- except:
- raise
-
- if single:
- if next_single_test:
- with open(filename, 'w') as fp:
- fp.write(next_single_test + '\n')
- else:
- os.unlink(filename)
-
- if trace:
- r = tracer.results()
- r.write_results(show_missing=True, summary=True, coverdir=coverdir)
-
- if runleaks:
- os.system("leaks %d" % os.getpid())
-
- sys.exit(len(bad) > 0 or interrupted)
-
-
-STDTESTS = [
- 'test_grammar',
- 'test_opcodes',
- 'test_dict',
- 'test_builtin',
- 'test_exceptions',
- 'test_types',
- 'test_unittest',
- 'test_doctest',
- 'test_doctest2',
-]
-
-NOTTESTS = set([
- 'test_support',
- 'test_future1',
- 'test_future2',
-])
-
-def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS):
- """Return a list of all applicable test modules."""
- testdir = findtestdir(testdir)
- names = os.listdir(testdir)
- tests = []
- others = set(stdtests) | nottests
- for name in names:
- modname, ext = os.path.splitext(name)
- if modname[:5] == "test_" and ext == ".py" and modname not in others:
- tests.append(modname)
- return stdtests + sorted(tests)
-
-def runtest(test, verbose, quiet,
- huntrleaks=False, use_resources=None):
- """Run a single test.
-
- test -- the name of the test
- verbose -- if true, print more messages
- quiet -- if true, don't print 'skipped' messages (probably redundant)
- test_times -- a list of (time, test_name) pairs
- huntrleaks -- run multiple times to test for leaks; requires a debug
- build; a triple corresponding to -R's three arguments
- Returns one of the test result constants:
- INTERRUPTED KeyboardInterrupt when run under -j
- RESOURCE_DENIED test skipped because resource denied
- SKIPPED test skipped for some other reason
- ENV_CHANGED test failed because it changed the execution environment
- FAILED test failed
- PASSED test passed
- """
-
- test_support.verbose = verbose # Tell tests to be moderately quiet
- if use_resources is not None:
- test_support.use_resources = use_resources
- try:
- return runtest_inner(test, verbose, quiet, huntrleaks)
- finally:
- cleanup_test_droppings(test, verbose)
-
-
-# Unit tests are supposed to leave the execution environment unchanged
-# once they complete. But sometimes tests have bugs, especially when
-# tests fail, and the changes to environment go on to mess up other
-# tests. This can cause issues with buildbot stability, since tests
-# are run in random order and so problems may appear to come and go.
-# There are a few things we can save and restore to mitigate this, and
-# the following context manager handles this task.
-
-class saved_test_environment(object):
- """Save bits of the test environment and restore them at block exit.
-
- with saved_test_environment(testname, verbose, quiet):
- #stuff
-
- Unless quiet is True, a warning is printed to stderr if any of
- the saved items was changed by the test. The attribute 'changed'
- is initially False, but is set to True if a change is detected.
-
- If verbose is more than 1, the before and after state of changed
- items is also printed.
- """
-
- changed = False
-
- def __init__(self, testname, verbose=0, quiet=False):
- self.testname = testname
- self.verbose = verbose
- self.quiet = quiet
-
- # To add things to save and restore, add a name XXX to the resources list
- # and add corresponding get_XXX/restore_XXX functions. get_XXX should
- # return the value to be saved and compared against a second call to the
- # get function when test execution completes. restore_XXX should accept
- # the saved value and restore the resource using it. It will be called if
- # and only if a change in the value is detected.
- #
- # Note: XXX will have any '.' replaced with '_' characters when determining
- # the corresponding method names.
-
- resources = ('sys.argv', 'cwd', 'sys.stdin', 'sys.stdout', 'sys.stderr',
- 'os.environ', 'sys.path', 'asyncore.socket_map',
- 'test_support.TESTFN',
- )
-
- def get_sys_argv(self):
- return id(sys.argv), sys.argv, sys.argv[:]
- def restore_sys_argv(self, saved_argv):
- sys.argv = saved_argv[1]
- sys.argv[:] = saved_argv[2]
-
- def get_cwd(self):
- return os.getcwd()
- def restore_cwd(self, saved_cwd):
- os.chdir(saved_cwd)
-
- def get_sys_stdout(self):
- return sys.stdout
- def restore_sys_stdout(self, saved_stdout):
- sys.stdout = saved_stdout
-
- def get_sys_stderr(self):
- return sys.stderr
- def restore_sys_stderr(self, saved_stderr):
- sys.stderr = saved_stderr
-
- def get_sys_stdin(self):
- return sys.stdin
- def restore_sys_stdin(self, saved_stdin):
- sys.stdin = saved_stdin
-
- def get_os_environ(self):
- return id(os.environ), os.environ, dict(os.environ)
- def restore_os_environ(self, saved_environ):
- os.environ = saved_environ[1]
- os.environ.clear()
- os.environ.update(saved_environ[2])
-
- def get_sys_path(self):
- return id(sys.path), sys.path, sys.path[:]
- def restore_sys_path(self, saved_path):
- sys.path = saved_path[1]
- sys.path[:] = saved_path[2]
-
- def get_asyncore_socket_map(self):
- asyncore = sys.modules.get('asyncore')
- # XXX Making a copy keeps objects alive until __exit__ gets called.
- return asyncore and asyncore.socket_map.copy() or {}
- def restore_asyncore_socket_map(self, saved_map):
- asyncore = sys.modules.get('asyncore')
- if asyncore is not None:
- asyncore.close_all(ignore_all=True)
- asyncore.socket_map.update(saved_map)
-
- def get_test_support_TESTFN(self):
- if os.path.isfile(test_support.TESTFN):
- result = 'f'
- elif os.path.isdir(test_support.TESTFN):
- result = 'd'
- else:
- result = None
- return result
- def restore_test_support_TESTFN(self, saved_value):
- if saved_value is None:
- if os.path.isfile(test_support.TESTFN):
- os.unlink(test_support.TESTFN)
- elif os.path.isdir(test_support.TESTFN):
- shutil.rmtree(test_support.TESTFN)
-
- def resource_info(self):
- for name in self.resources:
- method_suffix = name.replace('.', '_')
- get_name = 'get_' + method_suffix
- restore_name = 'restore_' + method_suffix
- yield name, getattr(self, get_name), getattr(self, restore_name)
-
- def __enter__(self):
- self.saved_values = dict((name, get()) for name, get, restore
- in self.resource_info())
- return self
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- saved_values = self.saved_values
- del self.saved_values
- for name, get, restore in self.resource_info():
- current = get()
- original = saved_values.pop(name)
- # Check for changes to the resource's value
- if current != original:
- self.changed = True
- restore(original)
- if not self.quiet:
- print((
- "Warning -- {0} was modified by {1}".format(
- name, self.testname)), file=sys.stderr)
- if self.verbose > 1:
- print((
- " Before: {0}\n After: {1} ".format(
- original, current)), file=sys.stderr)
- # XXX (ncoghlan): for most resources (e.g. sys.path) identity
- # matters at least as much as value. For others (e.g. cwd),
- # identity is irrelevant. Should we add a mechanism to check
- # for substitution in the cases where it matters?
- return False
-
-
-def runtest_inner(test, verbose, quiet, huntrleaks=False):
- test_support.unload(test)
- if verbose:
- capture_stdout = None
- else:
- capture_stdout = StringIO.StringIO()
-
- test_time = 0.0
- refleak = False # True if the test leaked references.
- try:
- save_stdout = sys.stdout
- try:
- if capture_stdout:
- sys.stdout = capture_stdout
- if test.startswith('test.'):
- abstest = test
- else:
- # Always import it from the test package
- abstest = 'test.' + test
- with saved_test_environment(test, verbose, quiet) as environment:
- start_time = time.time()
- the_package = __import__(abstest, globals(), locals(), [])
- the_module = getattr(the_package, test)
- # Old tests run to completion simply as a side-effect of
- # being imported. For tests based on unittest or doctest,
- # explicitly invoke their test_main() function (if it exists).
- indirect_test = getattr(the_module, "test_main", None)
- if indirect_test is not None:
- indirect_test()
- if huntrleaks:
- refleak = dash_R(the_module, test, indirect_test,
- huntrleaks)
- test_time = time.time() - start_time
- finally:
- sys.stdout = save_stdout
- except test_support.ResourceDenied as msg:
- if not quiet:
- print(test, "skipped --", msg)
- sys.stdout.flush()
- return RESOURCE_DENIED, test_time
- except unittest.SkipTest as msg:
- if not quiet:
- print(test, "skipped --", msg)
- sys.stdout.flush()
- return SKIPPED, test_time
- except KeyboardInterrupt:
- raise
- except test_support.TestFailed as msg:
- print("test", test, "failed --", msg, file=sys.stderr)
- sys.stderr.flush()
- return FAILED, test_time
- except:
- type, value = sys.exc_info()[:2]
- print("test", test, "crashed --", str(type) + ":", value, file=sys.stderr)
- sys.stderr.flush()
- if verbose:
- traceback.print_exc(file=sys.stderr)
- sys.stderr.flush()
- return FAILED, test_time
- else:
- if refleak:
- return FAILED, test_time
- if environment.changed:
- return ENV_CHANGED, test_time
- # Except in verbose mode, tests should not print anything
- if verbose or huntrleaks:
- return PASSED, test_time
- output = capture_stdout.getvalue()
- if not output:
- return PASSED, test_time
- print("test", test, "produced unexpected output:")
- print("*" * 70)
- print(output)
- print("*" * 70)
- sys.stdout.flush()
- return FAILED, test_time
-
-def cleanup_test_droppings(testname, verbose):
- import stat
- import gc
-
- # First kill any dangling references to open files etc.
- gc.collect()
-
- # Try to clean up junk commonly left behind. While tests shouldn't leave
- # any files or directories behind, when a test fails that can be tedious
- # for it to arrange. The consequences can be especially nasty on Windows,
- # since if a test leaves a file open, it cannot be deleted by name (while
- # there's nothing we can do about that here either, we can display the
- # name of the offending test, which is a real help).
- for name in (test_support.TESTFN,
- "db_home",
- ):
- if not os.path.exists(name):
- continue
-
- if os.path.isdir(name):
- kind, nuker = "directory", shutil.rmtree
- elif os.path.isfile(name):
- kind, nuker = "file", os.unlink
- else:
- raise SystemError("os.path says %r exists but is neither "
- "directory nor file" % name)
-
- if verbose:
- print("%r left behind %s %r" % (testname, kind, name))
- try:
- # if we have chmod, fix possible permissions problems
- # that might prevent cleanup
- if (hasattr(os, 'chmod')):
- os.chmod(name, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
- nuker(name)
- except Exception as msg:
- print(("%r left behind %s %r and it couldn't be "
- "removed: %s" % (testname, kind, name, msg)), file=sys.stderr)
-
-def dash_R(the_module, test, indirect_test, huntrleaks):
- """Run a test multiple times, looking for reference leaks.
-
- Returns:
- False if the test didn't leak references; True if we detected refleaks.
- """
- # This code is hackish and inelegant, but it seems to do the job.
- import copy_reg, _abcoll, _pyio
-
- if not hasattr(sys, 'gettotalrefcount'):
- raise Exception("Tracking reference leaks requires a debug build "
- "of Python")
-
- # Save current values for dash_R_cleanup() to restore.
- fs = warnings.filters[:]
- ps = copy_reg.dispatch_table.copy()
- pic = sys.path_importer_cache.copy()
- try:
- import zipimport
- except ImportError:
- zdc = None # Run unmodified on platforms without zipimport support
- else:
- zdc = zipimport._zip_directory_cache.copy()
- abcs = {}
- modules = _abcoll, _pyio
- for abc in [getattr(mod, a) for mod in modules for a in mod.__all__]:
- # XXX isinstance(abc, ABCMeta) leads to infinite recursion
- if not hasattr(abc, '_abc_registry'):
- continue
- for obj in abc.__subclasses__() + [abc]:
- abcs[obj] = obj._abc_registry.copy()
-
- if indirect_test:
- def run_the_test():
- indirect_test()
- else:
- def run_the_test():
- imp.reload(the_module)
-
- deltas = []
- nwarmup, ntracked, fname = huntrleaks
- fname = os.path.join(test_support.SAVEDCWD, fname)
- repcount = nwarmup + ntracked
- print("beginning", repcount, "repetitions", file=sys.stderr)
- print(("1234567890"*(repcount//10 + 1))[:repcount], file=sys.stderr)
- dash_R_cleanup(fs, ps, pic, zdc, abcs)
- for i in range(repcount):
- rc_before = sys.gettotalrefcount()
- run_the_test()
- sys.stderr.write('.')
- dash_R_cleanup(fs, ps, pic, zdc, abcs)
- rc_after = sys.gettotalrefcount()
- if i >= nwarmup:
- deltas.append(rc_after - rc_before)
- print(file=sys.stderr)
- if any(deltas):
- msg = '%s leaked %s references, sum=%s' % (test, deltas, sum(deltas))
- print(msg, file=sys.stderr)
- with open(fname, "a") as refrep:
- print(msg, file=refrep)
- refrep.flush()
- return True
- return False
-
-def dash_R_cleanup(fs, ps, pic, zdc, abcs):
- import gc, copy_reg
- import _strptime, linecache
- dircache = test_support.import_module('dircache', deprecated=True)
- import urlparse, urllib, urllib2, mimetypes, doctest
- import struct, filecmp
- from distutils.dir_util import _path_created
-
- # Clear the warnings registry, so they can be displayed again
- for mod in sys.modules.values():
- if hasattr(mod, '__warningregistry__'):
- del mod.__warningregistry__
-
- # Restore some original values.
- warnings.filters[:] = fs
- copy_reg.dispatch_table.clear()
- copy_reg.dispatch_table.update(ps)
- sys.path_importer_cache.clear()
- sys.path_importer_cache.update(pic)
- try:
- import zipimport
- except ImportError:
- pass # Run unmodified on platforms without zipimport support
- else:
- zipimport._zip_directory_cache.clear()
- zipimport._zip_directory_cache.update(zdc)
-
- # clear type cache
- sys._clear_type_cache()
-
- # Clear ABC registries, restoring previously saved ABC registries.
- for abc, registry in abcs.items():
- abc._abc_registry = registry.copy()
- abc._abc_cache.clear()
- abc._abc_negative_cache.clear()
-
- # Clear assorted module caches.
- _path_created.clear()
- re.purge()
- _strptime._regex_cache.clear()
- urlparse.clear_cache()
- urllib.urlcleanup()
- urllib2.install_opener(None)
- dircache.reset()
- linecache.clearcache()
- mimetypes._default_mime_types()
- filecmp._cache.clear()
- struct._clearcache()
- doctest.master = None
- try:
- import ctypes
- except ImportError:
- # Don't worry about resetting the cache if ctypes is not supported
- pass
- else:
- ctypes._reset_cache()
-
- # Collect cyclic trash.
- gc.collect()
-
-def findtestdir(path=None):
- return path or os.path.dirname(__file__) or os.curdir
-
-def removepy(names):
- if not names:
- return
- for idx, name in enumerate(names):
- basename, ext = os.path.splitext(name)
- if ext == '.py':
- names[idx] = basename
-
-def count(n, word):
- if n == 1:
- return "%d %s" % (n, word)
- else:
- return "%d %ss" % (n, word)
-
-def printlist(x, width=70, indent=4):
- """Print the elements of iterable x to stdout.
-
- Optional arg width (default 70) is the maximum line length.
- Optional arg indent (default 4) is the number of blanks with which to
- begin each line.
- """
-
- from textwrap import fill
- blanks = ' ' * indent
- # Print the sorted list: 'x' may be a '--random' list or a set()
- print(fill(' '.join(str(elt) for elt in sorted(x)), width,
- initial_indent=blanks, subsequent_indent=blanks))
-
-# Map sys.platform to a string containing the basenames of tests
-# expected to be skipped on that platform.
-#
-# Special cases:
-# test_pep277
-# The _ExpectedSkips constructor adds this to the set of expected
-# skips if not os.path.supports_unicode_filenames.
-# test_timeout
-# Controlled by test_timeout.skip_expected. Requires the network
-# resource and a socket module.
-#
-# Tests that are expected to be skipped everywhere except on one platform
-# are also handled separately.
-
-_expectations = {
- 'win32':
- """
- test__locale
- test_bsddb185
- test_bsddb3
- test_commands
- test_crypt
- test_curses
- test_dbm
- test_dl
- test_fcntl
- test_fork1
- test_epoll
- test_gdbm
- test_grp
- test_ioctl
- test_largefile
- test_kqueue
- test_mhlib
- test_openpty
- test_ossaudiodev
- test_pipes
- test_poll
- test_posix
- test_pty
- test_pwd
- test_resource
- test_signal
- test_threadsignals
- test_timing
- test_wait3
- test_wait4
- """,
- 'linux2':
- """
- test_bsddb185
- test_curses
- test_dl
- test_largefile
- test_kqueue
- test_ossaudiodev
- """,
- 'unixware7':
- """
- test_bsddb
- test_bsddb185
- test_dl
- test_epoll
- test_largefile
- test_kqueue
- test_minidom
- test_openpty
- test_pyexpat
- test_sax
- test_sundry
- """,
- 'openunix8':
- """
- test_bsddb
- test_bsddb185
- test_dl
- test_epoll
- test_largefile
- test_kqueue
- test_minidom
- test_openpty
- test_pyexpat
- test_sax
- test_sundry
- """,
- 'sco_sv3':
- """
- test_asynchat
- test_bsddb
- test_bsddb185
- test_dl
- test_fork1
- test_epoll
- test_gettext
- test_largefile
- test_locale
- test_kqueue
- test_minidom
- test_openpty
- test_pyexpat
- test_queue
- test_sax
- test_sundry
- test_thread
- test_threaded_import
- test_threadedtempfile
- test_threading
- """,
- 'riscos':
- """
- test_asynchat
- test_atexit
- test_bsddb
- test_bsddb185
- test_bsddb3
- test_commands
- test_crypt
- test_dbm
- test_dl
- test_fcntl
- test_fork1
- test_epoll
- test_gdbm
- test_grp
- test_largefile
- test_locale
- test_kqueue
- test_mmap
- test_openpty
- test_poll
- test_popen2
- test_pty
- test_pwd
- test_strop
- test_sundry
- test_thread
- test_threaded_import
- test_threadedtempfile
- test_threading
- test_timing
- """,
- 'darwin':
- """
- test__locale
- test_bsddb
- test_bsddb3
- test_curses
- test_epoll
- test_gdb
- test_gdbm
- test_largefile
- test_locale
- test_kqueue
- test_minidom
- test_ossaudiodev
- test_poll
- """,
- 'sunos5':
- """
- test_bsddb
- test_bsddb185
- test_curses
- test_dbm
- test_epoll
- test_kqueue
- test_gdbm
- test_gzip
- test_openpty
- test_zipfile
- test_zlib
- """,
- 'hp-ux11':
- """
- test_bsddb
- test_bsddb185
- test_curses
- test_dl
- test_epoll
- test_gdbm
- test_gzip
- test_largefile
- test_locale
- test_kqueue
- test_minidom
- test_openpty
- test_pyexpat
- test_sax
- test_zipfile
- test_zlib
- """,
- 'atheos':
- """
- test_bsddb185
- test_curses
- test_dl
- test_gdbm
- test_epoll
- test_largefile
- test_locale
- test_kqueue
- test_mhlib
- test_mmap
- test_poll
- test_popen2
- test_resource
- """,
- 'cygwin':
- """
- test_bsddb185
- test_bsddb3
- test_curses
- test_dbm
- test_epoll
- test_ioctl
- test_kqueue
- test_largefile
- test_locale
- test_ossaudiodev
- test_socketserver
- """,
- 'os2emx':
- """
- test_audioop
- test_bsddb185
- test_bsddb3
- test_commands
- test_curses
- test_dl
- test_epoll
- test_kqueue
- test_largefile
- test_mhlib
- test_mmap
- test_openpty
- test_ossaudiodev
- test_pty
- test_resource
- test_signal
- """,
- 'freebsd4':
- """
- test_bsddb
- test_bsddb3
- test_epoll
- test_gdbm
- test_locale
- test_ossaudiodev
- test_pep277
- test_pty
- test_socketserver
- test_tcl
- test_tk
- test_ttk_guionly
- test_ttk_textonly
- test_timeout
- test_urllibnet
- test_multiprocessing
- """,
- 'aix5':
- """
- test_bsddb
- test_bsddb185
- test_bsddb3
- test_bz2
- test_dl
- test_epoll
- test_gdbm
- test_gzip
- test_kqueue
- test_ossaudiodev
- test_tcl
- test_tk
- test_ttk_guionly
- test_ttk_textonly
- test_zipimport
- test_zlib
- """,
- 'openbsd3':
- """
- test_ascii_formatd
- test_bsddb
- test_bsddb3
- test_ctypes
- test_dl
- test_epoll
- test_gdbm
- test_locale
- test_normalization
- test_ossaudiodev
- test_pep277
- test_tcl
- test_tk
- test_ttk_guionly
- test_ttk_textonly
- test_multiprocessing
- """,
- 'netbsd3':
- """
- test_ascii_formatd
- test_bsddb
- test_bsddb185
- test_bsddb3
- test_ctypes
- test_curses
- test_dl
- test_epoll
- test_gdbm
- test_locale
- test_ossaudiodev
- test_pep277
- test_tcl
- test_tk
- test_ttk_guionly
- test_ttk_textonly
- test_multiprocessing
- """,
-}
-_expectations['freebsd5'] = _expectations['freebsd4']
-_expectations['freebsd6'] = _expectations['freebsd4']
-_expectations['freebsd7'] = _expectations['freebsd4']
-_expectations['freebsd8'] = _expectations['freebsd4']
-
-class _ExpectedSkips(object):
- def __init__(self):
- import os.path
- from test import test_timeout
-
- self.valid = False
- if sys.platform in _expectations:
- s = _expectations[sys.platform]
- self.expected = set(s.split())
-
- # expected to be skipped on every platform, even Linux
- self.expected.add('test_linuxaudiodev')
-
- if not os.path.supports_unicode_filenames:
- self.expected.add('test_pep277')
-
- if test_timeout.skip_expected:
- self.expected.add('test_timeout')
-
- if sys.maxint == 9223372036854775807:
- self.expected.add('test_imageop')
-
- if sys.platform != "darwin":
- MAC_ONLY = ["test_macos", "test_macostools", "test_aepack",
- "test_plistlib", "test_scriptpackages",
- "test_applesingle"]
- for skip in MAC_ONLY:
- self.expected.add(skip)
- elif len(u'\0'.encode('unicode-internal')) == 4:
- self.expected.add("test_macostools")
-
-
- if sys.platform != "win32":
- # test_sqlite is only reliable on Windows where the library
- # is distributed with Python
- WIN_ONLY = ["test_unicode_file", "test_winreg",
- "test_winsound", "test_startfile",
- "test_sqlite", "test_msilib"]
- for skip in WIN_ONLY:
- self.expected.add(skip)
-
- if sys.platform != 'irix':
- IRIX_ONLY = ["test_imageop", "test_al", "test_cd", "test_cl",
- "test_gl", "test_imgfile"]
- for skip in IRIX_ONLY:
- self.expected.add(skip)
-
- if sys.platform != 'sunos5':
- self.expected.add('test_sunaudiodev')
- self.expected.add('test_nis')
-
- if not sys.py3kwarning:
- self.expected.add('test_py3kwarn')
-
- self.valid = True
-
- def isvalid(self):
- "Return true iff _ExpectedSkips knows about the current platform."
- return self.valid
-
- def getexpected(self):
- """Return set of test names we expect to skip on current platform.
-
- self.isvalid() must be true.
- """
-
- assert self.isvalid()
- return self.expected
-
-if __name__ == '__main__':
- # findtestdir() gets the dirname out of __file__, so we have to make it
- # absolute before changing the working directory.
- # For example __file__ may be relative when running trace or profile.
- # See issue #9323.
- __file__ = os.path.abspath(__file__)
-
- # sanity check
- assert __file__ == os.path.abspath(sys.argv[0])
-
- # When tests are run from the Python build directory, it is best practice
- # to keep the test files in a subfolder. It eases the cleanup of leftover
- # files using command "make distclean".
- if sysconfig.is_python_build():
- TEMPDIR = os.path.join(sysconfig.get_config_var('srcdir'), 'build')
- TEMPDIR = os.path.abspath(TEMPDIR)
- if not os.path.exists(TEMPDIR):
- os.mkdir(TEMPDIR)
-
- # Define a writable temp dir that will be used as cwd while running
- # the tests. The name of the dir includes the pid to allow parallel
- # testing (see the -j option).
- TESTCWD = 'test_python_{0}'.format(os.getpid())
-
- TESTCWD = os.path.join(TEMPDIR, TESTCWD)
-
- # Run the tests in a context manager that temporary changes the CWD to a
- # temporary and writable directory. If it's not possible to create or
- # change the CWD, the original CWD will be used. The original CWD is
- # available from test_support.SAVEDCWD.
- with test_support.temp_cwd(TESTCWD, quiet=True):
- main()
diff --git a/future/standard_library/test/string_tests.py b/future/standard_library/test/string_tests.py
deleted file mode 100644
index 21c631db..00000000
--- a/future/standard_library/test/string_tests.py
+++ /dev/null
@@ -1,1393 +0,0 @@
-"""
-Common tests shared by test_str, test_unicode, test_userstring and test_string.
-"""
-from __future__ import (absolute_import, division,
- print_function, unicode_literals)
-from future import standard_library
-from future.builtins import *
-
-import string
-import sys
-import struct
-with standard_library.hooks():
- from test import support
- from collections import UserList
-import _testcapi
-
-class Sequence(object):
- def __init__(self, seq='wxyz'): self.seq = seq
- def __len__(self): return len(self.seq)
- def __getitem__(self, i): return self.seq[i]
-
-class BadSeq1(Sequence):
- def __init__(self): self.seq = [7, 'hello', 123]
- def __str__(self): return '{0} {1} {2}'.format(*self.seq)
-
-class BadSeq2(Sequence):
- def __init__(self): self.seq = ['a', 'b', 'c']
- def __len__(self): return 8
-
-class BaseTest(object):
- # These tests are for buffers of values (bytes) and not
- # specific to character interpretation, used for bytes objects
- # and various string implementations
-
- # The type to be tested
- # Change in subclasses to change the behaviour of fixtesttype()
- type2test = None
-
- # Whether the "contained items" of the container are integers in
- # range(0, 256) (i.e. bytes, bytearray) or strings of length 1
- # (str)
- contains_bytes = False
-
- # All tests pass their arguments to the testing methods
- # as str objects. fixtesttype() can be used to propagate
- # these arguments to the appropriate type
- def fixtype(self, obj):
- if isinstance(obj, str):
- return self.__class__.type2test(obj)
- elif isinstance(obj, list):
- return [self.fixtype(x) for x in obj]
- elif isinstance(obj, tuple):
- return tuple([self.fixtype(x) for x in obj])
- elif isinstance(obj, dict):
- return dict([
- (self.fixtype(key), self.fixtype(value))
- for (key, value) in obj.items()
- ])
- else:
- return obj
-
- # check that obj.method(*args) returns result
- def checkequal(self, result, obj, methodname, *args, **kwargs):
- result = self.fixtype(result)
- obj = self.fixtype(obj)
- args = self.fixtype(args)
- kwargs = dict((k, self.fixtype(v)) for k,v in kwargs.items())
- realresult = getattr(obj, methodname)(*args, **kwargs)
- self.assertEqual(
- result,
- realresult
- )
- # if the original is returned make sure that
- # this doesn't happen with subclasses
- if obj is realresult:
- try:
- class subtype(self.__class__.type2test):
- pass
- except TypeError:
- pass # Skip this if we can't subclass
- else:
- obj = subtype(obj)
- realresult = getattr(obj, methodname)(*args)
- self.assertIsNot(obj, realresult)
-
- # check that obj.method(*args) raises exc
- def checkraises(self, exc, obj, methodname, *args):
- obj = self.fixtype(obj)
- args = self.fixtype(args)
- self.assertRaises(
- exc,
- getattr(obj, methodname),
- *args
- )
-
- # call obj.method(*args) without any checks
- def checkcall(self, obj, methodname, *args):
- obj = self.fixtype(obj)
- args = self.fixtype(args)
- getattr(obj, methodname)(*args)
-
- def test_count(self):
- self.checkequal(3, 'aaa', 'count', 'a')
- self.checkequal(0, 'aaa', 'count', 'b')
- self.checkequal(3, 'aaa', 'count', 'a')
- self.checkequal(0, 'aaa', 'count', 'b')
- self.checkequal(3, 'aaa', 'count', 'a')
- self.checkequal(0, 'aaa', 'count', 'b')
- self.checkequal(0, 'aaa', 'count', 'b')
- self.checkequal(2, 'aaa', 'count', 'a', 1)
- self.checkequal(0, 'aaa', 'count', 'a', 10)
- self.checkequal(1, 'aaa', 'count', 'a', -1)
- self.checkequal(3, 'aaa', 'count', 'a', -10)
- self.checkequal(1, 'aaa', 'count', 'a', 0, 1)
- self.checkequal(3, 'aaa', 'count', 'a', 0, 10)
- self.checkequal(2, 'aaa', 'count', 'a', 0, -1)
- self.checkequal(0, 'aaa', 'count', 'a', 0, -10)
- self.checkequal(3, 'aaa', 'count', '', 1)
- self.checkequal(1, 'aaa', 'count', '', 3)
- self.checkequal(0, 'aaa', 'count', '', 10)
- self.checkequal(2, 'aaa', 'count', '', -1)
- self.checkequal(4, 'aaa', 'count', '', -10)
-
- self.checkequal(1, '', 'count', '')
- self.checkequal(0, '', 'count', '', 1, 1)
- self.checkequal(0, '', 'count', '', sys.maxsize, 0)
-
- self.checkequal(0, '', 'count', 'xx')
- self.checkequal(0, '', 'count', 'xx', 1, 1)
- self.checkequal(0, '', 'count', 'xx', sys.maxsize, 0)
-
- self.checkraises(TypeError, 'hello', 'count')
-
- if self.contains_bytes:
- self.checkequal(0, 'hello', 'count', 42)
- else:
- self.checkraises(TypeError, 'hello', 'count', 42)
-
- # For a variety of combinations,
- # verify that str.count() matches an equivalent function
- # replacing all occurrences and then differencing the string lengths
- charset = ['', 'a', 'b']
- digits = 7
- base = len(charset)
- teststrings = set()
- for i in range(base ** digits):
- entry = []
- for j in range(digits):
- i, m = divmod(i, base)
- entry.append(charset[m])
- teststrings.add(''.join(entry))
- teststrings = [self.fixtype(ts) for ts in teststrings]
- for i in teststrings:
- n = len(i)
- for j in teststrings:
- r1 = i.count(j)
- if j:
- r2, rem = divmod(n - len(i.replace(j, self.fixtype(''))),
- len(j))
- else:
- r2, rem = len(i)+1, 0
- if rem or r1 != r2:
- self.assertEqual(rem, 0, '%s != 0 for %s' % (rem, i))
- self.assertEqual(r1, r2, '%s != %s for %s' % (r1, r2, i))
-
- def test_find(self):
- self.checkequal(0, 'abcdefghiabc', 'find', 'abc')
- self.checkequal(9, 'abcdefghiabc', 'find', 'abc', 1)
- self.checkequal(-1, 'abcdefghiabc', 'find', 'def', 4)
-
- self.checkequal(0, 'abc', 'find', '', 0)
- self.checkequal(3, 'abc', 'find', '', 3)
- self.checkequal(-1, 'abc', 'find', '', 4)
-
- # to check the ability to pass None as defaults
- self.checkequal( 2, 'rrarrrrrrrrra', 'find', 'a')
- self.checkequal(12, 'rrarrrrrrrrra', 'find', 'a', 4)
- self.checkequal(-1, 'rrarrrrrrrrra', 'find', 'a', 4, 6)
- self.checkequal(12, 'rrarrrrrrrrra', 'find', 'a', 4, None)
- self.checkequal( 2, 'rrarrrrrrrrra', 'find', 'a', None, 6)
-
- self.checkraises(TypeError, 'hello', 'find')
-
- if self.contains_bytes:
- self.checkequal(-1, 'hello', 'find', 42)
- else:
- self.checkraises(TypeError, 'hello', 'find', 42)
-
- self.checkequal(0, '', 'find', '')
- self.checkequal(-1, '', 'find', '', 1, 1)
- self.checkequal(-1, '', 'find', '', sys.maxsize, 0)
-
- self.checkequal(-1, '', 'find', 'xx')
- self.checkequal(-1, '', 'find', 'xx', 1, 1)
- self.checkequal(-1, '', 'find', 'xx', sys.maxsize, 0)
-
- # issue 7458
- self.checkequal(-1, 'ab', 'find', 'xxx', sys.maxsize + 1, 0)
-
- # For a variety of combinations,
- # verify that str.find() matches __contains__
- # and that the found substring is really at that location
- charset = ['', 'a', 'b', 'c']
- digits = 5
- base = len(charset)
- teststrings = set()
- for i in range(base ** digits):
- entry = []
- for j in range(digits):
- i, m = divmod(i, base)
- entry.append(charset[m])
- teststrings.add(''.join(entry))
- teststrings = [self.fixtype(ts) for ts in teststrings]
- for i in teststrings:
- for j in teststrings:
- loc = i.find(j)
- r1 = (loc != -1)
- r2 = j in i
- self.assertEqual(r1, r2)
- if loc != -1:
- self.assertEqual(i[loc:loc+len(j)], j)
-
- def test_rfind(self):
- self.checkequal(9, 'abcdefghiabc', 'rfind', 'abc')
- self.checkequal(12, 'abcdefghiabc', 'rfind', '')
- self.checkequal(0, 'abcdefghiabc', 'rfind', 'abcd')
- self.checkequal(-1, 'abcdefghiabc', 'rfind', 'abcz')
-
- self.checkequal(3, 'abc', 'rfind', '', 0)
- self.checkequal(3, 'abc', 'rfind', '', 3)
- self.checkequal(-1, 'abc', 'rfind', '', 4)
-
- # to check the ability to pass None as defaults
- self.checkequal(12, 'rrarrrrrrrrra', 'rfind', 'a')
- self.checkequal(12, 'rrarrrrrrrrra', 'rfind', 'a', 4)
- self.checkequal(-1, 'rrarrrrrrrrra', 'rfind', 'a', 4, 6)
- self.checkequal(12, 'rrarrrrrrrrra', 'rfind', 'a', 4, None)
- self.checkequal( 2, 'rrarrrrrrrrra', 'rfind', 'a', None, 6)
-
- self.checkraises(TypeError, 'hello', 'rfind')
-
- if self.contains_bytes:
- self.checkequal(-1, 'hello', 'rfind', 42)
- else:
- self.checkraises(TypeError, 'hello', 'rfind', 42)
-
- # For a variety of combinations,
- # verify that str.rfind() matches __contains__
- # and that the found substring is really at that location
- charset = ['', 'a', 'b', 'c']
- digits = 5
- base = len(charset)
- teststrings = set()
- for i in range(base ** digits):
- entry = []
- for j in range(digits):
- i, m = divmod(i, base)
- entry.append(charset[m])
- teststrings.add(''.join(entry))
- teststrings = [self.fixtype(ts) for ts in teststrings]
- for i in teststrings:
- for j in teststrings:
- loc = i.rfind(j)
- r1 = (loc != -1)
- r2 = j in i
- self.assertEqual(r1, r2)
- if loc != -1:
- self.assertEqual(i[loc:loc+len(j)], j)
-
- # issue 7458
- self.checkequal(-1, 'ab', 'rfind', 'xxx', sys.maxsize + 1, 0)
-
- # issue #15534
- self.checkequal(0, '<......\u043c...', "rfind", "<")
-
- def test_index(self):
- self.checkequal(0, 'abcdefghiabc', 'index', '')
- self.checkequal(3, 'abcdefghiabc', 'index', 'def')
- self.checkequal(0, 'abcdefghiabc', 'index', 'abc')
- self.checkequal(9, 'abcdefghiabc', 'index', 'abc', 1)
-
- self.checkraises(ValueError, 'abcdefghiabc', 'index', 'hib')
- self.checkraises(ValueError, 'abcdefghiab', 'index', 'abc', 1)
- self.checkraises(ValueError, 'abcdefghi', 'index', 'ghi', 8)
- self.checkraises(ValueError, 'abcdefghi', 'index', 'ghi', -1)
-
- # to check the ability to pass None as defaults
- self.checkequal( 2, 'rrarrrrrrrrra', 'index', 'a')
- self.checkequal(12, 'rrarrrrrrrrra', 'index', 'a', 4)
- self.checkraises(ValueError, 'rrarrrrrrrrra', 'index', 'a', 4, 6)
- self.checkequal(12, 'rrarrrrrrrrra', 'index', 'a', 4, None)
- self.checkequal( 2, 'rrarrrrrrrrra', 'index', 'a', None, 6)
-
- self.checkraises(TypeError, 'hello', 'index')
-
- if self.contains_bytes:
- self.checkraises(ValueError, 'hello', 'index', 42)
- else:
- self.checkraises(TypeError, 'hello', 'index', 42)
-
- def test_rindex(self):
- self.checkequal(12, 'abcdefghiabc', 'rindex', '')
- self.checkequal(3, 'abcdefghiabc', 'rindex', 'def')
- self.checkequal(9, 'abcdefghiabc', 'rindex', 'abc')
- self.checkequal(0, 'abcdefghiabc', 'rindex', 'abc', 0, -1)
-
- self.checkraises(ValueError, 'abcdefghiabc', 'rindex', 'hib')
- self.checkraises(ValueError, 'defghiabc', 'rindex', 'def', 1)
- self.checkraises(ValueError, 'defghiabc', 'rindex', 'abc', 0, -1)
- self.checkraises(ValueError, 'abcdefghi', 'rindex', 'ghi', 0, 8)
- self.checkraises(ValueError, 'abcdefghi', 'rindex', 'ghi', 0, -1)
-
- # to check the ability to pass None as defaults
- self.checkequal(12, 'rrarrrrrrrrra', 'rindex', 'a')
- self.checkequal(12, 'rrarrrrrrrrra', 'rindex', 'a', 4)
- self.checkraises(ValueError, 'rrarrrrrrrrra', 'rindex', 'a', 4, 6)
- self.checkequal(12, 'rrarrrrrrrrra', 'rindex', 'a', 4, None)
- self.checkequal( 2, 'rrarrrrrrrrra', 'rindex', 'a', None, 6)
-
- self.checkraises(TypeError, 'hello', 'rindex')
-
- if self.contains_bytes:
- self.checkraises(ValueError, 'hello', 'rindex', 42)
- else:
- self.checkraises(TypeError, 'hello', 'rindex', 42)
-
- def test_lower(self):
- self.checkequal('hello', 'HeLLo', 'lower')
- self.checkequal('hello', 'hello', 'lower')
- self.checkraises(TypeError, 'hello', 'lower', 42)
-
- def test_upper(self):
- self.checkequal('HELLO', 'HeLLo', 'upper')
- self.checkequal('HELLO', 'HELLO', 'upper')
- self.checkraises(TypeError, 'hello', 'upper', 42)
-
- def test_expandtabs(self):
- self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs')
- self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs', 8)
- self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs', 4)
- self.checkequal('abc\r\nab def\ng hi', 'abc\r\nab\tdef\ng\thi', 'expandtabs', 4)
- self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs')
- self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs', 8)
- self.checkequal('abc\r\nab\r\ndef\ng\r\nhi', 'abc\r\nab\r\ndef\ng\r\nhi', 'expandtabs', 4)
- self.checkequal(' a\n b', ' \ta\n\tb', 'expandtabs', 1)
-
- self.checkraises(TypeError, 'hello', 'expandtabs', 42, 42)
- # This test is only valid when sizeof(int) == sizeof(void*) == 4.
- if sys.maxsize < (1 << 32) and struct.calcsize('P') == 4:
- self.checkraises(OverflowError,
- '\ta\n\tb', 'expandtabs', sys.maxsize)
-
- def test_split(self):
- # by a char
- self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'split', '|')
- self.checkequal(['a|b|c|d'], 'a|b|c|d', 'split', '|', 0)
- self.checkequal(['a', 'b|c|d'], 'a|b|c|d', 'split', '|', 1)
- self.checkequal(['a', 'b', 'c|d'], 'a|b|c|d', 'split', '|', 2)
- self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'split', '|', 3)
- self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'split', '|', 4)
- self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'split', '|',
- sys.maxsize-2)
- self.checkequal(['a|b|c|d'], 'a|b|c|d', 'split', '|', 0)
- self.checkequal(['a', '', 'b||c||d'], 'a||b||c||d', 'split', '|', 2)
- self.checkequal(['endcase ', ''], 'endcase |', 'split', '|')
- self.checkequal(['', ' startcase'], '| startcase', 'split', '|')
- self.checkequal(['', 'bothcase', ''], '|bothcase|', 'split', '|')
- self.checkequal(['a', '', 'b\x00c\x00d'], 'a\x00\x00b\x00c\x00d', 'split', '\x00', 2)
-
- self.checkequal(['a']*20, ('a|'*20)[:-1], 'split', '|')
- self.checkequal(['a']*15 +['a|a|a|a|a'],
- ('a|'*20)[:-1], 'split', '|', 15)
-
- # by string
- self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'split', '//')
- self.checkequal(['a', 'b//c//d'], 'a//b//c//d', 'split', '//', 1)
- self.checkequal(['a', 'b', 'c//d'], 'a//b//c//d', 'split', '//', 2)
- self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'split', '//', 3)
- self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'split', '//', 4)
- self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'split', '//',
- sys.maxsize-10)
- self.checkequal(['a//b//c//d'], 'a//b//c//d', 'split', '//', 0)
- self.checkequal(['a', '', 'b////c////d'], 'a////b////c////d', 'split', '//', 2)
- self.checkequal(['endcase ', ''], 'endcase test', 'split', 'test')
- self.checkequal(['', ' begincase'], 'test begincase', 'split', 'test')
- self.checkequal(['', ' bothcase ', ''], 'test bothcase test',
- 'split', 'test')
- self.checkequal(['a', 'bc'], 'abbbc', 'split', 'bb')
- self.checkequal(['', ''], 'aaa', 'split', 'aaa')
- self.checkequal(['aaa'], 'aaa', 'split', 'aaa', 0)
- self.checkequal(['ab', 'ab'], 'abbaab', 'split', 'ba')
- self.checkequal(['aaaa'], 'aaaa', 'split', 'aab')
- self.checkequal([''], '', 'split', 'aaa')
- self.checkequal(['aa'], 'aa', 'split', 'aaa')
- self.checkequal(['A', 'bobb'], 'Abbobbbobb', 'split', 'bbobb')
- self.checkequal(['A', 'B', ''], 'AbbobbBbbobb', 'split', 'bbobb')
-
- self.checkequal(['a']*20, ('aBLAH'*20)[:-4], 'split', 'BLAH')
- self.checkequal(['a']*20, ('aBLAH'*20)[:-4], 'split', 'BLAH', 19)
- self.checkequal(['a']*18 + ['aBLAHa'], ('aBLAH'*20)[:-4],
- 'split', 'BLAH', 18)
-
- # with keyword args
- self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'split', sep='|')
- self.checkequal(['a', 'b|c|d'],
- 'a|b|c|d', 'split', '|', maxsplit=1)
- self.checkequal(['a', 'b|c|d'],
- 'a|b|c|d', 'split', sep='|', maxsplit=1)
- self.checkequal(['a', 'b|c|d'],
- 'a|b|c|d', 'split', maxsplit=1, sep='|')
- self.checkequal(['a', 'b c d'],
- 'a b c d', 'split', maxsplit=1)
-
- # argument type
- self.checkraises(TypeError, 'hello', 'split', 42, 42, 42)
-
- # null case
- self.checkraises(ValueError, 'hello', 'split', '')
- self.checkraises(ValueError, 'hello', 'split', '', 0)
-
- def test_rsplit(self):
- # by a char
- self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'rsplit', '|')
- self.checkequal(['a|b|c', 'd'], 'a|b|c|d', 'rsplit', '|', 1)
- self.checkequal(['a|b', 'c', 'd'], 'a|b|c|d', 'rsplit', '|', 2)
- self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'rsplit', '|', 3)
- self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'rsplit', '|', 4)
- self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'rsplit', '|',
- sys.maxsize-100)
- self.checkequal(['a|b|c|d'], 'a|b|c|d', 'rsplit', '|', 0)
- self.checkequal(['a||b||c', '', 'd'], 'a||b||c||d', 'rsplit', '|', 2)
- self.checkequal(['', ' begincase'], '| begincase', 'rsplit', '|')
- self.checkequal(['endcase ', ''], 'endcase |', 'rsplit', '|')
- self.checkequal(['', 'bothcase', ''], '|bothcase|', 'rsplit', '|')
-
- self.checkequal(['a\x00\x00b', 'c', 'd'], 'a\x00\x00b\x00c\x00d', 'rsplit', '\x00', 2)
-
- self.checkequal(['a']*20, ('a|'*20)[:-1], 'rsplit', '|')
- self.checkequal(['a|a|a|a|a']+['a']*15,
- ('a|'*20)[:-1], 'rsplit', '|', 15)
-
- # by string
- self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'rsplit', '//')
- self.checkequal(['a//b//c', 'd'], 'a//b//c//d', 'rsplit', '//', 1)
- self.checkequal(['a//b', 'c', 'd'], 'a//b//c//d', 'rsplit', '//', 2)
- self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'rsplit', '//', 3)
- self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'rsplit', '//', 4)
- self.checkequal(['a', 'b', 'c', 'd'], 'a//b//c//d', 'rsplit', '//',
- sys.maxsize-5)
- self.checkequal(['a//b//c//d'], 'a//b//c//d', 'rsplit', '//', 0)
- self.checkequal(['a////b////c', '', 'd'], 'a////b////c////d', 'rsplit', '//', 2)
- self.checkequal(['', ' begincase'], 'test begincase', 'rsplit', 'test')
- self.checkequal(['endcase ', ''], 'endcase test', 'rsplit', 'test')
- self.checkequal(['', ' bothcase ', ''], 'test bothcase test',
- 'rsplit', 'test')
- self.checkequal(['ab', 'c'], 'abbbc', 'rsplit', 'bb')
- self.checkequal(['', ''], 'aaa', 'rsplit', 'aaa')
- self.checkequal(['aaa'], 'aaa', 'rsplit', 'aaa', 0)
- self.checkequal(['ab', 'ab'], 'abbaab', 'rsplit', 'ba')
- self.checkequal(['aaaa'], 'aaaa', 'rsplit', 'aab')
- self.checkequal([''], '', 'rsplit', 'aaa')
- self.checkequal(['aa'], 'aa', 'rsplit', 'aaa')
- self.checkequal(['bbob', 'A'], 'bbobbbobbA', 'rsplit', 'bbobb')
- self.checkequal(['', 'B', 'A'], 'bbobbBbbobbA', 'rsplit', 'bbobb')
-
- self.checkequal(['a']*20, ('aBLAH'*20)[:-4], 'rsplit', 'BLAH')
- self.checkequal(['a']*20, ('aBLAH'*20)[:-4], 'rsplit', 'BLAH', 19)
- self.checkequal(['aBLAHa'] + ['a']*18, ('aBLAH'*20)[:-4],
- 'rsplit', 'BLAH', 18)
-
- # with keyword args
- self.checkequal(['a', 'b', 'c', 'd'], 'a|b|c|d', 'rsplit', sep='|')
- self.checkequal(['a|b|c', 'd'],
- 'a|b|c|d', 'rsplit', '|', maxsplit=1)
- self.checkequal(['a|b|c', 'd'],
- 'a|b|c|d', 'rsplit', sep='|', maxsplit=1)
- self.checkequal(['a|b|c', 'd'],
- 'a|b|c|d', 'rsplit', maxsplit=1, sep='|')
- self.checkequal(['a b c', 'd'],
- 'a b c d', 'rsplit', maxsplit=1)
-
- # argument type
- self.checkraises(TypeError, 'hello', 'rsplit', 42, 42, 42)
-
- # null case
- self.checkraises(ValueError, 'hello', 'rsplit', '')
- self.checkraises(ValueError, 'hello', 'rsplit', '', 0)
-
- def test_replace(self):
- EQ = self.checkequal
-
- # Operations on the empty string
- EQ("", "", "replace", "", "")
- EQ("A", "", "replace", "", "A")
- EQ("", "", "replace", "A", "")
- EQ("", "", "replace", "A", "A")
- EQ("", "", "replace", "", "", 100)
- EQ("", "", "replace", "", "", sys.maxsize)
-
- # interleave (from=="", 'to' gets inserted everywhere)
- EQ("A", "A", "replace", "", "")
- EQ("*A*", "A", "replace", "", "*")
- EQ("*1A*1", "A", "replace", "", "*1")
- EQ("*-#A*-#", "A", "replace", "", "*-#")
- EQ("*-A*-A*-", "AA", "replace", "", "*-")
- EQ("*-A*-A*-", "AA", "replace", "", "*-", -1)
- EQ("*-A*-A*-", "AA", "replace", "", "*-", sys.maxsize)
- EQ("*-A*-A*-", "AA", "replace", "", "*-", 4)
- EQ("*-A*-A*-", "AA", "replace", "", "*-", 3)
- EQ("*-A*-A", "AA", "replace", "", "*-", 2)
- EQ("*-AA", "AA", "replace", "", "*-", 1)
- EQ("AA", "AA", "replace", "", "*-", 0)
-
- # single character deletion (from=="A", to=="")
- EQ("", "A", "replace", "A", "")
- EQ("", "AAA", "replace", "A", "")
- EQ("", "AAA", "replace", "A", "", -1)
- EQ("", "AAA", "replace", "A", "", sys.maxsize)
- EQ("", "AAA", "replace", "A", "", 4)
- EQ("", "AAA", "replace", "A", "", 3)
- EQ("A", "AAA", "replace", "A", "", 2)
- EQ("AA", "AAA", "replace", "A", "", 1)
- EQ("AAA", "AAA", "replace", "A", "", 0)
- EQ("", "AAAAAAAAAA", "replace", "A", "")
- EQ("BCD", "ABACADA", "replace", "A", "")
- EQ("BCD", "ABACADA", "replace", "A", "", -1)
- EQ("BCD", "ABACADA", "replace", "A", "", sys.maxsize)
- EQ("BCD", "ABACADA", "replace", "A", "", 5)
- EQ("BCD", "ABACADA", "replace", "A", "", 4)
- EQ("BCDA", "ABACADA", "replace", "A", "", 3)
- EQ("BCADA", "ABACADA", "replace", "A", "", 2)
- EQ("BACADA", "ABACADA", "replace", "A", "", 1)
- EQ("ABACADA", "ABACADA", "replace", "A", "", 0)
- EQ("BCD", "ABCAD", "replace", "A", "")
- EQ("BCD", "ABCADAA", "replace", "A", "")
- EQ("BCD", "BCD", "replace", "A", "")
- EQ("*************", "*************", "replace", "A", "")
- EQ("^A^", "^"+"A"*1000+"^", "replace", "A", "", 999)
-
- # substring deletion (from=="the", to=="")
- EQ("", "the", "replace", "the", "")
- EQ("ater", "theater", "replace", "the", "")
- EQ("", "thethe", "replace", "the", "")
- EQ("", "thethethethe", "replace", "the", "")
- EQ("aaaa", "theatheatheathea", "replace", "the", "")
- EQ("that", "that", "replace", "the", "")
- EQ("thaet", "thaet", "replace", "the", "")
- EQ("here and re", "here and there", "replace", "the", "")
- EQ("here and re and re", "here and there and there",
- "replace", "the", "", sys.maxsize)
- EQ("here and re and re", "here and there and there",
- "replace", "the", "", -1)
- EQ("here and re and re", "here and there and there",
- "replace", "the", "", 3)
- EQ("here and re and re", "here and there and there",
- "replace", "the", "", 2)
- EQ("here and re and there", "here and there and there",
- "replace", "the", "", 1)
- EQ("here and there and there", "here and there and there",
- "replace", "the", "", 0)
- EQ("here and re and re", "here and there and there", "replace", "the", "")
-
- EQ("abc", "abc", "replace", "the", "")
- EQ("abcdefg", "abcdefg", "replace", "the", "")
-
- # substring deletion (from=="bob", to=="")
- EQ("bob", "bbobob", "replace", "bob", "")
- EQ("bobXbob", "bbobobXbbobob", "replace", "bob", "")
- EQ("aaaaaaa", "aaaaaaabob", "replace", "bob", "")
- EQ("aaaaaaa", "aaaaaaa", "replace", "bob", "")
-
- # single character replace in place (len(from)==len(to)==1)
- EQ("Who goes there?", "Who goes there?", "replace", "o", "o")
- EQ("WhO gOes there?", "Who goes there?", "replace", "o", "O")
- EQ("WhO gOes there?", "Who goes there?", "replace", "o", "O", sys.maxsize)
- EQ("WhO gOes there?", "Who goes there?", "replace", "o", "O", -1)
- EQ("WhO gOes there?", "Who goes there?", "replace", "o", "O", 3)
- EQ("WhO gOes there?", "Who goes there?", "replace", "o", "O", 2)
- EQ("WhO goes there?", "Who goes there?", "replace", "o", "O", 1)
- EQ("Who goes there?", "Who goes there?", "replace", "o", "O", 0)
-
- EQ("Who goes there?", "Who goes there?", "replace", "a", "q")
- EQ("who goes there?", "Who goes there?", "replace", "W", "w")
- EQ("wwho goes there?ww", "WWho goes there?WW", "replace", "W", "w")
- EQ("Who goes there!", "Who goes there?", "replace", "?", "!")
- EQ("Who goes there!!", "Who goes there??", "replace", "?", "!")
-
- EQ("Who goes there?", "Who goes there?", "replace", ".", "!")
-
- # substring replace in place (len(from)==len(to) > 1)
- EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**")
- EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**", sys.maxsize)
- EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**", -1)
- EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**", 4)
- EQ("Th** ** a t**sue", "This is a tissue", "replace", "is", "**", 3)
- EQ("Th** ** a tissue", "This is a tissue", "replace", "is", "**", 2)
- EQ("Th** is a tissue", "This is a tissue", "replace", "is", "**", 1)
- EQ("This is a tissue", "This is a tissue", "replace", "is", "**", 0)
- EQ("cobob", "bobob", "replace", "bob", "cob")
- EQ("cobobXcobocob", "bobobXbobobob", "replace", "bob", "cob")
- EQ("bobob", "bobob", "replace", "bot", "bot")
-
- # replace single character (len(from)==1, len(to)>1)
- EQ("ReyKKjaviKK", "Reykjavik", "replace", "k", "KK")
- EQ("ReyKKjaviKK", "Reykjavik", "replace", "k", "KK", -1)
- EQ("ReyKKjaviKK", "Reykjavik", "replace", "k", "KK", sys.maxsize)
- EQ("ReyKKjaviKK", "Reykjavik", "replace", "k", "KK", 2)
- EQ("ReyKKjavik", "Reykjavik", "replace", "k", "KK", 1)
- EQ("Reykjavik", "Reykjavik", "replace", "k", "KK", 0)
- EQ("A----B----C----", "A.B.C.", "replace", ".", "----")
- # issue #15534
- EQ('...\u043c......<', '...\u043c......<', "replace", "<", "<")
-
- EQ("Reykjavik", "Reykjavik", "replace", "q", "KK")
-
- # replace substring (len(from)>1, len(to)!=len(from))
- EQ("ham, ham, eggs and ham", "spam, spam, eggs and spam",
- "replace", "spam", "ham")
- EQ("ham, ham, eggs and ham", "spam, spam, eggs and spam",
- "replace", "spam", "ham", sys.maxsize)
- EQ("ham, ham, eggs and ham", "spam, spam, eggs and spam",
- "replace", "spam", "ham", -1)
- EQ("ham, ham, eggs and ham", "spam, spam, eggs and spam",
- "replace", "spam", "ham", 4)
- EQ("ham, ham, eggs and ham", "spam, spam, eggs and spam",
- "replace", "spam", "ham", 3)
- EQ("ham, ham, eggs and spam", "spam, spam, eggs and spam",
- "replace", "spam", "ham", 2)
- EQ("ham, spam, eggs and spam", "spam, spam, eggs and spam",
- "replace", "spam", "ham", 1)
- EQ("spam, spam, eggs and spam", "spam, spam, eggs and spam",
- "replace", "spam", "ham", 0)
-
- EQ("bobob", "bobobob", "replace", "bobob", "bob")
- EQ("bobobXbobob", "bobobobXbobobob", "replace", "bobob", "bob")
- EQ("BOBOBOB", "BOBOBOB", "replace", "bob", "bobby")
-
- # XXX Commented out. Is there any reason to support buffer objects
- # as arguments for str.replace()? GvR
-## ba = bytearray('a')
-## bb = bytearray('b')
-## EQ("bbc", "abc", "replace", ba, bb)
-## EQ("aac", "abc", "replace", bb, ba)
-
- #
- self.checkequal('one@two!three!', 'one!two!three!', 'replace', '!', '@', 1)
- self.checkequal('onetwothree', 'one!two!three!', 'replace', '!', '')
- self.checkequal('one@two@three!', 'one!two!three!', 'replace', '!', '@', 2)
- self.checkequal('one@two@three@', 'one!two!three!', 'replace', '!', '@', 3)
- self.checkequal('one@two@three@', 'one!two!three!', 'replace', '!', '@', 4)
- self.checkequal('one!two!three!', 'one!two!three!', 'replace', '!', '@', 0)
- self.checkequal('one@two@three@', 'one!two!three!', 'replace', '!', '@')
- self.checkequal('one!two!three!', 'one!two!three!', 'replace', 'x', '@')
- self.checkequal('one!two!three!', 'one!two!three!', 'replace', 'x', '@', 2)
- self.checkequal('-a-b-c-', 'abc', 'replace', '', '-')
- self.checkequal('-a-b-c', 'abc', 'replace', '', '-', 3)
- self.checkequal('abc', 'abc', 'replace', '', '-', 0)
- self.checkequal('', '', 'replace', '', '')
- self.checkequal('abc', 'abc', 'replace', 'ab', '--', 0)
- self.checkequal('abc', 'abc', 'replace', 'xy', '--')
- # Next three for SF bug 422088: [OSF1 alpha] string.replace(); died with
- # MemoryError due to empty result (platform malloc issue when requesting
- # 0 bytes).
- self.checkequal('', '123', 'replace', '123', '')
- self.checkequal('', '123123', 'replace', '123', '')
- self.checkequal('x', '123x123', 'replace', '123', '')
-
- self.checkraises(TypeError, 'hello', 'replace')
- self.checkraises(TypeError, 'hello', 'replace', 42)
- self.checkraises(TypeError, 'hello', 'replace', 42, 'h')
- self.checkraises(TypeError, 'hello', 'replace', 'h', 42)
-
- def test_replace_overflow(self):
- # Check for overflow checking on 32 bit machines
- if sys.maxsize != 2147483647 or struct.calcsize("P") > 4:
- return
- A2_16 = "A" * (2**16)
- self.checkraises(OverflowError, A2_16, "replace", "", A2_16)
- self.checkraises(OverflowError, A2_16, "replace", "A", A2_16)
- self.checkraises(OverflowError, A2_16, "replace", "AA", A2_16+A2_16)
-
-
-
-class CommonTest(BaseTest):
- # This testcase contains test that can be used in all
- # stringlike classes. Currently this is str, unicode
- # UserString and the string module.
-
- def test_hash(self):
- # SF bug 1054139: += optimization was not invalidating cached hash value
- a = self.type2test('DNSSEC')
- b = self.type2test('')
- for c in a:
- b += c
- hash(b)
- self.assertEqual(hash(a), hash(b))
-
- def test_capitalize(self):
- self.checkequal(' hello ', ' hello ', 'capitalize')
- self.checkequal('Hello ', 'Hello ','capitalize')
- self.checkequal('Hello ', 'hello ','capitalize')
- self.checkequal('Aaaa', 'aaaa', 'capitalize')
- self.checkequal('Aaaa', 'AaAa', 'capitalize')
-
- # check that titlecased chars are lowered correctly
- # \u1ffc is the titlecased char
- self.checkequal('\u03a9\u0399\u1ff3\u1ff3\u1ff3',
- '\u1ff3\u1ff3\u1ffc\u1ffc', 'capitalize')
- # check with cased non-letter chars
- self.checkequal('\u24c5\u24e8\u24e3\u24d7\u24de\u24dd',
- '\u24c5\u24ce\u24c9\u24bd\u24c4\u24c3', 'capitalize')
- self.checkequal('\u24c5\u24e8\u24e3\u24d7\u24de\u24dd',
- '\u24df\u24e8\u24e3\u24d7\u24de\u24dd', 'capitalize')
- self.checkequal('\u2160\u2171\u2172',
- '\u2160\u2161\u2162', 'capitalize')
- self.checkequal('\u2160\u2171\u2172',
- '\u2170\u2171\u2172', 'capitalize')
- # check with Ll chars with no upper - nothing changes here
- self.checkequal('\u019b\u1d00\u1d86\u0221\u1fb7',
- '\u019b\u1d00\u1d86\u0221\u1fb7', 'capitalize')
-
- self.checkraises(TypeError, 'hello', 'capitalize', 42)
-
- def test_lower(self):
- self.checkequal('hello', 'HeLLo', 'lower')
- self.checkequal('hello', 'hello', 'lower')
- self.checkraises(TypeError, 'hello', 'lower', 42)
-
- def test_upper(self):
- self.checkequal('HELLO', 'HeLLo', 'upper')
- self.checkequal('HELLO', 'HELLO', 'upper')
- self.checkraises(TypeError, 'hello', 'upper', 42)
-
- def test_expandtabs(self):
- self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs')
- self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs', 8)
- self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs', 4)
- self.checkequal('abc\r\nab def\ng hi', 'abc\r\nab\tdef\ng\thi', 'expandtabs', 4)
- self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs')
- self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs', 8)
- self.checkequal('abc\r\nab\r\ndef\ng\r\nhi', 'abc\r\nab\r\ndef\ng\r\nhi', 'expandtabs', 4)
-
- self.checkraises(TypeError, 'hello', 'expandtabs', 42, 42)
-
- def test_additional_split(self):
- self.checkequal(['this', 'is', 'the', 'split', 'function'],
- 'this is the split function', 'split')
-
- # by whitespace
- self.checkequal(['a', 'b', 'c', 'd'], 'a b c d ', 'split')
- self.checkequal(['a', 'b c d'], 'a b c d', 'split', None, 1)
- self.checkequal(['a', 'b', 'c d'], 'a b c d', 'split', None, 2)
- self.checkequal(['a', 'b', 'c', 'd'], 'a b c d', 'split', None, 3)
- self.checkequal(['a', 'b', 'c', 'd'], 'a b c d', 'split', None, 4)
- self.checkequal(['a', 'b', 'c', 'd'], 'a b c d', 'split', None,
- sys.maxsize-1)
- self.checkequal(['a b c d'], 'a b c d', 'split', None, 0)
- self.checkequal(['a b c d'], ' a b c d', 'split', None, 0)
- self.checkequal(['a', 'b', 'c d'], 'a b c d', 'split', None, 2)
-
- self.checkequal([], ' ', 'split')
- self.checkequal(['a'], ' a ', 'split')
- self.checkequal(['a', 'b'], ' a b ', 'split')
- self.checkequal(['a', 'b '], ' a b ', 'split', None, 1)
- self.checkequal(['a', 'b c '], ' a b c ', 'split', None, 1)
- self.checkequal(['a', 'b', 'c '], ' a b c ', 'split', None, 2)
- self.checkequal(['a', 'b'], '\n\ta \t\r b \v ', 'split')
- aaa = ' a '*20
- self.checkequal(['a']*20, aaa, 'split')
- self.checkequal(['a'] + [aaa[4:]], aaa, 'split', None, 1)
- self.checkequal(['a']*19 + ['a '], aaa, 'split', None, 19)
-
- # mixed use of str and unicode
- self.checkequal(['a', 'b', 'c d'], 'a b c d', 'split', ' ', 2)
-
- def test_additional_rsplit(self):
- self.checkequal(['this', 'is', 'the', 'rsplit', 'function'],
- 'this is the rsplit function', 'rsplit')
-
- # by whitespace
- self.checkequal(['a', 'b', 'c', 'd'], 'a b c d ', 'rsplit')
- self.checkequal(['a b c', 'd'], 'a b c d', 'rsplit', None, 1)
- self.checkequal(['a b', 'c', 'd'], 'a b c d', 'rsplit', None, 2)
- self.checkequal(['a', 'b', 'c', 'd'], 'a b c d', 'rsplit', None, 3)
- self.checkequal(['a', 'b', 'c', 'd'], 'a b c d', 'rsplit', None, 4)
- self.checkequal(['a', 'b', 'c', 'd'], 'a b c d', 'rsplit', None,
- sys.maxsize-20)
- self.checkequal(['a b c d'], 'a b c d', 'rsplit', None, 0)
- self.checkequal(['a b c d'], 'a b c d ', 'rsplit', None, 0)
- self.checkequal(['a b', 'c', 'd'], 'a b c d', 'rsplit', None, 2)
-
- self.checkequal([], ' ', 'rsplit')
- self.checkequal(['a'], ' a ', 'rsplit')
- self.checkequal(['a', 'b'], ' a b ', 'rsplit')
- self.checkequal([' a', 'b'], ' a b ', 'rsplit', None, 1)
- self.checkequal([' a b','c'], ' a b c ', 'rsplit',
- None, 1)
- self.checkequal([' a', 'b', 'c'], ' a b c ', 'rsplit',
- None, 2)
- self.checkequal(['a', 'b'], '\n\ta \t\r b \v ', 'rsplit', None, 88)
- aaa = ' a '*20
- self.checkequal(['a']*20, aaa, 'rsplit')
- self.checkequal([aaa[:-4]] + ['a'], aaa, 'rsplit', None, 1)
- self.checkequal([' a a'] + ['a']*18, aaa, 'rsplit', None, 18)
-
- # mixed use of str and unicode
- self.checkequal(['a b', 'c', 'd'], 'a b c d', 'rsplit', ' ', 2)
-
- def test_strip(self):
- self.checkequal('hello', ' hello ', 'strip')
- self.checkequal('hello ', ' hello ', 'lstrip')
- self.checkequal(' hello', ' hello ', 'rstrip')
- self.checkequal('hello', 'hello', 'strip')
-
- # strip/lstrip/rstrip with None arg
- self.checkequal('hello', ' hello ', 'strip', None)
- self.checkequal('hello ', ' hello ', 'lstrip', None)
- self.checkequal(' hello', ' hello ', 'rstrip', None)
- self.checkequal('hello', 'hello', 'strip', None)
-
- # strip/lstrip/rstrip with str arg
- self.checkequal('hello', 'xyzzyhelloxyzzy', 'strip', 'xyz')
- self.checkequal('helloxyzzy', 'xyzzyhelloxyzzy', 'lstrip', 'xyz')
- self.checkequal('xyzzyhello', 'xyzzyhelloxyzzy', 'rstrip', 'xyz')
- self.checkequal('hello', 'hello', 'strip', 'xyz')
-
- self.checkraises(TypeError, 'hello', 'strip', 42, 42)
- self.checkraises(TypeError, 'hello', 'lstrip', 42, 42)
- self.checkraises(TypeError, 'hello', 'rstrip', 42, 42)
-
- def test_ljust(self):
- self.checkequal('abc ', 'abc', 'ljust', 10)
- self.checkequal('abc ', 'abc', 'ljust', 6)
- self.checkequal('abc', 'abc', 'ljust', 3)
- self.checkequal('abc', 'abc', 'ljust', 2)
- self.checkequal('abc*******', 'abc', 'ljust', 10, '*')
- self.checkraises(TypeError, 'abc', 'ljust')
-
- def test_rjust(self):
- self.checkequal(' abc', 'abc', 'rjust', 10)
- self.checkequal(' abc', 'abc', 'rjust', 6)
- self.checkequal('abc', 'abc', 'rjust', 3)
- self.checkequal('abc', 'abc', 'rjust', 2)
- self.checkequal('*******abc', 'abc', 'rjust', 10, '*')
- self.checkraises(TypeError, 'abc', 'rjust')
-
- def test_center(self):
- self.checkequal(' abc ', 'abc', 'center', 10)
- self.checkequal(' abc ', 'abc', 'center', 6)
- self.checkequal('abc', 'abc', 'center', 3)
- self.checkequal('abc', 'abc', 'center', 2)
- self.checkequal('***abc****', 'abc', 'center', 10, '*')
- self.checkraises(TypeError, 'abc', 'center')
-
- def test_swapcase(self):
- self.checkequal('hEllO CoMPuTErS', 'HeLLo cOmpUteRs', 'swapcase')
-
- self.checkraises(TypeError, 'hello', 'swapcase', 42)
-
- def test_zfill(self):
- self.checkequal('123', '123', 'zfill', 2)
- self.checkequal('123', '123', 'zfill', 3)
- self.checkequal('0123', '123', 'zfill', 4)
- self.checkequal('+123', '+123', 'zfill', 3)
- self.checkequal('+123', '+123', 'zfill', 4)
- self.checkequal('+0123', '+123', 'zfill', 5)
- self.checkequal('-123', '-123', 'zfill', 3)
- self.checkequal('-123', '-123', 'zfill', 4)
- self.checkequal('-0123', '-123', 'zfill', 5)
- self.checkequal('000', '', 'zfill', 3)
- self.checkequal('34', '34', 'zfill', 1)
- self.checkequal('0034', '34', 'zfill', 4)
-
- self.checkraises(TypeError, '123', 'zfill')
-
-class MixinStrUnicodeUserStringTest(object):
- # additional tests that only work for
- # stringlike objects, i.e. str, unicode, UserString
- # (but not the string module)
-
- def test_islower(self):
- self.checkequal(False, '', 'islower')
- self.checkequal(True, 'a', 'islower')
- self.checkequal(False, 'A', 'islower')
- self.checkequal(False, '\n', 'islower')
- self.checkequal(True, 'abc', 'islower')
- self.checkequal(False, 'aBc', 'islower')
- self.checkequal(True, 'abc\n', 'islower')
- self.checkraises(TypeError, 'abc', 'islower', 42)
-
- def test_isupper(self):
- self.checkequal(False, '', 'isupper')
- self.checkequal(False, 'a', 'isupper')
- self.checkequal(True, 'A', 'isupper')
- self.checkequal(False, '\n', 'isupper')
- self.checkequal(True, 'ABC', 'isupper')
- self.checkequal(False, 'AbC', 'isupper')
- self.checkequal(True, 'ABC\n', 'isupper')
- self.checkraises(TypeError, 'abc', 'isupper', 42)
-
- def test_istitle(self):
- self.checkequal(False, '', 'istitle')
- self.checkequal(False, 'a', 'istitle')
- self.checkequal(True, 'A', 'istitle')
- self.checkequal(False, '\n', 'istitle')
- self.checkequal(True, 'A Titlecased Line', 'istitle')
- self.checkequal(True, 'A\nTitlecased Line', 'istitle')
- self.checkequal(True, 'A Titlecased, Line', 'istitle')
- self.checkequal(False, 'Not a capitalized String', 'istitle')
- self.checkequal(False, 'Not\ta Titlecase String', 'istitle')
- self.checkequal(False, 'Not--a Titlecase String', 'istitle')
- self.checkequal(False, 'NOT', 'istitle')
- self.checkraises(TypeError, 'abc', 'istitle', 42)
-
- def test_isspace(self):
- self.checkequal(False, '', 'isspace')
- self.checkequal(False, 'a', 'isspace')
- self.checkequal(True, ' ', 'isspace')
- self.checkequal(True, '\t', 'isspace')
- self.checkequal(True, '\r', 'isspace')
- self.checkequal(True, '\n', 'isspace')
- self.checkequal(True, ' \t\r\n', 'isspace')
- self.checkequal(False, ' \t\r\na', 'isspace')
- self.checkraises(TypeError, 'abc', 'isspace', 42)
-
- def test_isalpha(self):
- self.checkequal(False, '', 'isalpha')
- self.checkequal(True, 'a', 'isalpha')
- self.checkequal(True, 'A', 'isalpha')
- self.checkequal(False, '\n', 'isalpha')
- self.checkequal(True, 'abc', 'isalpha')
- self.checkequal(False, 'aBc123', 'isalpha')
- self.checkequal(False, 'abc\n', 'isalpha')
- self.checkraises(TypeError, 'abc', 'isalpha', 42)
-
- def test_isalnum(self):
- self.checkequal(False, '', 'isalnum')
- self.checkequal(True, 'a', 'isalnum')
- self.checkequal(True, 'A', 'isalnum')
- self.checkequal(False, '\n', 'isalnum')
- self.checkequal(True, '123abc456', 'isalnum')
- self.checkequal(True, 'a1b3c', 'isalnum')
- self.checkequal(False, 'aBc000 ', 'isalnum')
- self.checkequal(False, 'abc\n', 'isalnum')
- self.checkraises(TypeError, 'abc', 'isalnum', 42)
-
- def test_isdigit(self):
- self.checkequal(False, '', 'isdigit')
- self.checkequal(False, 'a', 'isdigit')
- self.checkequal(True, '0', 'isdigit')
- self.checkequal(True, '0123456789', 'isdigit')
- self.checkequal(False, '0123456789a', 'isdigit')
-
- self.checkraises(TypeError, 'abc', 'isdigit', 42)
-
- def test_title(self):
- self.checkequal(' Hello ', ' hello ', 'title')
- self.checkequal('Hello ', 'hello ', 'title')
- self.checkequal('Hello ', 'Hello ', 'title')
- self.checkequal('Format This As Title String', "fOrMaT thIs aS titLe String", 'title')
- self.checkequal('Format,This-As*Title;String', "fOrMaT,thIs-aS*titLe;String", 'title', )
- self.checkequal('Getint', "getInt", 'title')
- self.checkraises(TypeError, 'hello', 'title', 42)
-
- def test_splitlines(self):
- self.checkequal(['abc', 'def', '', 'ghi'], "abc\ndef\n\rghi", 'splitlines')
- self.checkequal(['abc', 'def', '', 'ghi'], "abc\ndef\n\r\nghi", 'splitlines')
- self.checkequal(['abc', 'def', 'ghi'], "abc\ndef\r\nghi", 'splitlines')
- self.checkequal(['abc', 'def', 'ghi'], "abc\ndef\r\nghi\n", 'splitlines')
- self.checkequal(['abc', 'def', 'ghi', ''], "abc\ndef\r\nghi\n\r", 'splitlines')
- self.checkequal(['', 'abc', 'def', 'ghi', ''], "\nabc\ndef\r\nghi\n\r", 'splitlines')
- self.checkequal(['', 'abc', 'def', 'ghi', ''],
- "\nabc\ndef\r\nghi\n\r", 'splitlines', False)
- self.checkequal(['\n', 'abc\n', 'def\r\n', 'ghi\n', '\r'],
- "\nabc\ndef\r\nghi\n\r", 'splitlines', True)
- self.checkequal(['', 'abc', 'def', 'ghi', ''], "\nabc\ndef\r\nghi\n\r",
- 'splitlines', keepends=False)
- self.checkequal(['\n', 'abc\n', 'def\r\n', 'ghi\n', '\r'],
- "\nabc\ndef\r\nghi\n\r", 'splitlines', keepends=True)
-
- self.checkraises(TypeError, 'abc', 'splitlines', 42, 42)
-
- def test_startswith(self):
- self.checkequal(True, 'hello', 'startswith', 'he')
- self.checkequal(True, 'hello', 'startswith', 'hello')
- self.checkequal(False, 'hello', 'startswith', 'hello world')
- self.checkequal(True, 'hello', 'startswith', '')
- self.checkequal(False, 'hello', 'startswith', 'ello')
- self.checkequal(True, 'hello', 'startswith', 'ello', 1)
- self.checkequal(True, 'hello', 'startswith', 'o', 4)
- self.checkequal(False, 'hello', 'startswith', 'o', 5)
- self.checkequal(True, 'hello', 'startswith', '', 5)
- self.checkequal(False, 'hello', 'startswith', 'lo', 6)
- self.checkequal(True, 'helloworld', 'startswith', 'lowo', 3)
- self.checkequal(True, 'helloworld', 'startswith', 'lowo', 3, 7)
- self.checkequal(False, 'helloworld', 'startswith', 'lowo', 3, 6)
-
- # test negative indices
- self.checkequal(True, 'hello', 'startswith', 'he', 0, -1)
- self.checkequal(True, 'hello', 'startswith', 'he', -53, -1)
- self.checkequal(False, 'hello', 'startswith', 'hello', 0, -1)
- self.checkequal(False, 'hello', 'startswith', 'hello world', -1, -10)
- self.checkequal(False, 'hello', 'startswith', 'ello', -5)
- self.checkequal(True, 'hello', 'startswith', 'ello', -4)
- self.checkequal(False, 'hello', 'startswith', 'o', -2)
- self.checkequal(True, 'hello', 'startswith', 'o', -1)
- self.checkequal(True, 'hello', 'startswith', '', -3, -3)
- self.checkequal(False, 'hello', 'startswith', 'lo', -9)
-
- self.checkraises(TypeError, 'hello', 'startswith')
- self.checkraises(TypeError, 'hello', 'startswith', 42)
-
- # test tuple arguments
- self.checkequal(True, 'hello', 'startswith', ('he', 'ha'))
- self.checkequal(False, 'hello', 'startswith', ('lo', 'llo'))
- self.checkequal(True, 'hello', 'startswith', ('hellox', 'hello'))
- self.checkequal(False, 'hello', 'startswith', ())
- self.checkequal(True, 'helloworld', 'startswith', ('hellowo',
- 'rld', 'lowo'), 3)
- self.checkequal(False, 'helloworld', 'startswith', ('hellowo', 'ello',
- 'rld'), 3)
- self.checkequal(True, 'hello', 'startswith', ('lo', 'he'), 0, -1)
- self.checkequal(False, 'hello', 'startswith', ('he', 'hel'), 0, 1)
- self.checkequal(True, 'hello', 'startswith', ('he', 'hel'), 0, 2)
-
- self.checkraises(TypeError, 'hello', 'startswith', (42,))
-
- def test_endswith(self):
- self.checkequal(True, 'hello', 'endswith', 'lo')
- self.checkequal(False, 'hello', 'endswith', 'he')
- self.checkequal(True, 'hello', 'endswith', '')
- self.checkequal(False, 'hello', 'endswith', 'hello world')
- self.checkequal(False, 'helloworld', 'endswith', 'worl')
- self.checkequal(True, 'helloworld', 'endswith', 'worl', 3, 9)
- self.checkequal(True, 'helloworld', 'endswith', 'world', 3, 12)
- self.checkequal(True, 'helloworld', 'endswith', 'lowo', 1, 7)
- self.checkequal(True, 'helloworld', 'endswith', 'lowo', 2, 7)
- self.checkequal(True, 'helloworld', 'endswith', 'lowo', 3, 7)
- self.checkequal(False, 'helloworld', 'endswith', 'lowo', 4, 7)
- self.checkequal(False, 'helloworld', 'endswith', 'lowo', 3, 8)
- self.checkequal(False, 'ab', 'endswith', 'ab', 0, 1)
- self.checkequal(False, 'ab', 'endswith', 'ab', 0, 0)
-
- # test negative indices
- self.checkequal(True, 'hello', 'endswith', 'lo', -2)
- self.checkequal(False, 'hello', 'endswith', 'he', -2)
- self.checkequal(True, 'hello', 'endswith', '', -3, -3)
- self.checkequal(False, 'hello', 'endswith', 'hello world', -10, -2)
- self.checkequal(False, 'helloworld', 'endswith', 'worl', -6)
- self.checkequal(True, 'helloworld', 'endswith', 'worl', -5, -1)
- self.checkequal(True, 'helloworld', 'endswith', 'worl', -5, 9)
- self.checkequal(True, 'helloworld', 'endswith', 'world', -7, 12)
- self.checkequal(True, 'helloworld', 'endswith', 'lowo', -99, -3)
- self.checkequal(True, 'helloworld', 'endswith', 'lowo', -8, -3)
- self.checkequal(True, 'helloworld', 'endswith', 'lowo', -7, -3)
- self.checkequal(False, 'helloworld', 'endswith', 'lowo', 3, -4)
- self.checkequal(False, 'helloworld', 'endswith', 'lowo', -8, -2)
-
- self.checkraises(TypeError, 'hello', 'endswith')
- self.checkraises(TypeError, 'hello', 'endswith', 42)
-
- # test tuple arguments
- self.checkequal(False, 'hello', 'endswith', ('he', 'ha'))
- self.checkequal(True, 'hello', 'endswith', ('lo', 'llo'))
- self.checkequal(True, 'hello', 'endswith', ('hellox', 'hello'))
- self.checkequal(False, 'hello', 'endswith', ())
- self.checkequal(True, 'helloworld', 'endswith', ('hellowo',
- 'rld', 'lowo'), 3)
- self.checkequal(False, 'helloworld', 'endswith', ('hellowo', 'ello',
- 'rld'), 3, -1)
- self.checkequal(True, 'hello', 'endswith', ('hell', 'ell'), 0, -1)
- self.checkequal(False, 'hello', 'endswith', ('he', 'hel'), 0, 1)
- self.checkequal(True, 'hello', 'endswith', ('he', 'hell'), 0, 4)
-
- self.checkraises(TypeError, 'hello', 'endswith', (42,))
-
- def test___contains__(self):
- self.checkequal(True, '', '__contains__', '')
- self.checkequal(True, 'abc', '__contains__', '')
- self.checkequal(False, 'abc', '__contains__', '\0')
- self.checkequal(True, '\0abc', '__contains__', '\0')
- self.checkequal(True, 'abc\0', '__contains__', '\0')
- self.checkequal(True, '\0abc', '__contains__', 'a')
- self.checkequal(True, 'asdf', '__contains__', 'asdf')
- self.checkequal(False, 'asd', '__contains__', 'asdf')
- self.checkequal(False, '', '__contains__', 'asdf')
-
- def test_subscript(self):
- self.checkequal('a', 'abc', '__getitem__', 0)
- self.checkequal('c', 'abc', '__getitem__', -1)
- self.checkequal('a', 'abc', '__getitem__', 0)
- self.checkequal('abc', 'abc', '__getitem__', slice(0, 3))
- self.checkequal('abc', 'abc', '__getitem__', slice(0, 1000))
- self.checkequal('a', 'abc', '__getitem__', slice(0, 1))
- self.checkequal('', 'abc', '__getitem__', slice(0, 0))
-
- self.checkraises(TypeError, 'abc', '__getitem__', 'def')
-
- def test_slice(self):
- self.checkequal('abc', 'abc', '__getitem__', slice(0, 1000))
- self.checkequal('abc', 'abc', '__getitem__', slice(0, 3))
- self.checkequal('ab', 'abc', '__getitem__', slice(0, 2))
- self.checkequal('bc', 'abc', '__getitem__', slice(1, 3))
- self.checkequal('b', 'abc', '__getitem__', slice(1, 2))
- self.checkequal('', 'abc', '__getitem__', slice(2, 2))
- self.checkequal('', 'abc', '__getitem__', slice(1000, 1000))
- self.checkequal('', 'abc', '__getitem__', slice(2000, 1000))
- self.checkequal('', 'abc', '__getitem__', slice(2, 1))
-
- self.checkraises(TypeError, 'abc', '__getitem__', 'def')
-
- def test_extended_getslice(self):
- # Test extended slicing by comparing with list slicing.
- s = string.ascii_letters + string.digits
- indices = (0, None, 1, 3, 41, -1, -2, -37)
- for start in indices:
- for stop in indices:
- # Skip step 0 (invalid)
- for step in indices[1:]:
- L = list(s)[start:stop:step]
- self.checkequal("".join(L), s, '__getitem__',
- slice(start, stop, step))
-
- def test_mul(self):
- self.checkequal('', 'abc', '__mul__', -1)
- self.checkequal('', 'abc', '__mul__', 0)
- self.checkequal('abc', 'abc', '__mul__', 1)
- self.checkequal('abcabcabc', 'abc', '__mul__', 3)
- self.checkraises(TypeError, 'abc', '__mul__')
- self.checkraises(TypeError, 'abc', '__mul__', '')
- # XXX: on a 64-bit system, this doesn't raise an overflow error,
- # but either raises a MemoryError, or succeeds (if you have 54TiB)
- #self.checkraises(OverflowError, 10000*'abc', '__mul__', 2000000000)
-
- def test_join(self):
- # join now works with any sequence type
- # moved here, because the argument order is
- # different in string.join (see the test in
- # test.test_string.StringTest.test_join)
- self.checkequal('a b c d', ' ', 'join', ['a', 'b', 'c', 'd'])
- self.checkequal('abcd', '', 'join', ('a', 'b', 'c', 'd'))
- self.checkequal('bd', '', 'join', ('', 'b', '', 'd'))
- self.checkequal('ac', '', 'join', ('a', '', 'c', ''))
- self.checkequal('w x y z', ' ', 'join', Sequence())
- self.checkequal('abc', 'a', 'join', ('abc',))
- self.checkequal('z', 'a', 'join', UserList(['z']))
- self.checkequal('a.b.c', '.', 'join', ['a', 'b', 'c'])
- self.assertRaises(TypeError, '.'.join, ['a', 'b', 3])
- for i in [5, 25, 125]:
- self.checkequal(((('a' * i) + '-') * i)[:-1], '-', 'join',
- ['a' * i] * i)
- self.checkequal(((('a' * i) + '-') * i)[:-1], '-', 'join',
- ('a' * i,) * i)
-
- #self.checkequal(str(BadSeq1()), ' ', 'join', BadSeq1())
- self.checkequal('a b c', ' ', 'join', BadSeq2())
-
- self.checkraises(TypeError, ' ', 'join')
- self.checkraises(TypeError, ' ', 'join', 7)
- self.checkraises(TypeError, ' ', 'join', [1, 2, bytes()])
- try:
- def f():
- yield 4 + ""
- self.fixtype(' ').join(f())
- except TypeError as e:
- if '+' not in str(e):
- self.fail('join() ate exception message')
- else:
- self.fail('exception not raised')
-
- def test_formatting(self):
- self.checkequal('+hello+', '+%s+', '__mod__', 'hello')
- self.checkequal('+10+', '+%d+', '__mod__', 10)
- self.checkequal('a', "%c", '__mod__', "a")
- self.checkequal('a', "%c", '__mod__', "a")
- self.checkequal('"', "%c", '__mod__', 34)
- self.checkequal('$', "%c", '__mod__', 36)
- self.checkequal('10', "%d", '__mod__', 10)
- self.checkequal('\x7f', "%c", '__mod__', 0x7f)
-
- for ordinal in (-100, 0x200000):
- # unicode raises ValueError, str raises OverflowError
- self.checkraises((ValueError, OverflowError), '%c', '__mod__', ordinal)
-
- longvalue = sys.maxsize + 10
- slongvalue = str(longvalue)
- self.checkequal(' 42', '%3ld', '__mod__', 42)
- self.checkequal('42', '%d', '__mod__', 42.0)
- self.checkequal(slongvalue, '%d', '__mod__', longvalue)
- self.checkcall('%d', '__mod__', float(longvalue))
- self.checkequal('0042.00', '%07.2f', '__mod__', 42)
- self.checkequal('0042.00', '%07.2F', '__mod__', 42)
-
- self.checkraises(TypeError, 'abc', '__mod__')
- self.checkraises(TypeError, '%(foo)s', '__mod__', 42)
- self.checkraises(TypeError, '%s%s', '__mod__', (42,))
- self.checkraises(TypeError, '%c', '__mod__', (None,))
- self.checkraises(ValueError, '%(foo', '__mod__', {})
- self.checkraises(TypeError, '%(foo)s %(bar)s', '__mod__', ('foo', 42))
- self.checkraises(TypeError, '%d', '__mod__', "42") # not numeric
- self.checkraises(TypeError, '%d', '__mod__', (42+0j)) # no int conversion provided
-
- # argument names with properly nested brackets are supported
- self.checkequal('bar', '%((foo))s', '__mod__', {'(foo)': 'bar'})
-
- # 100 is a magic number in PyUnicode_Format, this forces a resize
- self.checkequal(103*'a'+'x', '%sx', '__mod__', 103*'a')
-
- self.checkraises(TypeError, '%*s', '__mod__', ('foo', 'bar'))
- self.checkraises(TypeError, '%10.*f', '__mod__', ('foo', 42.))
- self.checkraises(ValueError, '%10', '__mod__', (42,))
-
- # Outrageously large width or precision should raise ValueError.
- self.checkraises(ValueError, '%%%df' % (2**64), '__mod__', (3.2))
- self.checkraises(ValueError, '%%.%df' % (2**64), '__mod__', (3.2))
-
- self.checkraises(OverflowError, '%*s', '__mod__',
- (_testcapi.PY_SSIZE_T_MAX + 1, ''))
- self.checkraises(OverflowError, '%.*f', '__mod__',
- (_testcapi.INT_MAX + 1, 1. / 7))
- # Issue 15989
- self.checkraises(OverflowError, '%*s', '__mod__',
- (1 << (_testcapi.PY_SSIZE_T_MAX.bit_length() + 1), ''))
- self.checkraises(OverflowError, '%.*f', '__mod__',
- (_testcapi.UINT_MAX + 1, 1. / 7))
-
- class X(object): pass
- self.checkraises(TypeError, 'abc', '__mod__', X())
-
- def test_floatformatting(self):
- # float formatting
- for prec in range(100):
- format = '%%.%if' % prec
- value = 0.01
- for x in range(60):
- value = value * 3.14159265359 / 3.0 * 10.0
- self.checkcall(format, "__mod__", value)
-
- def test_inplace_rewrites(self):
- # Check that strings don't copy and modify cached single-character strings
- self.checkequal('a', 'A', 'lower')
- self.checkequal(True, 'A', 'isupper')
- self.checkequal('A', 'a', 'upper')
- self.checkequal(True, 'a', 'islower')
-
- self.checkequal('a', 'A', 'replace', 'A', 'a')
- self.checkequal(True, 'A', 'isupper')
-
- self.checkequal('A', 'a', 'capitalize')
- self.checkequal(True, 'a', 'islower')
-
- self.checkequal('A', 'a', 'swapcase')
- self.checkequal(True, 'a', 'islower')
-
- self.checkequal('A', 'a', 'title')
- self.checkequal(True, 'a', 'islower')
-
- def test_partition(self):
-
- self.checkequal(('this is the par', 'ti', 'tion method'),
- 'this is the partition method', 'partition', 'ti')
-
- # from raymond's original specification
- S = 'http://www.python.org'
- self.checkequal(('http', '://', 'www.python.org'), S, 'partition', '://')
- self.checkequal(('http://www.python.org', '', ''), S, 'partition', '?')
- self.checkequal(('', 'http://', 'www.python.org'), S, 'partition', 'http://')
- self.checkequal(('http://www.python.', 'org', ''), S, 'partition', 'org')
-
- self.checkraises(ValueError, S, 'partition', '')
- self.checkraises(TypeError, S, 'partition', None)
-
- def test_rpartition(self):
-
- self.checkequal(('this is the rparti', 'ti', 'on method'),
- 'this is the rpartition method', 'rpartition', 'ti')
-
- # from raymond's original specification
- S = 'http://www.python.org'
- self.checkequal(('http', '://', 'www.python.org'), S, 'rpartition', '://')
- self.checkequal(('', '', 'http://www.python.org'), S, 'rpartition', '?')
- self.checkequal(('', 'http://', 'www.python.org'), S, 'rpartition', 'http://')
- self.checkequal(('http://www.python.', 'org', ''), S, 'rpartition', 'org')
-
- self.checkraises(ValueError, S, 'rpartition', '')
- self.checkraises(TypeError, S, 'rpartition', None)
-
- def test_none_arguments(self):
- # issue 11828
- s = 'hello'
- self.checkequal(2, s, 'find', 'l', None)
- self.checkequal(3, s, 'find', 'l', -2, None)
- self.checkequal(2, s, 'find', 'l', None, -2)
- self.checkequal(0, s, 'find', 'h', None, None)
-
- self.checkequal(3, s, 'rfind', 'l', None)
- self.checkequal(3, s, 'rfind', 'l', -2, None)
- self.checkequal(2, s, 'rfind', 'l', None, -2)
- self.checkequal(0, s, 'rfind', 'h', None, None)
-
- self.checkequal(2, s, 'index', 'l', None)
- self.checkequal(3, s, 'index', 'l', -2, None)
- self.checkequal(2, s, 'index', 'l', None, -2)
- self.checkequal(0, s, 'index', 'h', None, None)
-
- self.checkequal(3, s, 'rindex', 'l', None)
- self.checkequal(3, s, 'rindex', 'l', -2, None)
- self.checkequal(2, s, 'rindex', 'l', None, -2)
- self.checkequal(0, s, 'rindex', 'h', None, None)
-
- self.checkequal(2, s, 'count', 'l', None)
- self.checkequal(1, s, 'count', 'l', -2, None)
- self.checkequal(1, s, 'count', 'l', None, -2)
- self.checkequal(0, s, 'count', 'x', None, None)
-
- self.checkequal(True, s, 'endswith', 'o', None)
- self.checkequal(True, s, 'endswith', 'lo', -2, None)
- self.checkequal(True, s, 'endswith', 'l', None, -2)
- self.checkequal(False, s, 'endswith', 'x', None, None)
-
- self.checkequal(True, s, 'startswith', 'h', None)
- self.checkequal(True, s, 'startswith', 'l', -2, None)
- self.checkequal(True, s, 'startswith', 'h', None, -2)
- self.checkequal(False, s, 'startswith', 'x', None, None)
-
- def test_find_etc_raise_correct_error_messages(self):
- # issue 11828
- s = 'hello'
- x = 'x'
- self.assertRaisesRegex(TypeError, r'^find\(', s.find,
- x, None, None, None)
- self.assertRaisesRegex(TypeError, r'^rfind\(', s.rfind,
- x, None, None, None)
- self.assertRaisesRegex(TypeError, r'^index\(', s.index,
- x, None, None, None)
- self.assertRaisesRegex(TypeError, r'^rindex\(', s.rindex,
- x, None, None, None)
- self.assertRaisesRegex(TypeError, r'^count\(', s.count,
- x, None, None, None)
- self.assertRaisesRegex(TypeError, r'^startswith\(', s.startswith,
- x, None, None, None)
- self.assertRaisesRegex(TypeError, r'^endswith\(', s.endswith,
- x, None, None, None)
-
- # issue #15534
- self.checkequal(10, "...\u043c......<", "find", "<")
-
-
-class MixinStrUnicodeTest(object):
- # Additional tests that only work with str and unicode.
-
- def test_bug1001011(self):
- # Make sure join returns a NEW object for single item sequences
- # involving a subclass.
- # Make sure that it is of the appropriate type.
- # Check the optimisation still occurs for standard objects.
- t = self.type2test
- class subclass(t):
- pass
- s1 = subclass("abcd")
- s2 = t().join([s1])
- self.assertIsNot(s1, s2)
- self.assertIs(type(s2), t)
-
- s1 = t("abcd")
- s2 = t().join([s1])
- self.assertIs(s1, s2)
-
- # Should also test mixed-type join.
- if t is str:
- s1 = subclass("abcd")
- s2 = "".join([s1])
- self.assertIsNot(s1, s2)
- self.assertIs(type(s2), t)
-
- s1 = t("abcd")
- s2 = "".join([s1])
- self.assertIs(s1, s2)
-
-## elif t is str8:
-## s1 = subclass("abcd")
-## s2 = "".join([s1])
-## self.assertIsNot(s1, s2)
-## self.assertIs(type(s2), str) # promotes!
-
-## s1 = t("abcd")
-## s2 = "".join([s1])
-## self.assertIsNot(s1, s2)
-## self.assertIs(type(s2), str) # promotes!
-
- else:
- self.fail("unexpected type for MixinStrUnicodeTest %r" % t)
-
diff --git a/future/tests/base.py b/future/tests/base.py
deleted file mode 100644
index a96c6fac..00000000
--- a/future/tests/base.py
+++ /dev/null
@@ -1,269 +0,0 @@
-import os
-import tempfile
-import unittest
-import sys
-if not hasattr(unittest, 'skip'):
- import unittest2 as unittest
-from textwrap import dedent
-import subprocess
-
-from future.utils import bind_method
-
-
-# For Python 2.6 compatibility: see http://stackoverflow.com/questions/4814970/
-if "check_output" not in dir(subprocess): # duck punch it in!
- def f(*popenargs, **kwargs):
- if 'stdout' in kwargs:
- raise ValueError('stdout argument not allowed, it will be overridden.')
- process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
- output, unused_err = process.communicate()
- retcode = process.poll()
- if retcode:
- cmd = kwargs.get("args")
- if cmd is None:
- cmd = popenargs[0]
- raise subprocess.CalledProcessError(retcode, cmd)
- return output
- subprocess.check_output = f
-
-class CodeHandler(unittest.TestCase):
- """
- Handy mixin for test classes for writing / reading / futurizing /
- running .py files in the test suite.
- """
- def setUp(self):
- """
- The outputs from the various futurize stages should have the
- following headers:
- """
- # After stage1:
- # TODO: use this form after implementing a fixer to consolidate
- # __future__ imports into a single line:
- # self.headers1 = """
- # from __future__ import absolute_import, division, print_function
- # """
- self.headers1 = self.reformat("""
- from __future__ import absolute_import
- from __future__ import division
- from __future__ import print_function
- """)
-
- # After stage2 --all-imports:
- # TODO: use this form after implementing a fixer to consolidate
- # __future__ imports into a single line:
- # self.headers2 = """
- # from __future__ import (absolute_import, division,
- # print_function, unicode_literals)
- # from future import standard_library
- # from future.builtins import *
- # """
- self.headers2 = self.reformat("""
- 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_hooks()
- from future.builtins import *
- """)
- self.interpreters = ['python']
- self.tempdir = tempfile.mkdtemp() + os.path.sep
- self.env = {'PYTHONPATH': os.getcwd()}
-
- def convert(self, code, stages=(1, 2), all_imports=False, from3=False,
- reformat=True, run=True):
- """
- Converts the code block using ``futurize`` and returns the
- resulting code.
-
- Passing stages=[1] or stages=[2] passes the flag ``--stage1`` or
- ``stage2`` to ``futurize``. Passing both stages runs ``futurize``
- with both stages by default.
-
- If from3 is False, runs ``futurize``, converting from Python 2 to
- both 2 and 3. If from3 is True, runs ``pasteurize`` to convert
- from Python 3 to both 2 and 3.
-
- Optionally reformats the code block first using the reformat()
- method.
-
- If run is True, runs the resulting code under all Python
- interpreters in self.interpreters.
- """
- if reformat:
- code = self.reformat(code)
- self._write_test_script(code)
- self._futurize_test_script(stages=stages, all_imports=all_imports,
- from3=from3)
- output = self._read_test_script()
- if run:
- for interpreter in self.interpreters:
- _ = self._run_test_script(interpreter=interpreter)
- return output
-
- def reformat(self, code):
- """
- Removes any leading \n and dedents.
- """
- if code.startswith('\n'):
- code = code[1:]
- return dedent(code)
-
- def compare(self, output, expected, ignore_imports=True):
- """
- Compares whether the code blocks are equal. If not, raises an
- exception so the test fails. Ignores any trailing whitespace like
- blank lines.
-
- If ignore_imports is True, passes the code blocks into the
- strip_future_imports method.
- """
- # self.assertEqual(expected.rstrip(),
- # self.order_future_lines(output).rstrip())
- if ignore_imports:
- output = self.strip_future_imports(output)
- expected = self.strip_future_imports(expected)
- self.assertEqual(self.order_future_lines(output.rstrip()),
- expected.rstrip())
-
- def strip_future_imports(self, code):
- """
- Strips any of these import lines:
-
- from __future__ import
- from future
- from future.
-
- or any line containing:
- install_hooks()
-
- Limitation: doesn't handle imports split across multiple lines like
- this:
-
- from __future__ import (absolute_import, division, print_function,
- unicode_literals)
- """
- output = []
- for line in code.splitlines():
- if not (line.startswith('from __future__ import ')
- or line.startswith('from future ')
- or 'install_hooks()' in line
- # but don't match "from future_builtins" :)
- or line.startswith('from future.')):
- output.append(line)
- return '\n'.join(output)
-
- def convert_check(self, before, expected, stages=(1, 2), all_imports=False,
- ignore_imports=True, from3=False, run=True):
- """
- Convenience method that calls convert() and compare().
-
- Reformats the code blocks automatically using the reformat()
- method.
-
- If all_imports is passed, we add the appropriate import headers
- for the stage(s) selected to the ``expected`` code-block, so they
- needn't appear repeatedly in the test code.
-
- If ignore_imports is True, ignores the presence of any lines
- beginning:
-
- from __future__ import ...
- from future import ...
-
- for the purpose of the comparison.
- """
- output = self.convert(before, stages=stages, all_imports=all_imports,
- from3=from3, run=run)
- if all_imports:
- headers = self.headers2 if 2 in stages else self.headers1
- else:
- headers = ''
-
- self.compare(output, self.reformat(headers + expected),
- ignore_imports=ignore_imports)
-
- def order_future_lines(self, code):
- """
- TODO: simplify this hideous code ...
-
- Returns the code block with any ``__future__`` import lines sorted, and
- then any ``future`` import lines sorted.
- """
- codelines = code.splitlines()
- # Under under future lines:
- uufuture_line_numbers = [i for i in range(len(codelines)) if codelines[i].startswith('from __future__ import ')]
- sorted_uufuture_lines = sorted([codelines[i] for i in uufuture_line_numbers])
-
- # future import lines:
- future_line_numbers = [i for i in range(len(codelines)) if codelines[i].startswith('from future')]
- sorted_future_lines = sorted([codelines[i] for i in future_line_numbers])
-
- # Replace the old unsorted "from __future__ import ..." lines with the
- # new sorted ones:
- codelines2 = []
- for i in range(len(codelines)):
- if i in uufuture_line_numbers:
- codelines2.append(sorted_uufuture_lines[i])
- elif i in future_line_numbers:
- codelines2.append(sorted_future_lines[i - len(uufuture_line_numbers)])
- else:
- codelines2.append(codelines[i])
- return '\n'.join(codelines2)
-
- def unchanged(self, code, **kwargs):
- """
- Convenience method to ensure the code is unchanged by the
- futurize process.
- """
- self.convert_check(code, code, **kwargs)
-
- def _write_test_script(self, code, filename='mytestscript.py'):
- """
- Dedents the given code (a multiline string) and writes it out to
- a file in a temporary folder like /tmp/tmpUDCn7x/mytestscript.py.
- """
- with open(self.tempdir + filename, 'w') as f:
- f.write(dedent(code))
-
- def _read_test_script(self, filename='mytestscript.py'):
- with open(self.tempdir + filename) as f:
- newsource = f.read()
- return newsource
-
- def _futurize_test_script(self, filename='mytestscript.py', stages=(1, 2),
- all_imports=False, from3=False):
- params = []
- stages = list(stages)
- if all_imports:
- params.append('--all-imports')
- if from3:
- script = 'pasteurize.py'
- else:
- script = 'futurize.py'
- if stages == [1]:
- params.append('--stage1')
- elif stages == [2]:
- params.append('--stage2')
- else:
- assert stages == [1, 2]
- # No extra params needed
-
- output = subprocess.check_output(['python', script] + params +
- ['-w', self.tempdir + filename],
- stderr=subprocess.STDOUT)
- return output
-
- def _run_test_script(self, filename='mytestscript.py',
- interpreter='python'):
- env = {'PYTHONPATH': os.getcwd()}
- return subprocess.check_output([interpreter, self.tempdir + filename],
- env=env)
-
-
-# Decorator to skip some tests on Python 2.6 ...
-skip26 = unittest.skipIf(sys.version_info[:2] == (2, 6), "this test is known to fail on Py2.6")
-
-
-# Renamed in Py3.3:
-unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp
diff --git a/future/tests/test_email/__init__.py b/future/tests/test_email/__init__.py
deleted file mode 100644
index 76fff10b..00000000
--- a/future/tests/test_email/__init__.py
+++ /dev/null
@@ -1,160 +0,0 @@
-from __future__ import unicode_literals
-from __future__ import print_function
-from __future__ import division
-from __future__ import absolute_import
-from future.builtins import open
-from future.builtins import range
-from future.builtins import super
-from future.builtins import str
-from future import standard_library
-import os
-import sys
-import unittest
-with standard_library.hooks():
- import test.support
- import email
- from email.message import Message
- from email._policybase import compat32
-from future.tests.test_email import __file__ as landmark
-
-# Run all tests in package for '-m unittest test.test_email'
-def load_tests(loader, standard_tests, pattern):
- this_dir = os.path.dirname(__file__)
- if pattern is None:
- pattern = "test*"
- package_tests = loader.discover(start_dir=this_dir, pattern=pattern)
- standard_tests.addTests(package_tests)
- return standard_tests
-
-
-# used by regrtest and __main__.
-def test_main():
- here = os.path.dirname(__file__)
- # Unittest mucks with the path, so we have to save and restore
- # it to keep regrtest happy.
- savepath = sys.path[:]
- test.support._run_suite(unittest.defaultTestLoader.discover(here))
- sys.path[:] = savepath
-
-
-# helper code used by a number of test modules.
-
-def openfile(filename, *args, **kws):
- path = os.path.join(os.path.dirname(landmark), 'data', filename)
- return open(path, *args, **kws)
-
-
-# Base test class
-class TestEmailBase(unittest.TestCase):
-
- maxDiff = None
- # Currently the default policy is compat32. By setting that as the default
- # here we make minimal changes in the test_email tests compared to their
- # pre-3.3 state.
- policy = compat32
-
- def __init__(self, *args, **kw):
- super().__init__(*args, **kw)
- self.addTypeEqualityFunc(bytes, self.assertBytesEqual)
-
- # Backward compatibility to minimize test_email test changes.
- ndiffAssertEqual = unittest.TestCase.assertEqual
-
- def _msgobj(self, filename):
- with openfile(filename) as fp:
- return email.message_from_file(fp, policy=self.policy)
-
- def _str_msg(self, string, message=Message, policy=None):
- if policy is None:
- policy = self.policy
- return email.message_from_string(string, message, policy=policy)
-
- def _bytes_repr(self, b):
- return [repr(x) for x in b.splitlines(keepends=True)]
-
- def assertBytesEqual(self, first, second, msg):
- """Our byte strings are really encoded strings; improve diff output"""
- self.assertEqual(self._bytes_repr(first), self._bytes_repr(second))
-
- def assertDefectsEqual(self, actual, expected):
- self.assertEqual(len(actual), len(expected), actual)
- for i in range(len(actual)):
- self.assertIsInstance(actual[i], expected[i],
- 'item {}'.format(i))
-
-
-def parameterize(cls):
- """A test method parameterization class decorator.
-
- Parameters are specified as the value of a class attribute that ends with
- the string '_params'. Call the portion before '_params' the prefix. Then
- a method to be parameterized must have the same prefix, the string
- '_as_', and an arbitrary suffix.
-
- The value of the _params attribute may be either a dictionary or a list.
- The values in the dictionary and the elements of the list may either be
- single values, or a list. If single values, they are turned into single
- element tuples. However derived, the resulting sequence is passed via
- *args to the parameterized test function.
-
- In a _params dictioanry, the keys become part of the name of the generated
- tests. In a _params list, the values in the list are converted into a
- string by joining the string values of the elements of the tuple by '_' and
- converting any blanks into '_'s, and this become part of the name.
- The full name of a generated test is a 'test_' prefix, the portion of the
- test function name after the '_as_' separator, plus an '_', plus the name
- derived as explained above.
-
- For example, if we have:
-
- count_params = range(2)
-
- def count_as_foo_arg(self, foo):
- self.assertEqual(foo+1, myfunc(foo))
-
- we will get parameterized test methods named:
- test_foo_arg_0
- test_foo_arg_1
- test_foo_arg_2
-
- Or we could have:
-
- example_params = {'foo': ('bar', 1), 'bing': ('bang', 2)}
-
- def example_as_myfunc_input(self, name, count):
- self.assertEqual(name+str(count), myfunc(name, count))
-
- and get:
- test_myfunc_input_foo
- test_myfunc_input_bing
-
- Note: if and only if the generated test name is a valid identifier can it
- be used to select the test individually from the unittest command line.
-
- """
- paramdicts = {}
- for name, attr in cls.__dict__.items():
- if name.endswith('_params'):
- if not hasattr(attr, 'keys'):
- d = {}
- for x in attr:
- if not hasattr(x, '__iter__'):
- x = (x,)
- n = '_'.join(str(v) for v in x).replace(' ', '_')
- d[n] = x
- attr = d
- paramdicts[name[:-7] + '_as_'] = attr
- testfuncs = {}
- for name, attr in cls.__dict__.items():
- for paramsname, paramsdict in paramdicts.items():
- if name.startswith(paramsname):
- testnameroot = 'test_' + name[len(paramsname):]
- for paramname, params in paramsdict.items():
- test = (lambda self, name=name, params=params:
- getattr(self, name)(*params))
- testname = testnameroot + '_' + paramname
- test.__name__ = testname
- testfuncs[testname] = test
- for key, value in testfuncs.items():
- setattr(cls, key, value)
- return cls
diff --git a/future/tests/test_email/__main__.py b/future/tests/test_email/__main__.py
deleted file mode 100644
index 98c1cf54..00000000
--- a/future/tests/test_email/__main__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from future.tests.test_email import test_main
-
-test_main()
diff --git a/future/tests/test_email/data/PyBanner048.gif b/future/tests/test_email/data/PyBanner048.gif
deleted file mode 100644
index 1a5c87f6..00000000
Binary files a/future/tests/test_email/data/PyBanner048.gif and /dev/null differ
diff --git a/future/tests/test_email/data/audiotest.au b/future/tests/test_email/data/audiotest.au
deleted file mode 100644
index f76b0501..00000000
Binary files a/future/tests/test_email/data/audiotest.au and /dev/null differ
diff --git a/future/tests/test_email/data/msg_01.txt b/future/tests/test_email/data/msg_01.txt
deleted file mode 100644
index 7e33bcf9..00000000
--- a/future/tests/test_email/data/msg_01.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Return-Path:
-Delivered-To: bbb@zzz.org
-Received: by mail.zzz.org (Postfix, from userid 889)
- id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT)
-MIME-Version: 1.0
-Content-Type: text/plain; charset=us-ascii
-Content-Transfer-Encoding: 7bit
-Message-ID: <15090.61304.110929.45684@aaa.zzz.org>
-From: bbb@ddd.com (John X. Doe)
-To: bbb@zzz.org
-Subject: This is a test message
-Date: Fri, 4 May 2001 14:05:44 -0400
-
-
-Hi,
-
-Do you like this message?
-
--Me
diff --git a/future/tests/test_email/data/msg_02.txt b/future/tests/test_email/data/msg_02.txt
deleted file mode 100644
index 43f24803..00000000
--- a/future/tests/test_email/data/msg_02.txt
+++ /dev/null
@@ -1,135 +0,0 @@
-MIME-version: 1.0
-From: ppp-request@zzz.org
-Sender: ppp-admin@zzz.org
-To: ppp@zzz.org
-Subject: Ppp digest, Vol 1 #2 - 5 msgs
-Date: Fri, 20 Apr 2001 20:18:00 -0400 (EDT)
-X-Mailer: Mailman v2.0.4
-X-Mailman-Version: 2.0.4
-Content-Type: multipart/mixed; boundary="192.168.1.2.889.32614.987812255.500.21814"
-
---192.168.1.2.889.32614.987812255.500.21814
-Content-type: text/plain; charset=us-ascii
-Content-description: Masthead (Ppp digest, Vol 1 #2)
-
-Send Ppp mailing list submissions to
- ppp@zzz.org
-
-To subscribe or unsubscribe via the World Wide Web, visit
- http://www.zzz.org/mailman/listinfo/ppp
-or, via email, send a message with subject or body 'help' to
- ppp-request@zzz.org
-
-You can reach the person managing the list at
- ppp-admin@zzz.org
-
-When replying, please edit your Subject line so it is more specific
-than "Re: Contents of Ppp digest..."
-
-
---192.168.1.2.889.32614.987812255.500.21814
-Content-type: text/plain; charset=us-ascii
-Content-description: Today's Topics (5 msgs)
-
-Today's Topics:
-
- 1. testing #1 (Barry A. Warsaw)
- 2. testing #2 (Barry A. Warsaw)
- 3. testing #3 (Barry A. Warsaw)
- 4. testing #4 (Barry A. Warsaw)
- 5. testing #5 (Barry A. Warsaw)
-
---192.168.1.2.889.32614.987812255.500.21814
-Content-Type: multipart/digest; boundary="__--__--"
-
---__--__--
-
-Message: 1
-Content-Type: text/plain; charset=us-ascii
-Content-Transfer-Encoding: 7bit
-Date: Fri, 20 Apr 2001 20:16:13 -0400
-To: ppp@zzz.org
-From: barry@digicool.com (Barry A. Warsaw)
-Subject: [Ppp] testing #1
-Precedence: bulk
-
-
-hello
-
-
---__--__--
-
-Message: 2
-Date: Fri, 20 Apr 2001 20:16:21 -0400
-Content-Type: text/plain; charset=us-ascii
-Content-Transfer-Encoding: 7bit
-To: ppp@zzz.org
-From: barry@digicool.com (Barry A. Warsaw)
-Precedence: bulk
-
-
-hello
-
-
---__--__--
-
-Message: 3
-Date: Fri, 20 Apr 2001 20:16:25 -0400
-Content-Type: text/plain; charset=us-ascii
-Content-Transfer-Encoding: 7bit
-To: ppp@zzz.org
-From: barry@digicool.com (Barry A. Warsaw)
-Subject: [Ppp] testing #3
-Precedence: bulk
-
-
-hello
-
-
---__--__--
-
-Message: 4
-Date: Fri, 20 Apr 2001 20:16:28 -0400
-Content-Type: text/plain; charset=us-ascii
-Content-Transfer-Encoding: 7bit
-To: ppp@zzz.org
-From: barry@digicool.com (Barry A. Warsaw)
-Subject: [Ppp] testing #4
-Precedence: bulk
-
-
-hello
-
-
---__--__--
-
-Message: 5
-Date: Fri, 20 Apr 2001 20:16:32 -0400
-Content-Type: text/plain; charset=us-ascii
-Content-Transfer-Encoding: 7bit
-To: ppp@zzz.org
-From: barry@digicool.com (Barry A. Warsaw)
-Subject: [Ppp] testing #5
-Precedence: bulk
-
-
-hello
-
-
-
-
---__--__----
---192.168.1.2.889.32614.987812255.500.21814
-Content-type: text/plain; charset=us-ascii
-Content-description: Digest Footer
-
-_______________________________________________
-Ppp mailing list
-Ppp@zzz.org
-http://www.zzz.org/mailman/listinfo/ppp
-
-
---192.168.1.2.889.32614.987812255.500.21814--
-
-End of Ppp Digest
-
diff --git a/future/tests/test_email/data/msg_03.txt b/future/tests/test_email/data/msg_03.txt
deleted file mode 100644
index c748ebf1..00000000
--- a/future/tests/test_email/data/msg_03.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-Return-Path:
-Delivered-To: bbb@zzz.org
-Received: by mail.zzz.org (Postfix, from userid 889)
- id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT)
-Message-ID: <15090.61304.110929.45684@aaa.zzz.org>
-From: bbb@ddd.com (John X. Doe)
-To: bbb@zzz.org
-Subject: This is a test message
-Date: Fri, 4 May 2001 14:05:44 -0400
-
-
-Hi,
-
-Do you like this message?
-
--Me
diff --git a/future/tests/test_email/data/msg_04.txt b/future/tests/test_email/data/msg_04.txt
deleted file mode 100644
index 1f633c44..00000000
--- a/future/tests/test_email/data/msg_04.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-Return-Path:
-Delivered-To: barry@python.org
-Received: by mail.python.org (Postfix, from userid 889)
- id C2BF0D37C6; Tue, 11 Sep 2001 00:05:05 -0400 (EDT)
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="h90VIIIKmx"
-Content-Transfer-Encoding: 7bit
-Message-ID: <15261.36209.358846.118674@anthem.python.org>
-From: barry@python.org (Barry A. Warsaw)
-To: barry@python.org
-Subject: a simple multipart
-Date: Tue, 11 Sep 2001 00:05:05 -0400
-X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid
-X-Attribution: BAW
-X-Oblique-Strategy: Make a door into a window
-
-
---h90VIIIKmx
-Content-Type: text/plain
-Content-Disposition: inline;
- filename="msg.txt"
-Content-Transfer-Encoding: 7bit
-
-a simple kind of mirror
-to reflect upon our own
-
---h90VIIIKmx
-Content-Type: text/plain
-Content-Disposition: inline;
- filename="msg.txt"
-Content-Transfer-Encoding: 7bit
-
-a simple kind of mirror
-to reflect upon our own
-
---h90VIIIKmx--
-
diff --git a/future/tests/test_email/data/msg_05.txt b/future/tests/test_email/data/msg_05.txt
deleted file mode 100644
index 87d5e9cb..00000000
--- a/future/tests/test_email/data/msg_05.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-From: foo
-Subject: bar
-To: baz
-MIME-Version: 1.0
-Content-Type: multipart/report; report-type=delivery-status;
- boundary="D1690A7AC1.996856090/mail.example.com"
-Message-Id: <20010803162810.0CA8AA7ACC@mail.example.com>
-
-This is a MIME-encapsulated message.
-
---D1690A7AC1.996856090/mail.example.com
-Content-Type: text/plain
-
-Yadda yadda yadda
-
---D1690A7AC1.996856090/mail.example.com
-
-Yadda yadda yadda
-
---D1690A7AC1.996856090/mail.example.com
-Content-Type: message/rfc822
-
-From: nobody@python.org
-
-Yadda yadda yadda
-
---D1690A7AC1.996856090/mail.example.com--
-
diff --git a/future/tests/test_email/data/msg_06.txt b/future/tests/test_email/data/msg_06.txt
deleted file mode 100644
index 69f3a47f..00000000
--- a/future/tests/test_email/data/msg_06.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-Return-Path:
-Delivered-To: barry@python.org
-MIME-Version: 1.0
-Content-Type: message/rfc822
-Content-Description: forwarded message
-Content-Transfer-Encoding: 7bit
-Message-ID: <15265.9482.641338.555352@python.org>
-From: barry@zope.com (Barry A. Warsaw)
-Sender: barry@python.org
-To: barry@python.org
-Subject: forwarded message from Barry A. Warsaw
-Date: Thu, 13 Sep 2001 17:28:42 -0400
-X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid
-X-Attribution: BAW
-X-Oblique-Strategy: Be dirty
-X-Url: http://barry.wooz.org
-
-MIME-Version: 1.0
-Content-Type: text/plain; charset=us-ascii
-Return-Path:
-Delivered-To: barry@python.org
-Message-ID: <15265.9468.713530.98441@python.org>
-From: barry@zope.com (Barry A. Warsaw)
-Sender: barry@python.org
-To: barry@python.org
-Subject: testing
-Date: Thu, 13 Sep 2001 17:28:28 -0400
-X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid
-X-Attribution: BAW
-X-Oblique-Strategy: Spectrum analysis
-X-Url: http://barry.wooz.org
-
-
diff --git a/future/tests/test_email/data/msg_07.txt b/future/tests/test_email/data/msg_07.txt
deleted file mode 100644
index 721f3a0d..00000000
--- a/future/tests/test_email/data/msg_07.txt
+++ /dev/null
@@ -1,83 +0,0 @@
-MIME-Version: 1.0
-From: Barry
-To: Dingus Lovers
-Subject: Here is your dingus fish
-Date: Fri, 20 Apr 2001 19:35:02 -0400
-Content-Type: multipart/mixed; boundary="BOUNDARY"
-
---BOUNDARY
-Content-Type: text/plain; charset="us-ascii"
-
-Hi there,
-
-This is the dingus fish.
-
---BOUNDARY
-Content-Type: image/gif; name="dingusfish.gif"
-Content-Transfer-Encoding: base64
-content-disposition: attachment; filename="dingusfish.gif"
-
-R0lGODdhAAEAAfAAAP///wAAACwAAAAAAAEAAQAC/oSPqcvtD6OctNqLs968+w+G4kiW5omm6sq2
-7gvH8kzX9o3n+s73/g8MCofEovGITGICTKbyCV0FDNOo9SqpQqpOrJfXzTQj2vD3TGtqL+NtGQ2f
-qTXmxzuOd7WXdcc9DyjU53ewFni4s0fGhdiYaEhGBelICTNoV1j5NUnFcrmUqemjNifJVWpaOqaI
-oFq3SspZsSraE7sHq3jr1MZqWvi662vxV4tD+pvKW6aLDOCLyur8PDwbanyDeq0N3DctbQYeLDvR
-RY6t95m6UB0d3mwIrV7e2VGNvjjffukeJp4w7F65KecGFsTHQGAygOrgrWs1jt28Rc88KESYcGLA
-/obvTkH6p+CinWJiJmIMqXGQwH/y4qk0SYjgQTczT3ajKZGfuI0uJ4kkVI/DT5s3/ejkxI0aT4Y+
-YTYgWbImUaXk9nlLmnSh1qJiJFl0OpUqRK4oOy7NyRQtHWofhoYVxkwWXKUSn0YsS+fUV6lhqfYb
-6ayd3Z5qQdG1B7bvQzaJjwUV2lixMUZ7JVsOlfjWVr/3NB/uFvnySBN6Dcb6rGwaRM3wsormw5cC
-M9NxWy/bWdufudCvy8bOAjXjVVwta/uO21sE5RHBCzNFXtgq9ORtH4eYjVP4Yryo026nvkFmCeyA
-B29efV6ravCMK5JwWd5897Qrx7ll38o6iHDZ/rXPR//feevhF4l7wjUGX3xq1eeRfM4RSJGBIV1D
-z1gKPkfWag3mVBVvva1RlX5bAJTPR/2YqNtw/FkIYYEi/pIZiAdpcxpoHtmnYYoZtvhUftzdx5ZX
-JSKDW405zkGcZzzGZ6KEv4FI224oDmijlEf+xp6MJK5ojY/ASeVUR+wsKRuJ+XFZ5o7ZeEime8t1
-ouUsU6YjF5ZtUihhkGfCdFQLWQFJ3UXxmElfhQnR+eCdcDbkFZp6vTRmj56ApCihn5QGpaToNZmR
-n3NVSpZcQpZ2KEONusaiCsKAug0wkQbJSFO+PTSjneGxOuFjPlUk3ovWvdIerjUg9ZGIOtGq/qeX
-eCYrrCX+1UPsgTKGGRSbzd5q156d/gpfbJxe66eD5iQKrXj7RGgruGxs62qebBHUKS32CKluCiqZ
-qh+pmehmEb71noAUoe5e9Zm17S7773V10pjrtG4CmuurCV/n6zLK5turWNhqOvFXbjhZrMD0YhKe
-wR0zOyuvsh6MWrGoIuzvyWu5y1WIFAqmJselypxXh6dKLNOKEB98L88bS2rkNqqlKzCNJp9c0G0j
-Gzh0iRrCbHSXmPR643QS+4rWhgFmnSbSuXCjS0xAOWkU2UdLqyuUNfHSFdUouy3bm5i5GnDM3tG8
-doJ4r5tqu3pPbRSVfvs8uJzeNXhp3n4j/tZ42SwH7eaWUUOjc3qFV9453UHTXZfcLH+OeNs5g36x
-lBnHvTm7EbMbLeuaLncao8vWCXimfo1o+843Ak6y4ChNeGntvAYvfLK4ezmoyNIbNCLTCXO9ZV3A
-E8/s88RczPzDwI4Ob7XZyl7+9Miban29h+tJZPrE21wgvBphDfrrfPdCTPKJD/y98L1rZwHcV6Jq
-Zab0metpuNIX/qAFPoz171WUaUb4HAhBSzHuHfjzHb3kha/2Cctis/ORArVHNYfFyYRH2pYIRzic
-isVOfPWD1b6mRTqpCRBozzof6UZVvFXRxWIr3GGrEviGYgyPMfahheiSaLs/9QeFu7oZ/ndSY8DD
-ya9x+uPed+7mxN2IzIISBOMLFYWVqC3Pew1T2nFuuCiwZS5/v6II10i4t1OJcUH2U9zxKodHsGGv
-Oa+zkvNUYUOa/TCCRutF9MzDwdlUMJADTCGSbDQ5OV4PTamDoPEi6Ecc/RF5RWwkcdSXvSOaDWSn
-I9LlvubFTQpuc6JKXLcKeb+xdbKRBnwREemXyjg6ME65aJiOuBgrktzykfPLJBKR9ClMavJ62/Ff
-BlNIyod9yX9wcSXexnXFpvkrbXk64xsx5Db7wXKP5fSgsvwIMM/9631VLBfkmtbHRXpqmtei52hG
-pUwSlo+BASQoeILDOBgREECxBBh5/iYmNsQ9dIv5+OI++QkqdsJPc3uykz5fkM+OraeekcQF7X4n
-B5S67za5U967PmooGQhUXfF7afXyCD7ONdRe17QogYjVx38uLwtrS6nhTnm15LQUnu9E2uK6CNI/
-1HOABj0ESwOjut4FEpFQpdNAm4K2LHnDWHNcmKB2ioKBogysVZtMO2nSxUdZ8Yk2kJc7URioLVI0
-YgmtIwZj4LoeKemgnOnbUdGnzZ4Oa6scqiolBGqS6RgWNLu0RMhcaE6rhhU4hiuqFXPAG8fGwTPW
-FKeLMtdVmXLSs5YJGF/YeVm7rREMlY3UYE+yCxbaMXX8y15m5zVHq6GOKDMynzII/jdUHdyVqIy0
-ifX2+r/EgtZcvRzSb72gU9ui87M2VecjKildW/aFqaYhKoryUjfB/g4qtyVuc60xFDGmCxwjW+qu
-zjuwl2GkOWn66+3QiiEctvd04OVvcCVzjgT7lrkvjVGKKHmmlDUKowSeikb5kK/mJReuWOxONx+s
-ULsl+Lqb0CVn0SrVyJ6wt4t6yTeSCafhPhAf0OXn6L60UMxiLolFAtmN35S2Ob1lZpQ1r/n0Qb5D
-oQ1zJiRVDgF8N3Q8TYfbi3DyWCy3lT1nxyBs6FT3S2GOzWRlxwKvlRP0RPJA9SjxEy0UoEnkA+M4
-cnzLMJrBGWLFEaaUb5lvpqbq/loOaU5+DFuHPxo82/OZuM8FXG3oVNZhtWpMpb/0Xu5m/LfLhHZQ
-7yuVI0MqZ7NE43imC8jH3IwGZlbPm0xkJYs7+2U48hXTsFSMqgGDvai0kLxyynKNT/waj+q1c1tz
-GjOpPBgdCSq3UKZxCSsqFIY+O6JbAWGWcV1pwqLyj5sGqCF1xb1F3varUWqrJv6cN3PrUXzijtfZ
-FshpBL3Xwr4GIPvU2N8EjrJgS1zl21rbXQMXeXc5jjFyrhpCzijSv/RQtyPSzHCFMhlME95fHglt
-pRsX+dfSQjUeHAlpWzJ5iOo79Ldnaxai6bXTcGO3fp07ri7HLEmXXPlYi8bv/qVxvNcdra6m7Rlb
-6JBTb5fd66VhFRjGArh2n7R1rDW4P5NOT9K0I183T2scYkeZ3q/VFyLb09U9ajzXBS8Kgkhc4mBS
-kYY9cy3Vy9lUnuNJH8HGIclUilwnBtjUOH0gteGOZ4c/XNrhXLSYDyxfnD8z1pDy7rYRvDolhnbe
-UMzxCZUs40s6s7UIvBnLgc0+vKuOkIXeOrDymlp+Zxra4MZLBbVrqD/jTJ597pDmnw5c4+DbyB88
-9Cg9DodYcSuMZT/114pptqc/EuTjRPvH/z5slzI3tluOEBBLqOXLOX+0I5929tO97wkvl/atCz+y
-xJrdwteW2FNW/NSmBP+f/maYtVs/bYyBC7Ox3jsYZHL05CIrBa/nS+b3bHfiYm4Ueil1YZZSgAUI
-fFZ1dxUmeA2oQRQ3RuGXNGLFV9/XbGFGPV6kfzk1TBBCd+izc7q1H+OHMJwmaBX2IQNYVAKHYepV
-SSGCe6CnbYHHETKGNe43EDvFgZr0gB/nVHPHZ80VV1ojOiI3XDvYIkl4ayo4bxQIgrFXWTvBI0nH
-VElWMuw2aLUWCRHHf8ymVCHjFlJnOSojfevCYyyyZDH0IcvHhrsnQ5O1OsWzONuVVKIxSxiFZ/tR
-fKDAf6xFTnw4O9Qig2VCfW2hJQrmMOuHW0W3dLQmCMO2ccdUd/xyfflH/olTiHZVdGwb8nIwRzSE
-J15jFlOJuBZBZ4CiyHyd2IFylFlB+HgHhYabhWOGwYO1ZH/Og1dtQlFMk352CGRSIFTapnWQEUtN
-l4zv8S0aaCFDyGCBqDUxZYpxGHX01y/JuH1xhn7TOCnNCI4eKDs5WGX4R425F4vF1o3BJ4vO0otq
-I3rimI7jJY1jISqnBxknCIvruF83mF5wN4X7qGLIhR8A2Vg0yFERSIXn9Vv3GHy3Vj/WIkKddlYi
-yIMv2I/VMjTLpW7pt05SWIZR0RPyxpB4SIUM9lBPGBl0GC7oSEEwRYLe4pJpZY2P0zbI1n+Oc44w
-qY3PUnmF0ixjVpDD/mJ9wpOBGTVgXlaCaZiPcIWK5NiKBIiPdGaQ0TWGvAiG7nMchdZb7Vgf8zNi
-MuMyzRdy/lePe9iC4TRx7WhhOQI/QiSVNAmAa2lT/piFbuh7ofJoYSZzrSZ1bvmWw3eN2nKUPVky
-uPN5/VRfohRd0VYZoqhKIlU6TXYhJxmPUIloAwc1bPmHEpaZYZORHNlXUJM07hATwHR8MJYqkwWR
-WaIezFhxSFlc8/Fq82hEnpeRozg3ULhhr9lAGtVEkCg5ZNRuuVleBPaZadhG0ZgkyPmDOTOKzViM
-YgOcpukKqQcbjAWS0IleQ2ROjdh6A+md1qWdBRSX7iSYgFRTtRmBpJioieXJiHfJiMGIR9fJOn8I
-MSfXYhspn4ooSa2mSAj4n+8Bmg03fBJZoPOJgsVZRxu1oOMRPXYYjdqjihFaEoZpXBREanuJoRI6
-cibFinq4ngUKh/wQd/H5ofYCZ0HJXR62opZFaAT0iFIZo4DIiUojkjeqKiuoZirKo5Y1a7AWckGa
-BkuYoD5lpDK6eUs6CkDqpETwl1EqpfhJpVeKpVl6EgUAADs=
-
---BOUNDARY--
diff --git a/future/tests/test_email/data/msg_08.txt b/future/tests/test_email/data/msg_08.txt
deleted file mode 100644
index b5630836..00000000
--- a/future/tests/test_email/data/msg_08.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-MIME-Version: 1.0
-From: Barry Warsaw
-To: Dingus Lovers
-Subject: Lyrics
-Date: Fri, 20 Apr 2001 19:35:02 -0400
-Content-Type: multipart/mixed; boundary="BOUNDARY"
-
---BOUNDARY
-Content-Type: text/plain; charset="us-ascii"
-
-
---BOUNDARY
-Content-Type: text/html; charset="iso-8859-1"
-
-
---BOUNDARY
-Content-Type: text/plain; charset="iso-8859-2"
-
-
---BOUNDARY
-Content-Type: text/plain; charset="koi8-r"
-
-
---BOUNDARY--
diff --git a/future/tests/test_email/data/msg_09.txt b/future/tests/test_email/data/msg_09.txt
deleted file mode 100644
index 575c4c20..00000000
--- a/future/tests/test_email/data/msg_09.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-MIME-Version: 1.0
-From: Barry Warsaw
-To: Dingus Lovers
-Subject: Lyrics
-Date: Fri, 20 Apr 2001 19:35:02 -0400
-Content-Type: multipart/mixed; boundary="BOUNDARY"
-
---BOUNDARY
-Content-Type: text/plain; charset="us-ascii"
-
-
---BOUNDARY
-Content-Type: text/html; charset="iso-8859-1"
-
-
---BOUNDARY
-Content-Type: text/plain
-
-
---BOUNDARY
-Content-Type: text/plain; charset="koi8-r"
-
-
---BOUNDARY--
diff --git a/future/tests/test_email/data/msg_10.txt b/future/tests/test_email/data/msg_10.txt
deleted file mode 100644
index 07903960..00000000
--- a/future/tests/test_email/data/msg_10.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-MIME-Version: 1.0
-From: Barry Warsaw
-To: Dingus Lovers
-Subject: Lyrics
-Date: Fri, 20 Apr 2001 19:35:02 -0400
-Content-Type: multipart/mixed; boundary="BOUNDARY"
-
---BOUNDARY
-Content-Type: text/plain; charset="us-ascii"
-Content-Transfer-Encoding: 7bit
-
-This is a 7bit encoded message.
-
---BOUNDARY
-Content-Type: text/html; charset="iso-8859-1"
-Content-Transfer-Encoding: Quoted-Printable
-
-=A1This is a Quoted Printable encoded message!
-
---BOUNDARY
-Content-Type: text/plain; charset="iso-8859-1"
-Content-Transfer-Encoding: Base64
-
-VGhpcyBpcyBhIEJhc2U2NCBlbmNvZGVkIG1lc3NhZ2Uu
-
-
---BOUNDARY
-Content-Type: text/plain; charset="iso-8859-1"
-Content-Transfer-Encoding: Base64
-
-VGhpcyBpcyBhIEJhc2U2NCBlbmNvZGVkIG1lc3NhZ2UuCg==
-
-
---BOUNDARY
-Content-Type: text/plain; charset="iso-8859-1"
-
-This has no Content-Transfer-Encoding: header.
-
---BOUNDARY--
diff --git a/future/tests/test_email/data/msg_11.txt b/future/tests/test_email/data/msg_11.txt
deleted file mode 100644
index 8f7f1991..00000000
--- a/future/tests/test_email/data/msg_11.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-Content-Type: message/rfc822
-MIME-Version: 1.0
-Subject: The enclosing message
-
-Subject: An enclosed message
-
-Here is the body of the message.
diff --git a/future/tests/test_email/data/msg_12.txt b/future/tests/test_email/data/msg_12.txt
deleted file mode 100644
index 4bec8d94..00000000
--- a/future/tests/test_email/data/msg_12.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-MIME-Version: 1.0
-From: Barry Warsaw
-To: Dingus Lovers
-Subject: Lyrics
-Date: Fri, 20 Apr 2001 19:35:02 -0400
-Content-Type: multipart/mixed; boundary="BOUNDARY"
-
---BOUNDARY
-Content-Type: text/plain; charset="us-ascii"
-
-
---BOUNDARY
-Content-Type: text/html; charset="iso-8859-1"
-
-
---BOUNDARY
-Content-Type: multipart/mixed; boundary="ANOTHER"
-
---ANOTHER
-Content-Type: text/plain; charset="iso-8859-2"
-
-
---ANOTHER
-Content-Type: text/plain; charset="iso-8859-3"
-
---ANOTHER--
-
---BOUNDARY
-Content-Type: text/plain; charset="us-ascii"
-
-
---BOUNDARY
-Content-Type: text/plain; charset="koi8-r"
-
-
---BOUNDARY--
diff --git a/future/tests/test_email/data/msg_12a.txt b/future/tests/test_email/data/msg_12a.txt
deleted file mode 100644
index e94224ec..00000000
--- a/future/tests/test_email/data/msg_12a.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-MIME-Version: 1.0
-From: Barry Warsaw
-To: Dingus Lovers
-Subject: Lyrics
-Date: Fri, 20 Apr 2001 19:35:02 -0400
-Content-Type: multipart/mixed; boundary="BOUNDARY"
-
---BOUNDARY
-Content-Type: text/plain; charset="us-ascii"
-
-
---BOUNDARY
-Content-Type: text/html; charset="iso-8859-1"
-
-
---BOUNDARY
-Content-Type: multipart/mixed; boundary="ANOTHER"
-
---ANOTHER
-Content-Type: text/plain; charset="iso-8859-2"
-
-
---ANOTHER
-Content-Type: text/plain; charset="iso-8859-3"
-
-
---ANOTHER--
-
-
---BOUNDARY
-Content-Type: text/plain; charset="us-ascii"
-
-
---BOUNDARY
-Content-Type: text/plain; charset="koi8-r"
-
-
---BOUNDARY--
diff --git a/future/tests/test_email/data/msg_13.txt b/future/tests/test_email/data/msg_13.txt
deleted file mode 100644
index 8e6d52d5..00000000
--- a/future/tests/test_email/data/msg_13.txt
+++ /dev/null
@@ -1,94 +0,0 @@
-MIME-Version: 1.0
-From: Barry
-To: Dingus Lovers
-Subject: Here is your dingus fish
-Date: Fri, 20 Apr 2001 19:35:02 -0400
-Content-Type: multipart/mixed; boundary="OUTER"
-
---OUTER
-Content-Type: text/plain; charset="us-ascii"
-
-A text/plain part
-
---OUTER
-Content-Type: multipart/mixed; boundary=BOUNDARY
-
-
---BOUNDARY
-Content-Type: text/plain; charset="us-ascii"
-
-Hi there,
-
-This is the dingus fish.
-
---BOUNDARY
-Content-Type: image/gif; name="dingusfish.gif"
-Content-Transfer-Encoding: base64
-content-disposition: attachment; filename="dingusfish.gif"
-
-R0lGODdhAAEAAfAAAP///wAAACwAAAAAAAEAAQAC/oSPqcvtD6OctNqLs968+w+G4kiW5omm6sq2
-7gvH8kzX9o3n+s73/g8MCofEovGITGICTKbyCV0FDNOo9SqpQqpOrJfXzTQj2vD3TGtqL+NtGQ2f
-qTXmxzuOd7WXdcc9DyjU53ewFni4s0fGhdiYaEhGBelICTNoV1j5NUnFcrmUqemjNifJVWpaOqaI
-oFq3SspZsSraE7sHq3jr1MZqWvi662vxV4tD+pvKW6aLDOCLyur8PDwbanyDeq0N3DctbQYeLDvR
-RY6t95m6UB0d3mwIrV7e2VGNvjjffukeJp4w7F65KecGFsTHQGAygOrgrWs1jt28Rc88KESYcGLA
-/obvTkH6p+CinWJiJmIMqXGQwH/y4qk0SYjgQTczT3ajKZGfuI0uJ4kkVI/DT5s3/ejkxI0aT4Y+
-YTYgWbImUaXk9nlLmnSh1qJiJFl0OpUqRK4oOy7NyRQtHWofhoYVxkwWXKUSn0YsS+fUV6lhqfYb
-6ayd3Z5qQdG1B7bvQzaJjwUV2lixMUZ7JVsOlfjWVr/3NB/uFvnySBN6Dcb6rGwaRM3wsormw5cC
-M9NxWy/bWdufudCvy8bOAjXjVVwta/uO21sE5RHBCzNFXtgq9ORtH4eYjVP4Yryo026nvkFmCeyA
-B29efV6ravCMK5JwWd5897Qrx7ll38o6iHDZ/rXPR//feevhF4l7wjUGX3xq1eeRfM4RSJGBIV1D
-z1gKPkfWag3mVBVvva1RlX5bAJTPR/2YqNtw/FkIYYEi/pIZiAdpcxpoHtmnYYoZtvhUftzdx5ZX
-JSKDW405zkGcZzzGZ6KEv4FI224oDmijlEf+xp6MJK5ojY/ASeVUR+wsKRuJ+XFZ5o7ZeEime8t1
-ouUsU6YjF5ZtUihhkGfCdFQLWQFJ3UXxmElfhQnR+eCdcDbkFZp6vTRmj56ApCihn5QGpaToNZmR
-n3NVSpZcQpZ2KEONusaiCsKAug0wkQbJSFO+PTSjneGxOuFjPlUk3ovWvdIerjUg9ZGIOtGq/qeX
-eCYrrCX+1UPsgTKGGRSbzd5q156d/gpfbJxe66eD5iQKrXj7RGgruGxs62qebBHUKS32CKluCiqZ
-qh+pmehmEb71noAUoe5e9Zm17S7773V10pjrtG4CmuurCV/n6zLK5turWNhqOvFXbjhZrMD0YhKe
-wR0zOyuvsh6MWrGoIuzvyWu5y1WIFAqmJselypxXh6dKLNOKEB98L88bS2rkNqqlKzCNJp9c0G0j
-Gzh0iRrCbHSXmPR643QS+4rWhgFmnSbSuXCjS0xAOWkU2UdLqyuUNfHSFdUouy3bm5i5GnDM3tG8
-doJ4r5tqu3pPbRSVfvs8uJzeNXhp3n4j/tZ42SwH7eaWUUOjc3qFV9453UHTXZfcLH+OeNs5g36x
-lBnHvTm7EbMbLeuaLncao8vWCXimfo1o+843Ak6y4ChNeGntvAYvfLK4ezmoyNIbNCLTCXO9ZV3A
-E8/s88RczPzDwI4Ob7XZyl7+9Miban29h+tJZPrE21wgvBphDfrrfPdCTPKJD/y98L1rZwHcV6Jq
-Zab0metpuNIX/qAFPoz171WUaUb4HAhBSzHuHfjzHb3kha/2Cctis/ORArVHNYfFyYRH2pYIRzic
-isVOfPWD1b6mRTqpCRBozzof6UZVvFXRxWIr3GGrEviGYgyPMfahheiSaLs/9QeFu7oZ/ndSY8DD
-ya9x+uPed+7mxN2IzIISBOMLFYWVqC3Pew1T2nFuuCiwZS5/v6II10i4t1OJcUH2U9zxKodHsGGv
-Oa+zkvNUYUOa/TCCRutF9MzDwdlUMJADTCGSbDQ5OV4PTamDoPEi6Ecc/RF5RWwkcdSXvSOaDWSn
-I9LlvubFTQpuc6JKXLcKeb+xdbKRBnwREemXyjg6ME65aJiOuBgrktzykfPLJBKR9ClMavJ62/Ff
-BlNIyod9yX9wcSXexnXFpvkrbXk64xsx5Db7wXKP5fSgsvwIMM/9631VLBfkmtbHRXpqmtei52hG
-pUwSlo+BASQoeILDOBgREECxBBh5/iYmNsQ9dIv5+OI++QkqdsJPc3uykz5fkM+OraeekcQF7X4n
-B5S67za5U967PmooGQhUXfF7afXyCD7ONdRe17QogYjVx38uLwtrS6nhTnm15LQUnu9E2uK6CNI/
-1HOABj0ESwOjut4FEpFQpdNAm4K2LHnDWHNcmKB2ioKBogysVZtMO2nSxUdZ8Yk2kJc7URioLVI0
-YgmtIwZj4LoeKemgnOnbUdGnzZ4Oa6scqiolBGqS6RgWNLu0RMhcaE6rhhU4hiuqFXPAG8fGwTPW
-FKeLMtdVmXLSs5YJGF/YeVm7rREMlY3UYE+yCxbaMXX8y15m5zVHq6GOKDMynzII/jdUHdyVqIy0
-ifX2+r/EgtZcvRzSb72gU9ui87M2VecjKildW/aFqaYhKoryUjfB/g4qtyVuc60xFDGmCxwjW+qu
-zjuwl2GkOWn66+3QiiEctvd04OVvcCVzjgT7lrkvjVGKKHmmlDUKowSeikb5kK/mJReuWOxONx+s
-ULsl+Lqb0CVn0SrVyJ6wt4t6yTeSCafhPhAf0OXn6L60UMxiLolFAtmN35S2Ob1lZpQ1r/n0Qb5D
-oQ1zJiRVDgF8N3Q8TYfbi3DyWCy3lT1nxyBs6FT3S2GOzWRlxwKvlRP0RPJA9SjxEy0UoEnkA+M4
-cnzLMJrBGWLFEaaUb5lvpqbq/loOaU5+DFuHPxo82/OZuM8FXG3oVNZhtWpMpb/0Xu5m/LfLhHZQ
-7yuVI0MqZ7NE43imC8jH3IwGZlbPm0xkJYs7+2U48hXTsFSMqgGDvai0kLxyynKNT/waj+q1c1tz
-GjOpPBgdCSq3UKZxCSsqFIY+O6JbAWGWcV1pwqLyj5sGqCF1xb1F3varUWqrJv6cN3PrUXzijtfZ
-FshpBL3Xwr4GIPvU2N8EjrJgS1zl21rbXQMXeXc5jjFyrhpCzijSv/RQtyPSzHCFMhlME95fHglt
-pRsX+dfSQjUeHAlpWzJ5iOo79Ldnaxai6bXTcGO3fp07ri7HLEmXXPlYi8bv/qVxvNcdra6m7Rlb
-6JBTb5fd66VhFRjGArh2n7R1rDW4P5NOT9K0I183T2scYkeZ3q/VFyLb09U9ajzXBS8Kgkhc4mBS
-kYY9cy3Vy9lUnuNJH8HGIclUilwnBtjUOH0gteGOZ4c/XNrhXLSYDyxfnD8z1pDy7rYRvDolhnbe
-UMzxCZUs40s6s7UIvBnLgc0+vKuOkIXeOrDymlp+Zxra4MZLBbVrqD/jTJ597pDmnw5c4+DbyB88
-9Cg9DodYcSuMZT/114pptqc/EuTjRPvH/z5slzI3tluOEBBLqOXLOX+0I5929tO97wkvl/atCz+y
-xJrdwteW2FNW/NSmBP+f/maYtVs/bYyBC7Ox3jsYZHL05CIrBa/nS+b3bHfiYm4Ueil1YZZSgAUI
-fFZ1dxUmeA2oQRQ3RuGXNGLFV9/XbGFGPV6kfzk1TBBCd+izc7q1H+OHMJwmaBX2IQNYVAKHYepV
-SSGCe6CnbYHHETKGNe43EDvFgZr0gB/nVHPHZ80VV1ojOiI3XDvYIkl4ayo4bxQIgrFXWTvBI0nH
-VElWMuw2aLUWCRHHf8ymVCHjFlJnOSojfevCYyyyZDH0IcvHhrsnQ5O1OsWzONuVVKIxSxiFZ/tR
-fKDAf6xFTnw4O9Qig2VCfW2hJQrmMOuHW0W3dLQmCMO2ccdUd/xyfflH/olTiHZVdGwb8nIwRzSE
-J15jFlOJuBZBZ4CiyHyd2IFylFlB+HgHhYabhWOGwYO1ZH/Og1dtQlFMk352CGRSIFTapnWQEUtN
-l4zv8S0aaCFDyGCBqDUxZYpxGHX01y/JuH1xhn7TOCnNCI4eKDs5WGX4R425F4vF1o3BJ4vO0otq
-I3rimI7jJY1jISqnBxknCIvruF83mF5wN4X7qGLIhR8A2Vg0yFERSIXn9Vv3GHy3Vj/WIkKddlYi
-yIMv2I/VMjTLpW7pt05SWIZR0RPyxpB4SIUM9lBPGBl0GC7oSEEwRYLe4pJpZY2P0zbI1n+Oc44w
-qY3PUnmF0ixjVpDD/mJ9wpOBGTVgXlaCaZiPcIWK5NiKBIiPdGaQ0TWGvAiG7nMchdZb7Vgf8zNi
-MuMyzRdy/lePe9iC4TRx7WhhOQI/QiSVNAmAa2lT/piFbuh7ofJoYSZzrSZ1bvmWw3eN2nKUPVky
-uPN5/VRfohRd0VYZoqhKIlU6TXYhJxmPUIloAwc1bPmHEpaZYZORHNlXUJM07hATwHR8MJYqkwWR
-WaIezFhxSFlc8/Fq82hEnpeRozg3ULhhr9lAGtVEkCg5ZNRuuVleBPaZadhG0ZgkyPmDOTOKzViM
-YgOcpukKqQcbjAWS0IleQ2ROjdh6A+md1qWdBRSX7iSYgFRTtRmBpJioieXJiHfJiMGIR9fJOn8I
-MSfXYhspn4ooSa2mSAj4n+8Bmg03fBJZoPOJgsVZRxu1oOMRPXYYjdqjihFaEoZpXBREanuJoRI6
-cibFinq4ngUKh/wQd/H5ofYCZ0HJXR62opZFaAT0iFIZo4DIiUojkjeqKiuoZirKo5Y1a7AWckGa
-BkuYoD5lpDK6eUs6CkDqpETwl1EqpfhJpVeKpVl6EgUAADs=
-
---BOUNDARY--
-
---OUTER--
diff --git a/future/tests/test_email/data/msg_14.txt b/future/tests/test_email/data/msg_14.txt
deleted file mode 100644
index 5d98d2fd..00000000
--- a/future/tests/test_email/data/msg_14.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-Return-Path:
-Delivered-To: bbb@zzz.org
-Received: by mail.zzz.org (Postfix, from userid 889)
- id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT)
-MIME-Version: 1.0
-Content-Type: text; charset=us-ascii
-Content-Transfer-Encoding: 7bit
-Message-ID: <15090.61304.110929.45684@aaa.zzz.org>
-From: bbb@ddd.com (John X. Doe)
-To: bbb@zzz.org
-Subject: This is a test message
-Date: Fri, 4 May 2001 14:05:44 -0400
-
-
-Hi,
-
-I'm sorry but I'm using a drainbread ISP, which although big and
-wealthy can't seem to generate standard compliant email. :(
-
-This message has a Content-Type: header with no subtype. I hope you
-can still read it.
-
--Me
diff --git a/future/tests/test_email/data/msg_15.txt b/future/tests/test_email/data/msg_15.txt
deleted file mode 100644
index 0025624e..00000000
--- a/future/tests/test_email/data/msg_15.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-Return-Path:
-Received: from fepD.post.tele.dk (195.41.46.149) by mail.groupcare.dk (LSMTP for Windows NT v1.1b) with SMTP id <0.0014F8A2@mail.groupcare.dk>; Mon, 30 Apr 2001 12:17:50 +0200
-User-Agent: Microsoft-Outlook-Express-Macintosh-Edition/5.02.2106
-Subject: XX
-From: xx@xx.dk
-To: XX
-Message-ID:
-Mime-version: 1.0
-Content-type: multipart/mixed;
- boundary="MS_Mac_OE_3071477847_720252_MIME_Part"
-
-> Denne meddelelse er i MIME-format. Da dit postl
-
---MS_Mac_OE_3071477847_720252_MIME_Part
-Content-type: multipart/alternative;
- boundary="MS_Mac_OE_3071477847_720252_MIME_Part"
-
-
---MS_Mac_OE_3071477847_720252_MIME_Part
-Content-type: text/plain; charset="ISO-8859-1"
-Content-transfer-encoding: quoted-printable
-
-Some removed test.
-
---MS_Mac_OE_3071477847_720252_MIME_Part
-Content-type: text/html; charset="ISO-8859-1"
-Content-transfer-encoding: quoted-printable
-
-
-
-Some removed HTML
-
-
-Some removed text.
-
-
-
-
---MS_Mac_OE_3071477847_720252_MIME_Part--
-
-
---MS_Mac_OE_3071477847_720252_MIME_Part
-Content-type: image/gif; name="xx.gif";
- x-mac-creator="6F676C65";
- x-mac-type="47494666"
-Content-disposition: attachment
-Content-transfer-encoding: base64
-
-Some removed base64 encoded chars.
-
---MS_Mac_OE_3071477847_720252_MIME_Part--
-
diff --git a/future/tests/test_email/data/msg_16.txt b/future/tests/test_email/data/msg_16.txt
deleted file mode 100644
index 56167e9f..00000000
--- a/future/tests/test_email/data/msg_16.txt
+++ /dev/null
@@ -1,123 +0,0 @@
-Return-Path: <>
-Delivered-To: scr-admin@socal-raves.org
-Received: from cougar.noc.ucla.edu (cougar.noc.ucla.edu [169.232.10.18])
- by babylon.socal-raves.org (Postfix) with ESMTP id CCC2C51B84
- for ; Sun, 23 Sep 2001 20:13:54 -0700 (PDT)
-Received: from sims-ms-daemon by cougar.noc.ucla.edu
- (Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10)
- id <0GK500B01D0B8Y@cougar.noc.ucla.edu> for scr-admin@socal-raves.org; Sun,
- 23 Sep 2001 20:14:35 -0700 (PDT)
-Received: from cougar.noc.ucla.edu
- (Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10)
- id <0GK500B01D0B8X@cougar.noc.ucla.edu>; Sun, 23 Sep 2001 20:14:35 -0700 (PDT)
-Date: Sun, 23 Sep 2001 20:14:35 -0700 (PDT)
-From: Internet Mail Delivery
-Subject: Delivery Notification: Delivery has failed
-To: scr-admin@socal-raves.org
-Message-id: <0GK500B04D0B8X@cougar.noc.ucla.edu>
-MIME-version: 1.0
-Sender: scr-owner@socal-raves.org
-Errors-To: scr-owner@socal-raves.org
-X-BeenThere: scr@socal-raves.org
-X-Mailman-Version: 2.1a3
-Precedence: bulk
-List-Help:
-List-Post:
-List-Subscribe: ,
-
-List-Id: SoCal-Raves
-List-Unsubscribe: ,
-
-List-Archive:
-Content-Type: multipart/report; boundary="Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)"
-
-
---Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)
-Content-type: text/plain; charset=ISO-8859-1
-
-This report relates to a message you sent with the following header fields:
-
- Message-id: <002001c144a6$8752e060$56104586@oxy.edu>
- Date: Sun, 23 Sep 2001 20:10:55 -0700
- From: "Ian T. Henry"
- To: SoCal Raves
- Subject: [scr] yeah for Ians!!
-
-Your message cannot be delivered to the following recipients:
-
- Recipient address: jangel1@cougar.noc.ucla.edu
- Reason: recipient reached disk quota
-
-
---Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)
-Content-type: message/DELIVERY-STATUS
-
-Original-envelope-id: 0GK500B4HD0888@cougar.noc.ucla.edu
-Reporting-MTA: dns; cougar.noc.ucla.edu
-
-Action: failed
-Status: 5.0.0 (recipient reached disk quota)
-Original-recipient: rfc822;jangel1@cougar.noc.ucla.edu
-Final-recipient: rfc822;jangel1@cougar.noc.ucla.edu
-
---Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)
-Content-type: MESSAGE/RFC822
-
-Return-path: scr-admin@socal-raves.org
-Received: from sims-ms-daemon by cougar.noc.ucla.edu
- (Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10)
- id <0GK500B01D0B8X@cougar.noc.ucla.edu>; Sun, 23 Sep 2001 20:14:35 -0700 (PDT)
-Received: from panther.noc.ucla.edu by cougar.noc.ucla.edu
- (Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10)
- with ESMTP id <0GK500B4GD0888@cougar.noc.ucla.edu> for jangel1@sims-ms-daemon;
- Sun, 23 Sep 2001 20:14:33 -0700 (PDT)
-Received: from babylon.socal-raves.org
- (ip-209-85-222-117.dreamhost.com [209.85.222.117])
- by panther.noc.ucla.edu (8.9.1a/8.9.1) with ESMTP id UAA09793 for
- ; Sun, 23 Sep 2001 20:14:32 -0700 (PDT)
-Received: from babylon (localhost [127.0.0.1]) by babylon.socal-raves.org
- (Postfix) with ESMTP id D3B2951B70; Sun, 23 Sep 2001 20:13:47 -0700 (PDT)
-Received: by babylon.socal-raves.org (Postfix, from userid 60001)
- id A611F51B82; Sun, 23 Sep 2001 20:13:46 -0700 (PDT)
-Received: from tiger.cc.oxy.edu (tiger.cc.oxy.edu [134.69.3.112])
- by babylon.socal-raves.org (Postfix) with ESMTP id ADA7351B70 for
- ; Sun, 23 Sep 2001 20:13:44 -0700 (PDT)
-Received: from ent (n16h86.dhcp.oxy.edu [134.69.16.86])
- by tiger.cc.oxy.edu (8.8.8/8.8.8) with SMTP id UAA08100 for
- ; Sun, 23 Sep 2001 20:14:24 -0700 (PDT)
-Date: Sun, 23 Sep 2001 20:10:55 -0700
-From: "Ian T. Henry"
-Subject: [scr] yeah for Ians!!
-Sender: scr-admin@socal-raves.org
-To: SoCal Raves
-Errors-to: scr-admin@socal-raves.org
-Message-id: <002001c144a6$8752e060$56104586@oxy.edu>
-MIME-version: 1.0
-X-Mailer: Microsoft Outlook Express 5.50.4522.1200
-Content-type: text/plain; charset=us-ascii
-Precedence: bulk
-Delivered-to: scr-post@babylon.socal-raves.org
-Delivered-to: scr@socal-raves.org
-X-Converted-To-Plain-Text: from multipart/alternative by demime 0.98e
-X-Converted-To-Plain-Text: Alternative section used was text/plain
-X-BeenThere: scr@socal-raves.org
-X-Mailman-Version: 2.1a3
-List-Help:
-List-Post:
-List-Subscribe: ,
-
-List-Id: SoCal-Raves
-List-Unsubscribe: ,
-
-List-Archive:
-
-I always love to find more Ian's that are over 3 years old!!
-
-Ian
-_______________________________________________
-For event info, list questions, or to unsubscribe, see http://www.socal-raves.org/
-
-
-
---Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)--
-
diff --git a/future/tests/test_email/data/msg_17.txt b/future/tests/test_email/data/msg_17.txt
deleted file mode 100644
index 8d86e418..00000000
--- a/future/tests/test_email/data/msg_17.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-MIME-Version: 1.0
-From: Barry
-To: Dingus Lovers
-Subject: Here is your dingus fish
-Date: Fri, 20 Apr 2001 19:35:02 -0400
-Content-Type: multipart/mixed; boundary="BOUNDARY"
-
-Hi there,
-
-This is the dingus fish.
-
-[Non-text (image/gif) part of message omitted, filename dingusfish.gif]
diff --git a/future/tests/test_email/data/msg_18.txt b/future/tests/test_email/data/msg_18.txt
deleted file mode 100644
index f9f4904d..00000000
--- a/future/tests/test_email/data/msg_18.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-Content-Type: text/plain; charset="us-ascii"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-X-Foobar-Spoink-Defrobnit: wasnipoop; giraffes="very-long-necked-animals";
- spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"
-
diff --git a/future/tests/test_email/data/msg_19.txt b/future/tests/test_email/data/msg_19.txt
deleted file mode 100644
index 49bf7fcc..00000000
--- a/future/tests/test_email/data/msg_19.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-Send Ppp mailing list submissions to
- ppp@zzz.org
-
-To subscribe or unsubscribe via the World Wide Web, visit
- http://www.zzz.org/mailman/listinfo/ppp
-or, via email, send a message with subject or body 'help' to
- ppp-request@zzz.org
-
-You can reach the person managing the list at
- ppp-admin@zzz.org
-
-When replying, please edit your Subject line so it is more specific
-than "Re: Contents of Ppp digest..."
-
-Today's Topics:
-
- 1. testing #1 (Barry A. Warsaw)
- 2. testing #2 (Barry A. Warsaw)
- 3. testing #3 (Barry A. Warsaw)
- 4. testing #4 (Barry A. Warsaw)
- 5. testing #5 (Barry A. Warsaw)
-
-hello
-
-
-hello
-
-
-hello
-
-
-hello
-
-
-hello
-
-
-
-_______________________________________________
-Ppp mailing list
-Ppp@zzz.org
-http://www.zzz.org/mailman/listinfo/ppp
-
diff --git a/future/tests/test_email/data/msg_20.txt b/future/tests/test_email/data/msg_20.txt
deleted file mode 100644
index 1a6a8878..00000000
--- a/future/tests/test_email/data/msg_20.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-Return-Path:
-Delivered-To: bbb@zzz.org
-Received: by mail.zzz.org (Postfix, from userid 889)
- id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT)
-MIME-Version: 1.0
-Content-Type: text/plain; charset=us-ascii
-Content-Transfer-Encoding: 7bit
-Message-ID: <15090.61304.110929.45684@aaa.zzz.org>
-From: bbb@ddd.com (John X. Doe)
-To: bbb@zzz.org
-Cc: ccc@zzz.org
-CC: ddd@zzz.org
-cc: eee@zzz.org
-Subject: This is a test message
-Date: Fri, 4 May 2001 14:05:44 -0400
-
-
-Hi,
-
-Do you like this message?
-
--Me
diff --git a/future/tests/test_email/data/msg_21.txt b/future/tests/test_email/data/msg_21.txt
deleted file mode 100644
index 23590b25..00000000
--- a/future/tests/test_email/data/msg_21.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-From: aperson@dom.ain
-To: bperson@dom.ain
-Subject: Test
-Content-Type: multipart/mixed; boundary="BOUNDARY"
-
-MIME message
---BOUNDARY
-Content-Type: text/plain; charset="us-ascii"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-
-One
---BOUNDARY
-Content-Type: text/plain; charset="us-ascii"
-MIME-Version: 1.0
-Content-Transfer-Encoding: 7bit
-
-Two
---BOUNDARY--
-End of MIME message
diff --git a/future/tests/test_email/data/msg_22.txt b/future/tests/test_email/data/msg_22.txt
deleted file mode 100644
index af9de5fa..00000000
--- a/future/tests/test_email/data/msg_22.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-Mime-Version: 1.0
-Message-Id:
-Date: Tue, 16 Oct 2001 13:59:25 +0300
-To: a@example.com
-From: b@example.com
-Content-Type: multipart/mixed; boundary="============_-1208892523==_============"
-
---============_-1208892523==_============
-Content-Type: text/plain; charset="us-ascii" ; format="flowed"
-
-Text text text.
---============_-1208892523==_============
-Content-Id:
-Content-Type: image/jpeg; name="wibble.JPG"
- ; x-mac-type="4A504547"
- ; x-mac-creator="474B4F4E"
-Content-Disposition: attachment; filename="wibble.JPG"
-Content-Transfer-Encoding: base64
-
-/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
-AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAALCAXABIEBAREA
-g6bCjjw/pIZSjO6FWFpldjySOmCNrO7DBZibUXhTwtCixw+GtAijVdqxxaPp0aKvmGXa
-qrbBQvms0mAMeYS/3iTV1dG0hHaRNK01XblnWxtVdjkHLMIgTyqnk9VB7CrP2KzIINpa
-4O7I+zxYO9WV8jZg71Zlb+8rMDkEirAVQFAUAKAFAAAUAYAUDgADgY6DjpRtXj5RxjHA
-4wQRj0wQCMdCAewpaKKK/9k=
---============_-1208892523==_============
-Content-Id:
-Content-Type: image/jpeg; name="wibble2.JPG"
- ; x-mac-type="4A504547"
- ; x-mac-creator="474B4F4E"
-Content-Disposition: attachment; filename="wibble2.JPG"
-Content-Transfer-Encoding: base64
-
-/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
-AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAALCAXABJ0BAREA
-/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA
-W6NFJJBEkU10kKGTcWMDwxuU+0JHvk8qAtOpNwqSR0n8c3BlDyXHlqsUltHEiTvdXLxR
-7vMiGDNJAJWkAMk8ZkCFp5G2oo5W++INrbQtNfTQxJAuXlupz9oS4d5Y1W+E2XlWZJJE
-Y7LWYQxTLE1zuMbfBPxw8X2fibVdIbSbI6nLZxX635t9TjtYreWR7WGKJTLJFFKSlozO
-0ShxIXM43uC3/9k=
---============_-1208892523==_============
-Content-Type: text/plain; charset="us-ascii" ; format="flowed"
-
-Text text text.
---============_-1208892523==_============--
-
diff --git a/future/tests/test_email/data/msg_23.txt b/future/tests/test_email/data/msg_23.txt
deleted file mode 100644
index bb2e8ec3..00000000
--- a/future/tests/test_email/data/msg_23.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-From: aperson@dom.ain
-Content-Type: multipart/mixed; boundary="BOUNDARY"
-
---BOUNDARY
-Content-Type: text/plain
-
-A message part
---BOUNDARY--
diff --git a/future/tests/test_email/data/msg_24.txt b/future/tests/test_email/data/msg_24.txt
deleted file mode 100644
index 4e52339e..00000000
--- a/future/tests/test_email/data/msg_24.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Content-Type: multipart/mixed; boundary="BOUNDARY"
-MIME-Version: 1.0
-Subject: A subject
-To: aperson@dom.ain
-From: bperson@dom.ain
-
---BOUNDARY
-
-
---BOUNDARY--
diff --git a/future/tests/test_email/data/msg_25.txt b/future/tests/test_email/data/msg_25.txt
deleted file mode 100644
index 9e35275f..00000000
--- a/future/tests/test_email/data/msg_25.txt
+++ /dev/null
@@ -1,117 +0,0 @@
-From MAILER-DAEMON Fri Apr 06 16:46:09 2001
-Received: from [204.245.199.98] (helo=zinfandel.lacita.com)
- by www.linux.org.uk with esmtp (Exim 3.13 #1)
- id 14lYR6-0008Iv-00
- for linuxuser-admin@www.linux.org.uk; Fri, 06 Apr 2001 16:46:09 +0100
-Received: from localhost (localhost) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with internal id JAB03225; Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800)
-Date: Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800)
-From: Mail Delivery Subsystem
-Subject: Returned mail: Too many hops 19 (17 max): from via [199.164.235.226], to
-Message-Id: <200104061723.JAB03225@zinfandel.lacita.com>
-To:
-To: postmaster@zinfandel.lacita.com
-MIME-Version: 1.0
-Content-Type: multipart/report; report-type=delivery-status;
- bo
-Auto-Submitted: auto-generated (failure)
-
-This is a MIME-encapsulated message
-
---JAB03225.986577786/zinfandel.lacita.com
-
-The original message was received at Fri, 6 Apr 2001 09:23:03 -0800 (GMT-0800)
-from [199.164.235.226]
-
- ----- The following addresses have delivery notifications -----
- (unrecoverable error)
-
- ----- Transcript of session follows -----
-554 Too many hops 19 (17 max): from via [199.164.235.226], to
-
---JAB03225.986577786/zinfandel.lacita.com
-Content-Type: message/delivery-status
-
-Reporting-MTA: dns; zinfandel.lacita.com
-Received-From-MTA: dns; [199.164.235.226]
-Arrival-Date: Fri, 6 Apr 2001 09:23:03 -0800 (GMT-0800)
-
-Final-Recipient: rfc822; scoffman@wellpartner.com
-Action: failed
-Status: 5.4.6
-Last-Attempt-Date: Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800)
-
---JAB03225.986577786/zinfandel.lacita.com
-Content-Type: text/rfc822-headers
-
-Return-Path: linuxuser-admin@www.linux.org.uk
-Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03225 for ; Fri, 6 Apr 2001 09:23:03 -0800 (GMT-0800)
-Received: from zinfandel.lacita.com ([204.245.199.98])
- by
- fo
-Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03221 for ; Fri, 6 Apr 2001 09:22:18 -0800 (GMT-0800)
-Received: from zinfandel.lacita.com ([204.245.199.98])
- by
- fo
-Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03217 for ; Fri, 6 Apr 2001 09:21:37 -0800 (GMT-0800)
-Received: from zinfandel.lacita.com ([204.245.199.98])
- by
- fo
-Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03213 for ; Fri, 6 Apr 2001 09:20:56 -0800 (GMT-0800)
-Received: from zinfandel.lacita.com ([204.245.199.98])
- by
- fo
-Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03209 for ; Fri, 6 Apr 2001 09:20:15 -0800 (GMT-0800)
-Received: from zinfandel.lacita.com ([204.245.199.98])
- by
- fo
-Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03205 for ; Fri, 6 Apr 2001 09:19:33 -0800 (GMT-0800)
-Received: from zinfandel.lacita.com ([204.245.199.98])
- by
- fo
-Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03201 for ; Fri, 6 Apr 2001 09:18:52 -0800 (GMT-0800)
-Received: from zinfandel.lacita.com ([204.245.199.98])
- by
- fo
-Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03197 for ; Fri, 6 Apr 2001 09:17:54 -0800 (GMT-0800)
-Received: from www.linux.org.uk (parcelfarce.linux.theplanet.co.uk [195.92.249.252])
- by
- fo
-Received: from localhost.localdomain
- ([
- by
- id
-Received: from [212.1.130.11] (helo=s1.uklinux.net ident=root)
- by
- id
- fo
-Received: from server (ppp-2-22.cvx4.telinco.net [212.1.149.22])
- by
- fo
-From: Daniel James
-Organization: LinuxUser
-To: linuxuser@www.linux.org.uk
-X-Mailer: KMail [version 1.1.99]
-Content-Type: text/plain;
- c
-MIME-Version: 1.0
-Message-Id: <01040616033903.00962@server>
-Content-Transfer-Encoding: 8bit
-Subject: [LinuxUser] bulletin no. 45
-Sender: linuxuser-admin@www.linux.org.uk
-Errors-To: linuxuser-admin@www.linux.org.uk
-X-BeenThere: linuxuser@www.linux.org.uk
-X-Mailman-Version: 2.0.3
-Precedence: bulk
-List-Help:
-List-Post:
-List-Subscribe: ,
-
-List-Unsubscribe: ,
-
-Date: Fri, 6 Apr 2001 16:03:39 +0100
-
---JAB03225.986577786/zinfandel.lacita.com--
-
-
diff --git a/future/tests/test_email/data/msg_26.txt b/future/tests/test_email/data/msg_26.txt
deleted file mode 100644
index 58efaa9c..00000000
--- a/future/tests/test_email/data/msg_26.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-Received: from xcar [192.168.0.2] by jeeves.wooster.local
- (SMTPD32-7.07 EVAL) id AFF92F0214; Sun, 12 May 2002 08:55:37 +0100
-Date: Sun, 12 May 2002 08:56:15 +0100
-From: Father Time
-To: timbo@jeeves.wooster.local
-Subject: IMAP file test
-Message-ID: <6df65d354b.father.time@rpc.wooster.local>
-X-Organization: Home
-User-Agent: Messenger-Pro/2.50a (MsgServe/1.50) (RISC-OS/4.02) POPstar/2.03
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="1618492860--2051301190--113853680"
-Status: R
-X-UIDL: 319998302
-
-This message is in MIME format which your mailer apparently does not support.
-You either require a newer version of your software which supports MIME, or
-a separate MIME decoding utility. Alternatively, ask the sender of this
-message to resend it in a different format.
-
---1618492860--2051301190--113853680
-Content-Type: text/plain; charset=us-ascii
-
-Simple email with attachment.
-
-
---1618492860--2051301190--113853680
-Content-Type: application/riscos; name="clock.bmp,69c"; type=BMP;
- load=&fff69c4b; exec=&355dd4d1; access=&03
-Content-Disposition: attachment; filename="clock.bmp"
-Content-Transfer-Encoding: base64
-
-Qk12AgAAAAAAAHYAAAAoAAAAIAAAACAAAAABAAQAAAAAAAAAAADXDQAA1w0AAAAAAAAA
-AAAAAAAAAAAAiAAAiAAAAIiIAIgAAACIAIgAiIgAALu7uwCIiIgAERHdACLuIgAz//8A
-zAAAAN0R3QDu7iIA////AAAAAAAAAAAAAAAAAAAAAAAAAAi3AAAAAAAAADeAAAAAAAAA
-C3ADMzMzMANwAAAAAAAAAAAHMAAAAANwAAAAAAAAAACAMAd3zPfwAwgAAAAAAAAIAwd/
-f8x/f3AwgAAAAAAAgDB0x/f3//zPAwgAAAAAAAcHfM9////8z/AwAAAAAAiwd/f3////
-////A4AAAAAAcEx/f///////zAMAAAAAiwfM9////3///8zwOAAAAAcHf3////B/////
-8DAAAAALB/f3///wd3d3//AwAAAABwTPf//wCQAAD/zAMAAAAAsEx/f///B////8wDAA
-AAAHB39////wf/////AwAAAACwf39///8H/////wMAAAAIcHfM9///B////M8DgAAAAA
-sHTH///wf///xAMAAAAACHB3f3//8H////cDgAAAAAALB3zH//D//M9wMAAAAAAAgLB0
-z39///xHAwgAAAAAAAgLB3d3RHd3cDCAAAAAAAAAgLAHd0R3cAMIAAAAAAAAgAgLcAAA
-AAMwgAgAAAAACDAAAAu7t7cwAAgDgAAAAABzcIAAAAAAAAgDMwAAAAAAN7uwgAAAAAgH
-MzMAAAAACH97tzAAAAALu3c3gAAAAAAL+7tzDABAu7f7cAAAAAAACA+3MA7EQAv/sIAA
-AAAAAAAIAAAAAAAAAIAAAAAA
-
---1618492860--2051301190--113853680--
diff --git a/future/tests/test_email/data/msg_27.txt b/future/tests/test_email/data/msg_27.txt
deleted file mode 100644
index d0191769..00000000
--- a/future/tests/test_email/data/msg_27.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-Return-Path:
-Received: by mail.dom.ain (Postfix, from userid 889)
- id B9D0AD35DB; Tue, 4 Jun 2002 21:46:59 -0400 (EDT)
-Message-ID: <15613.28051.707126.569693@dom.ain>
-Date: Tue, 4 Jun 2002 21:46:59 -0400
-MIME-Version: 1.0
-Content-Type: text/plain; charset=us-ascii
-Content-Transfer-Encoding: 7bit
-Subject: bug demonstration
- 12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
- more text
-From: aperson@dom.ain (Anne P. Erson)
-To: bperson@dom.ain (Barney P. Erson)
-
-test
diff --git a/future/tests/test_email/data/msg_28.txt b/future/tests/test_email/data/msg_28.txt
deleted file mode 100644
index 1e4824ca..00000000
--- a/future/tests/test_email/data/msg_28.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-From: aperson@dom.ain
-MIME-Version: 1.0
-Content-Type: multipart/digest; boundary=BOUNDARY
-
---BOUNDARY
-Content-Type: message/rfc822
-
-Content-Type: text/plain; charset=us-ascii
-To: aa@bb.org
-From: cc@dd.org
-Subject: ee
-
-message 1
-
---BOUNDARY
-Content-Type: message/rfc822
-
-Content-Type: text/plain; charset=us-ascii
-To: aa@bb.org
-From: cc@dd.org
-Subject: ee
-
-message 2
-
---BOUNDARY--
diff --git a/future/tests/test_email/data/msg_29.txt b/future/tests/test_email/data/msg_29.txt
deleted file mode 100644
index 1fab5616..00000000
--- a/future/tests/test_email/data/msg_29.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-Return-Path:
-Delivered-To: bbb@zzz.org
-Received: by mail.zzz.org (Postfix, from userid 889)
- id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT)
-MIME-Version: 1.0
-Content-Type: text/plain; charset=us-ascii;
- title*0*="us-ascii'en'This%20is%20even%20more%20";
- title*1*="%2A%2A%2Afun%2A%2A%2A%20";
- title*2="isn't it!"
-Content-Transfer-Encoding: 7bit
-Message-ID: <15090.61304.110929.45684@aaa.zzz.org>
-From: bbb@ddd.com (John X. Doe)
-To: bbb@zzz.org
-Subject: This is a test message
-Date: Fri, 4 May 2001 14:05:44 -0400
-
-
-Hi,
-
-Do you like this message?
-
--Me
diff --git a/future/tests/test_email/data/msg_30.txt b/future/tests/test_email/data/msg_30.txt
deleted file mode 100644
index 4334bb6e..00000000
--- a/future/tests/test_email/data/msg_30.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-From: aperson@dom.ain
-MIME-Version: 1.0
-Content-Type: multipart/digest; boundary=BOUNDARY
-
---BOUNDARY
-
-Content-Type: text/plain; charset=us-ascii
-To: aa@bb.org
-From: cc@dd.org
-Subject: ee
-
-message 1
-
---BOUNDARY
-
-Content-Type: text/plain; charset=us-ascii
-To: aa@bb.org
-From: cc@dd.org
-Subject: ee
-
-message 2
-
---BOUNDARY--
diff --git a/future/tests/test_email/data/msg_31.txt b/future/tests/test_email/data/msg_31.txt
deleted file mode 100644
index 1e58e56c..00000000
--- a/future/tests/test_email/data/msg_31.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-From: aperson@dom.ain
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary=BOUNDARY_
-
---BOUNDARY
-Content-Type: text/plain
-
-message 1
-
---BOUNDARY
-Content-Type: text/plain
-
-message 2
-
---BOUNDARY--
diff --git a/future/tests/test_email/data/msg_32.txt b/future/tests/test_email/data/msg_32.txt
deleted file mode 100644
index 07ec5af9..00000000
--- a/future/tests/test_email/data/msg_32.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Delivered-To: freebsd-isp@freebsd.org
-Date: Tue, 26 Sep 2000 12:23:03 -0500
-From: Anne Person
-To: Barney Dude
-Subject: Re: Limiting Perl CPU Utilization...
-Mime-Version: 1.0
-Content-Type: text/plain; charset*=ansi-x3.4-1968''us-ascii
-Content-Disposition: inline
-User-Agent: Mutt/1.3.8i
-Sender: owner-freebsd-isp@FreeBSD.ORG
-Precedence: bulk
-X-Loop: FreeBSD.org
-
-Some message.
diff --git a/future/tests/test_email/data/msg_33.txt b/future/tests/test_email/data/msg_33.txt
deleted file mode 100644
index 042787a4..00000000
--- a/future/tests/test_email/data/msg_33.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-Delivered-To: freebsd-isp@freebsd.org
-Date: Wed, 27 Sep 2000 11:11:09 -0500
-From: Anne Person
-To: Barney Dude
-Subject: Re: Limiting Perl CPU Utilization...
-Mime-Version: 1.0
-Content-Type: multipart/signed; micalg*=ansi-x3.4-1968''pgp-md5;
- protocol*=ansi-x3.4-1968''application%2Fpgp-signature;
- boundary*="ansi-x3.4-1968''EeQfGwPcQSOJBaQU"
-Content-Disposition: inline
-Sender: owner-freebsd-isp@FreeBSD.ORG
-Precedence: bulk
-X-Loop: FreeBSD.org
-
-
---EeQfGwPcQSOJBaQU
-Content-Type: text/plain; charset*=ansi-x3.4-1968''us-ascii
-Content-Disposition: inline
-Content-Transfer-Encoding: quoted-printable
-
-part 1
-
---EeQfGwPcQSOJBaQU
-Content-Type: text/plain
-Content-Disposition: inline
-
-part 2
-
---EeQfGwPcQSOJBaQU--
diff --git a/future/tests/test_email/data/msg_34.txt b/future/tests/test_email/data/msg_34.txt
deleted file mode 100644
index 055dfea5..00000000
--- a/future/tests/test_email/data/msg_34.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-From: aperson@dom.ain
-To: bperson@dom.ain
-Content-Type: multipart/digest; boundary=XYZ
-
---XYZ
-Content-Type: text/plain
-
-
-This is a text plain part that is counter to recommended practice in
-RFC 2046, $5.1.5, but is not illegal
-
---XYZ
-
-From: cperson@dom.ain
-To: dperson@dom.ain
-
-A submessage
-
---XYZ--
diff --git a/future/tests/test_email/data/msg_35.txt b/future/tests/test_email/data/msg_35.txt
deleted file mode 100644
index be7d5a2f..00000000
--- a/future/tests/test_email/data/msg_35.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-From: aperson@dom.ain
-To: bperson@dom.ain
-Subject: here's something interesting
-counter to RFC 2822, there's no separating newline here
diff --git a/future/tests/test_email/data/msg_36.txt b/future/tests/test_email/data/msg_36.txt
deleted file mode 100644
index 5632c306..00000000
--- a/future/tests/test_email/data/msg_36.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-Mime-Version: 1.0
-Content-Type: Multipart/Mixed; Boundary="NextPart"
-To: IETF-Announce:;
-From: Internet-Drafts@ietf.org
-Subject: I-D ACTION:draft-ietf-mboned-mix-00.txt
-Date: Tue, 22 Dec 1998 16:55:06 -0500
-
---NextPart
-
-Blah blah blah
-
---NextPart
-Content-Type: Multipart/Alternative; Boundary="OtherAccess"
-
---OtherAccess
-Content-Type: Message/External-body;
- access-type="mail-server";
- server="mailserv@ietf.org"
-
-Content-Type: text/plain
-Content-ID: <19981222151406.I-D@ietf.org>
-
-ENCODING mime
-FILE /internet-drafts/draft-ietf-mboned-mix-00.txt
-
---OtherAccess
-Content-Type: Message/External-body;
- name="draft-ietf-mboned-mix-00.txt";
- site="ftp.ietf.org";
- access-type="anon-ftp";
- directory="internet-drafts"
-
-Content-Type: text/plain
-Content-ID: <19981222151406.I-D@ietf.org>
-
-
---OtherAccess--
-
---NextPart--
-
diff --git a/future/tests/test_email/data/msg_37.txt b/future/tests/test_email/data/msg_37.txt
deleted file mode 100644
index 038d34a1..00000000
--- a/future/tests/test_email/data/msg_37.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-Content-Type: multipart/mixed; boundary=ABCDE
-
---ABCDE
-Content-Type: text/x-one
-
-Blah
-
---ABCDE
---ABCDE
-Content-Type: text/x-two
-
-Blah
-
---ABCDE
---ABCDE
---ABCDE
---ABCDE
-Content-Type: text/x-two
-
-Blah
-
---ABCDE--
diff --git a/future/tests/test_email/data/msg_38.txt b/future/tests/test_email/data/msg_38.txt
deleted file mode 100644
index 006df81c..00000000
--- a/future/tests/test_email/data/msg_38.txt
+++ /dev/null
@@ -1,101 +0,0 @@
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0"
-
-------- =_aaaaaaaaaa0
-Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa1"
-Content-ID: <20592.1022586929.1@example.com>
-
-------- =_aaaaaaaaaa1
-Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa2"
-Content-ID: <20592.1022586929.2@example.com>
-
-------- =_aaaaaaaaaa2
-Content-Type: text/plain
-Content-ID: <20592.1022586929.3@example.com>
-Content-Description: very tricky
-Content-Transfer-Encoding: 7bit
-
-
-Unlike the test test_nested-multiples-with-internal-boundary, this
-piece of text not only contains the outer boundary tags
-------- =_aaaaaaaaaa1
-and
-------- =_aaaaaaaaaa0
-but puts them at the start of a line! And, to be even nastier, it
-even includes a couple of end tags, such as this one:
-
-------- =_aaaaaaaaaa1--
-
-and this one, which is from a multipart we haven't even seen yet!
-
-------- =_aaaaaaaaaa4--
-
-This will, I'm sure, cause much breakage of MIME parsers. But, as
-far as I can tell, it's perfectly legal. I have not yet ever seen
-a case of this in the wild, but I've seen *similar* things.
-
-
-------- =_aaaaaaaaaa2
-Content-Type: application/octet-stream
-Content-ID: <20592.1022586929.4@example.com>
-Content-Description: patch2
-Content-Transfer-Encoding: base64
-
-XXX
-
-------- =_aaaaaaaaaa2--
-
-------- =_aaaaaaaaaa1
-Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa3"
-Content-ID: <20592.1022586929.6@example.com>
-
-------- =_aaaaaaaaaa3
-Content-Type: application/octet-stream
-Content-ID: <20592.1022586929.7@example.com>
-Content-Description: patch3
-Content-Transfer-Encoding: base64
-
-XXX
-
-------- =_aaaaaaaaaa3
-Content-Type: application/octet-stream
-Content-ID: <20592.1022586929.8@example.com>
-Content-Description: patch4
-Content-Transfer-Encoding: base64
-
-XXX
-
-------- =_aaaaaaaaaa3--
-
-------- =_aaaaaaaaaa1
-Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa4"
-Content-ID: <20592.1022586929.10@example.com>
-
-------- =_aaaaaaaaaa4
-Content-Type: application/octet-stream
-Content-ID: <20592.1022586929.11@example.com>
-Content-Description: patch5
-Content-Transfer-Encoding: base64
-
-XXX
-
-------- =_aaaaaaaaaa4
-Content-Type: application/octet-stream
-Content-ID: <20592.1022586929.12@example.com>
-Content-Description: patch6
-Content-Transfer-Encoding: base64
-
-XXX
-
-------- =_aaaaaaaaaa4--
-
-------- =_aaaaaaaaaa1--
-
-------- =_aaaaaaaaaa0
-Content-Type: text/plain; charset="us-ascii"
-Content-ID: <20592.1022586929.15@example.com>
-
---
-It's never too late to have a happy childhood.
-
-------- =_aaaaaaaaaa0--
diff --git a/future/tests/test_email/data/msg_39.txt b/future/tests/test_email/data/msg_39.txt
deleted file mode 100644
index 124b2691..00000000
--- a/future/tests/test_email/data/msg_39.txt
+++ /dev/null
@@ -1,83 +0,0 @@
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0"
-
-------- =_aaaaaaaaaa0
-Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa1"
-Content-ID: <20592.1022586929.1@example.com>
-
-------- =_aaaaaaaaaa1
-Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa1"
-Content-ID: <20592.1022586929.2@example.com>
-
-------- =_aaaaaaaaaa1
-Content-Type: application/octet-stream
-Content-ID: <20592.1022586929.3@example.com>
-Content-Description: patch1
-Content-Transfer-Encoding: base64
-
-XXX
-
-------- =_aaaaaaaaaa1
-Content-Type: application/octet-stream
-Content-ID: <20592.1022586929.4@example.com>
-Content-Description: patch2
-Content-Transfer-Encoding: base64
-
-XXX
-
-------- =_aaaaaaaaaa1--
-
-------- =_aaaaaaaaaa1
-Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa1"
-Content-ID: <20592.1022586929.6@example.com>
-
-------- =_aaaaaaaaaa1
-Content-Type: application/octet-stream
-Content-ID: <20592.1022586929.7@example.com>
-Content-Description: patch3
-Content-Transfer-Encoding: base64
-
-XXX
-
-------- =_aaaaaaaaaa1
-Content-Type: application/octet-stream
-Content-ID: <20592.1022586929.8@example.com>
-Content-Description: patch4
-Content-Transfer-Encoding: base64
-
-XXX
-
-------- =_aaaaaaaaaa1--
-
-------- =_aaaaaaaaaa1
-Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa1"
-Content-ID: <20592.1022586929.10@example.com>
-
-------- =_aaaaaaaaaa1
-Content-Type: application/octet-stream
-Content-ID: <20592.1022586929.11@example.com>
-Content-Description: patch5
-Content-Transfer-Encoding: base64
-
-XXX
-
-------- =_aaaaaaaaaa1
-Content-Type: application/octet-stream
-Content-ID: <20592.1022586929.12@example.com>
-Content-Description: patch6
-Content-Transfer-Encoding: base64
-
-XXX
-
-------- =_aaaaaaaaaa1--
-
-------- =_aaaaaaaaaa1--
-
-------- =_aaaaaaaaaa0
-Content-Type: text/plain; charset="us-ascii"
-Content-ID: <20592.1022586929.15@example.com>
-
---
-It's never too late to have a happy childhood.
-
-------- =_aaaaaaaaaa0--
diff --git a/future/tests/test_email/data/msg_40.txt b/future/tests/test_email/data/msg_40.txt
deleted file mode 100644
index 1435fa1e..00000000
--- a/future/tests/test_email/data/msg_40.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-MIME-Version: 1.0
-Content-Type: text/html; boundary="--961284236552522269"
-
-----961284236552522269
-Content-Type: text/html;
-Content-Transfer-Encoding: 7Bit
-
-
-
-----961284236552522269--
diff --git a/future/tests/test_email/data/msg_41.txt b/future/tests/test_email/data/msg_41.txt
deleted file mode 100644
index 76cdd1cb..00000000
--- a/future/tests/test_email/data/msg_41.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-From: "Allison Dunlap"
-To: yyy@example.com
-Subject: 64423
-Date: Sun, 11 Jul 2004 16:09:27 -0300
-MIME-Version: 1.0
-Content-Type: multipart/alternative;
-
-Blah blah blah
diff --git a/future/tests/test_email/data/msg_42.txt b/future/tests/test_email/data/msg_42.txt
deleted file mode 100644
index a75f8f4a..00000000
--- a/future/tests/test_email/data/msg_42.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-Content-Type: multipart/mixed; boundary="AAA"
-From: Mail Delivery Subsystem
-To: yyy@example.com
-
-This is a MIME-encapsulated message
-
---AAA
-
-Stuff
-
---AAA
-Content-Type: message/rfc822
-
-From: webmaster@python.org
-To: zzz@example.com
-Content-Type: multipart/mixed; boundary="BBB"
-
---BBB--
-
---AAA--
diff --git a/future/tests/test_email/data/msg_43.txt b/future/tests/test_email/data/msg_43.txt
deleted file mode 100644
index 797d12c5..00000000
--- a/future/tests/test_email/data/msg_43.txt
+++ /dev/null
@@ -1,217 +0,0 @@
-From SRS0=aO/p=ON=bag.python.org=None@bounce2.pobox.com Fri Nov 26 21:40:36 2004
-X-VM-v5-Data: ([nil nil nil nil nil nil nil nil nil]
- [nil nil nil nil nil nil nil "MAILER DAEMON <>" "MAILER DAEMON <>" nil nil "Banned file: auto__mail.python.bat in mail from you" "^From:" nil nil nil nil "Banned file: auto__mail.python.bat in mail from you" nil nil nil nil nil nil nil]
- nil)
-MIME-Version: 1.0
-Message-Id:
-Content-Type: multipart/report; report-type=delivery-status;
- charset=utf-8;
- boundary="----------=_1101526904-1956-5"
-X-Virus-Scanned: by XS4ALL Virus Scanner
-X-UIDL: 4\G!!!
-To:
-Subject: Banned file: auto__mail.python.bat in mail from you
-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-This is a multi-part message in MIME format...
-
-------------=_1101526904-1956-5
-Content-Type: text/plain; charset="utf-8"
-Content-Disposition: inline
-Content-Transfer-Encoding: 7bit
-
-BANNED FILENAME ALERT
-
-Your message to: xxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxxxxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxx@dot.ca.gov, xxxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxx@dot.ca.gov, xxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxx@dot.ca.gov, xxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxxxxxxxx@dot.ca.gov
-was blocked by our Spam Firewall. The email you sent with the following subject has NOT BEEN DELIVERED:
-
-Subject: Delivery_failure_notice
-
-An attachment in that mail was of a file type that the Spam Firewall is set to block.
-
-
-
-------------=_1101526904-1956-5
-Content-Type: message/delivery-status
-Content-Disposition: inline
-Content-Transfer-Encoding: 7bit
-Content-Description: Delivery error report
-
-Reporting-MTA: dns; sacspam01.dot.ca.gov
-Received-From-MTA: smtp; sacspam01.dot.ca.gov ([127.0.0.1])
-Arrival-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-Final-Recipient: rfc822; xxxxxxx@dot.ca.gov
-Action: failed
-Status: 5.7.1
-Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat
-Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST)
-
-------------=_1101526904-1956-5
-Content-Type: text/rfc822-headers
-Content-Disposition: inline
-Content-Transfer-Encoding: 7bit
-Content-Description: Undelivered-message headers
-
-Received: from kgsav.org (ppp-70-242-162-63.dsl.spfdmo.swbell.net [70.242.162.63])
- by sacspam01.dot.ca.gov (Spam Firewall) with SMTP
- id A232AD03DE3A; Fri, 26 Nov 2004 19:41:35 -0800 (PST)
-From: webmaster@python.org
-To: xxxxx@dot.ca.gov
-Date: Sat, 27 Nov 2004 03:35:30 UTC
-Subject: Delivery_failure_notice
-Importance: Normal
-X-Priority: 3 (Normal)
-X-MSMail-Priority: Normal
-Message-ID:
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="====67bd2b7a5.f99f7"
-Content-Transfer-Encoding: 7bit
-
-------------=_1101526904-1956-5--
-
diff --git a/future/tests/test_email/data/msg_44.txt b/future/tests/test_email/data/msg_44.txt
deleted file mode 100644
index 15a22528..00000000
--- a/future/tests/test_email/data/msg_44.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-Return-Path:
-Delivered-To: barry@python.org
-Received: by mail.python.org (Postfix, from userid 889)
- id C2BF0D37C6; Tue, 11 Sep 2001 00:05:05 -0400 (EDT)
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="h90VIIIKmx"
-Content-Transfer-Encoding: 7bit
-Message-ID: <15261.36209.358846.118674@anthem.python.org>
-From: barry@python.org (Barry A. Warsaw)
-To: barry@python.org
-Subject: a simple multipart
-Date: Tue, 11 Sep 2001 00:05:05 -0400
-X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid
-X-Attribution: BAW
-X-Oblique-Strategy: Make a door into a window
-
-
---h90VIIIKmx
-Content-Type: text/plain; name="msg.txt"
-Content-Transfer-Encoding: 7bit
-
-a simple kind of mirror
-to reflect upon our own
-
---h90VIIIKmx
-Content-Type: text/plain; name="msg.txt"
-Content-Transfer-Encoding: 7bit
-
-a simple kind of mirror
-to reflect upon our own
-
---h90VIIIKmx--
-
diff --git a/future/tests/test_email/data/msg_45.txt b/future/tests/test_email/data/msg_45.txt
deleted file mode 100644
index 58fde956..00000000
--- a/future/tests/test_email/data/msg_45.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-From:
-To:
-Subject: test
-X-Long-Line: Some really long line contains a lot of text and thus has to be rewrapped because it is some
- really long
- line
-MIME-Version: 1.0
-Content-Type: multipart/signed; boundary="borderline";
- protocol="application/pgp-signature"; micalg=pgp-sha1
-
-This is an OpenPGP/MIME signed message (RFC 2440 and 3156)
---borderline
-Content-Type: text/plain
-X-Long-Line: Another really long line contains a lot of text and thus has to be rewrapped because it is another
- really long
- line
-
-This is the signed contents.
-
---borderline
-Content-Type: application/pgp-signature; name="signature.asc"
-Content-Description: OpenPGP digital signature
-Content-Disposition: attachment; filename="signature.asc"
-
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v2.0.6 (GNU/Linux)
-
-iD8DBQFG03voRhp6o4m9dFsRApSZAKCCAN3IkJlVRg6NvAiMHlvvIuMGPQCeLZtj
-FGwfnRHFBFO/S4/DKysm0lI=
-=t7+s
------END PGP SIGNATURE-----
-
---borderline--
diff --git a/future/tests/test_email/data/msg_46.txt b/future/tests/test_email/data/msg_46.txt
deleted file mode 100644
index 1e22c4f6..00000000
--- a/future/tests/test_email/data/msg_46.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-Return-Path:
-Delivery-Date: Mon, 08 Feb 2010 14:05:16 +0100
-Received: from example.org (example.org [64.5.53.58])
- by example.net (node=mxbap2) with ESMTP (Nemesis)
- id UNIQUE for someone@example.com; Mon, 08 Feb 2010 14:05:16 +0100
-Date: Mon, 01 Feb 2010 12:21:16 +0100
-From: "Sender"
-To:
-Subject: GroupwiseForwardingTest
-Mime-Version: 1.0
-Content-Type: message/rfc822
-
-Return-path:
-Message-ID: <4B66B890.4070408@teconcept.de>
-Date: Mon, 01 Feb 2010 12:18:40 +0100
-From: "Dr. Sender"
-MIME-Version: 1.0
-To: "Recipient"
-Subject: GroupwiseForwardingTest
-Content-Type: text/plain; charset=ISO-8859-15
-Content-Transfer-Encoding: 7bit
-
-Testing email forwarding with Groupwise 1.2.2010
diff --git a/future/tests/test_email/test__encoded_words.py b/future/tests/test_email/test__encoded_words.py
deleted file mode 100644
index d84d5704..00000000
--- a/future/tests/test_email/test__encoded_words.py
+++ /dev/null
@@ -1,193 +0,0 @@
-from __future__ import unicode_literals
-from __future__ import print_function
-from __future__ import division
-from __future__ import absolute_import
-from future import standard_library
-import unittest
-with standard_library.hooks():
- from email import _encoded_words as _ew
- from email import errors
-from future.tests.test_email import TestEmailBase
-
-
-class TestDecodeQ(TestEmailBase):
-
- def _test(self, source, ex_result, ex_defects=[]):
- result, defects = _ew.decode_q(source)
- self.assertEqual(result, ex_result)
- self.assertDefectsEqual(defects, ex_defects)
-
- def test_no_encoded(self):
- self._test(b'foobar', b'foobar')
-
- def test_spaces(self):
- self._test(b'foo=20bar=20', b'foo bar ')
- self._test(b'foo_bar_', b'foo bar ')
-
- def test_run_of_encoded(self):
- self._test(b'foo=20=20=21=2Cbar', b'foo !,bar')
-
-
-class TestDecodeB(TestEmailBase):
-
- def _test(self, source, ex_result, ex_defects=[]):
- result, defects = _ew.decode_b(source)
- self.assertEqual(result, ex_result)
- self.assertDefectsEqual(defects, ex_defects)
-
- def test_simple(self):
- self._test(b'Zm9v', b'foo')
-
- def test_missing_padding(self):
- self._test(b'dmk', b'vi', [errors.InvalidBase64PaddingDefect])
-
- def test_invalid_character(self):
- self._test(b'dm\x01k===', b'vi', [errors.InvalidBase64CharactersDefect])
-
- def test_invalid_character_and_bad_padding(self):
- self._test(b'dm\x01k', b'vi', [errors.InvalidBase64CharactersDefect,
- errors.InvalidBase64PaddingDefect])
-
-
-class TestDecode(TestEmailBase):
-
- def test_wrong_format_input_raises(self):
- with self.assertRaises(ValueError):
- _ew.decode('=?badone?=')
- with self.assertRaises(ValueError):
- _ew.decode('=?')
- with self.assertRaises(ValueError):
- _ew.decode('')
-
- def _test(self, source, result, charset='us-ascii', lang='', defects=[]):
- res, char, l, d = _ew.decode(source)
- self.assertEqual(res, result)
- self.assertEqual(char, charset)
- self.assertEqual(l, lang)
- self.assertDefectsEqual(d, defects)
-
- def test_simple_q(self):
- self._test('=?us-ascii?q?foo?=', 'foo')
-
- def test_simple_b(self):
- self._test('=?us-ascii?b?dmk=?=', 'vi')
-
- def test_q_case_ignored(self):
- self._test('=?us-ascii?Q?foo?=', 'foo')
-
- def test_b_case_ignored(self):
- self._test('=?us-ascii?B?dmk=?=', 'vi')
-
- def test_non_trivial_q(self):
- self._test('=?latin-1?q?=20F=fcr=20Elise=20?=', ' Für Elise ', 'latin-1')
-
- def test_q_escpaed_bytes_preserved(self):
- self._test(b'=?us-ascii?q?=20\xACfoo?='.decode('us-ascii',
- 'surrogateescape'),
- ' \uDCACfoo',
- defects = [errors.UndecodableBytesDefect])
-
- def test_b_undecodable_bytes_ignored_with_defect(self):
- self._test(b'=?us-ascii?b?dm\xACk?='.decode('us-ascii',
- 'surrogateescape'),
- 'vi',
- defects = [
- errors.InvalidBase64CharactersDefect,
- errors.InvalidBase64PaddingDefect])
-
- def test_b_invalid_bytes_ignored_with_defect(self):
- self._test('=?us-ascii?b?dm\x01k===?=',
- 'vi',
- defects = [errors.InvalidBase64CharactersDefect])
-
- def test_b_invalid_bytes_incorrect_padding(self):
- self._test('=?us-ascii?b?dm\x01k?=',
- 'vi',
- defects = [
- errors.InvalidBase64CharactersDefect,
- errors.InvalidBase64PaddingDefect])
-
- def test_b_padding_defect(self):
- self._test('=?us-ascii?b?dmk?=',
- 'vi',
- defects = [errors.InvalidBase64PaddingDefect])
-
- def test_nonnull_lang(self):
- self._test('=?us-ascii*jive?q?test?=', 'test', lang='jive')
-
- def test_unknown_8bit_charset(self):
- self._test('=?unknown-8bit?q?foo=ACbar?=',
- b'foo\xacbar'.decode('ascii', 'surrogateescape'),
- charset = 'unknown-8bit',
- defects = [])
-
- def test_unknown_charset(self):
- self._test('=?foobar?q?foo=ACbar?=',
- b'foo\xacbar'.decode('ascii', 'surrogateescape'),
- charset = 'foobar',
- # XXX Should this be a new Defect instead?
- defects = [errors.CharsetError])
-
-
-class TestEncodeQ(TestEmailBase):
-
- def _test(self, src, expected):
- self.assertEqual(_ew.encode_q(src), expected)
-
- def test_all_safe(self):
- self._test(b'foobar', 'foobar')
-
- def test_spaces(self):
- self._test(b'foo bar ', 'foo_bar_')
-
- def test_run_of_encodables(self):
- self._test(b'foo ,,bar', 'foo__=2C=2Cbar')
-
-
-class TestEncodeB(TestEmailBase):
-
- def test_simple(self):
- self.assertEqual(_ew.encode_b(b'foo'), 'Zm9v')
-
- def test_padding(self):
- self.assertEqual(_ew.encode_b(b'vi'), 'dmk=')
-
-
-class TestEncode(TestEmailBase):
-
- def test_q(self):
- self.assertEqual(_ew.encode('foo', 'utf-8', 'q'), '=?utf-8?q?foo?=')
-
- def test_b(self):
- self.assertEqual(_ew.encode('foo', 'utf-8', 'b'), '=?utf-8?b?Zm9v?=')
-
- def test_auto_q(self):
- self.assertEqual(_ew.encode('foo', 'utf-8'), '=?utf-8?q?foo?=')
-
- def test_auto_q_if_short_mostly_safe(self):
- self.assertEqual(_ew.encode('vi.', 'utf-8'), '=?utf-8?q?vi=2E?=')
-
- def test_auto_b_if_enough_unsafe(self):
- self.assertEqual(_ew.encode('.....', 'utf-8'), '=?utf-8?b?Li4uLi4=?=')
-
- def test_auto_b_if_long_unsafe(self):
- self.assertEqual(_ew.encode('vi.vi.vi.vi.vi.', 'utf-8'),
- '=?utf-8?b?dmkudmkudmkudmkudmku?=')
-
- def test_auto_q_if_long_mostly_safe(self):
- self.assertEqual(_ew.encode('vi vi vi.vi ', 'utf-8'),
- '=?utf-8?q?vi_vi_vi=2Evi_?=')
-
- def test_utf8_default(self):
- self.assertEqual(_ew.encode('foo'), '=?utf-8?q?foo?=')
-
- def test_lang(self):
- self.assertEqual(_ew.encode('foo', lang='jive'), '=?utf-8*jive?q?foo?=')
-
- def test_unknown_8bit(self):
- self.assertEqual(_ew.encode('foo\uDCACbar', charset='unknown-8bit'),
- '=?unknown-8bit?q?foo=ACbar?=')
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/future/tests/test_email/test__header_value_parser.py b/future/tests/test_email/test__header_value_parser.py
deleted file mode 100644
index 4c312778..00000000
--- a/future/tests/test_email/test__header_value_parser.py
+++ /dev/null
@@ -1,2562 +0,0 @@
-from __future__ import unicode_literals
-from __future__ import print_function
-from __future__ import division
-from __future__ import absolute_import
-from future.builtins import bytes
-from future.builtins import range
-from future.builtins import str
-from future import standard_library
-import string
-import unittest
-with standard_library.hooks():
- from email import _header_value_parser as parser
- from email import errors
- from email import policy
- from test.test_email import TestEmailBase, parameterize
-
-
-class TestTokens(TestEmailBase):
-
- # EWWhiteSpaceTerminal
-
- def test_EWWhiteSpaceTerminal(self):
- x = parser.EWWhiteSpaceTerminal(' \t', 'fws')
- self.assertEqual(x, ' \t')
- self.assertEqual(str(x), '')
- self.assertEqual(x.value, '')
- self.assertEqual(x.encoded, ' \t')
-
- # UnstructuredTokenList
-
- def test_undecodable_bytes_error_preserved(self):
- badstr = b"le pouf c\xaflebre".decode('ascii', 'surrogateescape')
- unst = parser.get_unstructured(badstr)
- self.assertDefectsEqual(unst.all_defects, [errors.UndecodableBytesDefect])
- parts = list(unst.parts)
- self.assertDefectsEqual(parts[0].all_defects, [])
- self.assertDefectsEqual(parts[1].all_defects, [])
- self.assertDefectsEqual(parts[2].all_defects, [errors.UndecodableBytesDefect])
-
-
-class TestParserMixin(object):
-
- def _assert_results(self, tl, rest, string, value, defects, remainder,
- comments=None):
- self.assertEqual(str(tl), string)
- self.assertEqual(tl.value, value)
- self.assertDefectsEqual(tl.all_defects, defects)
- self.assertEqual(rest, remainder)
- if comments is not None:
- self.assertEqual(tl.comments, comments)
-
- def _test_get_x(self, method, source, string, value, defects,
- remainder, comments=None):
- tl, rest = method(source)
- self._assert_results(tl, rest, string, value, defects, remainder,
- comments=None)
- return tl
-
- def _test_parse_x(self, method, input, string, value, defects,
- comments=None):
- tl = method(input)
- self._assert_results(tl, '', string, value, defects, '', comments)
- return tl
-
-
-class TestParser(TestParserMixin, TestEmailBase):
-
- # _wsp_splitter
-
- rfc_printable_ascii = bytes(range(33, 127)).decode('ascii')
- rfc_atext_chars = (string.ascii_letters + string.digits +
- "!#$%&\'*+-/=?^_`{}|~")
- rfc_dtext_chars = rfc_printable_ascii.translate(str.maketrans('','',r'\[]'))
-
- def test__wsp_splitter_one_word(self):
- self.assertEqual(parser._wsp_splitter('foo', 1), ['foo'])
-
- def test__wsp_splitter_two_words(self):
- self.assertEqual(parser._wsp_splitter('foo def', 1),
- ['foo', ' ', 'def'])
-
- def test__wsp_splitter_ws_runs(self):
- self.assertEqual(parser._wsp_splitter('foo \t def jik', 1),
- ['foo', ' \t ', 'def jik'])
-
-
- # get_fws
-
- def test_get_fws_only(self):
- fws = self._test_get_x(parser.get_fws, ' \t ', ' \t ', ' ', [], '')
- self.assertEqual(fws.token_type, 'fws')
-
- def test_get_fws_space(self):
- self._test_get_x(parser.get_fws, ' foo', ' ', ' ', [], 'foo')
-
- def test_get_fws_ws_run(self):
- self._test_get_x(parser.get_fws, ' \t foo ', ' \t ', ' ', [], 'foo ')
-
- # get_encoded_word
-
- def test_get_encoded_word_missing_start_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_encoded_word('abc')
-
- def test_get_encoded_word_missing_end_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_encoded_word('=?abc')
-
- def test_get_encoded_word_missing_middle_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_encoded_word('=?abc?=')
-
- def test_get_encoded_word_valid_ew(self):
- self._test_get_x(parser.get_encoded_word,
- '=?us-ascii?q?this_is_a_test?= bird',
- 'this is a test',
- 'this is a test',
- [],
- ' bird')
-
- def test_get_encoded_word_internal_spaces(self):
- self._test_get_x(parser.get_encoded_word,
- '=?us-ascii?q?this is a test?= bird',
- 'this is a test',
- 'this is a test',
- [errors.InvalidHeaderDefect],
- ' bird')
-
- def test_get_encoded_word_gets_first(self):
- self._test_get_x(parser.get_encoded_word,
- '=?us-ascii?q?first?= =?utf-8?q?second?=',
- 'first',
- 'first',
- [],
- ' =?utf-8?q?second?=')
-
- def test_get_encoded_word_gets_first_even_if_no_space(self):
- self._test_get_x(parser.get_encoded_word,
- '=?us-ascii?q?first?==?utf-8?q?second?=',
- 'first',
- 'first',
- [],
- '=?utf-8?q?second?=')
-
- def test_get_encoded_word_sets_extra_attributes(self):
- ew = self._test_get_x(parser.get_encoded_word,
- '=?us-ascii*jive?q?first_second?=',
- 'first second',
- 'first second',
- [],
- '')
- self.assertEqual(ew.encoded, '=?us-ascii*jive?q?first_second?=')
- self.assertEqual(ew.charset, 'us-ascii')
- self.assertEqual(ew.lang, 'jive')
-
- def test_get_encoded_word_lang_default_is_blank(self):
- ew = self._test_get_x(parser.get_encoded_word,
- '=?us-ascii?q?first_second?=',
- 'first second',
- 'first second',
- [],
- '')
- self.assertEqual(ew.encoded, '=?us-ascii?q?first_second?=')
- self.assertEqual(ew.charset, 'us-ascii')
- self.assertEqual(ew.lang, '')
-
- def test_get_encoded_word_non_printable_defect(self):
- self._test_get_x(parser.get_encoded_word,
- '=?us-ascii?q?first\x02second?=',
- 'first\x02second',
- 'first\x02second',
- [errors.NonPrintableDefect],
- '')
-
- def test_get_encoded_word_leading_internal_space(self):
- self._test_get_x(parser.get_encoded_word,
- '=?us-ascii?q?=20foo?=',
- ' foo',
- ' foo',
- [],
- '')
-
- # get_unstructured
-
- def _get_unst(self, value):
- token = parser.get_unstructured(value)
- return token, ''
-
- def test_get_unstructured_null(self):
- self._test_get_x(self._get_unst, '', '', '', [], '')
-
- def test_get_unstructured_one_word(self):
- self._test_get_x(self._get_unst, 'foo', 'foo', 'foo', [], '')
-
- def test_get_unstructured_normal_phrase(self):
- self._test_get_x(self._get_unst, 'foo bar bird',
- 'foo bar bird',
- 'foo bar bird',
- [],
- '')
-
- def test_get_unstructured_normal_phrase_with_whitespace(self):
- self._test_get_x(self._get_unst, 'foo \t bar bird',
- 'foo \t bar bird',
- 'foo bar bird',
- [],
- '')
-
- def test_get_unstructured_leading_whitespace(self):
- self._test_get_x(self._get_unst, ' foo bar',
- ' foo bar',
- ' foo bar',
- [],
- '')
-
- def test_get_unstructured_trailing_whitespace(self):
- self._test_get_x(self._get_unst, 'foo bar ',
- 'foo bar ',
- 'foo bar ',
- [],
- '')
-
- def test_get_unstructured_leading_and_trailing_whitespace(self):
- self._test_get_x(self._get_unst, ' foo bar ',
- ' foo bar ',
- ' foo bar ',
- [],
- '')
-
- def test_get_unstructured_one_valid_ew_no_ws(self):
- self._test_get_x(self._get_unst, '=?us-ascii?q?bar?=',
- 'bar',
- 'bar',
- [],
- '')
-
- def test_get_unstructured_one_ew_trailing_ws(self):
- self._test_get_x(self._get_unst, '=?us-ascii?q?bar?= ',
- 'bar ',
- 'bar ',
- [],
- '')
-
- def test_get_unstructured_one_valid_ew_trailing_text(self):
- self._test_get_x(self._get_unst, '=?us-ascii?q?bar?= bird',
- 'bar bird',
- 'bar bird',
- [],
- '')
-
- def test_get_unstructured_phrase_with_ew_in_middle_of_text(self):
- self._test_get_x(self._get_unst, 'foo =?us-ascii?q?bar?= bird',
- 'foo bar bird',
- 'foo bar bird',
- [],
- '')
-
- def test_get_unstructured_phrase_with_two_ew(self):
- self._test_get_x(self._get_unst,
- 'foo =?us-ascii?q?bar?= =?us-ascii?q?bird?=',
- 'foo barbird',
- 'foo barbird',
- [],
- '')
-
- def test_get_unstructured_phrase_with_two_ew_trailing_ws(self):
- self._test_get_x(self._get_unst,
- 'foo =?us-ascii?q?bar?= =?us-ascii?q?bird?= ',
- 'foo barbird ',
- 'foo barbird ',
- [],
- '')
-
- def test_get_unstructured_phrase_with_ew_with_leading_ws(self):
- self._test_get_x(self._get_unst,
- ' =?us-ascii?q?bar?=',
- ' bar',
- ' bar',
- [],
- '')
-
- def test_get_unstructured_phrase_with_two_ew_extra_ws(self):
- self._test_get_x(self._get_unst,
- 'foo =?us-ascii?q?bar?= \t =?us-ascii?q?bird?=',
- 'foo barbird',
- 'foo barbird',
- [],
- '')
-
- def test_get_unstructured_two_ew_extra_ws_trailing_text(self):
- self._test_get_x(self._get_unst,
- '=?us-ascii?q?test?= =?us-ascii?q?foo?= val',
- 'testfoo val',
- 'testfoo val',
- [],
- '')
-
- def test_get_unstructured_ew_with_internal_ws(self):
- self._test_get_x(self._get_unst,
- '=?iso-8859-1?q?hello=20world?=',
- 'hello world',
- 'hello world',
- [],
- '')
-
- def test_get_unstructured_ew_with_internal_leading_ws(self):
- self._test_get_x(self._get_unst,
- ' =?us-ascii?q?=20test?= =?us-ascii?q?=20foo?= val',
- ' test foo val',
- ' test foo val',
- [],
- '')
-
- def test_get_unstructured_invaild_ew(self):
- self._test_get_x(self._get_unst,
- '=?test val',
- '=?test val',
- '=?test val',
- [],
- '')
-
- def test_get_unstructured_undecodable_bytes(self):
- self._test_get_x(self._get_unst,
- b'test \xACfoo val'.decode('ascii', 'surrogateescape'),
- 'test \uDCACfoo val',
- 'test \uDCACfoo val',
- [errors.UndecodableBytesDefect],
- '')
-
- def test_get_unstructured_undecodable_bytes_in_EW(self):
- self._test_get_x(self._get_unst,
- (b'=?us-ascii?q?=20test?= =?us-ascii?q?=20\xACfoo?='
- b' val').decode('ascii', 'surrogateescape'),
- ' test \uDCACfoo val',
- ' test \uDCACfoo val',
- [errors.UndecodableBytesDefect]*2,
- '')
-
- def test_get_unstructured_missing_base64_padding(self):
- self._test_get_x(self._get_unst,
- '=?utf-8?b?dmk?=',
- 'vi',
- 'vi',
- [errors.InvalidBase64PaddingDefect],
- '')
-
- def test_get_unstructured_invalid_base64_character(self):
- self._test_get_x(self._get_unst,
- '=?utf-8?b?dm\x01k===?=',
- 'vi',
- 'vi',
- [errors.InvalidBase64CharactersDefect],
- '')
-
- def test_get_unstructured_invalid_base64_character_and_bad_padding(self):
- self._test_get_x(self._get_unst,
- '=?utf-8?b?dm\x01k?=',
- 'vi',
- 'vi',
- [errors.InvalidBase64CharactersDefect,
- errors.InvalidBase64PaddingDefect],
- '')
-
- def test_get_unstructured_no_whitespace_between_ews(self):
- self._test_get_x(self._get_unst,
- '=?utf-8?q?foo?==?utf-8?q?bar?=',
- 'foobar',
- 'foobar',
- [errors.InvalidHeaderDefect],
- '')
-
- # get_qp_ctext
-
- def test_get_qp_ctext_only(self):
- ptext = self._test_get_x(parser.get_qp_ctext,
- 'foobar', 'foobar', ' ', [], '')
- self.assertEqual(ptext.token_type, 'ptext')
-
- def test_get_qp_ctext_all_printables(self):
- with_qp = self.rfc_printable_ascii.replace('\\', '\\\\')
- with_qp = with_qp. replace('(', r'\(')
- with_qp = with_qp.replace(')', r'\)')
- ptext = self._test_get_x(parser.get_qp_ctext,
- with_qp, self.rfc_printable_ascii, ' ', [], '')
-
- def test_get_qp_ctext_two_words_gets_first(self):
- self._test_get_x(parser.get_qp_ctext,
- 'foo de', 'foo', ' ', [], ' de')
-
- def test_get_qp_ctext_following_wsp_preserved(self):
- self._test_get_x(parser.get_qp_ctext,
- 'foo \t\tde', 'foo', ' ', [], ' \t\tde')
-
- def test_get_qp_ctext_up_to_close_paren_only(self):
- self._test_get_x(parser.get_qp_ctext,
- 'foo)', 'foo', ' ', [], ')')
-
- def test_get_qp_ctext_wsp_before_close_paren_preserved(self):
- self._test_get_x(parser.get_qp_ctext,
- 'foo )', 'foo', ' ', [], ' )')
-
- def test_get_qp_ctext_close_paren_mid_word(self):
- self._test_get_x(parser.get_qp_ctext,
- 'foo)bar', 'foo', ' ', [], ')bar')
-
- def test_get_qp_ctext_up_to_open_paren_only(self):
- self._test_get_x(parser.get_qp_ctext,
- 'foo(', 'foo', ' ', [], '(')
-
- def test_get_qp_ctext_wsp_before_open_paren_preserved(self):
- self._test_get_x(parser.get_qp_ctext,
- 'foo (', 'foo', ' ', [], ' (')
-
- def test_get_qp_ctext_open_paren_mid_word(self):
- self._test_get_x(parser.get_qp_ctext,
- 'foo(bar', 'foo', ' ', [], '(bar')
-
- def test_get_qp_ctext_non_printables(self):
- ptext = self._test_get_x(parser.get_qp_ctext,
- 'foo\x00bar)', 'foo\x00bar', ' ',
- [errors.NonPrintableDefect], ')')
- self.assertEqual(ptext.defects[0].non_printables[0], '\x00')
-
- # get_qcontent
-
- def test_get_qcontent_only(self):
- ptext = self._test_get_x(parser.get_qcontent,
- 'foobar', 'foobar', 'foobar', [], '')
- self.assertEqual(ptext.token_type, 'ptext')
-
- def test_get_qcontent_all_printables(self):
- with_qp = self.rfc_printable_ascii.replace('\\', '\\\\')
- with_qp = with_qp. replace('"', r'\"')
- ptext = self._test_get_x(parser.get_qcontent, with_qp,
- self.rfc_printable_ascii,
- self.rfc_printable_ascii, [], '')
-
- def test_get_qcontent_two_words_gets_first(self):
- self._test_get_x(parser.get_qcontent,
- 'foo de', 'foo', 'foo', [], ' de')
-
- def test_get_qcontent_following_wsp_preserved(self):
- self._test_get_x(parser.get_qcontent,
- 'foo \t\tde', 'foo', 'foo', [], ' \t\tde')
-
- def test_get_qcontent_up_to_dquote_only(self):
- self._test_get_x(parser.get_qcontent,
- 'foo"', 'foo', 'foo', [], '"')
-
- def test_get_qcontent_wsp_before_close_paren_preserved(self):
- self._test_get_x(parser.get_qcontent,
- 'foo "', 'foo', 'foo', [], ' "')
-
- def test_get_qcontent_close_paren_mid_word(self):
- self._test_get_x(parser.get_qcontent,
- 'foo"bar', 'foo', 'foo', [], '"bar')
-
- def test_get_qcontent_non_printables(self):
- ptext = self._test_get_x(parser.get_qcontent,
- 'foo\x00fg"', 'foo\x00fg', 'foo\x00fg',
- [errors.NonPrintableDefect], '"')
- self.assertEqual(ptext.defects[0].non_printables[0], '\x00')
-
- # get_atext
-
- def test_get_atext_only(self):
- atext = self._test_get_x(parser.get_atext,
- 'foobar', 'foobar', 'foobar', [], '')
- self.assertEqual(atext.token_type, 'atext')
-
- def test_get_atext_all_atext(self):
- atext = self._test_get_x(parser.get_atext, self.rfc_atext_chars,
- self.rfc_atext_chars,
- self.rfc_atext_chars, [], '')
-
- def test_get_atext_two_words_gets_first(self):
- self._test_get_x(parser.get_atext,
- 'foo bar', 'foo', 'foo', [], ' bar')
-
- def test_get_atext_following_wsp_preserved(self):
- self._test_get_x(parser.get_atext,
- 'foo \t\tbar', 'foo', 'foo', [], ' \t\tbar')
-
- def test_get_atext_up_to_special(self):
- self._test_get_x(parser.get_atext,
- 'foo@bar', 'foo', 'foo', [], '@bar')
-
- def test_get_atext_non_printables(self):
- atext = self._test_get_x(parser.get_atext,
- 'foo\x00bar(', 'foo\x00bar', 'foo\x00bar',
- [errors.NonPrintableDefect], '(')
- self.assertEqual(atext.defects[0].non_printables[0], '\x00')
-
- # get_bare_quoted_string
-
- def test_get_bare_quoted_string_only(self):
- bqs = self._test_get_x(parser.get_bare_quoted_string,
- '"foo"', '"foo"', 'foo', [], '')
- self.assertEqual(bqs.token_type, 'bare-quoted-string')
-
- def test_get_bare_quoted_string_must_start_with_dquote(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_bare_quoted_string('foo"')
- with self.assertRaises(errors.HeaderParseError):
- parser.get_bare_quoted_string(' "foo"')
-
- def test_get_bare_quoted_string_following_wsp_preserved(self):
- self._test_get_x(parser.get_bare_quoted_string,
- '"foo"\t bar', '"foo"', 'foo', [], '\t bar')
-
- def test_get_bare_quoted_string_multiple_words(self):
- self._test_get_x(parser.get_bare_quoted_string,
- '"foo bar moo"', '"foo bar moo"', 'foo bar moo', [], '')
-
- def test_get_bare_quoted_string_multiple_words_wsp_preserved(self):
- self._test_get_x(parser.get_bare_quoted_string,
- '" foo moo\t"', '" foo moo\t"', ' foo moo\t', [], '')
-
- def test_get_bare_quoted_string_end_dquote_mid_word(self):
- self._test_get_x(parser.get_bare_quoted_string,
- '"foo"bar', '"foo"', 'foo', [], 'bar')
-
- def test_get_bare_quoted_string_quoted_dquote(self):
- self._test_get_x(parser.get_bare_quoted_string,
- r'"foo\"in"a', r'"foo\"in"', 'foo"in', [], 'a')
-
- def test_get_bare_quoted_string_non_printables(self):
- self._test_get_x(parser.get_bare_quoted_string,
- '"a\x01a"', '"a\x01a"', 'a\x01a',
- [errors.NonPrintableDefect], '')
-
- def test_get_bare_quoted_string_no_end_dquote(self):
- self._test_get_x(parser.get_bare_quoted_string,
- '"foo', '"foo"', 'foo',
- [errors.InvalidHeaderDefect], '')
- self._test_get_x(parser.get_bare_quoted_string,
- '"foo ', '"foo "', 'foo ',
- [errors.InvalidHeaderDefect], '')
-
- def test_get_bare_quoted_string_empty_quotes(self):
- self._test_get_x(parser.get_bare_quoted_string,
- '""', '""', '', [], '')
-
- # get_comment
-
- def test_get_comment_only(self):
- comment = self._test_get_x(parser.get_comment,
- '(comment)', '(comment)', ' ', [], '', ['comment'])
- self.assertEqual(comment.token_type, 'comment')
-
- def test_get_comment_must_start_with_paren(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_comment('foo"')
- with self.assertRaises(errors.HeaderParseError):
- parser.get_comment(' (foo"')
-
- def test_get_comment_following_wsp_preserved(self):
- self._test_get_x(parser.get_comment,
- '(comment) \t', '(comment)', ' ', [], ' \t', ['comment'])
-
- def test_get_comment_multiple_words(self):
- self._test_get_x(parser.get_comment,
- '(foo bar) \t', '(foo bar)', ' ', [], ' \t', ['foo bar'])
-
- def test_get_comment_multiple_words_wsp_preserved(self):
- self._test_get_x(parser.get_comment,
- '( foo bar\t ) \t', '( foo bar\t )', ' ', [], ' \t',
- [' foo bar\t '])
-
- def test_get_comment_end_paren_mid_word(self):
- self._test_get_x(parser.get_comment,
- '(foo)bar', '(foo)', ' ', [], 'bar', ['foo'])
-
- def test_get_comment_quoted_parens(self):
- self._test_get_x(parser.get_comment,
- '(foo\) \(\)bar)', '(foo\) \(\)bar)', ' ', [], '', ['foo) ()bar'])
-
- def test_get_comment_non_printable(self):
- self._test_get_x(parser.get_comment,
- '(foo\x7Fbar)', '(foo\x7Fbar)', ' ',
- [errors.NonPrintableDefect], '', ['foo\x7Fbar'])
-
- def test_get_comment_no_end_paren(self):
- self._test_get_x(parser.get_comment,
- '(foo bar', '(foo bar)', ' ',
- [errors.InvalidHeaderDefect], '', ['foo bar'])
- self._test_get_x(parser.get_comment,
- '(foo bar ', '(foo bar )', ' ',
- [errors.InvalidHeaderDefect], '', ['foo bar '])
-
- def test_get_comment_nested_comment(self):
- comment = self._test_get_x(parser.get_comment,
- '(foo(bar))', '(foo(bar))', ' ', [], '', ['foo(bar)'])
- self.assertEqual(comment[1].content, 'bar')
-
- def test_get_comment_nested_comment_wsp(self):
- comment = self._test_get_x(parser.get_comment,
- '(foo ( bar ) )', '(foo ( bar ) )', ' ', [], '', ['foo ( bar ) '])
- self.assertEqual(comment[2].content, ' bar ')
-
- def test_get_comment_empty_comment(self):
- self._test_get_x(parser.get_comment,
- '()', '()', ' ', [], '', [''])
-
- def test_get_comment_multiple_nesting(self):
- comment = self._test_get_x(parser.get_comment,
- '(((((foo)))))', '(((((foo)))))', ' ', [], '', ['((((foo))))'])
- for i in range(4, 0, -1):
- self.assertEqual(comment[0].content, '('*(i-1)+'foo'+')'*(i-1))
- comment = comment[0]
- self.assertEqual(comment.content, 'foo')
-
- def test_get_comment_missing_end_of_nesting(self):
- self._test_get_x(parser.get_comment,
- '(((((foo)))', '(((((foo)))))', ' ',
- [errors.InvalidHeaderDefect]*2, '', ['((((foo))))'])
-
- def test_get_comment_qs_in_nested_comment(self):
- comment = self._test_get_x(parser.get_comment,
- '(foo (b\)))', '(foo (b\)))', ' ', [], '', ['foo (b\))'])
- self.assertEqual(comment[2].content, 'b)')
-
- # get_cfws
-
- def test_get_cfws_only_ws(self):
- cfws = self._test_get_x(parser.get_cfws,
- ' \t \t', ' \t \t', ' ', [], '', [])
- self.assertEqual(cfws.token_type, 'cfws')
-
- def test_get_cfws_only_comment(self):
- cfws = self._test_get_x(parser.get_cfws,
- '(foo)', '(foo)', ' ', [], '', ['foo'])
- self.assertEqual(cfws[0].content, 'foo')
-
- def test_get_cfws_only_mixed(self):
- cfws = self._test_get_x(parser.get_cfws,
- ' (foo ) ( bar) ', ' (foo ) ( bar) ', ' ', [], '',
- ['foo ', ' bar'])
- self.assertEqual(cfws[1].content, 'foo ')
- self.assertEqual(cfws[3].content, ' bar')
-
- def test_get_cfws_ends_at_non_leader(self):
- cfws = self._test_get_x(parser.get_cfws,
- '(foo) bar', '(foo) ', ' ', [], 'bar', ['foo'])
- self.assertEqual(cfws[0].content, 'foo')
-
- def test_get_cfws_ends_at_non_printable(self):
- cfws = self._test_get_x(parser.get_cfws,
- '(foo) \x07', '(foo) ', ' ', [], '\x07', ['foo'])
- self.assertEqual(cfws[0].content, 'foo')
-
- def test_get_cfws_non_printable_in_comment(self):
- cfws = self._test_get_x(parser.get_cfws,
- '(foo \x07) "test"', '(foo \x07) ', ' ',
- [errors.NonPrintableDefect], '"test"', ['foo \x07'])
- self.assertEqual(cfws[0].content, 'foo \x07')
-
- def test_get_cfws_header_ends_in_comment(self):
- cfws = self._test_get_x(parser.get_cfws,
- ' (foo ', ' (foo )', ' ',
- [errors.InvalidHeaderDefect], '', ['foo '])
- self.assertEqual(cfws[1].content, 'foo ')
-
- def test_get_cfws_multiple_nested_comments(self):
- cfws = self._test_get_x(parser.get_cfws,
- '(foo (bar)) ((a)(a))', '(foo (bar)) ((a)(a))', ' ', [],
- '', ['foo (bar)', '(a)(a)'])
- self.assertEqual(cfws[0].comments, ['foo (bar)'])
- self.assertEqual(cfws[2].comments, ['(a)(a)'])
-
- # get_quoted_string
-
- def test_get_quoted_string_only(self):
- qs = self._test_get_x(parser.get_quoted_string,
- '"bob"', '"bob"', 'bob', [], '')
- self.assertEqual(qs.token_type, 'quoted-string')
- self.assertEqual(qs.quoted_value, '"bob"')
- self.assertEqual(qs.content, 'bob')
-
- def test_get_quoted_string_with_wsp(self):
- qs = self._test_get_x(parser.get_quoted_string,
- '\t "bob" ', '\t "bob" ', ' bob ', [], '')
- self.assertEqual(qs.quoted_value, ' "bob" ')
- self.assertEqual(qs.content, 'bob')
-
- def test_get_quoted_string_with_comments_and_wsp(self):
- qs = self._test_get_x(parser.get_quoted_string,
- ' (foo) "bob"(bar)', ' (foo) "bob"(bar)', ' bob ', [], '')
- self.assertEqual(qs[0][1].content, 'foo')
- self.assertEqual(qs[2][0].content, 'bar')
- self.assertEqual(qs.content, 'bob')
- self.assertEqual(qs.quoted_value, ' "bob" ')
-
- def test_get_quoted_string_with_multiple_comments(self):
- qs = self._test_get_x(parser.get_quoted_string,
- ' (foo) (bar) "bob"(bird)', ' (foo) (bar) "bob"(bird)', ' bob ',
- [], '')
- self.assertEqual(qs[0].comments, ['foo', 'bar'])
- self.assertEqual(qs[2].comments, ['bird'])
- self.assertEqual(qs.content, 'bob')
- self.assertEqual(qs.quoted_value, ' "bob" ')
-
- def test_get_quoted_string_non_printable_in_comment(self):
- qs = self._test_get_x(parser.get_quoted_string,
- ' (\x0A) "bob"', ' (\x0A) "bob"', ' bob',
- [errors.NonPrintableDefect], '')
- self.assertEqual(qs[0].comments, ['\x0A'])
- self.assertEqual(qs.content, 'bob')
- self.assertEqual(qs.quoted_value, ' "bob"')
-
- def test_get_quoted_string_non_printable_in_qcontent(self):
- qs = self._test_get_x(parser.get_quoted_string,
- ' (a) "a\x0B"', ' (a) "a\x0B"', ' a\x0B',
- [errors.NonPrintableDefect], '')
- self.assertEqual(qs[0].comments, ['a'])
- self.assertEqual(qs.content, 'a\x0B')
- self.assertEqual(qs.quoted_value, ' "a\x0B"')
-
- def test_get_quoted_string_internal_ws(self):
- qs = self._test_get_x(parser.get_quoted_string,
- ' (a) "foo bar "', ' (a) "foo bar "', ' foo bar ',
- [], '')
- self.assertEqual(qs[0].comments, ['a'])
- self.assertEqual(qs.content, 'foo bar ')
- self.assertEqual(qs.quoted_value, ' "foo bar "')
-
- def test_get_quoted_string_header_ends_in_comment(self):
- qs = self._test_get_x(parser.get_quoted_string,
- ' (a) "bob" (a', ' (a) "bob" (a)', ' bob ',
- [errors.InvalidHeaderDefect], '')
- self.assertEqual(qs[0].comments, ['a'])
- self.assertEqual(qs[2].comments, ['a'])
- self.assertEqual(qs.content, 'bob')
- self.assertEqual(qs.quoted_value, ' "bob" ')
-
- def test_get_quoted_string_header_ends_in_qcontent(self):
- qs = self._test_get_x(parser.get_quoted_string,
- ' (a) "bob', ' (a) "bob"', ' bob',
- [errors.InvalidHeaderDefect], '')
- self.assertEqual(qs[0].comments, ['a'])
- self.assertEqual(qs.content, 'bob')
- self.assertEqual(qs.quoted_value, ' "bob"')
-
- def test_get_quoted_string_no_quoted_string(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_quoted_string(' (ab) xyz')
-
- def test_get_quoted_string_qs_ends_at_noncfws(self):
- qs = self._test_get_x(parser.get_quoted_string,
- '\t "bob" fee', '\t "bob" ', ' bob ', [], 'fee')
- self.assertEqual(qs.content, 'bob')
- self.assertEqual(qs.quoted_value, ' "bob" ')
-
- # get_atom
-
- def test_get_atom_only(self):
- atom = self._test_get_x(parser.get_atom,
- 'bob', 'bob', 'bob', [], '')
- self.assertEqual(atom.token_type, 'atom')
-
- def test_get_atom_with_wsp(self):
- self._test_get_x(parser.get_atom,
- '\t bob ', '\t bob ', ' bob ', [], '')
-
- def test_get_atom_with_comments_and_wsp(self):
- atom = self._test_get_x(parser.get_atom,
- ' (foo) bob(bar)', ' (foo) bob(bar)', ' bob ', [], '')
- self.assertEqual(atom[0][1].content, 'foo')
- self.assertEqual(atom[2][0].content, 'bar')
-
- def test_get_atom_with_multiple_comments(self):
- atom = self._test_get_x(parser.get_atom,
- ' (foo) (bar) bob(bird)', ' (foo) (bar) bob(bird)', ' bob ',
- [], '')
- self.assertEqual(atom[0].comments, ['foo', 'bar'])
- self.assertEqual(atom[2].comments, ['bird'])
-
- def test_get_atom_non_printable_in_comment(self):
- atom = self._test_get_x(parser.get_atom,
- ' (\x0A) bob', ' (\x0A) bob', ' bob',
- [errors.NonPrintableDefect], '')
- self.assertEqual(atom[0].comments, ['\x0A'])
-
- def test_get_atom_non_printable_in_atext(self):
- atom = self._test_get_x(parser.get_atom,
- ' (a) a\x0B', ' (a) a\x0B', ' a\x0B',
- [errors.NonPrintableDefect], '')
- self.assertEqual(atom[0].comments, ['a'])
-
- def test_get_atom_header_ends_in_comment(self):
- atom = self._test_get_x(parser.get_atom,
- ' (a) bob (a', ' (a) bob (a)', ' bob ',
- [errors.InvalidHeaderDefect], '')
- self.assertEqual(atom[0].comments, ['a'])
- self.assertEqual(atom[2].comments, ['a'])
-
- def test_get_atom_no_atom(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_atom(' (ab) ')
-
- def test_get_atom_no_atom_before_special(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_atom(' (ab) @')
-
- def test_get_atom_atom_ends_at_special(self):
- atom = self._test_get_x(parser.get_atom,
- ' (foo) bob(bar) @bang', ' (foo) bob(bar) ', ' bob ', [], '@bang')
- self.assertEqual(atom[0].comments, ['foo'])
- self.assertEqual(atom[2].comments, ['bar'])
-
- def test_get_atom_atom_ends_at_noncfws(self):
- atom = self._test_get_x(parser.get_atom,
- 'bob fred', 'bob ', 'bob ', [], 'fred')
-
- # get_dot_atom_text
-
- def test_get_dot_atom_text(self):
- dot_atom_text = self._test_get_x(parser.get_dot_atom_text,
- 'foo.bar.bang', 'foo.bar.bang', 'foo.bar.bang', [], '')
- self.assertEqual(dot_atom_text.token_type, 'dot-atom-text')
- self.assertEqual(len(dot_atom_text), 5)
-
- def test_get_dot_atom_text_lone_atom_is_valid(self):
- dot_atom_text = self._test_get_x(parser.get_dot_atom_text,
- 'foo', 'foo', 'foo', [], '')
-
- def test_get_dot_atom_text_raises_on_leading_dot(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_dot_atom_text('.foo.bar')
-
- def test_get_dot_atom_text_raises_on_trailing_dot(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_dot_atom_text('foo.bar.')
-
- def test_get_dot_atom_text_raises_on_leading_non_atext(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_dot_atom_text(' foo.bar')
- with self.assertRaises(errors.HeaderParseError):
- parser.get_dot_atom_text('@foo.bar')
- with self.assertRaises(errors.HeaderParseError):
- parser.get_dot_atom_text('"foo.bar"')
-
- def test_get_dot_atom_text_trailing_text_preserved(self):
- dot_atom_text = self._test_get_x(parser.get_dot_atom_text,
- 'foo@bar', 'foo', 'foo', [], '@bar')
-
- def test_get_dot_atom_text_trailing_ws_preserved(self):
- dot_atom_text = self._test_get_x(parser.get_dot_atom_text,
- 'foo .bar', 'foo', 'foo', [], ' .bar')
-
- # get_dot_atom
-
- def test_get_dot_atom_only(self):
- dot_atom = self._test_get_x(parser.get_dot_atom,
- 'foo.bar.bing', 'foo.bar.bing', 'foo.bar.bing', [], '')
- self.assertEqual(dot_atom.token_type, 'dot-atom')
- self.assertEqual(len(dot_atom), 1)
-
- def test_get_dot_atom_with_wsp(self):
- self._test_get_x(parser.get_dot_atom,
- '\t foo.bar.bing ', '\t foo.bar.bing ', ' foo.bar.bing ', [], '')
-
- def test_get_dot_atom_with_comments_and_wsp(self):
- self._test_get_x(parser.get_dot_atom,
- ' (sing) foo.bar.bing (here) ', ' (sing) foo.bar.bing (here) ',
- ' foo.bar.bing ', [], '')
-
- def test_get_dot_atom_space_ends_dot_atom(self):
- self._test_get_x(parser.get_dot_atom,
- ' (sing) foo.bar .bing (here) ', ' (sing) foo.bar ',
- ' foo.bar ', [], '.bing (here) ')
-
- def test_get_dot_atom_no_atom_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_dot_atom(' (foo) ')
-
- def test_get_dot_atom_leading_dot_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_dot_atom(' (foo) .bar')
-
- def test_get_dot_atom_two_dots_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_dot_atom('bar..bang')
-
- def test_get_dot_atom_trailing_dot_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_dot_atom(' (foo) bar.bang. foo')
-
- # get_word (if this were black box we'd repeat all the qs/atom tests)
-
- def test_get_word_atom_yields_atom(self):
- word = self._test_get_x(parser.get_word,
- ' (foo) bar (bang) :ah', ' (foo) bar (bang) ', ' bar ', [], ':ah')
- self.assertEqual(word.token_type, 'atom')
- self.assertEqual(word[0].token_type, 'cfws')
-
- def test_get_word_qs_yields_qs(self):
- word = self._test_get_x(parser.get_word,
- '"bar " (bang) ah', '"bar " (bang) ', 'bar ', [], 'ah')
- self.assertEqual(word.token_type, 'quoted-string')
- self.assertEqual(word[0].token_type, 'bare-quoted-string')
- self.assertEqual(word[0].value, 'bar ')
- self.assertEqual(word.content, 'bar ')
-
- def test_get_word_ends_at_dot(self):
- self._test_get_x(parser.get_word,
- 'foo.', 'foo', 'foo', [], '.')
-
- # get_phrase
-
- def test_get_phrase_simple(self):
- phrase = self._test_get_x(parser.get_phrase,
- '"Fred A. Johnson" is his name, oh.',
- '"Fred A. Johnson" is his name',
- 'Fred A. Johnson is his name',
- [],
- ', oh.')
- self.assertEqual(phrase.token_type, 'phrase')
-
- def test_get_phrase_complex(self):
- phrase = self._test_get_x(parser.get_phrase,
- ' (A) bird (in (my|your)) "hand " is messy\t<>\t',
- ' (A) bird (in (my|your)) "hand " is messy\t',
- ' bird hand is messy ',
- [],
- '<>\t')
- self.assertEqual(phrase[0][0].comments, ['A'])
- self.assertEqual(phrase[0][2].comments, ['in (my|your)'])
-
- def test_get_phrase_obsolete(self):
- phrase = self._test_get_x(parser.get_phrase,
- 'Fred A.(weird).O Johnson',
- 'Fred A.(weird).O Johnson',
- 'Fred A. .O Johnson',
- [errors.ObsoleteHeaderDefect]*3,
- '')
- self.assertEqual(len(phrase), 7)
- self.assertEqual(phrase[3].comments, ['weird'])
-
- def test_get_phrase_pharse_must_start_with_word(self):
- phrase = self._test_get_x(parser.get_phrase,
- '(even weirder).name',
- '(even weirder).name',
- ' .name',
- [errors.InvalidHeaderDefect] + [errors.ObsoleteHeaderDefect]*2,
- '')
- self.assertEqual(len(phrase), 3)
- self.assertEqual(phrase[0].comments, ['even weirder'])
-
- def test_get_phrase_ending_with_obsolete(self):
- phrase = self._test_get_x(parser.get_phrase,
- 'simple phrase.(with trailing comment):boo',
- 'simple phrase.(with trailing comment)',
- 'simple phrase. ',
- [errors.ObsoleteHeaderDefect]*2,
- ':boo')
- self.assertEqual(len(phrase), 4)
- self.assertEqual(phrase[3].comments, ['with trailing comment'])
-
- def get_phrase_cfws_only_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_phrase(' (foo) ')
-
- # get_local_part
-
- def test_get_local_part_simple(self):
- local_part = self._test_get_x(parser.get_local_part,
- 'dinsdale@python.org', 'dinsdale', 'dinsdale', [], '@python.org')
- self.assertEqual(local_part.token_type, 'local-part')
- self.assertEqual(local_part.local_part, 'dinsdale')
-
- def test_get_local_part_with_dot(self):
- local_part = self._test_get_x(parser.get_local_part,
- 'Fred.A.Johnson@python.org',
- 'Fred.A.Johnson',
- 'Fred.A.Johnson',
- [],
- '@python.org')
- self.assertEqual(local_part.local_part, 'Fred.A.Johnson')
-
- def test_get_local_part_with_whitespace(self):
- local_part = self._test_get_x(parser.get_local_part,
- ' Fred.A.Johnson @python.org',
- ' Fred.A.Johnson ',
- ' Fred.A.Johnson ',
- [],
- '@python.org')
- self.assertEqual(local_part.local_part, 'Fred.A.Johnson')
-
- def test_get_local_part_with_cfws(self):
- local_part = self._test_get_x(parser.get_local_part,
- ' (foo) Fred.A.Johnson (bar (bird)) @python.org',
- ' (foo) Fred.A.Johnson (bar (bird)) ',
- ' Fred.A.Johnson ',
- [],
- '@python.org')
- self.assertEqual(local_part.local_part, 'Fred.A.Johnson')
- self.assertEqual(local_part[0][0].comments, ['foo'])
- self.assertEqual(local_part[0][2].comments, ['bar (bird)'])
-
- def test_get_local_part_simple_quoted(self):
- local_part = self._test_get_x(parser.get_local_part,
- '"dinsdale"@python.org', '"dinsdale"', '"dinsdale"', [], '@python.org')
- self.assertEqual(local_part.token_type, 'local-part')
- self.assertEqual(local_part.local_part, 'dinsdale')
-
- def test_get_local_part_with_quoted_dot(self):
- local_part = self._test_get_x(parser.get_local_part,
- '"Fred.A.Johnson"@python.org',
- '"Fred.A.Johnson"',
- '"Fred.A.Johnson"',
- [],
- '@python.org')
- self.assertEqual(local_part.local_part, 'Fred.A.Johnson')
-
- def test_get_local_part_quoted_with_whitespace(self):
- local_part = self._test_get_x(parser.get_local_part,
- ' "Fred A. Johnson" @python.org',
- ' "Fred A. Johnson" ',
- ' "Fred A. Johnson" ',
- [],
- '@python.org')
- self.assertEqual(local_part.local_part, 'Fred A. Johnson')
-
- def test_get_local_part_quoted_with_cfws(self):
- local_part = self._test_get_x(parser.get_local_part,
- ' (foo) " Fred A. Johnson " (bar (bird)) @python.org',
- ' (foo) " Fred A. Johnson " (bar (bird)) ',
- ' " Fred A. Johnson " ',
- [],
- '@python.org')
- self.assertEqual(local_part.local_part, ' Fred A. Johnson ')
- self.assertEqual(local_part[0][0].comments, ['foo'])
- self.assertEqual(local_part[0][2].comments, ['bar (bird)'])
-
-
- def test_get_local_part_simple_obsolete(self):
- local_part = self._test_get_x(parser.get_local_part,
- 'Fred. A.Johnson@python.org',
- 'Fred. A.Johnson',
- 'Fred. A.Johnson',
- [errors.ObsoleteHeaderDefect],
- '@python.org')
- self.assertEqual(local_part.local_part, 'Fred.A.Johnson')
-
- def test_get_local_part_complex_obsolete_1(self):
- local_part = self._test_get_x(parser.get_local_part,
- ' (foo )Fred (bar).(bird) A.(sheep)Johnson."and dogs "@python.org',
- ' (foo )Fred (bar).(bird) A.(sheep)Johnson."and dogs "',
- ' Fred . A. Johnson.and dogs ',
- [errors.ObsoleteHeaderDefect],
- '@python.org')
- self.assertEqual(local_part.local_part, 'Fred.A.Johnson.and dogs ')
-
- def test_get_local_part_complex_obsolete_invalid(self):
- local_part = self._test_get_x(parser.get_local_part,
- ' (foo )Fred (bar).(bird) A.(sheep)Johnson "and dogs"@python.org',
- ' (foo )Fred (bar).(bird) A.(sheep)Johnson "and dogs"',
- ' Fred . A. Johnson and dogs',
- [errors.InvalidHeaderDefect]*2,
- '@python.org')
- self.assertEqual(local_part.local_part, 'Fred.A.Johnson and dogs')
-
- def test_get_local_part_no_part_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_local_part(' (foo) ')
-
- def test_get_local_part_special_instead_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_local_part(' (foo) @python.org')
-
- def test_get_local_part_trailing_dot(self):
- local_part = self._test_get_x(parser.get_local_part,
- ' borris.@python.org',
- ' borris.',
- ' borris.',
- [errors.InvalidHeaderDefect]*2,
- '@python.org')
- self.assertEqual(local_part.local_part, 'borris.')
-
- def test_get_local_part_trailing_dot_with_ws(self):
- local_part = self._test_get_x(parser.get_local_part,
- ' borris. @python.org',
- ' borris. ',
- ' borris. ',
- [errors.InvalidHeaderDefect]*2,
- '@python.org')
- self.assertEqual(local_part.local_part, 'borris.')
-
- def test_get_local_part_leading_dot(self):
- local_part = self._test_get_x(parser.get_local_part,
- '.borris@python.org',
- '.borris',
- '.borris',
- [errors.InvalidHeaderDefect]*2,
- '@python.org')
- self.assertEqual(local_part.local_part, '.borris')
-
- def test_get_local_part_leading_dot_after_ws(self):
- local_part = self._test_get_x(parser.get_local_part,
- ' .borris@python.org',
- ' .borris',
- ' .borris',
- [errors.InvalidHeaderDefect]*2,
- '@python.org')
- self.assertEqual(local_part.local_part, '.borris')
-
- def test_get_local_part_double_dot_raises(self):
- local_part = self._test_get_x(parser.get_local_part,
- ' borris.(foo).natasha@python.org',
- ' borris.(foo).natasha',
- ' borris. .natasha',
- [errors.InvalidHeaderDefect]*2,
- '@python.org')
- self.assertEqual(local_part.local_part, 'borris..natasha')
-
- def test_get_local_part_quoted_strings_in_atom_list(self):
- local_part = self._test_get_x(parser.get_local_part,
- '""example" example"@example.com',
- '""example" example"',
- 'example example',
- [errors.InvalidHeaderDefect]*3,
- '@example.com')
- self.assertEqual(local_part.local_part, 'example example')
-
- def test_get_local_part_valid_and_invalid_qp_in_atom_list(self):
- local_part = self._test_get_x(parser.get_local_part,
- r'"\\"example\\" example"@example.com',
- r'"\\"example\\" example"',
- r'\example\\ example',
- [errors.InvalidHeaderDefect]*5,
- '@example.com')
- self.assertEqual(local_part.local_part, r'\example\\ example')
-
- def test_get_local_part_unicode_defect(self):
- # Currently this only happens when parsing unicode, not when parsing
- # stuff that was originally binary.
- local_part = self._test_get_x(parser.get_local_part,
- 'exámple@example.com',
- 'exámple',
- 'exámple',
- [errors.NonASCIILocalPartDefect],
- '@example.com')
- self.assertEqual(local_part.local_part, 'exámple')
-
- # get_dtext
-
- def test_get_dtext_only(self):
- dtext = self._test_get_x(parser.get_dtext,
- 'foobar', 'foobar', 'foobar', [], '')
- self.assertEqual(dtext.token_type, 'ptext')
-
- def test_get_dtext_all_dtext(self):
- dtext = self._test_get_x(parser.get_dtext, self.rfc_dtext_chars,
- self.rfc_dtext_chars,
- self.rfc_dtext_chars, [], '')
-
- def test_get_dtext_two_words_gets_first(self):
- self._test_get_x(parser.get_dtext,
- 'foo bar', 'foo', 'foo', [], ' bar')
-
- def test_get_dtext_following_wsp_preserved(self):
- self._test_get_x(parser.get_dtext,
- 'foo \t\tbar', 'foo', 'foo', [], ' \t\tbar')
-
- def test_get_dtext_non_printables(self):
- dtext = self._test_get_x(parser.get_dtext,
- 'foo\x00bar]', 'foo\x00bar', 'foo\x00bar',
- [errors.NonPrintableDefect], ']')
- self.assertEqual(dtext.defects[0].non_printables[0], '\x00')
-
- def test_get_dtext_with_qp(self):
- ptext = self._test_get_x(parser.get_dtext,
- r'foo\]\[\\bar\b\e\l\l',
- r'foo][\barbell',
- r'foo][\barbell',
- [errors.ObsoleteHeaderDefect],
- '')
-
- def test_get_dtext_up_to_close_bracket_only(self):
- self._test_get_x(parser.get_dtext,
- 'foo]', 'foo', 'foo', [], ']')
-
- def test_get_dtext_wsp_before_close_bracket_preserved(self):
- self._test_get_x(parser.get_dtext,
- 'foo ]', 'foo', 'foo', [], ' ]')
-
- def test_get_dtext_close_bracket_mid_word(self):
- self._test_get_x(parser.get_dtext,
- 'foo]bar', 'foo', 'foo', [], ']bar')
-
- def test_get_dtext_up_to_open_bracket_only(self):
- self._test_get_x(parser.get_dtext,
- 'foo[', 'foo', 'foo', [], '[')
-
- def test_get_dtext_wsp_before_open_bracket_preserved(self):
- self._test_get_x(parser.get_dtext,
- 'foo [', 'foo', 'foo', [], ' [')
-
- def test_get_dtext_open_bracket_mid_word(self):
- self._test_get_x(parser.get_dtext,
- 'foo[bar', 'foo', 'foo', [], '[bar')
-
- # get_domain_literal
-
- def test_get_domain_literal_only(self):
- domain_literal = domain_literal = self._test_get_x(parser.get_domain_literal,
- '[127.0.0.1]',
- '[127.0.0.1]',
- '[127.0.0.1]',
- [],
- '')
- self.assertEqual(domain_literal.token_type, 'domain-literal')
- self.assertEqual(domain_literal.domain, '[127.0.0.1]')
- self.assertEqual(domain_literal.ip, '127.0.0.1')
-
- def test_get_domain_literal_with_internal_ws(self):
- domain_literal = self._test_get_x(parser.get_domain_literal,
- '[ 127.0.0.1\t ]',
- '[ 127.0.0.1\t ]',
- '[ 127.0.0.1 ]',
- [],
- '')
- self.assertEqual(domain_literal.domain, '[127.0.0.1]')
- self.assertEqual(domain_literal.ip, '127.0.0.1')
-
- def test_get_domain_literal_with_surrounding_cfws(self):
- domain_literal = self._test_get_x(parser.get_domain_literal,
- '(foo)[ 127.0.0.1] (bar)',
- '(foo)[ 127.0.0.1] (bar)',
- ' [ 127.0.0.1] ',
- [],
- '')
- self.assertEqual(domain_literal.domain, '[127.0.0.1]')
- self.assertEqual(domain_literal.ip, '127.0.0.1')
-
- def test_get_domain_literal_no_start_char_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_domain_literal('(foo) ')
-
- def test_get_domain_literal_no_start_char_before_special_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_domain_literal('(foo) @')
-
- def test_get_domain_literal_bad_dtext_char_before_special_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_domain_literal('(foo) [abc[@')
-
- # get_domain
-
- def test_get_domain_regular_domain_only(self):
- domain = self._test_get_x(parser.get_domain,
- 'example.com',
- 'example.com',
- 'example.com',
- [],
- '')
- self.assertEqual(domain.token_type, 'domain')
- self.assertEqual(domain.domain, 'example.com')
-
- def test_get_domain_domain_literal_only(self):
- domain = self._test_get_x(parser.get_domain,
- '[127.0.0.1]',
- '[127.0.0.1]',
- '[127.0.0.1]',
- [],
- '')
- self.assertEqual(domain.token_type, 'domain')
- self.assertEqual(domain.domain, '[127.0.0.1]')
-
- def test_get_domain_with_cfws(self):
- domain = self._test_get_x(parser.get_domain,
- '(foo) example.com(bar)\t',
- '(foo) example.com(bar)\t',
- ' example.com ',
- [],
- '')
- self.assertEqual(domain.domain, 'example.com')
-
- def test_get_domain_domain_literal_with_cfws(self):
- domain = self._test_get_x(parser.get_domain,
- '(foo)[127.0.0.1]\t(bar)',
- '(foo)[127.0.0.1]\t(bar)',
- ' [127.0.0.1] ',
- [],
- '')
- self.assertEqual(domain.domain, '[127.0.0.1]')
-
- def test_get_domain_domain_with_cfws_ends_at_special(self):
- domain = self._test_get_x(parser.get_domain,
- '(foo)example.com\t(bar), next',
- '(foo)example.com\t(bar)',
- ' example.com ',
- [],
- ', next')
- self.assertEqual(domain.domain, 'example.com')
-
- def test_get_domain_domain_literal_with_cfws_ends_at_special(self):
- domain = self._test_get_x(parser.get_domain,
- '(foo)[127.0.0.1]\t(bar), next',
- '(foo)[127.0.0.1]\t(bar)',
- ' [127.0.0.1] ',
- [],
- ', next')
- self.assertEqual(domain.domain, '[127.0.0.1]')
-
- def test_get_domain_obsolete(self):
- domain = self._test_get_x(parser.get_domain,
- '(foo) example . (bird)com(bar)\t',
- '(foo) example . (bird)com(bar)\t',
- ' example . com ',
- [errors.ObsoleteHeaderDefect],
- '')
- self.assertEqual(domain.domain, 'example.com')
-
- def test_get_domain_no_non_cfws_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_domain(" (foo)\t")
-
- def test_get_domain_no_atom_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_domain(" (foo)\t, broken")
-
-
- # get_addr_spec
-
- def test_get_addr_spec_normal(self):
- addr_spec = self._test_get_x(parser.get_addr_spec,
- 'dinsdale@example.com',
- 'dinsdale@example.com',
- 'dinsdale@example.com',
- [],
- '')
- self.assertEqual(addr_spec.token_type, 'addr-spec')
- self.assertEqual(addr_spec.local_part, 'dinsdale')
- self.assertEqual(addr_spec.domain, 'example.com')
- self.assertEqual(addr_spec.addr_spec, 'dinsdale@example.com')
-
- def test_get_addr_spec_with_doamin_literal(self):
- addr_spec = self._test_get_x(parser.get_addr_spec,
- 'dinsdale@[127.0.0.1]',
- 'dinsdale@[127.0.0.1]',
- 'dinsdale@[127.0.0.1]',
- [],
- '')
- self.assertEqual(addr_spec.local_part, 'dinsdale')
- self.assertEqual(addr_spec.domain, '[127.0.0.1]')
- self.assertEqual(addr_spec.addr_spec, 'dinsdale@[127.0.0.1]')
-
- def test_get_addr_spec_with_cfws(self):
- addr_spec = self._test_get_x(parser.get_addr_spec,
- '(foo) dinsdale(bar)@ (bird) example.com (bog)',
- '(foo) dinsdale(bar)@ (bird) example.com (bog)',
- ' dinsdale@example.com ',
- [],
- '')
- self.assertEqual(addr_spec.local_part, 'dinsdale')
- self.assertEqual(addr_spec.domain, 'example.com')
- self.assertEqual(addr_spec.addr_spec, 'dinsdale@example.com')
-
- def test_get_addr_spec_with_qouoted_string_and_cfws(self):
- addr_spec = self._test_get_x(parser.get_addr_spec,
- '(foo) "roy a bug"(bar)@ (bird) example.com (bog)',
- '(foo) "roy a bug"(bar)@ (bird) example.com (bog)',
- ' "roy a bug"@example.com ',
- [],
- '')
- self.assertEqual(addr_spec.local_part, 'roy a bug')
- self.assertEqual(addr_spec.domain, 'example.com')
- self.assertEqual(addr_spec.addr_spec, '"roy a bug"@example.com')
-
- def test_get_addr_spec_ends_at_special(self):
- addr_spec = self._test_get_x(parser.get_addr_spec,
- '(foo) "roy a bug"(bar)@ (bird) example.com (bog) , next',
- '(foo) "roy a bug"(bar)@ (bird) example.com (bog) ',
- ' "roy a bug"@example.com ',
- [],
- ', next')
- self.assertEqual(addr_spec.local_part, 'roy a bug')
- self.assertEqual(addr_spec.domain, 'example.com')
- self.assertEqual(addr_spec.addr_spec, '"roy a bug"@example.com')
-
- def test_get_addr_spec_quoted_strings_in_atom_list(self):
- addr_spec = self._test_get_x(parser.get_addr_spec,
- '""example" example"@example.com',
- '""example" example"@example.com',
- 'example example@example.com',
- [errors.InvalidHeaderDefect]*3,
- '')
- self.assertEqual(addr_spec.local_part, 'example example')
- self.assertEqual(addr_spec.domain, 'example.com')
- self.assertEqual(addr_spec.addr_spec, '"example example"@example.com')
-
- def test_get_addr_spec_dot_atom(self):
- addr_spec = self._test_get_x(parser.get_addr_spec,
- 'star.a.star@example.com',
- 'star.a.star@example.com',
- 'star.a.star@example.com',
- [],
- '')
- self.assertEqual(addr_spec.local_part, 'star.a.star')
- self.assertEqual(addr_spec.domain, 'example.com')
- self.assertEqual(addr_spec.addr_spec, 'star.a.star@example.com')
-
- # get_obs_route
-
- def test_get_obs_route_simple(self):
- obs_route = self._test_get_x(parser.get_obs_route,
- '@example.com, @two.example.com:',
- '@example.com, @two.example.com:',
- '@example.com, @two.example.com:',
- [],
- '')
- self.assertEqual(obs_route.token_type, 'obs-route')
- self.assertEqual(obs_route.domains, ['example.com', 'two.example.com'])
-
- def test_get_obs_route_complex(self):
- obs_route = self._test_get_x(parser.get_obs_route,
- '(foo),, (blue)@example.com (bar),@two.(foo) example.com (bird):',
- '(foo),, (blue)@example.com (bar),@two.(foo) example.com (bird):',
- ' ,, @example.com ,@two. example.com :',
- [errors.ObsoleteHeaderDefect], # This is the obs-domain
- '')
- self.assertEqual(obs_route.token_type, 'obs-route')
- self.assertEqual(obs_route.domains, ['example.com', 'two.example.com'])
-
- def test_get_obs_route_no_route_before_end_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_obs_route('(foo) @example.com,')
-
- def test_get_obs_route_no_route_before_special_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_obs_route('(foo) [abc],')
-
- def test_get_obs_route_no_route_before_special_raises2(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_obs_route('(foo) @example.com [abc],')
-
- # get_angle_addr
-
- def test_get_angle_addr_simple(self):
- angle_addr = self._test_get_x(parser.get_angle_addr,
- '',
- '',
- '',
- [],
- '')
- self.assertEqual(angle_addr.token_type, 'angle-addr')
- self.assertEqual(angle_addr.local_part, 'dinsdale')
- self.assertEqual(angle_addr.domain, 'example.com')
- self.assertIsNone(angle_addr.route)
- self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_angle_addr_empty(self):
- angle_addr = self._test_get_x(parser.get_angle_addr,
- '<>',
- '<>',
- '<>',
- [errors.InvalidHeaderDefect],
- '')
- self.assertEqual(angle_addr.token_type, 'angle-addr')
- self.assertIsNone(angle_addr.local_part)
- self.assertIsNone(angle_addr.domain)
- self.assertIsNone(angle_addr.route)
- self.assertEqual(angle_addr.addr_spec, '<>')
-
- def test_get_angle_addr_with_cfws(self):
- angle_addr = self._test_get_x(parser.get_angle_addr,
- ' (foo) (bar)',
- ' (foo) (bar)',
- ' ',
- [],
- '')
- self.assertEqual(angle_addr.token_type, 'angle-addr')
- self.assertEqual(angle_addr.local_part, 'dinsdale')
- self.assertEqual(angle_addr.domain, 'example.com')
- self.assertIsNone(angle_addr.route)
- self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_angle_addr_qs_and_domain_literal(self):
- angle_addr = self._test_get_x(parser.get_angle_addr,
- '<"Fred Perfect"@[127.0.0.1]>',
- '<"Fred Perfect"@[127.0.0.1]>',
- '<"Fred Perfect"@[127.0.0.1]>',
- [],
- '')
- self.assertEqual(angle_addr.local_part, 'Fred Perfect')
- self.assertEqual(angle_addr.domain, '[127.0.0.1]')
- self.assertIsNone(angle_addr.route)
- self.assertEqual(angle_addr.addr_spec, '"Fred Perfect"@[127.0.0.1]')
-
- def test_get_angle_addr_internal_cfws(self):
- angle_addr = self._test_get_x(parser.get_angle_addr,
- '<(foo) dinsdale@example.com(bar)>',
- '<(foo) dinsdale@example.com(bar)>',
- '< dinsdale@example.com >',
- [],
- '')
- self.assertEqual(angle_addr.local_part, 'dinsdale')
- self.assertEqual(angle_addr.domain, 'example.com')
- self.assertIsNone(angle_addr.route)
- self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_angle_addr_obs_route(self):
- angle_addr = self._test_get_x(parser.get_angle_addr,
- '(foo)<@example.com, (bird) @two.example.com: dinsdale@example.com> (bar) ',
- '(foo)<@example.com, (bird) @two.example.com: dinsdale@example.com> (bar) ',
- ' <@example.com, @two.example.com: dinsdale@example.com> ',
- [errors.ObsoleteHeaderDefect],
- '')
- self.assertEqual(angle_addr.local_part, 'dinsdale')
- self.assertEqual(angle_addr.domain, 'example.com')
- self.assertEqual(angle_addr.route, ['example.com', 'two.example.com'])
- self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_angle_addr_missing_closing_angle(self):
- angle_addr = self._test_get_x(parser.get_angle_addr,
- '',
- '',
- [errors.InvalidHeaderDefect],
- '')
- self.assertEqual(angle_addr.local_part, 'dinsdale')
- self.assertEqual(angle_addr.domain, 'example.com')
- self.assertIsNone(angle_addr.route)
- self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_angle_addr_missing_closing_angle_with_cfws(self):
- angle_addr = self._test_get_x(parser.get_angle_addr,
- '',
- '',
- [errors.InvalidHeaderDefect],
- '')
- self.assertEqual(angle_addr.local_part, 'dinsdale')
- self.assertEqual(angle_addr.domain, 'example.com')
- self.assertIsNone(angle_addr.route)
- self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_angle_addr_ends_at_special(self):
- angle_addr = self._test_get_x(parser.get_angle_addr,
- ' (foo), next',
- ' (foo)',
- ' ',
- [],
- ', next')
- self.assertEqual(angle_addr.local_part, 'dinsdale')
- self.assertEqual(angle_addr.domain, 'example.com')
- self.assertIsNone(angle_addr.route)
- self.assertEqual(angle_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_angle_addr_no_angle_raise(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_angle_addr('(foo) ')
-
- def test_get_angle_addr_no_angle_before_special_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_angle_addr('(foo) , next')
-
- def test_get_angle_addr_no_angle_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_angle_addr('bar')
-
- def test_get_angle_addr_special_after_angle_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_angle_addr('(foo) <, bar')
-
- # get_display_name This is phrase but with a different value.
-
- def test_get_display_name_simple(self):
- display_name = self._test_get_x(parser.get_display_name,
- 'Fred A Johnson',
- 'Fred A Johnson',
- 'Fred A Johnson',
- [],
- '')
- self.assertEqual(display_name.token_type, 'display-name')
- self.assertEqual(display_name.display_name, 'Fred A Johnson')
-
- def test_get_display_name_complex1(self):
- display_name = self._test_get_x(parser.get_display_name,
- '"Fred A. Johnson" is his name, oh.',
- '"Fred A. Johnson" is his name',
- '"Fred A. Johnson is his name"',
- [],
- ', oh.')
- self.assertEqual(display_name.token_type, 'display-name')
- self.assertEqual(display_name.display_name, 'Fred A. Johnson is his name')
-
- def test_get_display_name_complex2(self):
- display_name = self._test_get_x(parser.get_display_name,
- ' (A) bird (in (my|your)) "hand " is messy\t<>\t',
- ' (A) bird (in (my|your)) "hand " is messy\t',
- ' "bird hand is messy" ',
- [],
- '<>\t')
- self.assertEqual(display_name[0][0].comments, ['A'])
- self.assertEqual(display_name[0][2].comments, ['in (my|your)'])
- self.assertEqual(display_name.display_name, 'bird hand is messy')
-
- def test_get_display_name_obsolete(self):
- display_name = self._test_get_x(parser.get_display_name,
- 'Fred A.(weird).O Johnson',
- 'Fred A.(weird).O Johnson',
- '"Fred A. .O Johnson"',
- [errors.ObsoleteHeaderDefect]*3,
- '')
- self.assertEqual(len(display_name), 7)
- self.assertEqual(display_name[3].comments, ['weird'])
- self.assertEqual(display_name.display_name, 'Fred A. .O Johnson')
-
- def test_get_display_name_pharse_must_start_with_word(self):
- display_name = self._test_get_x(parser.get_display_name,
- '(even weirder).name',
- '(even weirder).name',
- ' ".name"',
- [errors.InvalidHeaderDefect] + [errors.ObsoleteHeaderDefect]*2,
- '')
- self.assertEqual(len(display_name), 3)
- self.assertEqual(display_name[0].comments, ['even weirder'])
- self.assertEqual(display_name.display_name, '.name')
-
- def test_get_display_name_ending_with_obsolete(self):
- display_name = self._test_get_x(parser.get_display_name,
- 'simple phrase.(with trailing comment):boo',
- 'simple phrase.(with trailing comment)',
- '"simple phrase." ',
- [errors.ObsoleteHeaderDefect]*2,
- ':boo')
- self.assertEqual(len(display_name), 4)
- self.assertEqual(display_name[3].comments, ['with trailing comment'])
- self.assertEqual(display_name.display_name, 'simple phrase.')
-
- # get_name_addr
-
- def test_get_name_addr_angle_addr_only(self):
- name_addr = self._test_get_x(parser.get_name_addr,
- '',
- '',
- '',
- [],
- '')
- self.assertEqual(name_addr.token_type, 'name-addr')
- self.assertIsNone(name_addr.display_name)
- self.assertEqual(name_addr.local_part, 'dinsdale')
- self.assertEqual(name_addr.domain, 'example.com')
- self.assertIsNone(name_addr.route)
- self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_name_addr_atom_name(self):
- name_addr = self._test_get_x(parser.get_name_addr,
- 'Dinsdale ',
- 'Dinsdale ',
- 'Dinsdale ',
- [],
- '')
- self.assertEqual(name_addr.token_type, 'name-addr')
- self.assertEqual(name_addr.display_name, 'Dinsdale')
- self.assertEqual(name_addr.local_part, 'dinsdale')
- self.assertEqual(name_addr.domain, 'example.com')
- self.assertIsNone(name_addr.route)
- self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_name_addr_atom_name_with_cfws(self):
- name_addr = self._test_get_x(parser.get_name_addr,
- '(foo) Dinsdale (bar) (bird)',
- '(foo) Dinsdale (bar) (bird)',
- ' Dinsdale ',
- [],
- '')
- self.assertEqual(name_addr.display_name, 'Dinsdale')
- self.assertEqual(name_addr.local_part, 'dinsdale')
- self.assertEqual(name_addr.domain, 'example.com')
- self.assertIsNone(name_addr.route)
- self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_name_addr_name_with_cfws_and_dots(self):
- name_addr = self._test_get_x(parser.get_name_addr,
- '(foo) Roy.A.Bear (bar) (bird)',
- '(foo) Roy.A.Bear (bar) (bird)',
- ' "Roy.A.Bear" ',
- [errors.ObsoleteHeaderDefect]*2,
- '')
- self.assertEqual(name_addr.display_name, 'Roy.A.Bear')
- self.assertEqual(name_addr.local_part, 'dinsdale')
- self.assertEqual(name_addr.domain, 'example.com')
- self.assertIsNone(name_addr.route)
- self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_name_addr_qs_name(self):
- name_addr = self._test_get_x(parser.get_name_addr,
- '"Roy.A.Bear" ',
- '"Roy.A.Bear" ',
- '"Roy.A.Bear" ',
- [],
- '')
- self.assertEqual(name_addr.display_name, 'Roy.A.Bear')
- self.assertEqual(name_addr.local_part, 'dinsdale')
- self.assertEqual(name_addr.domain, 'example.com')
- self.assertIsNone(name_addr.route)
- self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_name_addr_with_route(self):
- name_addr = self._test_get_x(parser.get_name_addr,
- '"Roy.A.Bear" <@two.example.com: dinsdale@example.com>',
- '"Roy.A.Bear" <@two.example.com: dinsdale@example.com>',
- '"Roy.A.Bear" <@two.example.com: dinsdale@example.com>',
- [errors.ObsoleteHeaderDefect],
- '')
- self.assertEqual(name_addr.display_name, 'Roy.A.Bear')
- self.assertEqual(name_addr.local_part, 'dinsdale')
- self.assertEqual(name_addr.domain, 'example.com')
- self.assertEqual(name_addr.route, ['two.example.com'])
- self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_name_addr_ends_at_special(self):
- name_addr = self._test_get_x(parser.get_name_addr,
- '"Roy.A.Bear" , next',
- '"Roy.A.Bear" ',
- '"Roy.A.Bear" ',
- [],
- ', next')
- self.assertEqual(name_addr.display_name, 'Roy.A.Bear')
- self.assertEqual(name_addr.local_part, 'dinsdale')
- self.assertEqual(name_addr.domain, 'example.com')
- self.assertIsNone(name_addr.route)
- self.assertEqual(name_addr.addr_spec, 'dinsdale@example.com')
-
- def test_get_name_addr_no_content_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_name_addr(' (foo) ')
-
- def test_get_name_addr_no_content_before_special_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_name_addr(' (foo) ,')
-
- def test_get_name_addr_no_angle_after_display_name_raises(self):
- with self.assertRaises(errors.HeaderParseError):
- parser.get_name_addr('foo bar')
-
- # get_mailbox
-
- def test_get_mailbox_addr_spec_only(self):
- mailbox = self._test_get_x(parser.get_mailbox,
- 'dinsdale@example.com',
- 'dinsdale@example.com',
- 'dinsdale@example.com',
- [],
- '')
- self.assertEqual(mailbox.token_type, 'mailbox')
- self.assertIsNone(mailbox.display_name)
- self.assertEqual(mailbox.local_part, 'dinsdale')
- self.assertEqual(mailbox.domain, 'example.com')
- self.assertIsNone(mailbox.route)
- self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com')
-
- def test_get_mailbox_angle_addr_only(self):
- mailbox = self._test_get_x(parser.get_mailbox,
- '',
- '',
- '',
- [],
- '')
- self.assertEqual(mailbox.token_type, 'mailbox')
- self.assertIsNone(mailbox.display_name)
- self.assertEqual(mailbox.local_part, 'dinsdale')
- self.assertEqual(mailbox.domain, 'example.com')
- self.assertIsNone(mailbox.route)
- self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com')
-
- def test_get_mailbox_name_addr(self):
- mailbox = self._test_get_x(parser.get_mailbox,
- '"Roy A. Bear" ',
- '"Roy A. Bear" ',
- '"Roy A. Bear" ',
- [],
- '')
- self.assertEqual(mailbox.token_type, 'mailbox')
- self.assertEqual(mailbox.display_name, 'Roy A. Bear')
- self.assertEqual(mailbox.local_part, 'dinsdale')
- self.assertEqual(mailbox.domain, 'example.com')
- self.assertIsNone(mailbox.route)
- self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com')
-
- def test_get_mailbox_ends_at_special(self):
- mailbox = self._test_get_x(parser.get_mailbox,
- '"Roy A. Bear" , rest',
- '"Roy A. Bear" ',
- '"Roy A. Bear" ',
- [],
- ', rest')
- self.assertEqual(mailbox.token_type, 'mailbox')
- self.assertEqual(mailbox.display_name, 'Roy A. Bear')
- self.assertEqual(mailbox.local_part, 'dinsdale')
- self.assertEqual(mailbox.domain, 'example.com')
- self.assertIsNone(mailbox.route)
- self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com')
-
- def test_get_mailbox_quoted_strings_in_atom_list(self):
- mailbox = self._test_get_x(parser.get_mailbox,
- '""example" example"@example.com',
- '""example" example"@example.com',
- 'example example@example.com',
- [errors.InvalidHeaderDefect]*3,
- '')
- self.assertEqual(mailbox.local_part, 'example example')
- self.assertEqual(mailbox.domain, 'example.com')
- self.assertEqual(mailbox.addr_spec, '"example example"@example.com')
-
- # get_mailbox_list
-
- def test_get_mailbox_list_single_addr(self):
- mailbox_list = self._test_get_x(parser.get_mailbox_list,
- 'dinsdale@example.com',
- 'dinsdale@example.com',
- 'dinsdale@example.com',
- [],
- '')
- self.assertEqual(mailbox_list.token_type, 'mailbox-list')
- self.assertEqual(len(mailbox_list.mailboxes), 1)
- mailbox = mailbox_list.mailboxes[0]
- self.assertIsNone(mailbox.display_name)
- self.assertEqual(mailbox.local_part, 'dinsdale')
- self.assertEqual(mailbox.domain, 'example.com')
- self.assertIsNone(mailbox.route)
- self.assertEqual(mailbox.addr_spec, 'dinsdale@example.com')
- self.assertEqual(mailbox_list.mailboxes,
- mailbox_list.all_mailboxes)
-
- def test_get_mailbox_list_two_simple_addr(self):
- mailbox_list = self._test_get_x(parser.get_mailbox_list,
- 'dinsdale@example.com, dinsdale@test.example.com',
- 'dinsdale@example.com, dinsdale@test.example.com',
- 'dinsdale@example.com, dinsdale@test.example.com',
- [],
- '')
- self.assertEqual(mailbox_list.token_type, 'mailbox-list')
- self.assertEqual(len(mailbox_list.mailboxes), 2)
- self.assertEqual(mailbox_list.mailboxes[0].addr_spec,
- 'dinsdale@example.com')
- self.assertEqual(mailbox_list.mailboxes[1].addr_spec,
- 'dinsdale@test.example.com')
- self.assertEqual(mailbox_list.mailboxes,
- mailbox_list.all_mailboxes)
-
- def test_get_mailbox_list_two_name_addr(self):
- mailbox_list = self._test_get_x(parser.get_mailbox_list,
- ('"Roy A. Bear" ,'
- ' "Fred Flintstone" '),
- ('"Roy A. Bear" ,'
- ' "Fred Flintstone" '),
- ('"Roy A. Bear" ,'
- ' "Fred Flintstone" '),
- [],
- '')
- self.assertEqual(len(mailbox_list.mailboxes), 2)
- self.assertEqual(mailbox_list.mailboxes[0].addr_spec,
- 'dinsdale@example.com')
- self.assertEqual(mailbox_list.mailboxes[0].display_name,
- 'Roy A. Bear')
- self.assertEqual(mailbox_list.mailboxes[1].addr_spec,
- 'dinsdale@test.example.com')
- self.assertEqual(mailbox_list.mailboxes[1].display_name,
- 'Fred Flintstone')
- self.assertEqual(mailbox_list.mailboxes,
- mailbox_list.all_mailboxes)
-
- def test_get_mailbox_list_two_complex(self):
- mailbox_list = self._test_get_x(parser.get_mailbox_list,
- ('(foo) "Roy A. Bear" (bar),'
- ' "Fred Flintstone" '),
- ('(foo) "Roy A. Bear" (bar),'
- ' "Fred Flintstone" '),
- (' "Roy A. Bear" ,'
- ' "Fred Flintstone" '),
- [errors.ObsoleteHeaderDefect],
- '')
- self.assertEqual(len(mailbox_list.mailboxes), 2)
- self.assertEqual(mailbox_list.mailboxes[0].addr_spec,
- 'dinsdale@example.com')
- self.assertEqual(mailbox_list.mailboxes[0].display_name,
- 'Roy A. Bear')
- self.assertEqual(mailbox_list.mailboxes[1].addr_spec,
- 'dinsdale@test.example.com')
- self.assertEqual(mailbox_list.mailboxes[1].display_name,
- 'Fred Flintstone')
- self.assertEqual(mailbox_list.mailboxes,
- mailbox_list.all_mailboxes)
-
- def test_get_mailbox_list_unparseable_mailbox_null(self):
- mailbox_list = self._test_get_x(parser.get_mailbox_list,
- ('"Roy A. Bear"[] dinsdale@example.com,'
- ' "Fred Flintstone" '),
- ('"Roy A. Bear"[] dinsdale@example.com,'
- ' "Fred Flintstone" '),
- ('"Roy A. Bear"[] dinsdale@example.com,'
- ' "Fred Flintstone" '),
- [errors.InvalidHeaderDefect, # the 'extra' text after the local part
- errors.InvalidHeaderDefect, # the local part with no angle-addr
- errors.ObsoleteHeaderDefect, # period in extra text (example.com)
- errors.ObsoleteHeaderDefect], # (bird) in valid address.
- '')
- self.assertEqual(len(mailbox_list.mailboxes), 1)
- self.assertEqual(len(mailbox_list.all_mailboxes), 2)
- self.assertEqual(mailbox_list.all_mailboxes[0].token_type,
- 'invalid-mailbox')
- self.assertIsNone(mailbox_list.all_mailboxes[0].display_name)
- self.assertEqual(mailbox_list.all_mailboxes[0].local_part,
- 'Roy A. Bear')
- self.assertIsNone(mailbox_list.all_mailboxes[0].domain)
- self.assertEqual(mailbox_list.all_mailboxes[0].addr_spec,
- '"Roy A. Bear"')
- self.assertIs(mailbox_list.all_mailboxes[1],
- mailbox_list.mailboxes[0])
- self.assertEqual(mailbox_list.mailboxes[0].addr_spec,
- 'dinsdale@test.example.com')
- self.assertEqual(mailbox_list.mailboxes[0].display_name,
- 'Fred Flintstone')
-
- def test_get_mailbox_list_junk_after_valid_address(self):
- mailbox_list = self._test_get_x(parser.get_mailbox_list,
- ('"Roy A. Bear" @@,'
- ' "Fred Flintstone" '),
- ('"Roy A. Bear" @@,'
- ' "Fred Flintstone" '),
- ('"Roy A. Bear" @@,'
- ' "Fred Flintstone" '),
- [errors.InvalidHeaderDefect],
- '')
- self.assertEqual(len(mailbox_list.mailboxes), 1)
- self.assertEqual(len(mailbox_list.all_mailboxes), 2)
- self.assertEqual(mailbox_list.all_mailboxes[0].addr_spec,
- 'dinsdale@example.com')
- self.assertEqual(mailbox_list.all_mailboxes[0].display_name,
- 'Roy A. Bear')
- self.assertEqual(mailbox_list.all_mailboxes[0].token_type,
- 'invalid-mailbox')
- self.assertIs(mailbox_list.all_mailboxes[1],
- mailbox_list.mailboxes[0])
- self.assertEqual(mailbox_list.mailboxes[0].addr_spec,
- 'dinsdale@test.example.com')
- self.assertEqual(mailbox_list.mailboxes[0].display_name,
- 'Fred Flintstone')
-
- def test_get_mailbox_list_empty_list_element(self):
- mailbox_list = self._test_get_x(parser.get_mailbox_list,
- ('"Roy A. Bear" , (bird),,'
- ' "Fred Flintstone" '),
- ('"Roy A. Bear" , (bird),,'
- ' "Fred Flintstone" '),
- ('"Roy A. Bear" , ,,'
- ' "Fred Flintstone" '),
- [errors.ObsoleteHeaderDefect]*2,
- '')
- self.assertEqual(len(mailbox_list.mailboxes), 2)
- self.assertEqual(mailbox_list.all_mailboxes,
- mailbox_list.mailboxes)
- self.assertEqual(mailbox_list.all_mailboxes[0].addr_spec,
- 'dinsdale@example.com')
- self.assertEqual(mailbox_list.all_mailboxes[0].display_name,
- 'Roy A. Bear')
- self.assertEqual(mailbox_list.mailboxes[1].addr_spec,
- 'dinsdale@test.example.com')
- self.assertEqual(mailbox_list.mailboxes[1].display_name,
- 'Fred Flintstone')
-
- def test_get_mailbox_list_only_empty_elements(self):
- mailbox_list = self._test_get_x(parser.get_mailbox_list,
- '(foo),, (bar)',
- '(foo),, (bar)',
- ' ,, ',
- [errors.ObsoleteHeaderDefect]*3,
- '')
- self.assertEqual(len(mailbox_list.mailboxes), 0)
- self.assertEqual(mailbox_list.all_mailboxes,
- mailbox_list.mailboxes)
-
- # get_group_list
-
- def test_get_group_list_cfws_only(self):
- group_list = self._test_get_x(parser.get_group_list,
- '(hidden);',
- '(hidden)',
- ' ',
- [],
- ';')
- self.assertEqual(group_list.token_type, 'group-list')
- self.assertEqual(len(group_list.mailboxes), 0)
- self.assertEqual(group_list.mailboxes,
- group_list.all_mailboxes)
-
- def test_get_group_list_mailbox_list(self):
- group_list = self._test_get_x(parser.get_group_list,
- 'dinsdale@example.org, "Fred A. Bear" ',
- 'dinsdale@example.org, "Fred A. Bear" ',
- 'dinsdale@example.org, "Fred A. Bear" ',
- [],
- '')
- self.assertEqual(group_list.token_type, 'group-list')
- self.assertEqual(len(group_list.mailboxes), 2)
- self.assertEqual(group_list.mailboxes,
- group_list.all_mailboxes)
- self.assertEqual(group_list.mailboxes[1].display_name,
- 'Fred A. Bear')
-
- def test_get_group_list_obs_group_list(self):
- group_list = self._test_get_x(parser.get_group_list,
- ', (foo),,(bar)',
- ', (foo),,(bar)',
- ', ,, ',
- [errors.ObsoleteHeaderDefect],
- '')
- self.assertEqual(group_list.token_type, 'group-list')
- self.assertEqual(len(group_list.mailboxes), 0)
- self.assertEqual(group_list.mailboxes,
- group_list.all_mailboxes)
-
- def test_get_group_list_comment_only_invalid(self):
- group_list = self._test_get_x(parser.get_group_list,
- '(bar)',
- '(bar)',
- ' ',
- [errors.InvalidHeaderDefect],
- '')
- self.assertEqual(group_list.token_type, 'group-list')
- self.assertEqual(len(group_list.mailboxes), 0)
- self.assertEqual(group_list.mailboxes,
- group_list.all_mailboxes)
-
- # get_group
-
- def test_get_group_empty(self):
- group = self._test_get_x(parser.get_group,
- 'Monty Python:;',
- 'Monty Python:;',
- 'Monty Python:;',
- [],
- '')
- self.assertEqual(group.token_type, 'group')
- self.assertEqual(group.display_name, 'Monty Python')
- self.assertEqual(len(group.mailboxes), 0)
- self.assertEqual(group.mailboxes,
- group.all_mailboxes)
-
- def test_get_group_null_addr_spec(self):
- group = self._test_get_x(parser.get_group,
- 'foo: <>;',
- 'foo: <>;',
- 'foo: <>;',
- [errors.InvalidHeaderDefect],
- '')
- self.assertEqual(group.display_name, 'foo')
- self.assertEqual(len(group.mailboxes), 0)
- self.assertEqual(len(group.all_mailboxes), 1)
- self.assertEqual(group.all_mailboxes[0].value, '<>')
-
- def test_get_group_cfws_only(self):
- group = self._test_get_x(parser.get_group,
- 'Monty Python: (hidden);',
- 'Monty Python: (hidden);',
- 'Monty Python: ;',
- [],
- '')
- self.assertEqual(group.token_type, 'group')
- self.assertEqual(group.display_name, 'Monty Python')
- self.assertEqual(len(group.mailboxes), 0)
- self.assertEqual(group.mailboxes,
- group.all_mailboxes)
-
- def test_get_group_single_mailbox(self):
- group = self._test_get_x(parser.get_group,
- 'Monty Python: "Fred A. Bear" ;',
- 'Monty Python: "Fred A. Bear" ;',
- 'Monty Python: "Fred A. Bear" ;',
- [],
- '')
- self.assertEqual(group.token_type, 'group')
- self.assertEqual(group.display_name, 'Monty Python')
- self.assertEqual(len(group.mailboxes), 1)
- self.assertEqual(group.mailboxes,
- group.all_mailboxes)
- self.assertEqual(group.mailboxes[0].addr_spec,
- 'dinsdale@example.com')
-
- def test_get_group_mixed_list(self):
- group = self._test_get_x(parser.get_group,
- ('Monty Python: "Fred A. Bear" ,'
- '(foo) Roger , x@test.example.com;'),
- ('Monty Python: "Fred A. Bear" ,'
- '(foo) Roger , x@test.example.com;'),
- ('Monty Python: "Fred A. Bear" ,'
- ' Roger , x@test.example.com;'),
- [],
- '')
- self.assertEqual(group.token_type, 'group')
- self.assertEqual(group.display_name, 'Monty Python')
- self.assertEqual(len(group.mailboxes), 3)
- self.assertEqual(group.mailboxes,
- group.all_mailboxes)
- self.assertEqual(group.mailboxes[0].display_name,
- 'Fred A. Bear')
- self.assertEqual(group.mailboxes[1].display_name,
- 'Roger')
- self.assertEqual(group.mailboxes[2].local_part, 'x')
-
- def test_get_group_one_invalid(self):
- group = self._test_get_x(parser.get_group,
- ('Monty Python: "Fred A. Bear" ,'
- '(foo) Roger ping@exampele.com, x@test.example.com;'),
- ('Monty Python: "Fred A. Bear" ,'
- '(foo) Roger ping@exampele.com, x@test.example.com;'),
- ('Monty Python: "Fred A. Bear" ,'
- ' Roger ping@exampele.com, x@test.example.com;'),
- [errors.InvalidHeaderDefect, # non-angle addr makes local part invalid
- errors.InvalidHeaderDefect], # and its not obs-local either: no dots.
- '')
- self.assertEqual(group.token_type, 'group')
- self.assertEqual(group.display_name, 'Monty Python')
- self.assertEqual(len(group.mailboxes), 2)
- self.assertEqual(len(group.all_mailboxes), 3)
- self.assertEqual(group.mailboxes[0].display_name,
- 'Fred A. Bear')
- self.assertEqual(group.mailboxes[1].local_part, 'x')
- self.assertIsNone(group.all_mailboxes[1].display_name)
-
- # get_address
-
- def test_get_address_simple(self):
- address = self._test_get_x(parser.get_address,
- 'dinsdale@example.com',
- 'dinsdale@example.com',
- 'dinsdale@example.com',
- [],
- '')
- self.assertEqual(address.token_type, 'address')
- self.assertEqual(len(address.mailboxes), 1)
- self.assertEqual(address.mailboxes,
- address.all_mailboxes)
- self.assertEqual(address.mailboxes[0].domain,
- 'example.com')
- self.assertEqual(address[0].token_type,
- 'mailbox')
-
- def test_get_address_complex(self):
- address = self._test_get_x(parser.get_address,
- '(foo) "Fred A. Bear" <(bird)dinsdale@example.com>',
- '(foo) "Fred A. Bear" <(bird)dinsdale@example.com>',
- ' "Fred A. Bear" < dinsdale@example.com>',
- [],
- '')
- self.assertEqual(address.token_type, 'address')
- self.assertEqual(len(address.mailboxes), 1)
- self.assertEqual(address.mailboxes,
- address.all_mailboxes)
- self.assertEqual(address.mailboxes[0].display_name,
- 'Fred A. Bear')
- self.assertEqual(address[0].token_type,
- 'mailbox')
-
- def test_get_address_empty_group(self):
- address = self._test_get_x(parser.get_address,
- 'Monty Python:;',
- 'Monty Python:;',
- 'Monty Python:;',
- [],
- '')
- self.assertEqual(address.token_type, 'address')
- self.assertEqual(len(address.mailboxes), 0)
- self.assertEqual(address.mailboxes,
- address.all_mailboxes)
- self.assertEqual(address[0].token_type,
- 'group')
- self.assertEqual(address[0].display_name,
- 'Monty Python')
-
- def test_get_address_group(self):
- address = self._test_get_x(parser.get_address,
- 'Monty Python: x@example.com, y@example.com;',
- 'Monty Python: x@example.com, y@example.com;',
- 'Monty Python: x@example.com, y@example.com;',
- [],
- '')
- self.assertEqual(address.token_type, 'address')
- self.assertEqual(len(address.mailboxes), 2)
- self.assertEqual(address.mailboxes,
- address.all_mailboxes)
- self.assertEqual(address[0].token_type,
- 'group')
- self.assertEqual(address[0].display_name,
- 'Monty Python')
- self.assertEqual(address.mailboxes[0].local_part, 'x')
-
- def test_get_address_quoted_local_part(self):
- address = self._test_get_x(parser.get_address,
- '"foo bar"@example.com',
- '"foo bar"@example.com',
- '"foo bar"@example.com',
- [],
- '')
- self.assertEqual(address.token_type, 'address')
- self.assertEqual(len(address.mailboxes), 1)
- self.assertEqual(address.mailboxes,
- address.all_mailboxes)
- self.assertEqual(address.mailboxes[0].domain,
- 'example.com')
- self.assertEqual(address.mailboxes[0].local_part,
- 'foo bar')
- self.assertEqual(address[0].token_type, 'mailbox')
-
- def test_get_address_ends_at_special(self):
- address = self._test_get_x(parser.get_address,
- 'dinsdale@example.com, next',
- 'dinsdale@example.com',
- 'dinsdale@example.com',
- [],
- ', next')
- self.assertEqual(address.token_type, 'address')
- self.assertEqual(len(address.mailboxes), 1)
- self.assertEqual(address.mailboxes,
- address.all_mailboxes)
- self.assertEqual(address.mailboxes[0].domain,
- 'example.com')
- self.assertEqual(address[0].token_type, 'mailbox')
-
- def test_get_address_invalid_mailbox_invalid(self):
- address = self._test_get_x(parser.get_address,
- 'ping example.com, next',
- 'ping example.com',
- 'ping example.com',
- [errors.InvalidHeaderDefect, # addr-spec with no domain
- errors.InvalidHeaderDefect, # invalid local-part
- errors.InvalidHeaderDefect, # missing .s in local-part
- ],
- ', next')
- self.assertEqual(address.token_type, 'address')
- self.assertEqual(len(address.mailboxes), 0)
- self.assertEqual(len(address.all_mailboxes), 1)
- self.assertIsNone(address.all_mailboxes[0].domain)
- self.assertEqual(address.all_mailboxes[0].local_part, 'ping example.com')
- self.assertEqual(address[0].token_type, 'invalid-mailbox')
-
- def test_get_address_quoted_strings_in_atom_list(self):
- address = self._test_get_x(parser.get_address,
- '""example" example"@example.com',
- '""example" example"@example.com',
- 'example example@example.com',
- [errors.InvalidHeaderDefect]*3,
- '')
- self.assertEqual(address.all_mailboxes[0].local_part, 'example example')
- self.assertEqual(address.all_mailboxes[0].domain, 'example.com')
- self.assertEqual(address.all_mailboxes[0].addr_spec, '"example example"@example.com')
-
-
- # get_address_list
-
- def test_get_address_list_mailboxes_simple(self):
- address_list = self._test_get_x(parser.get_address_list,
- 'dinsdale@example.com',
- 'dinsdale@example.com',
- 'dinsdale@example.com',
- [],
- '')
- self.assertEqual(address_list.token_type, 'address-list')
- self.assertEqual(len(address_list.mailboxes), 1)
- self.assertEqual(address_list.mailboxes,
- address_list.all_mailboxes)
- self.assertEqual([str(x) for x in address_list.mailboxes],
- [str(x) for x in address_list.addresses])
- self.assertEqual(address_list.mailboxes[0].domain, 'example.com')
- self.assertEqual(address_list[0].token_type, 'address')
- self.assertIsNone(address_list[0].display_name)
-
- def test_get_address_list_mailboxes_two_simple(self):
- address_list = self._test_get_x(parser.get_address_list,
- 'foo@example.com, "Fred A. Bar" ',
- 'foo@example.com, "Fred A. Bar" ',
- 'foo@example.com, "Fred A. Bar" ',
- [],
- '')
- self.assertEqual(address_list.token_type, 'address-list')
- self.assertEqual(len(address_list.mailboxes), 2)
- self.assertEqual(address_list.mailboxes,
- address_list.all_mailboxes)
- self.assertEqual([str(x) for x in address_list.mailboxes],
- [str(x) for x in address_list.addresses])
- self.assertEqual(address_list.mailboxes[0].local_part, 'foo')
- self.assertEqual(address_list.mailboxes[1].display_name, "Fred A. Bar")
-
- def test_get_address_list_mailboxes_complex(self):
- address_list = self._test_get_x(parser.get_address_list,
- ('"Roy A. Bear" , '
- '(ping) Foo ,'
- 'Nobody Is. Special '),
- ('"Roy A. Bear" , '
- '(ping) Foo ,'
- 'Nobody Is. Special '),
- ('"Roy A. Bear" , '
- 'Foo