Rajat
Rajat

Reputation: 1836

Defining Python decorators for a complete module

I have a module which contains a lot of functions (more than 25). I want to add a common decorator function to each of these functions. The normal way to do is to add a @decorator line above each function, but I was wondering if there is a better way to do it? Probably I can declare a global decorator at the top of the module or something else?

Note that since I am using someone else's code, I want to minimize the number of lines changed, so modifying the module is not ideal for me.

Thanks.

Upvotes: 17

Views: 6767

Answers (2)

Ben
Ben

Reputation: 71545

I think applying a decorator en-masse such that it's not obvious where you will go looking to find out about the function (at its definition) is generally a bad idea. Explicit is better than implicit, and all that.

If you want to apply the decorator to some third party module's functions, without modifying the third-party code, here is how I would do it:

# my_wrapper_module.py

import some_module
import functools

def some_decorator(func):
    @functools.wraps(func):
    def wrapper(*args, **kwargs):
        ...
    return wrapper

FUNCTION_NAMES = [
    'some_func_1',
    'some_func_2',
    'some_func_3',
    ...
]

for name in FUNCTION_NAMES:
    globals()[name] = some_decorator(getattr(some_module, name))

And then use these functions elsewhere by doing from my_wrapper_module import some_func_2, etc.

For me, this has the following advantages:

  1. No need to modify the third-party source file
  2. It is clear from the call site that I should go look at my_wrapper_module to see what I'm calling, and that I'm not using the undecorated versions of the functions
  3. It is clear from my_wrapper_module what functions are being exported, that they originally come from some_module, and that they all have the same decorator applied
  4. Any code that imports some_module directly isn't silently and inexplicably affected; this could be particularly important if the third-party code is more than one module

But if what you're trying to do is hack a third-party library so that internal calls are affected, then this is not what you want.

Upvotes: 10

John La Rooy
John La Rooy

Reputation: 304393

If your decorator is called my_decorator

### Decorate all the above functions
import types
for k,v in globals().items():
    if isinstance(v, types.FunctionType):
        globals()[k] = my_decorator(v)

You could also apply this to the module after importing it

import othermodule
import types
for k,v in vars(othermodule).items():
    if isinstance(v, types.FunctionType):
        vars(othermodule)[k] = my_decorator(v)

Upvotes: 20

Related Questions