Staxks
Staxks

Reputation: 143

Why is this function decorator failing?

Here is my code:

name    = "Arthur"
message = "Hello!"

def decorate(func_to_decorate):

    def wrap(*args, **kwargs):

        print           ("........................")
        func_to_decorate(*args, **kwargs)
        print           ("........................")
        return wrap


@decorate
def send_message(your_name="unassigned", your_message="blank"):

    print(your_name)
    print(your_message)

send_message(name, message)

My error is in line 20:

send_message(name, message)
TypeError: 'NoneType' object is not callable

My understanding is that the wrapper is "replacing" itself with the function immediately following the decorator. This seems work when I am not passing arguments to the function being decorated, but not with the decorator present.

Upvotes: 1

Views: 144

Answers (1)

abarnert
abarnert

Reputation: 365777

There are two things wrong with your decorator.

First, there's an indentation mistake:

def decorate(func_to_decorate):

    def wrap(*args, **kwargs):

        print           ("........................")
        func_to_decorate(*args, **kwargs)
        print           ("........................")
        return wrap

That return wrap is part of the wrap function body, not part of the decorate function body. So, decorate has no return statement, which means it returns None. Hence the error you see: the decorator is in fact "replacing" the wrapped function with the wrapper it returns—but that wrapper is None, so you end up trying to call None as a function.

And meanwhile, you seem to understand that wrap should return something, but that something definitely shouldn't be itself. Usually, what you want to return is the result of the wrapped function (or some post-processed version of that result). In your test, you're only trying to wrap up a function that's used only for side-effects, not to mention that you never get to call the wrapper because of your first problem, so you wouldn't notice this problem yet, but you still want to fix it.

So:

def decorate(func_to_decorate):

    def wrap(*args, **kwargs):

        print           ("........................")
        retval = func_to_decorate(*args, **kwargs)
        print           ("........................")
        return retval

    return wrap

Upvotes: 1

Related Questions