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.0
Register 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 call
Context'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 imports
Also 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 patched
Although you can run your script via aop runner:
$ python3 -m aop special_cases/2_after.py
cos (0,) {}