Why can't Python decorators be chained across definitions?

decorator, python

Solution

The problem with the second example is that

@makebold
def makeitalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

is trying to decorate `makeitalic`, the decorator, and not `wrapped`, the function it returns.

You can do what I think you intend with something like this:

def makeitalic(fn):
    @makebold
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

Here `makeitalic` uses `makebold` to decorate `wrapped`.

Problem

Why arn't the following two scripts equivalent? (Taken from another question: Understanding Python Decorators) ``` def makebold(fn): def wrapped(): return "<b>" + fn() + "</b>" return wrapped def makeitalic(fn): def wrapped(): return "<i>" + fn() + "</i>" return wrapped @makebold @makeitalic def hello(): return "hello world" print hello() ## returns <b><i>hello world</i></b> ``` and with a decorated decorator: ``` def makebold(fn): def wrapped(): return "<b>" + fn() + "</b>" return wrapped @makebold def makeitalic(fn): def wrapped(): return "<i>" + fn() + "</i>" return wrapped @makeitalic def hello(): return "hello world" print hello() ## TypeError: wrapped() takes no arguments (1 given) ``` Why do I want to know? I've written a `retry` decorator to catch MySQLdb exceptions - if the exception is transient (e.g. Timeout) it will re-call the function after sleeping a bit. I've also got a `modifies_db` decorator which takes care of some cache-related housekeeping. `modifies_db` is decorated with `retry`, so I assumed that all functions decorated with `modifies_db` would also retry implicitly. Where did I go wrong?

Original source

Related problems