summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Peterson <benjamin@python.org>2015-01-02 09:37:52 -0600
committerBenjamin Peterson <benjamin@python.org>2015-01-02 09:37:52 -0600
commitb7673a05941b7c847224849cf5f6fd41d09aca8a (patch)
tree9feb83ee02af3e343531b38476637523cd1faec3
parent318a802db349df01dde2071942034c979d899c5e (diff)
parent678cfb323729045c73ce405052955c9b0e4edc86 (diff)
downloadsix-b7673a05941b7c847224849cf5f6fd41d09aca8a.tar.gz
Merged in graingert/six (pull request #48)
-rw-r--r--.hgignore1
-rw-r--r--CHANGES44
-rw-r--r--CONTRIBUTORS2
-rw-r--r--README4
-rw-r--r--documentation/conf.py4
-rw-r--r--documentation/index.rst64
-rw-r--r--six.py53
-rw-r--r--test_six.py88
8 files changed, 236 insertions, 24 deletions
diff --git a/.hgignore b/.hgignore
index 2a22acf..8815294 100644
--- a/.hgignore
+++ b/.hgignore
@@ -4,3 +4,4 @@ dist
MANIFEST
documentation/_build
.tox
+six.egg-info
diff --git a/CHANGES b/CHANGES
index 87315e3..e12508a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3,38 +3,56 @@ Changelog for six
This file lists the changes in each six version.
+Development version
+-------------------
+
+- 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.
+
+- Issue #97: Optimize `six.iterbytes` on Python 2.
+
+- Issue #98: Fix `six.moves` race condition in multi-threaded code.
+
+- Pull request #51: Add `six.view(keys|values|itmes)`, which provide dictionary
+ views on Python 2.7+.
+
1.8.0
-----
-- Issue #90: Add six.moves.shlex_quote.
+- Issue #90: Add `six.moves.shlex_quote`.
-- Issue #59: Add six.moves.intern.
+- Issue #59: Add `six.moves.intern`.
-- Add six.urllib.parse.uses_(fragment|netloc|params|query|relative).
+- Add `six.urllib.parse.uses_(fragment|netloc|params|query|relative)`.
-- Issue #88: Fix add_metaclass when the class has __slots__ containing
- "__weakref__" or "__dict__".
+- Issue #88: Fix add_metaclass when the class has `__slots__` containing
+ `__weakref__` or `__dict__`.
- Issue #89: Make six use absolute imports.
-- Issue #85: Always accept *updated* and *assigned* arguments for wraps().
+- Issue #85: Always accept *updated* and *assigned* arguments for `wraps()`.
-- Issue #86: In reraise(), instantiate the exception if the second argument is
- None.
+- Issue #86: In `reraise()`, instantiate the exception if the second argument is
+ `None`.
-- Pull request #45: Add six.moves.email_mime_nonmultipart.
+- Pull request #45: Add `six.moves.email_mime_nonmultipart`.
-- Issue #81: Add six.urllib.request.splittag mapping.
+- Issue #81: Add `six.urllib.request.splittag` mapping.
-- Issue #80: Add six.urllib.request.splituser mapping.
+- Issue #80: Add `six.urllib.request.splituser` mapping.
1.7.3
-----
- Issue #77: Fix import six on Python 3.4 with a custom loader.
-- Issue #74: six.moves.xmlrpc_server should map to SimpleXMLRPCServer on Python
- 2 as documented not xmlrpclib.
+- Issue #74: `six.moves.xmlrpc_server` should map to `SimpleXMLRPCServer` on Python
+ 2 as documented not `xmlrpclib`.
1.7.2
-----
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 29b0f6a..1c8181f 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -3,12 +3,14 @@ acknowledge the following people who submitted bug reports, pull requests, and
otherwise worked to improve six:
Marc Abramowitz
+Alexander Artemenko
Aymeric Augustin
Ned Batchelder
Jason R. Coombs
Julien Danjou
Ben Darnell
Ben Davis
+Tim Graham
Joshua Harlow
Anselm Kruis
Alexander Lukanin
diff --git a/README b/README
index 4de73fa..32bab7c 100644
--- a/README
+++ b/README
@@ -9,8 +9,8 @@ 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 fd285c7..7e54287 100644
--- a/documentation/conf.py
+++ b/documentation/conf.py
@@ -213,5 +213,5 @@ man_pages = [
# -- Intersphinx ---------------------------------------------------------------
-intersphinx_mapping = {"py2" : ("http://docs.python.org/2/", None),
- "py3" : ("http://docs.python.org/3/", None)}
+intersphinx_mapping = {"py2" : ("https://docs.python.org/2/", None),
+ "py3" : ("https://docs.python.org/3/", None)}
diff --git a/documentation/index.rst b/documentation/index.rst
index 0adadc2..3317596 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
@@ -160,7 +160,7 @@ functions and methods is the stdlib :mod:`py3:inspect` module.
.. function:: next(it)
-.. function:: advance_iterator(it)
+ advance_iterator(it)
Get the next item of iterator *it*. :exc:`py3:StopIteration` is raised if
the iterator is exhausted. This is a replacement for calling ``it.next()``
@@ -203,6 +203,27 @@ functions and methods is the stdlib :mod:`py3:inspect` module.
*kwargs* are passed through to the underlying method.
+.. function:: viewkeys(dictionary)
+
+ Return a view over *dictionary*\'s keys. This replaces
+ :meth:`py2:dict.viewkeys` on Python 2.7 and :meth:`py3:dict.keys` on
+ Python 3.
+
+
+.. function:: viewvalues(dictionary)
+
+ Return a view over *dictionary*\'s values. This replaces
+ :meth:`py2:dict.viewvalues` on Python 2.7 and :meth:`py3:dict.values` on
+ Python 3.
+
+
+.. function:: viewitems(dictionary)
+
+ Return a view over *dictionary*\'s items. This replaces
+ :meth:`py2:dict.viewitems` on Python 2.7 and :meth:`py3:dict.items` on
+ Python 3.
+
+
.. function:: create_bound_method(func, obj)
Return a method object wrapping *func* and bound to *obj*. On both Python 2
@@ -261,6 +282,13 @@ Python 2 and 3.
ok. :)
+.. function:: raise_from(exc_value, exc_value_from)
+
+ Raise an exception from a context. On Python 3, this is equivalent to
+ ``raise exc_value from exc_value_from``. On Python 2, which does not support
+ exception chaining, it is equivalent to ``raise exc_value``.
+
+
.. function:: reraise(exc_type, exc_value, exc_traceback=None)
Reraise an exception, possibly with a different traceback. In the simple
@@ -409,6 +437,38 @@ string data in all Python versions.
:class:`py3:io.BytesIO`.
+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])
+
+
+.. function:: assertCountEqual()
+
+ Alias for :meth:`~py3:unittest.TestCase.assertCountEqual` on Python 3 and
+ :meth:`~py2:unittest.TestCase.assertItemsEqual` on Python 2. This method is
+ only available on Python 2.7 and later.
+
+.. 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
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
diff --git a/six.py b/six.py
index e7667af..b54590f 100644
--- a/six.py
+++ b/six.py
@@ -23,6 +23,7 @@
from __future__ import absolute_import
import functools
+import itertools
import operator
import sys
import types
@@ -88,8 +89,12 @@ class _LazyDescr(object):
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)
+ try:
+ # This is a bit ugly, but it avoids running this again by
+ # removing this descriptor.
+ delattr(obj.__class__, self.name)
+ except AttributeError:
+ pass
return result
@@ -554,6 +559,12 @@ if PY3:
def iterlists(d, **kw):
return iter(d.lists(**kw))
+
+ viewkeys = operator.methodcaller("keys")
+
+ viewvalues = operator.methodcaller("values")
+
+ viewitems = operator.methodcaller("items")
else:
def iterkeys(d, **kw):
return iter(d.iterkeys(**kw))
@@ -567,6 +578,12 @@ else:
def iterlists(d, **kw):
return iter(d.iterlists(**kw))
+ viewkeys = operator.methodcaller("viewkeys")
+
+ viewvalues = operator.methodcaller("viewvalues")
+
+ viewitems = operator.methodcaller("viewitems")
+
_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
_add_doc(iteritems,
@@ -593,6 +610,9 @@ if PY3:
import io
StringIO = io.StringIO
BytesIO = io.BytesIO
+ _assertCountEqual = "assertCountEqual"
+ _assertRaisesRegex = "assertRaisesRegex"
+ _assertRegex = "assertRegex"
else:
def b(s):
return s
@@ -605,14 +625,28 @@ else:
return ord(bs[0])
def indexbytes(buf, i):
return ord(buf[i])
- def iterbytes(buf):
- return (ord(byte) for byte in buf)
+ 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")
@@ -643,6 +677,15 @@ else:
""")
+if sys.version_info > (3, 2):
+ exec_("""def raise_from(value, from_value):
+ raise value from from_value
+""")
+else:
+ def raise_from(value, from_value):
+ raise value
+
+
print_ = getattr(moves.builtins, "print", None)
if print_ is None:
def print_(*args, **kwargs):
@@ -704,7 +747,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
diff --git a/test_six.py b/test_six.py
index c744b29..4e95394 100644
--- a/test_six.py
+++ b/test_six.py
@@ -1,6 +1,7 @@
import operator
import sys
import types
+import unittest
import py
@@ -389,6 +390,24 @@ def test_dictionary_iterators(monkeypatch):
monkeypatch.undo()
+@py.test.mark.skipif(sys.version_info[:2] < (2, 7),
+ reason="view methods on dictionaries only available on 2.7+")
+def test_dictionary_views():
+ def stock_method_name(viewwhat):
+ """Given a method suffix like "keys" or "values", return the name
+ of the dict method that delivers those on the version of Python
+ we're running in."""
+ if six.PY3:
+ return viewwhat
+ return 'view' + viewwhat
+
+ d = dict(zip(range(10), (range(11, 20))))
+ for name in "keys", "values", "items":
+ meth = getattr(six, "view" + name)
+ view = meth(d)
+ assert set(view) == set(getattr(d, name)())
+
+
def test_advance_iterator():
assert six.next is six.advance_iterator
l = [1, 2]
@@ -570,6 +589,27 @@ def test_reraise():
assert tb is get_next(tb2)
+def test_raise_from():
+ try:
+ try:
+ raise Exception("blah")
+ except Exception:
+ ctx = sys.exc_info()[1]
+ f = Exception("foo")
+ six.raise_from(f, None)
+ except Exception:
+ tp, val, tb = sys.exc_info()
+ if sys.version_info[:2] > (3, 0):
+ # We should have done a raise f from None equivalent.
+ assert val.__cause__ is None
+ assert val.__context__ is ctx
+ if sys.version_info[:2] >= (3, 3):
+ # And that should suppress the context on the exception.
+ assert val.__suppress_context__
+ # For all versions the outer exception should have raised successfully.
+ assert str(val) == "foo"
+
+
def test_print_():
save = sys.stdout
out = sys.stdout = six.moves.StringIO()
@@ -662,6 +702,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):
@@ -736,6 +788,42 @@ def test_add_metaclass():
assert type(MySlotsWeakref) is Meta
+@py.test.mark.skipif("sys.version_info[:2] < (2, 7)")
+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()
+
+
+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()
+
+
+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):