Skip to content

Commit

Permalink
Fix wraps handing of missing attrs. (benjaminp#251)
Browse files Browse the repository at this point in the history
This is pretty-much a straight backport of Py3 implementations of update_wrapper and (privately) wraps.

Fixes benjaminp#250 
Fixes benjaminp#165

Co-authored-by: Benjamin Peterson <benjamin@python.org>
  • Loading branch information
immerrr and benjaminp committed Jan 7, 2020
1 parent a4d9af9 commit 1988faf
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 13 deletions.
7 changes: 4 additions & 3 deletions documentation/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 25 additions & 6 deletions six.py
Original file line number Diff line number Diff line change
Expand Up @@ -808,13 +808,33 @@ def print_(*args, **kwargs):
_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

Expand Down Expand Up @@ -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.
Expand Down
27 changes: 23 additions & 4 deletions test_six.py
Original file line number Diff line number Diff line change
Expand Up @@ -832,14 +832,33 @@ def k():
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():
Expand Down

0 comments on commit 1988faf

Please sign in to comment.