summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorimmerrr again <immerrr@gmail.com>2020-01-07 03:55:42 +0100
committerBenjamin Peterson <benjamin@python.org>2020-01-06 18:55:42 -0800
commit1988faf3f7a7f77c0eccbbc5192f365400d44deb (patch)
treecbb35c98e90da9dcd8f319b16c4ffe607d2c2a50
parenta4d9af96b122e98b7be3649b6545e0f6a04c8335 (diff)
downloadsix-git-1988faf3f7a7f77c0eccbbc5192f365400d44deb.tar.gz
Fix wraps handing of missing attrs. (#251)
This is pretty-much a straight backport of Py3 implementations of update_wrapper and (privately) wraps. Fixes #250 Fixes #165 Co-authored-by: Benjamin Peterson <benjamin@python.org>
-rw-r--r--documentation/index.rst7
-rw-r--r--six.py31
-rw-r--r--test_six.py27
3 files changed, 52 insertions, 13 deletions
diff --git a/documentation/index.rst b/documentation/index.rst
index b7ec275..f4c90c9 100644
--- a/documentation/index.rst
+++ b/documentation/index.rst
@@ -257,9 +257,10 @@ functions and methods is the stdlib :mod:`py3:inspect` module.
.. 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`
- does on Python versions after 3.2.
+ This is Python 3.2's :func:`py3:functools.wraps` decorator. It sets the
+ ``__wrapped__`` attribute on what it decorates. It doesn't raise an error if
+ any of the attributes mentioned in ``assigned`` and ``updated`` are missing
+ on ``wrapped`` object.
Syntax compatibility
diff --git a/six.py b/six.py
index de1fcc5..d68d91c 100644
--- a/six.py
+++ b/six.py
@@ -808,13 +808,33 @@ if sys.version_info[:2] < (3, 3):
_add_doc(reraise, """Reraise an exception.""")
if sys.version_info[0:2] < (3, 4):
+ # This does exactly the same what the :func:`py3:functools.update_wrapper`
+ # function does on Python versions after 3.2. It sets the ``__wrapped__``
+ # attribute on ``wrapper`` object and it doesn't raise an error if any of
+ # the attributes mentioned in ``assigned`` and ``updated`` are missing on
+ # ``wrapped`` object.
+ def _update_wrapper(wrapper, wrapped,
+ assigned=functools.WRAPPER_ASSIGNMENTS,
+ updated=functools.WRAPPER_UPDATES):
+ for attr in assigned:
+ try:
+ value = getattr(wrapped, attr)
+ except AttributeError:
+ continue
+ else:
+ setattr(wrapper, attr, value)
+ for attr in updated:
+ getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
+ wrapper.__wrapped__ = wrapped
+ return wrapper
+ _update_wrapper.__doc__ = functools.update_wrapper.__doc__
+
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
updated=functools.WRAPPER_UPDATES):
- def wrapper(f):
- f = functools.wraps(wrapped, assigned, updated)(f)
- f.__wrapped__ = wrapped
- return f
- return wrapper
+ return functools.partial(_update_wrapper, wrapped=wrapped,
+ assigned=assigned, updated=updated)
+ wraps.__doc__ = functools.wraps.__doc__
+
else:
wraps = functools.wraps
@@ -919,7 +939,6 @@ def ensure_text(s, encoding='utf-8', errors='strict'):
raise TypeError("not expecting type '%s'" % type(s))
-
def python_2_unicode_compatible(klass):
"""
A class decorator that defines __unicode__ and __str__ methods under Python 2.
diff --git a/test_six.py b/test_six.py
index 8ada517..ab99ce9 100644
--- a/test_six.py
+++ b/test_six.py
@@ -832,14 +832,33 @@ def test_wraps():
def f(g, assign, update):
def w():
return 42
- w.glue = {"foo" : "bar"}
+ w.glue = {"foo": "bar"}
+ w.xyzzy = {"qux": "quux"}
return six.wraps(g, assign, update)(w)
- k.glue = {"melon" : "egg"}
+ k.glue = {"melon": "egg"}
k.turnip = 43
- k = f(k, ["turnip"], ["glue"])
+ k = f(k, ["turnip", "baz"], ["glue", "xyzzy"])
assert k.__name__ == "w"
assert k.turnip == 43
- assert k.glue == {"melon" : "egg", "foo" : "bar"}
+ assert not hasattr(k, "baz")
+ assert k.glue == {"melon": "egg", "foo": "bar"}
+ assert k.xyzzy == {"qux": "quux"}
+
+
+def test_wraps_raises_on_missing_updated_field_on_wrapper():
+ """Ensure six.wraps doesn't ignore missing attrs wrapper.
+
+ Because that's what happens in Py3's functools.update_wrapper.
+ """
+ def wrapped():
+ pass
+
+ def wrapper():
+ pass
+
+ with pytest.raises(AttributeError, match='has no attribute.*xyzzy'):
+ six.wraps(wrapped, [], ['xyzzy'])(wrapper)
+
def test_add_metaclass():