diff options
author | Benjamin Peterson <benjamin@python.org> | 2015-01-02 09:37:52 -0600 |
---|---|---|
committer | Benjamin Peterson <benjamin@python.org> | 2015-01-02 09:37:52 -0600 |
commit | b7673a05941b7c847224849cf5f6fd41d09aca8a (patch) | |
tree | 9feb83ee02af3e343531b38476637523cd1faec3 | |
parent | 318a802db349df01dde2071942034c979d899c5e (diff) | |
parent | 678cfb323729045c73ce405052955c9b0e4edc86 (diff) | |
download | six-b7673a05941b7c847224849cf5f6fd41d09aca8a.tar.gz |
Merged in graingert/six (pull request #48)
-rw-r--r-- | .hgignore | 1 | ||||
-rw-r--r-- | CHANGES | 44 | ||||
-rw-r--r-- | CONTRIBUTORS | 2 | ||||
-rw-r--r-- | README | 4 | ||||
-rw-r--r-- | documentation/conf.py | 4 | ||||
-rw-r--r-- | documentation/index.rst | 64 | ||||
-rw-r--r-- | six.py | 53 | ||||
-rw-r--r-- | test_six.py | 88 |
8 files changed, 236 insertions, 24 deletions
@@ -4,3 +4,4 @@ dist MANIFEST documentation/_build .tox +six.egg-info @@ -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 @@ -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 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -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): |