As of python 3.5+:
@functools.wraps(f)
def g():
pass
Is an alias for g = functools.update_wrapper(g, f)
. It does exactly three things:
__module__
, __name__
, __qualname__
, __doc__
, and __annotations__
attributes of f
on g
. This default list is in WRAPPER_ASSIGNMENTS
, you can see it in the functools source.__dict__
of g
with all elements from f.__dict__
. (see WRAPPER_UPDATES
in the source)__wrapped__=f
attribute on g
The consequence is that g
appears as having the same name, docstring, module name, and signature than f
. The only problem is that concerning the signature this is not actually true: it is just that inspect.signature
follows wrapper chains by default. You can check it by using inspect.signature(g, follow_wrapped=False)
as explained in the doc. This has annoying consequences:
Signature.bind()
.Now there is a bit of confusion between functools.wraps
and decorators, because a very frequent use case for developing decorators is to wrap functions. But both are completely independent concepts. If you're interested in understanding the difference, I implemented helper libraries for both: decopatch to write decorators easily, and makefun to provide a signature-preserving replacement for @wraps
. Note that makefun
relies on the same proven trick than the famous decorator
library.