Skip to content

Instantly share code, notes, and snippets.

@nitori
Last active September 19, 2024 19:57
Show Gist options
  • Save nitori/4dfc60b25bf2e9aa82c39b89202a2e47 to your computer and use it in GitHub Desktop.
Save nitori/4dfc60b25bf2e9aa82c39b89202a2e47 to your computer and use it in GitHub Desktop.
Convert a decorator into one that allows multiple forms
from functools import wraps, partial
def argumentarize(func):
@wraps(func)
def decorator(decorated_func=None, /, **kwargs):
if decorated_func is None:
# decorator was used with (...)
# so return the decorator again as partial
return partial(func, **kwargs)
if not callable(decorated_func):
raise TypeError(f"First argument of {func} must be callable. Use keyword arguments instead.")
return func(decorated_func, **kwargs)
return decorator
# Usage, apply to a decorator.
# Signature needs to be: deconame(func, /, **kwargs)
@argumentarize
def decorate(func, /, **kwargs):
@wraps(func)
def wrapper():
print("Before", kwargs)
r = func()
print("After", kwargs)
return r
return wrapper
# Now with that decorator, all three forms are possible:
# regular decorator
@decorate
def foo():
print('foo')
# decorator called
@decorate()
def bar():
print('bar')
# decorator called with keyword arguments (no positional possible)
@decorate(foo='bar')
def spam():
print('spam')
def main():
foo()
bar()
spam()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment