proof-of-concept for a simple AOP implementation
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1961 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
		
							parent
							
								
									6a57f72e6c
								
							
						
					
					
						commit
						cce4ec33f7
					
				
					 3 changed files with 141 additions and 0 deletions
				
			
		
							
								
								
									
										76
									
								
								util/aop.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								util/aop.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,76 @@ | ||||||
|  | # | ||||||
|  | #  Copyright (c) 2007 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | $Id$ | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def getNotifier(method): | ||||||
|  |     if isinstance(method, Notifier): | ||||||
|  |         return method | ||||||
|  |     return Notifier(method) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Notifier(object): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, method): | ||||||
|  |         self.method = method | ||||||
|  |         obj = method.im_self or method.im_class | ||||||
|  |         name = method.im_func.func_name | ||||||
|  |         setattr(obj, name, self) | ||||||
|  |         self.beforeSubs = [] | ||||||
|  |         self.afterSubs = [] | ||||||
|  | 
 | ||||||
|  |     def __get__(self, instance, cls=None): | ||||||
|  |         return BoundNotifier(self, instance) | ||||||
|  | 
 | ||||||
|  |     def __call__(self, *args, **kw): | ||||||
|  |         for sub, sargs, skw in self.beforeSubs: | ||||||
|  |             sub(None, *sargs, **skw) | ||||||
|  |         result = self.method(*args, **kw) | ||||||
|  |         for sub, aargs, akw in self.afterSubs: | ||||||
|  |             sub(result, *aargs, **akw) | ||||||
|  |         return result | ||||||
|  | 
 | ||||||
|  |     def subscribe(self, before, after, *args, **kw): | ||||||
|  |         if before is not None: | ||||||
|  |             self.beforeSubs.append((before, args, kw)) | ||||||
|  |         if after is not None: | ||||||
|  |             self.afterSubs.append((after, args, kw)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class BoundNotifier(object): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, context, instance): | ||||||
|  |         self.context = context | ||||||
|  |         self.instance = instance | ||||||
|  | 
 | ||||||
|  |     def __call__(self, *args, **kw): | ||||||
|  |         for sub, sargs, skw in self.context.beforeSubs: | ||||||
|  |             sub(None, *sargs, **skw) | ||||||
|  |         result = self.context.method(self.instance, *args, **kw) | ||||||
|  |         for sub, aargs, akw in self.context.afterSubs: | ||||||
|  |             sub(result, *aargs, **akw) | ||||||
|  |         return result | ||||||
|  | 
 | ||||||
|  |     def subscribe(self, before, after, *args, **kw): | ||||||
|  |         self.context.subscribe(before, after, *args, **kw) | ||||||
|  | 
 | ||||||
							
								
								
									
										64
									
								
								util/aop.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								util/aop.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | ||||||
|  | ======================================= | ||||||
|  | Aspect-oriented Programming for Dummies | ||||||
|  | ======================================= | ||||||
|  | 
 | ||||||
|  | $Id$ | ||||||
|  | 
 | ||||||
|  |   >>> from cybertools.util.aop import getNotifier | ||||||
|  | 
 | ||||||
|  |   >>> class Demo(object): | ||||||
|  |   ...     def foo(self, num): | ||||||
|  |   ...         return 38 + num | ||||||
|  | 
 | ||||||
|  |   >>> demo = Demo() | ||||||
|  |   >>> demo.foo(4) | ||||||
|  |   42 | ||||||
|  | 
 | ||||||
|  |   >>> wrappedFoo = getNotifier(demo.foo) | ||||||
|  |   >>> demo.foo is wrappedFoo | ||||||
|  |   True | ||||||
|  | 
 | ||||||
|  | Repeated calls of ``getNotifier()`` return the same wrapped object. | ||||||
|  | 
 | ||||||
|  |   >>> getNotifier(demo.foo) is wrappedFoo | ||||||
|  |   True | ||||||
|  | 
 | ||||||
|  | Calling the wrapped method still works. | ||||||
|  | 
 | ||||||
|  |   >>> demo.foo(8) | ||||||
|  |   46 | ||||||
|  | 
 | ||||||
|  | We can now get information about the returning of the method by | ||||||
|  | subscribing to it using some log function. | ||||||
|  | 
 | ||||||
|  |   >>> def log(result, msg): | ||||||
|  |   ...     print 'logging: %s, result=%s' % (msg, str(result)) | ||||||
|  | 
 | ||||||
|  |   >>> demo.foo.subscribe(log, None, 'before foo') | ||||||
|  | 
 | ||||||
|  |   >>> demo.foo(4) | ||||||
|  |   logging: before foo, result=None | ||||||
|  |   42 | ||||||
|  | 
 | ||||||
|  |   >>> demo.foo.subscribe(log, log, "that's foo") | ||||||
|  |   >>> demo.foo(4) | ||||||
|  |   logging: before foo, result=None | ||||||
|  |   logging: that's foo, result=None | ||||||
|  |   logging: that's foo, result=42 | ||||||
|  |   42 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Wrapping methods on class level | ||||||
|  | =============================== | ||||||
|  | 
 | ||||||
|  |   >>> demo = Demo() | ||||||
|  |   >>> demo.foo(4) | ||||||
|  |   42 | ||||||
|  | 
 | ||||||
|  |   >>> getNotifier(Demo.foo) | ||||||
|  |   <...Notifier object ...> | ||||||
|  |   >>> Demo.foo.subscribe(None, log, 'after foo') | ||||||
|  |   >>> demo.foo(4) | ||||||
|  |   logging: after foo, result=42 | ||||||
|  |   42 | ||||||
|  | 
 | ||||||
|  | @ -19,6 +19,7 @@ def test_suite(): | ||||||
|         #unittest.makeSuite(Test),  # we don't need this |         #unittest.makeSuite(Test),  # we don't need this | ||||||
|         #doctest.DocTestSuite(cybertools.util.property, optionflags=flags), |         #doctest.DocTestSuite(cybertools.util.property, optionflags=flags), | ||||||
|         doctest.DocFileSuite('adapter.txt', optionflags=flags), |         doctest.DocFileSuite('adapter.txt', optionflags=flags), | ||||||
|  |         doctest.DocFileSuite('aop.txt', optionflags=flags), | ||||||
|         doctest.DocFileSuite('defer.txt', optionflags=flags), |         doctest.DocFileSuite('defer.txt', optionflags=flags), | ||||||
|         doctest.DocFileSuite('format.txt', optionflags=flags), |         doctest.DocFileSuite('format.txt', optionflags=flags), | ||||||
|         doctest.DocFileSuite('property.txt', optionflags=flags), |         doctest.DocFileSuite('property.txt', optionflags=flags), | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 helmutm
						helmutm