Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get a Python decorator to run after the decorated function has completed?

I want to use a decorator to handle auditing of various functions (mainly Django view functions, but not exclusively). In order to do this I would like to be able to audit the function post-execution - i.e. the function runs as normal, and if it returns without an exception, then the decorator logs the fact.

Something like:

@audit_action(action='did something') def do_something(*args, **kwargs):     if args[0] == 'foo':         return 'bar'     else:         return 'baz' 

Where audit_action would only run after the function has completed.

like image 287
Hugo Rodger-Brown Avatar asked Feb 05 '13 08:02

Hugo Rodger-Brown


People also ask

How do you call a decorator in Python?

The call() decorator is used in place of the helper functions. In python, or in any other languages, we use helper functions for three major motives: To identify the purpose of the method. The helper function is removed as soon as its job is completed.

What is a decorated function Python?

A decorator in Python is a function that takes another function as its argument, and returns yet another function . Decorators can be extremely useful as they allow the extension of an existing function, without any modification to the original function source code.

What is the return type of a decorator?

The object that the decorator returns, is what becomes known as hello . Essentially, it's the same thing as if you were going to write your own normal function, such as: hello = decorate(hello) . Decorate is passed the function -- which it can use however it wants -- then returns another object.

Is Python decorator a closure?

Decorators return a closure. A closure is what is returned by a decorator. The second function shown in this gist is say_hello . This function prints a string and returns 'None'.


1 Answers

Decorators usually return a wrapper function; just put your logic in the wrapper function after invoking the wrapped function.

def audit_action(action):     def decorator_func(func):         def wrapper_func(*args, **kwargs):             # Invoke the wrapped function first             retval = func(*args, **kwargs)             # Now do something here with retval and/or action             print('In wrapper_func, handling action {!r} after wrapped function returned {!r}'.format(action, retval))             return retval         return wrapper_func     return decorator_func 

So audit_action(action='did something') is a decorator factory that returns a scoped decorator_func, which is used to decorate your do_something (do_something = decorator_func(do_something)).

After decorating, your do_something reference has been replaced by wrapper_func. Calling wrapper_func() causes the original do_something() to be called, and then your code in the wrapper func can do things.

The above code, combined with your example function, gives the following output:

>>> do_something('foo') In wrapper_func, handling action 'did something' after wrapped function returned 'bar' 'bar' 
like image 197
Martijn Pieters Avatar answered Sep 17 '22 12:09

Martijn Pieters