"""AOPython examples
Copyright (c) 2005 Daniel Miller
Code in this file may be used freely in your own projects.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Setup logging and test aspects to be used in the following doctests
>>> log = LoggerAspect(hideAddresses=True)
>>> ta = TestAspect()
>>> ta2 = TestAspect2()
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Wrap an instance method with Aspect
>>> ta3 = Aspect()
>>> test3 = Test()
>>> test3.func = ta3.wrap(test3.func, allowUnwrap=True)
>>> test3.func(23)
23
>>> test3.func.exposed
True
>>> test3.func = unwrap(test3.func)
>>> test3.func(23)
23
>>> test3.func.exposed
True
>>> del ta3
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Wrap an instance method with multiple aspects
>>> test = Test()
>>> test.func = log.wrap(test.func, allowUnwrap=True)
>>> test.func = ta.wrap(test.func, allowUnwrap=True)
>>> test.func = ta2.wrap(test.func, allowUnwrap=True)
>>> test.func(x=2)
TestAspect2.advise change last arg from <Test object> to 'TestAspect2 replaced arg'
TestAspect2.advise fall back to original args due to exception: instance: unbound method func() must be called with Test instance as first argument (got str instance instead)
TestAspect.advise checking attribute func.exposed = True
Test.func(<Test object>, x=2)
2
>>> test.func.exposed
True
>>> test.func = unwrap(test.func, all=True)
>>> test.func(x=2)
2
>>> test.func.exposed
True
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Wrap a class method with multiple aspects
>>> Test.func2 = log.wrap(Test.func2, allowUnwrap=True)
>>> Test.func2 = ta.wrap(Test.func2, allowUnwrap=True)
>>> Test.func2 = ta2.wrap(Test.func2, allowUnwrap=True)
>>> test.func2(5)
TestAspect2.advise change last arg from 5 to 'TestAspect2 replaced arg'
TestAspect.advise checking attribute func2.exposed = False
Test.func2(<Test object>, 'TestAspect2 replaced arg')
'TestAspect2 replaced arg + 100'
>>> test.func2.exposed
False
>>> Test.func2 = unwrap(Test.func2, all=True)
>>> test.func2(5)
'5 + 100'
>>> test.func2.exposed
False
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Weave an instance with multiple aspects
>>> test = Test()
>>> weave((log, ta, ta2), test, [lambda n: n and n.startswith('func')], ['func3'], allowUnwrap=True)
>>> test.func(x=2)
TestAspect2.advise change last arg from <Test object> to 'TestAspect2 replaced arg'
TestAspect2.advise fall back to original args due to exception: instance: unbound method func() must be called with Test instance as first argument (got str instance instead)
TestAspect.advise checking attribute func.exposed = True
Test.func(<Test object>, x=2)
2
>>> test.func.exposed
True
>>> test.func2(2)
TestAspect2.advise change last arg from 2 to 'TestAspect2 replaced arg'
TestAspect.advise checking attribute func2.exposed = False
Test.func2(<Test object>, 'TestAspect2 replaced arg')
'TestAspect2 replaced arg + 100'
>>> test.func2.exposed
False
>>> test.func3(x=2)
2
>>> test._func(x=2)
Traceback (most recent call last):
...
Exception: _func should not be wrapped
>>> unweave(test, all=True)
>>> test.func(x=2)
2
>>> test.func.exposed
True
>>> test.func2(2)
'2 + 100'
>>> test.func2.exposed
False
>>> test.func3(x=2)
2
>>> test._func(x=2)
Traceback (most recent call last):
...
Exception: _func should not be wrapped
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Wrap a user-defined function
>>> def bar(x):
... return x
...
>>> bar.exposed = True
>>> bar = log.wrap(bar, allowUnwrap=True)
>>> bar = ta2.wrap(bar, allowUnwrap=True)
>>> bar = ta.wrap(bar, allowUnwrap=True)
>>> bar(3)
TestAspect.advise checking attribute bar.exposed = True
TestAspect2.advise change last arg from 3 to 'TestAspect2 replaced arg'
aopythonexamples.bar('TestAspect2 replaced arg')
'TestAspect2 replaced arg'
>>> bar.exposed
True
>>> bar = unwrap(bar, all=True)
>>> bar(3)
3
>>> bar.exposed
True
>>> del bar
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Wrap a lambda function
>>> f = lambda x: x + 500
>>> f = log.wrap(f, allowUnwrap=True)
>>> f = ta2.wrap(f, allowUnwrap=True)
>>> f = ta.wrap(f, allowUnwrap=True)
>>> f.exposed = True
>>> f(3)
TestAspect.advise checking attribute <lambda>.exposed = True
TestAspect2.advise change last arg from 3 to 'TestAspect2 replaced arg'
aopythonexamples.<lambda>('TestAspect2 replaced arg')
TestAspect2.advise fall back to original args due to exception: instance: cannot concatenate 'str' and 'int' objects
aopythonexamples.<lambda>(3)
503
>>> f.exposed
True
>>> f = unwrap(f, all=True)
>>> f(3)
503
>>> f.exposed
True
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Wrap list.append
>>> lst = []
>>> listAppend = lst.append
>>> listAppend = log.wrap(listAppend, allowUnwrap=True)
>>> listAppend = ta2.wrap(listAppend, allowUnwrap=True)
>>> listAppend = ta.wrap(listAppend, allowUnwrap=True)
>>> listAppend('something')
TestAspect.advise checking attribute append.exposed = <undefined>
TestAspect2.advise change last arg from 'something' to 'TestAspect2 replaced arg'
?.append('TestAspect2 replaced arg')
>>> lst
['TestAspect2 replaced arg']
>>> lst.append('something else')
>>> lst
['TestAspect2 replaced arg', 'something else']
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Something that will not turn out the way you expected
>>> Test = log.wrap(Test, allowUnwrap=True)
>>> Test = ta.wrap(Test, allowUnwrap=True)
>>> Test.gerryrigged = True # Set an attribute on the class after the wrap
>>> t = Test()
TestAspect.advise checking attribute Test.exposed = <undefined>
aopythonexamples.Test()
>>> t.func(5) # This works...
5
>>> Test.func(t, 5) # So does this...
5
>>> t.gerryrigged
Traceback (most recent call last):
...
AttributeError: 'Test' object has no attribute 'gerryrigged'
# Explanataion: at this point Test is not a class; it is a function that
# returns instances of Test
>>> Test = unwrap(Test, all=True)
# One possible workaround: wrap __init__ instead of the class
>>> Test.__init__ = log.wrap(Test.__init__, allowUnwrap=True)
>>> Test.__init__ = ta.wrap(Test.__init__, allowUnwrap=True)
>>> Test.gerryrigged = True # Set an attribute after the wrap
>>> t = Test() # Notice logger advice output
TestAspect.advise checking attribute __init__.exposed = <undefined>
?.__init__(<Test object>)
>>> t.func(5) # This works...
5
>>> Test.func(t, 5) # So does this...
5
>>> t.gerryrigged
True
>>> Test.__init__ = unwrap(Test.__init__, all=True)
>>> del t, Test.gerryrigged
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Things you can't do
>>> ta.wrap(4)
Traceback (most recent call last):
...
TypeError: cannot apply TestAspect: '4' of <type 'int'> is not callable
>>> lst = []
>>> lst.append = ta.wrap(lst.append)
Traceback (most recent call last):
...
AttributeError: 'list' object attribute 'append' is read-only
>>> lst.append2 = ta.wrap(lst.append)
Traceback (most recent call last):
...
AttributeError: 'list' object has no attribute 'append2'
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Weave an entire module and the classes contained in that module
>>> import re, sre
>>> log.weave(re, depth=1, allowUnwrap=True, wrapIfStartsWithUnderscore=True)
>>> log.weave(sre, depth=1, allowUnwrap=True, wrapIfStartsWithUnderscore=True)
# Now we get a trace of the inner workings of re.compile
>>> regexp = re.compile('test[x]+')
sre.compile('test[x]+')
sre._compile('test[x]+', 0)
sre_compile.isstring('test[x]+')
sre_compile.compile('test[x]+', 0)
sre_compile.isstring('test[x]+')
sre_parse.parse('test[x]+', 0)
sre_parse._parse_sub(<sre_parse.Tokenizer instance>, <sre_parse.Pattern instance>, 0)
sre_parse._parse(<sre_parse.Tokenizer instance>, <sre_parse.Pattern instance>)
sre_compile._code([('literal', 116), ('literal', 101), ('literal', 115), ('literal', 116), ('max_repeat', (1, 65535, [('literal', 120)]))], 0)
sre_compile._compile_info([], [('literal', 116), ('literal', 101), ('literal', 115), ('literal', 116), ('max_repeat', (1, 65535, [('literal', 120)]))], 0)
sre_compile._compile([16, 14, 1, 5, 0, 4, 4, 116, 101, 115, 116, 0, 0, 0, 1], [('literal', 116), ('literal', 101), ('literal', 115), ('literal', 116), ('max_repeat', (1, 65535, [('literal', 120)]))], 0)
sre_compile._simple((1, 65535, [('literal', 120)]))
sre_compile._compile([16, 14, 1, 5, 0, 4, 4, 116, 101, 115, 116, 0, 0, 0, 1, 18, 116, 18, 101, 18, 115, 18, 116, 28, 0, 1, 65535], [('literal', 120)], 0)
>>> regexp.match('test') # this one is not logged
>>> not not re.match(regexp, 'testxxx') # Convert result to boolean to eliminate memory address
sre.match(<_sre.SRE_Pattern object>, 'testxxx')
sre._compile(<_sre.SRE_Pattern object>, 0)
True
>>> re.match(regexp, 'xxxtestxxx')
sre.match(<_sre.SRE_Pattern object>, 'xxxtestxxx')
sre._compile(<_sre.SRE_Pattern object>, 0)
>>> re.match(regexp, 'test')
sre.match(<_sre.SRE_Pattern object>, 'test')
sre._compile(<_sre.SRE_Pattern object>, 0)
>>> unweave(re, depth=1, all=True)
>>> unweave(sre, depth=1, all=True)
# Sanity check (this one wasn't getting unwrapped, and then showed up later)
>>> sre.string.join(['abc', 'def'], '')
'abcdef'
>>> del regexp, re, sre
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Non-wrapped instance method
# Sanity check to make sure we haven't wrapped things we didn't intend to wrap
>>> test2 = Test()
>>> test2.func3(6)
6
>>> test2.func2.exposed
False
>>> test2.func(2)
2
>>> test2.func.exposed
True
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""
import doctest
import re
from aopython import Aspect,weave,iswrapped,unwrap,unweave
# Example aspects
class LoggerAspect(Aspect):
# A logger aspect that prints method invokation details
def __init__(self, verbose=False, hideAddresses=False):
self.verbose = verbose
self.hideAddresses = hideAddresses
self.memAddrRe = re.compile(r' at 0x[0123456789ABCDEF]+>')
def getArgStr(self, args, kwargs={}):
return ', '.join([self.repr_(arg) for arg in args] + ['%s=%s' % (key, repr(val)) for key, val in kwargs.items()])
def repr_(self, obj):
if self.hideAddresses:
# Remove memory addresses from representation
return self.memAddrRe.sub('>', repr(obj))
return repr(obj)
def printFunctionDetails(self, function, indent=1):
def attrRepr(obj, attrs, level=0):
results = []
for attr in attrs:
results.append(('\t' * (indent + level)) + attr + ' = ' + str(getattr(obj, attr, None)))
return '\n'.join(results)
try:
print '\t%s' % inspect.formatargspec(inspect.getargspec(function))
except:
pass
print '\t' * indent + 'repr =', self.repr_(function)
print '\t' * indent + 'dir =', dir(function)
print '\t' * indent + 'class =', getattr(function, 'im_class', type(function)).__name__
attrs = ('__class__', '__name__', '__module__', '__dict__', 'func_code')
print attrRepr(function, attrs)
if getattr(function, 'func_code', False):
attrs = ('co_name', 'co_argcount', 'co_nlocals', 'co_varnames', 'co_cellvars', 'co_freevars', 'co_consts', 'co_names', 'co_stacksize', 'co_flags')
print attrRepr(function.func_code, attrs, 1)
attrs = ('func_closure', 'func_defaults', 'func_globals', 'func_name') #, 'func_doc'
print attrRepr(function, attrs)
def advise(self, method, *args, **kwargs):
if getattr(method, 'im_class', False):
name = type(args[0]).__name__
elif hasattr(method, "__module__") and method.__module__ is not None:
name = method.__module__
else:
name = '?'
argStr = ', '.join([self.repr_(arg) for arg in args] + ['%s=%s' % (key, self.repr_(val)) for key, val in kwargs.items()])
call = '%s.%s(%s)' % (name, method.__name__, argStr)
print call
if self.verbose:
self.printFunctionDetails(method)
try:
rvalue = method(*args, **kwargs)
print 'exec %s -> %s' % (call, rvalue)
return rvalue
except Exception, e:
print 'exec %s -> %s: %s' % (call, type(e).__name__, e)
raise
else:
return method(*args, **kwargs)
class FunctionResultLoggerAspect(LoggerAspect):
# A logger aspect that can be used to wrap __getattribute__
def advise(self, method, *args, **kwargs):
rvalue = method(*args, **kwargs)
print '%s returned %s' % (method.__name__, rvalue)
if self.verbose:
self.printFunctionDetails(rvalue)
return rvalue
class TestAspect(Aspect):
# Test aspect prints method signature before and after method execution
def advise(self, method, *args, **kwargs):
print 'TestAspect.advise checking attribute %s.exposed = %s' % (method.__name__, getattr(method, 'exposed', '<undefined>'))
return method(*args, **kwargs)
class TestAspect2(Aspect):
def advise(self, method, *args, **kwargs):
newArgs = args[0:-1] + ('TestAspect2 replaced arg',)
try:
print "TestAspect2.advise change last arg from %s to %s" % (repr(args[-1]), repr(newArgs[-1]))
return method(*newArgs, **kwargs)
except Exception, e:
print "TestAspect2.advise fall back to original args due to exception: %s: %s" % (type(e).__name__, e)
return method(*args, **kwargs)
class Test(object):
def __repr__(self):
return '<Test object>'
def __str__(self):
return 'Test'
def func(self, x=1):
return x
def func2(self, x):
return '%s + 100' % x
def func3(self, x):
return x
def _func(self, x):
raise Exception('_func should not be wrapped')
func.exposed = True
func2.exposed = False
def _test():
import doctest, aopythonexamples
return doctest.testmod(aopythonexamples)
if __name__ == "__main__":
_test()
|