summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRick van Hattem <wolph@wol.ph>2015-02-09 23:04:05 +0100
committerRick van Hattem <wolph@wol.ph>2015-02-09 23:04:05 +0100
commit5775ae5db6e0e24445ef57147e4f5925812d0597 (patch)
tree4429eae6ab6d63a9512049ca6eb4060617096f0d
parentd6e0eecb771084a8fe5eba3859898e43ef924535 (diff)
parent8a388d3812535bfbdbe6e4629d33e1cb69179dcc (diff)
downloadsix-5775ae5db6e0e24445ef57147e4f5925812d0597.tar.gz
merging...
-rw-r--r--CHANGES18
-rw-r--r--CONTRIBUTORS3
-rw-r--r--LICENSE2
-rw-r--r--README6
-rw-r--r--documentation/conf.py2
-rw-r--r--documentation/index.rst57
-rw-r--r--six.py67
-rw-r--r--test_six.py83
8 files changed, 220 insertions, 18 deletions
diff --git a/CHANGES b/CHANGES
index cae8ef0..3434ff5 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3,8 +3,19 @@ Changelog for six
This file lists the changes in each six version.
-Development version
--------------------
+1.9.0
+-----
+
+- Issue #106: Support the `flush` parameter to `six.print_`.
+
+- Pull request #48 and issue #15: Add the `python_2_unicode_compatible`
+ decorator.
+
+- Pull request #57 and issue #50: Add several compatibility methods for unittest
+ assertions that were renamed between Python 2 and 3.
+
+- Issue #105 and pull request #58: Ensure `six.wraps` respects the *updated* and
+ *assigned* arguments.
- Issue #102: Add `raise_from` to abstract out Python 3's raise from syntax.
@@ -15,6 +26,9 @@ Development version
- Pull request #51: Add `six.view(keys|values|itmes)`, which provide dictionary
views on Python 2.7+.
+- Issue #112: `six.moves.reload_module` now uses the importlib module on
+ Python 3.4+.
+
1.8.0
-----
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 0cbd0a4..206bb0f 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -10,10 +10,13 @@ Jason R. Coombs
Julien Danjou
Ben Darnell
Ben Davis
+Tim Graham
+Thomas Grainger
Joshua Harlow
Anselm Kruis
Alexander Lukanin
James Mills
+Berker Peksag
Sridhar Ratnakumar
Erik Rose
Peter Ruibal
diff --git a/LICENSE b/LICENSE
index d76e024..e558f9d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2010-2014 Benjamin Peterson
+Copyright (c) 2010-2015 Benjamin Peterson
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
diff --git a/README b/README
index 4de73fa..2a22717 100644
--- a/README
+++ b/README
@@ -3,14 +3,14 @@ for smoothing over the differences between the Python versions with the goal of
writing Python code that is compatible on both Python versions. See the
documentation for more information on what is provided.
-Six supports every Python version since 2.5. It is contained in only one Python
+Six supports every Python version since 2.6. It is contained in only one Python
file, so it can be easily copied into your project. (The copyright and license
notice must be retained.)
Online documentation is at http://pythonhosted.org/six/.
-Bugs can be reported to http://bitbucket.org/gutworth/six. The code can also be
-found there.
+Bugs can be reported to https://bitbucket.org/gutworth/six. The code can also
+be found there.
For questions about six or porting in general, email the python-porting mailing
list: http://mail.python.org/mailman/listinfo/python-porting
diff --git a/documentation/conf.py b/documentation/conf.py
index 7e54287..0215bdd 100644
--- a/documentation/conf.py
+++ b/documentation/conf.py
@@ -33,7 +33,7 @@ master_doc = "index"
# General information about the project.
project = u"six"
-copyright = u"2010-2014, Benjamin Peterson"
+copyright = u"2010-2015, Benjamin Peterson"
sys.path.append(os.path.abspath(os.path.join(".", "..")))
from six import __version__ as six_version
diff --git a/documentation/index.rst b/documentation/index.rst
index 7851421..a8632e7 100644
--- a/documentation/index.rst
+++ b/documentation/index.rst
@@ -18,7 +18,7 @@ tracker and code hosting is on `BitBucket <http://bitbucket.org/gutworth/six>`_.
The name, "six", comes from the fact that 2*3 equals 6. Why not addition?
Multiplication is more powerful, and, anyway, "five" has already been snatched
-away by the Zope Five project.
+away by the (admittedly now moribund) Zope Five project.
Indices and tables
@@ -243,7 +243,7 @@ functions and methods is the stdlib :mod:`py3:inspect` module.
aliased to :class:`py3:object`.)
-.. function:: wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, updated=functools.WRAPPER_UPDATES)
+.. decorator:: wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, updated=functools.WRAPPER_UPDATES)
This is exactly the :func:`py3:functools.wraps` decorator, but it sets the
``__wrapped__`` attribute on what it decorates as :func:`py3:functools.wraps`
@@ -270,10 +270,11 @@ Python 2 and 3.
:func:`exec` with them should be avoided.
-.. function:: print_(*args, *, file=sys.stdout, end="\\n", sep=" ")
+.. function:: print_(*args, *, file=sys.stdout, end="\\n", sep=" ", flush=False)
Print *args* into *file*. Each argument will be separated with *sep* and
- *end* will be written to the file after the last argument is printed.
+ *end* will be written to the file after the last argument is printed. If
+ *flush* is true, ``file.flush()`` will be called after all data is written.
.. note::
@@ -320,7 +321,7 @@ Python 2 and 3.
decorator.
-.. function:: add_metaclass(metaclass)
+.. decorator:: add_metaclass(metaclass)
Class decorator that replaces a normally-constructed class with a
metaclass-constructed one. Example usage: ::
@@ -437,6 +438,48 @@ string data in all Python versions.
:class:`py3:io.BytesIO`.
+.. decorator:: python_2_unicode_compatible
+
+ A class decorator that takes a class defining a ``__str__`` method. On
+ Python 3, the decorator does nothing. On Python 2, it aliases the
+ ``__str__`` method to ``__unicode__`` and creates a new ``__str__`` method
+ that returns the result of ``__unicode__()`` encoded with UTF-8.
+
+
+unittest assertions
+>>>>>>>>>>>>>>>>>>>
+
+Six contains compatibility shims for unittest assertions that have been renamed.
+The parameters are the same as their aliases, but you must pass the test method
+as the first argument. For example::
+
+ import six
+ import unittest
+
+ class TestAssertCountEqual(unittest.TestCase):
+ def test(self):
+ six.assertCountEqual(self, (1, 2), [2, 1])
+
+Note these functions are only available on Python 2.7 or later.
+
+.. function:: assertCountEqual()
+
+ Alias for :meth:`~py3:unittest.TestCase.assertCountEqual` on Python 3 and
+ :meth:`~py2:unittest.TestCase.assertItemsEqual` on Python 2.
+
+
+.. function:: assertRaisesRegex()
+
+ Alias for :meth:`~py3:unittest.TestCase.assertRaisesRegex` on Python 3 and
+ :meth:`~py2:unittest.TestCase.assertRaisesRegexp` on Python 2.
+
+
+.. function:: assertRegex()
+
+ Alias for :meth:`~py3:unittest.TestCase.assertRegex` on Python 3 and
+ :meth:`~py2:unittest.TestCase.assertRegexpMatches` on Python 2.
+
+
Renamed modules and attributes compatibility
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -555,7 +598,9 @@ Supported renames:
+------------------------------+-------------------------------------+-------------------------------------+
| ``reduce`` | :func:`py2:reduce` | :func:`py3:functools.reduce` |
+------------------------------+-------------------------------------+-------------------------------------+
-| ``reload_module`` | :func:`py2:reload` | :func:`py3:imp.reload` |
+| ``reload_module`` | :func:`py2:reload` | :func:`py3:imp.reload`, |
+| | | :func:`py3:importlib.reload` |
+| | | on Python 3.4+ |
+------------------------------+-------------------------------------+-------------------------------------+
| ``reprlib`` | :mod:`py2:repr` | :mod:`py3:reprlib` |
+------------------------------+-------------------------------------+-------------------------------------+
diff --git a/six.py b/six.py
index b64c59e..be8e8f7 100644
--- a/six.py
+++ b/six.py
@@ -1,6 +1,6 @@
"""Utilities for writing code that runs on Python 2 and 3"""
-# Copyright (c) 2010-2014 Benjamin Peterson
+# Copyright (c) 2010-2015 Benjamin Peterson
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -29,12 +29,13 @@ import sys
import types
__author__ = "Benjamin Peterson <benjamin@python.org>"
-__version__ = "1.8.0"
+__version__ = "1.9.0"
# Useful for very coarse version differentiation.
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
+PY34 = sys.version_info[0:2] >= (3, 4)
if PY3:
string_types = str,
@@ -244,7 +245,7 @@ _moved_attributes = [
MovedAttribute("intern", "__builtin__", "sys"),
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
- MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
+ MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"),
MovedAttribute("reduce", "__builtin__", "functools"),
MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
MovedAttribute("StringIO", "StringIO", "io"),
@@ -659,6 +660,13 @@ if PY3:
import io
StringIO = io.StringIO
BytesIO = io.BytesIO
+ _assertCountEqual = "assertCountEqual"
+ if sys.version_info[1] <= 1:
+ _assertRaisesRegex = "assertRaisesRegexp"
+ _assertRegex = "assertRegexpMatches"
+ else:
+ _assertRaisesRegex = "assertRaisesRegex"
+ _assertRegex = "assertRegex"
else:
def b(s):
return s
@@ -677,10 +685,25 @@ else:
iterbytes = functools.partial(itertools.imap, ord)
import StringIO
StringIO = BytesIO = StringIO.StringIO
+ _assertCountEqual = "assertItemsEqual"
+ _assertRaisesRegex = "assertRaisesRegexp"
+ _assertRegex = "assertRegexpMatches"
_add_doc(b, """Byte literal""")
_add_doc(u, """Text literal""")
+def assertCountEqual(self, *args, **kwargs):
+ return getattr(self, _assertCountEqual)(*args, **kwargs)
+
+
+def assertRaisesRegex(self, *args, **kwargs):
+ return getattr(self, _assertRaisesRegex)(*args, **kwargs)
+
+
+def assertRegex(self, *args, **kwargs):
+ return getattr(self, _assertRegex)(*args, **kwargs)
+
+
if PY3:
exec_ = getattr(moves.builtins, "exec")
@@ -709,7 +732,13 @@ else:
""")
-if sys.version_info > (3, 2):
+if sys.version_info[:2] == (3, 2):
+ exec_("""def raise_from(value, from_value):
+ if from_value is None:
+ raise value
+ raise value from from_value
+""")
+elif sys.version_info[:2] > (3, 2):
exec_("""def raise_from(value, from_value):
raise value from from_value
""")
@@ -773,6 +802,15 @@ if print_ is None:
write(sep)
write(arg)
write(end)
+if sys.version_info[:2] < (3, 3):
+ _print = print_
+
+ def print_(*args, **kwargs):
+ fp = kwargs.get("file", sys.stdout)
+ flush = kwargs.pop("flush", False)
+ _print(*args, **kwargs)
+ if flush and fp is not None:
+ fp.flush()
_add_doc(reraise, """Reraise an exception.""")
@@ -780,7 +818,7 @@ if sys.version_info[0:2] < (3, 4):
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
updated=functools.WRAPPER_UPDATES):
def wrapper(f):
- f = functools.wraps(wrapped)(f)
+ f = functools.wraps(wrapped, assigned, updated)(f)
f.__wrapped__ = wrapped
return f
return wrapper
@@ -815,6 +853,25 @@ def add_metaclass(metaclass):
return metaclass(cls.__name__, cls.__bases__, orig_vars)
return wrapper
+
+def python_2_unicode_compatible(klass):
+ """
+ A decorator that defines __unicode__ and __str__ methods under Python 2.
+ Under Python 3 it does nothing.
+
+ To support Python 2 and 3 with a single code base, define a __str__ method
+ returning text and apply this decorator to the class.
+ """
+ if PY2:
+ if '__str__' not in klass.__dict__:
+ raise ValueError("@python_2_unicode_compatible cannot be applied "
+ "to %s because it doesn't define __str__()." %
+ klass.__name__)
+ klass.__unicode__ = klass.__str__
+ klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
+ return klass
+
+
# Complete the moves implementation.
# This code is at the end of this module to speed up module loading.
# Turn this module into a package.
diff --git a/test_six.py b/test_six.py
index b0ccd8d..f33f882 100644
--- a/test_six.py
+++ b/test_six.py
@@ -1,6 +1,7 @@
import operator
import sys
import types
+import unittest
import py
@@ -635,6 +636,17 @@ def test_print_():
out = six.StringIO()
six.print_(None, file=out)
assert out.getvalue() == "None\n"
+ class FlushableStringIO(six.StringIO):
+ def __init__(self):
+ six.StringIO.__init__(self)
+ self.flushed = False
+ def flush(self):
+ self.flushed = True
+ out = FlushableStringIO()
+ six.print_("Hello", file=out)
+ assert not out.flushed
+ six.print_("Hello", file=out, flush=True)
+ assert out.flushed
@py.test.mark.skipif("sys.version_info[:2] >= (2, 6)")
@@ -701,6 +713,18 @@ def test_wraps():
assert k is original_k
assert not hasattr(k, '__wrapped__')
+ def f(g, assign, update):
+ def w():
+ return 42
+ w.glue = {"foo" : "bar"}
+ return six.wraps(g, assign, update)(w)
+ k.glue = {"melon" : "egg"}
+ k.turnip = 43
+ k = f(k, ["turnip"], ["glue"])
+ assert k.__name__ == "w"
+ assert k.turnip == 43
+ assert k.glue == {"melon" : "egg", "foo" : "bar"}
+
def test_add_metaclass():
class Meta(type):
@@ -773,3 +797,62 @@ def test_add_metaclass():
__slots__ = "__weakref__",
MySlotsWeakref = six.add_metaclass(Meta)(MySlotsWeakref)
assert type(MySlotsWeakref) is Meta
+
+
+@py.test.mark.skipif("sys.version_info[:2] < (2, 7) or sys.version_info[:2] in ((3, 0), (3, 1))")
+def test_assertCountEqual():
+ class TestAssertCountEqual(unittest.TestCase):
+ def test(self):
+ with self.assertRaises(AssertionError):
+ six.assertCountEqual(self, (1, 2), [3, 4, 5])
+
+ six.assertCountEqual(self, (1, 2), [2, 1])
+
+ TestAssertCountEqual('test').test()
+
+
+@py.test.mark.skipif("sys.version_info[:2] < (2, 7)")
+def test_assertRegex():
+ class TestAssertRegex(unittest.TestCase):
+ def test(self):
+ with self.assertRaises(AssertionError):
+ six.assertRegex(self, 'test', r'^a')
+
+ six.assertRegex(self, 'test', r'^t')
+
+ TestAssertRegex('test').test()
+
+
+@py.test.mark.skipif("sys.version_info[:2] < (2, 7)")
+def test_assertRaisesRegex():
+ class TestAssertRaisesRegex(unittest.TestCase):
+ def test(self):
+ with six.assertRaisesRegex(self, AssertionError, '^Foo'):
+ raise AssertionError('Foo')
+
+ with self.assertRaises(AssertionError):
+ with six.assertRaisesRegex(self, AssertionError, r'^Foo'):
+ raise AssertionError('Bar')
+
+ TestAssertRaisesRegex('test').test()
+
+
+def test_python_2_unicode_compatible():
+ @six.python_2_unicode_compatible
+ class MyTest(object):
+ def __str__(self):
+ return six.u('hello')
+
+ def __bytes__(self):
+ return six.b('hello')
+
+ my_test = MyTest()
+
+ if six.PY2:
+ assert str(my_test) == six.b("hello")
+ assert unicode(my_test) == six.u("hello")
+ elif six.PY3:
+ assert bytes(my_test) == six.b("hello")
+ assert str(my_test) == six.u("hello")
+
+ assert getattr(six.moves.builtins, 'bytes', str)(my_test) == six.b("hello")