Badges? We ain’t got no badges! We don’t need no badges! I don’t have to show you any stinking badges!
Features:
- Patch any module: your project, stdlib, built-ins.
- Patch any object: functions, class instances.
- Pure Python implementation: run it on CPython or PyPy.
TODO:
- Patch already imported objects
- Patch
__init__and__new__. - Test PyPy
import aop
def multiply(context):
print(context.aspect, context.args, context.kwargs)
yield
context.result *= 100
aop.register(
handler=multiply,
modules=aop.match(equals='math'),
targets=aop.match(regexp='(sin|cos)')
)Ok, let's check:
import math
math.cos(0)
# prints: cos (0,) {}
# returns: 100.0Register new advice:
aop.register(
handler=some_handler,
modules=aop.match(equals='math'),
targets=aop.match(regexp='(sin|cos)')
)Parameters for aop.register:
handler-- advice for joinpoint processing.paths-- expression for path to module.modules-- expression for module name.targets-- expression for object name.methods-- expression for called object's method. It's__call__for functions.
Available kwargs for aop.match:
regexp-- object name fully match to regular expression.startswith-- object name starts with specified string.endswith-- object name ends with specified string.contains-- object contains specified string.equals-- object name equal to specified string.
Handler looks like:
def multiply(context):
... # before aspect call
yield
... # after aspect callContext's properties:
aspect-- name of target.method-- name of called method or__call__for functions.module-- name of module where aspect defined.path-- path to module where aspect defined.args-- tuple of passed positional argskwargs-- dict of passed keyword argsresult-- target's method response
Register all advices or just enable patching before all other imports in project:
import aop
aop.enable()
... # all other importsAlso it's recommend finally enable patching after register last advice:
aop.register(...)
aop.register(...)
aop.enable(final=True)If you want to disable patching:
aop.disable()Inspect object:
aop.inspect(math.isclose, print=True)Now this package can't patch modules that imported before aop.enable() or aop.register(...):
$ python3 special_cases/2_after.py
...
AssertionError: not patchedAlthough you can run your script via aop runner:
$ python3 -m aop special_cases/2_after.py
cos (0,) {}