CodeBlanks Logo Icon CodeBlanks

functools.wraps

Preserve decorator metadata with functools.wraps.

The wrapper loses identity

When a decorator wraps a target, metadata like __name__ and __doc__ moves to the wrapper. So hello.__name__ is no longer "hello" but "wrapper". This breaks debugging and help output.

The fix: write @functools.wraps(target) at the top of the wrapper. It copies the target's name, docstring and other metadata to the wrapper. From the outside the wrapper looks like the target.

What does wraps copy?

@functools.wraps(fn) copies not just the name but also attributes like __doc__, __module__, __qualname__ and __dict__. So help(hello) shows the real hello docstring, not the wrapper's.

Signature information also looks more accurate. This matters when writing libraries; users must know which function they call. Using wraps when writing decorators is a good habit.

Practice A decorator that uses wraps

Start with import functools. Define a decorator named call; signature def call(fn):. Inside, write @functools.wraps(fn) on the line above def wrapper(*args, **kwargs):. The wrapper body should be return fn(*args, **kwargs). End with return wrapper. Decorate def hello(): with @call; body returns "Hi!". Finally call print(hello.__name__).

Starter code

main.py
# Write the call decorator and the hello function

Sample solution

main.py
# Write the call decorator and the hello function
import functools

def call(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        return fn(*args, **kwargs)
    return wrapper

@call
def hello():
    return "Hi!"

print(hello.__name__)

Expected output

hello

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What does @functools.wraps prevent from being lost?

  • The parameters
  • The function's return value
  • The function name and docstring Correct answer
  • Error raising

Explanation: @functools.wraps prevents the target's name and docstring from moving to the wrapper.

2 Complete the wraps line

Question
def call(fn):
    @____(fn)
    def wrapper(*args, **kwargs):
        return fn(*args, **kwargs)
    return wrapper
Answer
def call(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        return fn(*args, **kwargs)
    return wrapper

Explanation: functools.wraps is written at the top of the wrapper.

3 Match wraps and metadata

Term Meaning
__name__ Function name
__doc__ Docstring
__module__ Defining module
@functools.wraps Copies metadata

All lessons in Intermediate