Skip to content

Commit a020a97

Browse files
committed
Improved the comments and docs
Signed-off-by: David Caro <[email protected]>
1 parent a40d277 commit a020a97

2 files changed

Lines changed: 29 additions & 11 deletions

File tree

Doc/library/functools.rst

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -572,10 +572,22 @@ The :mod:`functools` module defines the following functions:
572572
on the wrapper function). :exc:`AttributeError` is still raised if the
573573
wrapper function itself is missing any attributes named in *updated*.
574574

575-
If the wrapper function defined any type annotations, they will always be
576-
preserved. But if not, they will be extracted from the wrapped function
577-
``__annotations__`` if available and ``__annotations__`` is in the *updated*
578-
list.
575+
When assigning the ``__annotations__`` attribute (when it's part of the
576+
``assigned`` list parameter), it will be populated only if missing some
577+
annotations as follows:
578+
579+
* If the wrapper function defines it's own return type and parameter
580+
annotations, those will be preserved.
581+
582+
* If the wrapper function does not annotate the return type, it will be
583+
copied from the wrapped if any.
584+
585+
* If the wrapped function does not annotate any parameters, the annotations
586+
for them will be copied over from the wrapped if any.
587+
588+
That ensures that any custom annotations from the wrapper will not be
589+
overridden by the wrapped, allowing to change the annotated types of
590+
the wrapped function.
579591

580592
.. versionadded:: 3.2
581593
Automatic addition of the ``__wrapped__`` attribute.

Lib/functools.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ def update_wrapper(wrapper,
4646
updated is a tuple naming the attributes of the wrapper that
4747
are updated with the corresponding attribute from the wrapped
4848
function (defaults to functools.WRAPPER_UPDATES)
49+
50+
There's a special treatment for the `__annotations__` attribute:
51+
* If the wrapper defines a return type, then that will be used, if not,
52+
the one from the wrapped function will be used if any.
53+
* If the wrapper defines any parameter types, those will be used, if
54+
not, the ones from the wrapped function will be used if any.
4955
"""
5056
for attr in assigned:
5157
try:
@@ -59,24 +65,24 @@ def update_wrapper(wrapper,
5965

6066
if '__annotations__' in assigned:
6167
try:
62-
# Issue #41231: copy the annotations only if there's none defined
63-
# in the wrapper
68+
# Issue #41231: copy the annotations from wrapped, only if they are
69+
# not already defined in the wrapper
6470
if not wrapper.__annotations__:
6571
wrapper.__annotations__ = wrapped.__annotations__
66-
# if only the return wrapped has annotations, assume that the
67-
# parameters stay the same
72+
# if the wrapper does not annotate the parameters, copy their
73+
# annotations over from the wrapped
6874
elif ('return' in wrapper.__annotations__
6975
and len(wrapper.__annotations__) == 1):
7076
wrapper.__annotations__ = {
7177
**wrapped.__annotations__,
7278
**wrapper.__annotations__,
7379
}
74-
# if only the parameters have annotations, assume that the return
75-
# type stays the same
80+
# if the wrapper does not annotate the return type, copy the
81+
# annotation from the wrapped
7682
elif ('return' not in wrapper.__annotations__
7783
and 'return' in wrapped.__annotations__):
7884
wrapper.__annotations__['return'] = wrapped.__annotations__['return']
79-
except AttributeError as error:
85+
except AttributeError:
8086
pass
8187

8288
for attr in updated:

0 commit comments

Comments
 (0)