decopatch Library
decopatch is a Python library designed to simplify the creation of decorators. It addresses the common challenge in Python where writing decorators requires explicit handling of both with-parenthesis and without-parenthesis usages. The library is currently at version 1.4.10 and receives regular minor updates, maintaining an active development status.
Warnings
- breaking Enabling the experimental 'stack introspection' feature (`enable_stack_introspection=True`) now raises an explicit `NotImplementedError` on Python 3.8 and newer versions. Users relying on this beta feature on recent Python versions will encounter errors.
- gotcha While `decopatch` significantly simplifies decorator creation, prior to version 1.4.6, there were reported bugs concerning incorrect injection of arguments, particularly when variable-positional arguments (`*args`) were present in the decorated function's signature. Although fixed, this highlights a potential 'gotcha' area for complex function signatures if not fully leveraging `decopatch`'s mechanisms or if custom wrapper logic is introduced without careful consideration.
- gotcha The primary problem `decopatch` solves is enabling decorators to work with or without parentheses seamlessly. Manually attempting to detect how a decorator was called (e.g., by checking `callable(args[0])`) is a common and error-prone workaround in Python. Using `decopatch`'s `function_decorator`, `class_decorator`, or `decorator` correctly abstracts this complexity, preventing a significant footgun for decorator authors.
Install
-
pip install decopatch
Imports
- function_decorator
from decopatch import function_decorator
- class_decorator
from decopatch import class_decorator
- decorator
from decopatch import decorator
- WRAPPED, F_ARGS, F_KWARGS
from decopatch import WRAPPED, F_ARGS, F_KWARGS
Quickstart
from decopatch import function_decorator
@function_decorator
def add_tag(tag='hi!'):
"""
Example decorator to add a 'tag' attribute to a function.
:param tag: the 'tag' value to set on the decorated function.
"""
def _apply_decorator(f):
"""
This method is called when `@add_tag` is used on a function `f`.
It should return a replacement for `f`.
"""
setattr(f, 'tag', tag)
return f
return _apply_decorator
# Usage without parenthesis
@add_tag
def foo1():
pass
assert foo1.tag == 'hi!'
# Usage with empty parenthesis
@add_tag()
def foo2():
pass
assert foo2.tag == 'hi!'
# Usage with arguments
@add_tag('hello')
def foo3():
pass
assert foo3.tag == 'hello'
print(f"foo1 tag: {foo1.tag}")
print(f"foo2 tag: {foo2.tag}")
print(f"foo3 tag: {foo3.tag}")