| 1 |
======================================= |
|---|
| 2 |
Some explanations on the aspects module |
|---|
| 3 |
======================================= |
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
The main parts |
|---|
| 7 |
-------------- |
|---|
| 8 |
|
|---|
| 9 |
- The **core** module defines the base class for all aspects. |
|---|
| 10 |
- The **weaver** module defines the class which is responsible for |
|---|
| 11 |
*weaving* code. The Weaver class is a Singleton, and you |
|---|
| 12 |
can import the existing instance created in the *weaver* module, using |
|---|
| 13 |
this import command : :: |
|---|
| 14 |
|
|---|
| 15 |
from logilab.aspects.weaver import weaver |
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
The main methods which can be called on the weaver's instance are |
|---|
| 20 |
*weave_methods* and *unweave_methods* which weave and unweave some |
|---|
| 21 |
aspect-related code on classes or instances. |
|---|
| 22 |
- The *lib* module contains some useful aspects like : |
|---|
| 23 |
* **LoggerAspect** which will trace all method calls. |
|---|
| 24 |
* **ContractAspect** which will enable design by contract in |
|---|
| 25 |
Python. |
|---|
| 26 |
* Other aspects exist like **ProfilerAspect**, **DispatcherAspect**, |
|---|
| 27 |
**ConfirmationAspect** or **ObserverAspect**, but they are not |
|---|
| 28 |
always fully implemented. Anyway, you can use them or want to |
|---|
| 29 |
have a look at them to give you ideas of useful aspects or |
|---|
| 30 |
improvements. |
|---|
| 31 |
- Some unit tests, and some examples can be found in the tests or examples |
|---|
| 32 |
directory : |
|---|
| 33 |
:: |
|---|
| 34 |
|
|---|
| 35 |
aspects/examples/contract_example.py |
|---|
| 36 |
aspects/examples/logger_example.py |
|---|
| 37 |
|
|---|
| 38 |
|
|---|
| 39 |
|
|---|
| 40 |
A simple use case |
|---|
| 41 |
----------------- |
|---|
| 42 |
|
|---|
| 43 |
(Taken from logger_example.py) : |
|---|
| 44 |
:: |
|---|
| 45 |
|
|---|
| 46 |
# Import the weaver and the aspect to use |
|---|
| 47 |
from logilab.aspects.weaver import weaver |
|---|
| 48 |
from logilab.aspects.lib.logger import LoggerApsect |
|---|
| 49 |
import sys |
|---|
| 50 |
|
|---|
| 51 |
stack = StackImpl() |
|---|
| 52 |
|
|---|
| 53 |
# Push an element on the stack, the method call is not traced |
|---|
| 54 |
stack.push("an element") |
|---|
| 55 |
|
|---|
| 56 |
# Weave aspect code (log will be done on sys.stderr) |
|---|
| 57 |
weaver.weave_methods(stack, LoggerAspect, sys.stderr) |
|---|
| 58 |
|
|---|
| 59 |
# Push an other element, method call will now be traced |
|---|
| 60 |
stack.push("another element") |
|---|
| 61 |
|
|---|
| 62 |
# Unweave logger aspect |
|---|
| 63 |
weaver.unweave(stack, LoggerAspect) |
|---|
| 64 |
|
|---|
| 65 |
# Now, call methods aren't traced anymore |
|---|
| 66 |
stack.push("a third element") |
|---|
| 67 |
|
|---|
| 68 |
|
|---|
| 69 |
In this example, we have weaved an aspect on a given instance. As a |
|---|
| 70 |
consequence, other instances of the same class will not be |
|---|
| 71 |
aspected. The best way of weaving all class instances, is to weave the |
|---|
| 72 |
aspect directly on the class, not on the instances. The syntax is |
|---|
| 73 |
exactly the same : :: |
|---|
| 74 |
|
|---|
| 75 |
weaver.weave_methods(StackImpl, LoggerAspect, sys.stderr) |
|---|
| 76 |
|
|---|
| 77 |
|
|---|
| 78 |
Creating your own aspects |
|---|
| 79 |
------------------------- |
|---|
| 80 |
|
|---|
| 81 |
For now, it's only possible to wrap methods, not attribute accesses. |
|---|
| 82 |
|
|---|
| 83 |
To create a new aspect, you must define a class which inherits from |
|---|
| 84 |
*AbstractAspect* (in *aspects.core*), and define *before()*, *after()* and |
|---|
| 85 |
*around()* methods. Note that you can choose to override only one of this |
|---|
| 86 |
three methods since the default behaviour is to "simply pass". It is |
|---|
| 87 |
**important**, when overriding the *around* method to explicitly call |
|---|
| 88 |
*self._proceed(...)* which is the effective call to the wrapped method. |
|---|
| 89 |
|
|---|
| 90 |
|
|---|
| 91 |
Let's write a simple aspect which will write **BEFORE** before the method |
|---|
| 92 |
call and **AFTER** after. |
|---|
| 93 |
|
|---|
| 94 |
:: |
|---|
| 95 |
|
|---|
| 96 |
from logilab.aspects.core import AbstractAspect |
|---|
| 97 |
from logilab.aspects.prototypes import reassign_function_arguments |
|---|
| 98 |
|
|---|
| 99 |
|
|---|
| 100 |
class SimpleAspect(AbstractAspect): |
|---|
| 101 |
|
|---|
| 102 |
def before(self, wobj, *args, **kwargs): |
|---|
| 103 |
"""Before method |
|---|
| 104 |
""" |
|---|
| 105 |
print "BEFORE ",self.method_name |
|---|
| 106 |
|
|---|
| 107 |
|
|---|
| 108 |
def after(self, wobj, ret_v, exec_excpt, *args, **kwargs): |
|---|
| 109 |
"""After method. |
|---|
| 110 |
print the return value |
|---|
| 111 |
""" |
|---|
| 112 |
print "AFTER ",self.method_name,", return value is ", ret_v |
|---|
| 113 |
|
|---|
| 114 |
|
|---|
| 115 |
This example is quite simple, and is not really useful, but it should |
|---|
| 116 |
show how to define your own aspects. |
|---|
| 117 |
|
|---|
| 118 |
Here is some information on the above code: |
|---|
| 119 |
|
|---|
| 120 |
- The *before()* parameters are : |
|---|
| 121 |
|
|---|
| 122 |
* *self* : The aspect instance |
|---|
| 123 |
* *wobj* : The weaved object instance (on which is called |
|---|
| 124 |
the wrapped method) |
|---|
| 125 |
* *args* and *kwargs* are the arguments passed to the wrapped method. |
|---|
| 126 |
If inside *before()*, you want to have the name and the value of |
|---|
| 127 |
each argument, you can use the function *reassign_function_arguments* |
|---|
| 128 |
in the *aspects.prototypes* module. It will return a dictionnary |
|---|
| 129 |
containing arg names as keys, and arg values as values. |
|---|
| 130 |
- The *after()* parameters are the same, with two more arguments : |
|---|
| 131 |
|
|---|
| 132 |
* *ret_v* which is the value returned by the wrapped method. |
|---|
| 133 |
* *exec_excpt* which is the exception raised by the wrapped method, |
|---|
| 134 |
or None if no exception was raised. |
|---|
| 135 |
|
|---|
| 136 |
|
|---|
| 137 |
|
|---|
| 138 |
**IMPORTANT** : for now, each aspect instance is attached to a specific |
|---|
| 139 |
method, this will very soon change because it is not handy, and quite |
|---|
| 140 |
expensive. |
|---|
| 141 |
|
|---|