Skip to content

orsinium-labs/aop

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
aop
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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

About

Aspect oriented programming for Python. Patch everything!

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages