Apply a single decorator to multiple functions
Question:
I’ve searched for this, but the results I’ve seen involve the opposite: applying multiple decorators to a single function.
I’d like to simplify this pattern. Is there a way to apply this single decorator to multiple functions? If not, how can I rewrite the above to be less repetitious?
from mock import patch
@patch('somelongmodulename.somelongmodulefunction')
def test_a(patched):
pass # test one behavior using the above function
@patch('somelongmodulename.somelongmodulefunction')
def test_b(patched):
pass # test another behavior
@patch('somelongmodulename.somelongmodulefunction')
def test_c(patched):
pass # test a third behavior
from mock import patch
patched_name = 'somelongmodulename.somelongmodulefunction'
@patch(patched_name)
def test_a(patched):
pass # test one behavior using the above function
@patch(patched_name)
def test_b(patched):
pass # test another behavior
@patch(patched_name)
def test_c(patched):
pass # test a third behavior
Answers:
If you want to make the “long” function call only once and decorate all three functions with the result, just do exactly that.
my_patch = patch('somelongmodulename.somelongmodulefunction')
@my_patch
def test_a(patched):
pass
@my_patch
def test_b(patched):
pass
If you want to get fancy, you can modify globals()
. The general idea below should work.
test_pattern = re.compile(r'test_w+')
test_names = [name for name in globals().keys() if test_pattern.match(name)]
for name in test_names:
globals()[name] = decorate(globals()[name])
To add on what justhecuke said about using globals(), in case the function is located in a different module, you could do something like this:
import inspect
def decorator_func(func):
def inner():
print("Decorated")
func()
return inner
moduleA = __import__('moduleA')
for name, obj in inspect.getmembers(moduleA):
if name.startswith('test_') and inspect.isfunction(obj): #Conditions to apply decorators on expected functions
moduleA.__dict__[name] = decorator_func(moduleA.__dict__[name])
I’ve searched for this, but the results I’ve seen involve the opposite: applying multiple decorators to a single function.
I’d like to simplify this pattern. Is there a way to apply this single decorator to multiple functions? If not, how can I rewrite the above to be less repetitious?
from mock import patch
@patch('somelongmodulename.somelongmodulefunction')
def test_a(patched):
pass # test one behavior using the above function
@patch('somelongmodulename.somelongmodulefunction')
def test_b(patched):
pass # test another behavior
@patch('somelongmodulename.somelongmodulefunction')
def test_c(patched):
pass # test a third behavior
from mock import patch
patched_name = 'somelongmodulename.somelongmodulefunction'
@patch(patched_name)
def test_a(patched):
pass # test one behavior using the above function
@patch(patched_name)
def test_b(patched):
pass # test another behavior
@patch(patched_name)
def test_c(patched):
pass # test a third behavior
If you want to make the “long” function call only once and decorate all three functions with the result, just do exactly that.
my_patch = patch('somelongmodulename.somelongmodulefunction')
@my_patch
def test_a(patched):
pass
@my_patch
def test_b(patched):
pass
If you want to get fancy, you can modify globals()
. The general idea below should work.
test_pattern = re.compile(r'test_w+')
test_names = [name for name in globals().keys() if test_pattern.match(name)]
for name in test_names:
globals()[name] = decorate(globals()[name])
To add on what justhecuke said about using globals(), in case the function is located in a different module, you could do something like this:
import inspect
def decorator_func(func):
def inner():
print("Decorated")
func()
return inner
moduleA = __import__('moduleA')
for name, obj in inspect.getmembers(moduleA):
if name.startswith('test_') and inspect.isfunction(obj): #Conditions to apply decorators on expected functions
moduleA.__dict__[name] = decorator_func(moduleA.__dict__[name])