=======================================
Some explanations on the aspects module
=======================================


The main parts
--------------

	- The **core** module defines the base class for all aspects.
	- The **weaver** module defines the class which is responsible for
	  *weaving* code. The Weaver class is a Singleton, and you
	  can import the existing instance created in the *weaver* module, using
	  this import command : ::

		 from logilab.aspects.weaver import weaver


  
	  The main methods which can be called on the weaver's instance are
	  *weave_methods* and *unweave_methods* which weave and unweave some
	  aspect-related code on classes or instances.
	- The *lib* module contains some useful aspects like :	  
	       * **LoggerAspect** which will trace all method calls.
	       * **ContractAspect** which will enable design by contract in
	         Python.
	       * Other aspects exist like **ProfilerAspect**, **DispatcherAspect**,
	         **ConfirmationAspect** or **ObserverAspect**, but they are not
		 always fully implemented. Anyway, you can use them or want to
		 have a look at them to give you ideas of useful aspects or
		 improvements.
	- Some unit tests, and some examples can be found in the tests or examples
	  directory :
	  ::

		aspects/examples/contract_example.py
		aspects/examples/logger_example.py



A simple use case
-----------------

(Taken from logger_example.py) :
::

	# Import the weaver and the aspect to use
	from logilab.aspects.weaver import weaver
	from logilab.aspects.lib.logger import LoggerApsect
	import sys
	
	stack = StackImpl()

	# Push an element on the stack, the method call is not traced
	stack.push("an element")

	# Weave aspect code (log will be done on sys.stderr)
	weaver.weave_methods(stack, LoggerAspect, sys.stderr)

	# Push an other element, method call will now be traced
	stack.push("another element")

	# Unweave logger aspect
	weaver.unweave(stack, LoggerAspect)

	# Now, call methods aren't traced anymore
	stack.push("a third element")


In this example, we have weaved an aspect on a given instance. As a
consequence, other instances of the same class will not be
aspected. The best way of weaving all class instances, is to weave the
aspect directly on the class, not on the instances. The syntax is
exactly the same : ::

	    weaver.weave_methods(StackImpl, LoggerAspect, sys.stderr)


Creating your own aspects
-------------------------

For now, it's only possible to wrap methods, not attribute accesses.

To create a new aspect, you must define a class which inherits from
*AbstractAspect* (in *aspects.core*), and define *before()*, *after()* and
*around()* methods. Note that you can choose to override only one of this
three methods since the default behaviour is to "simply pass". It is
**important**, when overriding the *around* method to explicitly call
*self._proceed(...)* which is the effective call to the wrapped method. 


Let's write a simple aspect which will write **BEFORE** before the method
call and **AFTER** after.

::

    from logilab.aspects.core import AbstractAspect
    from logilab.aspects.prototypes import reassign_function_arguments


    class SimpleAspect(AbstractAspect):
    
	def before(self, wobj, *args, **kwargs):
	    """Before method
	    """
	    print "BEFORE ",self.method_name
	    

	def after(self, wobj, ret_v, exec_excpt, *args, **kwargs):
	    """After method.
	    print the return value
	    """
	    print "AFTER ",self.method_name,", return value is ", ret_v


This example is quite simple, and is not really useful, but it should
show how to define your own aspects.

Here is some information on the above code:

	 - The *before()* parameters are :
	 
	       * *self* : The aspect instance
	       * *wobj* : The weaved object instance (on which is called
	         the wrapped method)
	       * *args* and *kwargs* are the arguments passed to the wrapped method.
		 If inside *before()*, you want to have the name and the value of
		 each argument, you can use the function *reassign_function_arguments*
		 in the *aspects.prototypes* module. It will return a dictionnary
		 containing arg names as keys, and arg values as values.
	 - The *after()* parameters are the same, with two more arguments :

	       * *ret_v* which is the value returned by the wrapped method.
	       * *exec_excpt* which is the exception raised by the wrapped method,
	         or None if no exception was raised.



**IMPORTANT** : for now, each aspect instance is attached to a specific
method, this will very soon change because it is not handy, and quite
expensive.

