Safe toolkit for writing decorators (hereby called aspects)
aspectlib.Aspect | Container for the advice yielding generator. |
aspectlib.Proceed | Instruction for calling the decorated function. |
aspectlib.Return | Instruction for returning a optional value. |
Power tools for patching functions (hereby glorified as weaving)
aspectlib.ALL_METHODS | Compiled regular expression objects |
aspectlib.NORMAL_METHODS | Compiled regular expression objects |
aspectlib.weave | Send a message to a recipient |
aspectlib.Rollback | When called, rollbacks all the patches and changes the weave() has done. |
Instruction for calling the decorated function. Can be used multiple times.
If not used as an instance then the default args and kwargs are used.
Instruction for returning a optional value.
If not used as an instance then None is returned.
Container for the advice yielding generator. Can be used as a decorator on other function to change behavior according to the advices yielded from the generator.
Parameters: |
|
---|
Usage:
>>> @Aspect
... def my_decorator(*args, **kwargs):
... print("Got called with args: %s kwargs: %s" % (args, kwargs))
... result = yield
... print(" ... and the result is: %s" % (result,))
>>> @my_decorator
... def foo(a, b, c=1):
... print((a, b, c))
>>> foo(1, 2, c=3)
Got called with args: (1, 2) kwargs: {'c': 3}
(1, 2, 3)
... and the result is: None
Normally you don’t have access to the cutpoints (the functions you’re going to use the aspect/decorator on) because you don’t and should not call them directly. There are situations where you’d want to get the name or other data from the function. This is where you use the bind=True option:
>>> @Aspect(bind=True)
... def my_decorator(cutpoint, *args, **kwargs):
... print("`%s` got called with args: %s kwargs: %s" % (cutpoint.__name__, args, kwargs))
... result = yield
... print(" ... and the result is: %s" % (result,))
>>> @my_decorator
... def foo(a, b, c=1):
... print((a, b, c))
>>> foo(1, 2, c=3)
`foo` got called with args: (1, 2) kwargs: {'c': 3}
(1, 2, 3)
... and the result is: None
You can use these advices:
Note
The Aspect will correctly handle generators and coroutines (consume them, capture result).
Example:
>>> from aspectlib import Aspect
>>> @Aspect
... def log_errors(*args, **kwargs):
... try:
... yield
... except Exception as exc:
... print("Raised %r for %s/%s" % (exc, args, kwargs))
... raise
Will work as expected with generators (and coroutines):
>>> @log_errors
... def broken_generator():
... yield 1
... raise RuntimeError()
>>> from pytest import raises
>>> raises(RuntimeError, lambda: list(broken_generator()))
Raised RuntimeError() for ()/{}
...
>>> @log_errors
... def broken_function():
... raise RuntimeError()
>>> raises(RuntimeError, broken_function)
Raised RuntimeError() for ()/{}
...
And it will handle results:
>>> from aspectlib import Aspect
>>> @Aspect
... def log_results(*args, **kwargs):
... try:
... value = yield
... except Exception as exc:
... print("Raised %r for %s/%s" % (exc, args, kwargs))
... raise
... else:
... print("Returned %r for %s/%s" % (value, args, kwargs))
>>> @log_results
... def weird_function():
... yield 1
... raise StopIteration('foobar') # in Python 3 it's the same as: return 'foobar'
>>> list(weird_function())
Returned 'foobar' for ()/{}
[1]
When called, rollbacks all the patches and changes the weave() has done.
Alias of __exit__.
Alias of __exit__.
Compiled regular expression objects
Compiled regular expression objects
Send a message to a recipient
Parameters: |
|
---|---|
Returns: | An object that can rollback the patches. |
Return type: | aspectlib.Rollback |
Raises: | TypeError – If target is a unacceptable object, or the specified options are not available for that type of object. |
Changed in version 0.4.0: Replaced only_methods, skip_methods, skip_magicmethods options with methods. Renamed on_init option to lazy. Added aliases option. Replaced skip_subclasses option with subclasses.