Skip to content

Aspect oriented programming for Python. Patch everything!

Notifications You must be signed in to change notification settings

orsinium-labs/aop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

79 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AOP

Badges? We ain’t got no badges! We don’t need no badges! I don’t have to show you any stinking badges!

Aspect-oriented programming

Features:

  1. Patch any module: your project, stdlib, built-ins.
  2. Patch any object: functions, class instances.
  3. Pure Python implementation: run it on CPython or PyPy.

TODO:

  1. Patch already imported objects
  2. Patch __init__ and __new__.
  3. Test PyPy

Example

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

Usage

Register

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.

Match

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 and context

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 args
  • kwargs -- dict of passed keyword args
  • result -- target's method response

Enable and disable

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

Inspect object:

aop.inspect(math.isclose, print=True)

Patch import system automatically

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,) {}

Releases

No releases published

Packages

No packages published

Languages