I really appreciate your usage of Spring Python AOP, mostly because you exposed two key bugs that I was able to code automated tests for and get fixed this morning.
I can't merge these changes to the trunk yet because I'm waiting on the build team to update the CI server. However, I included a patch you can apply to src/springpython/aop/__init__.py that should get things going. NOTE: This patch fixes both the original problem you found, along with the RegexpMethodPointcut problem you just reported.
Code:
Index: src/springpython/aop/__init__.py
===================================================================
--- src/springpython/aop/__init__.py (revision 356)
+++ src/springpython/aop/__init__.py (working copy)
@@ -16,6 +16,7 @@
NOTE: This module contains parts of PyContainer written by Rafal Sniezynski.
They have been adapted to work outside the container.
"""
+import copy
import logging
import re
import types
@@ -46,7 +47,7 @@
self.method_name = method_name
self.args = args
self.kwargs = kwargs
- self.intercept_stack = interceptors
+ self.intercept_stack = copy.copy(interceptors)
self.intercept_stack.append(FinalInterceptor())
self.logger = logging.getLogger("springpython.aop.MethodInvocation")
@@ -137,7 +138,7 @@
return invocation.proceed()
else:
self.logger.debug("No match, bypassing advice, going straight to targetClass.")
- return getattr(invocation.instance, invocation.method_name)(invocation.args)
+ return getattr(invocation.instance, invocation.method_name)(*invocation.args, **invocation.kwargs)
def __setattr__(self, name, value):
"""If "advice", make sure it is a list. Anything else, pass through to simple assignment.
@@ -178,13 +179,6 @@
self.interceptors = [interceptors]
self.logger = logging.getLogger("springpython.aop.AopProxy")
- def dispatch(self, *args, **kwargs):
- """This method is returned to the caller through __getattr__, to emulate all function calls being sent to the
- target object. This allow this object to serve as a proxying agent for the target object."""
- self.logger.debug("Calling AopProxy.%s(%s)" % (self.method_name, args))
- invocation = MethodInvocation(self.target, self.method_name, args, kwargs, self.interceptors)
- return invocation.__getattr__(self.method_name)(*args, **kwargs)
-
def __getattr__(self, name):
"""If any of the parameters are local objects, they are immediately retrieved. Callables cause the dispatch method
to be return, which forwards callables through the interceptor stack. Target attributes are retrieved directly from
@@ -194,9 +188,20 @@
else:
attr = getattr(self.target, name)
if not callable(attr):
- return attr
- self.method_name = name
- return self.dispatch
+ return attr
+
+ def dispatch(*args, **kwargs):
+ """This method is returned to the caller emulating the function call being sent to the
+ target object. This services as a proxying agent for the target object."""
+ invocation = MethodInvocation(self.target, name, args, kwargs, self.interceptors)
+ ##############################################################################
+ # NOTE:
+ # getattr(invocation, name) doesn't work here, because __str__ will print
+ # the MethodInvocation's string, instead of trigger the interceptor stack.
+ ##############################################################################
+ return invocation.__getattr__(name)(*args, **kwargs)
+
+ return dispatch
class ProxyFactory(object):
"""This object helps to build AopProxy objects programmatically. It allows configuring advice and target objects.
With this patch, I was able to revert some changes to ai4games, so that NOW the only patches I have to it are:
Code:
### Eclipse Workspace Patch 1.0
#P ai4games
Index: tag/brain.py
===================================================================
--- tag/brain.py (revision 428)
+++ tag/brain.py (working copy)
@@ -63,7 +63,7 @@
class BrainWander(Brain):
def calcAction(self):
- vec.normalize(vec.random(self.action.direction), self.action.direction)
+ vec.normalize(vec.randomVec(self.action.direction), self.action.direction)
self.action.speed = util.clamp(util.uniform(), 0.25, 1.0)
class BrainPeriodic(Brain):
and
Code:
### Eclipse Workspace Patch 1.0
#P ai4games
Index: tag/vec.py
===================================================================
--- tag/vec.py (revision 428)
+++ tag/vec.py (working copy)
@@ -44,10 +44,8 @@
return w
def randomVec(u):
- # random.uniform(low=-1.0, high=1.0, size=u.shape, u)
+ return random.random(u.shape)
- return u
-
def zeroize(u):
return set(0.0, u)
and finally
Code:
### Eclipse Workspace Patch 1.0
#P ai4games
Index: tag/tag.py
===================================================================
--- tag/tag.py (revision 428)
+++ tag/tag.py (working copy)
@@ -16,6 +16,11 @@
import pygame
+import logging
+from springpython.aop import RegexpMethodPointcutAdvisor
+from springpython.aop import ProxyFactoryComponent
+from springpython.aop import MethodInterceptor
+
class Constants:
pass
@@ -57,6 +62,12 @@
minPeriod,
maxPeriod)))
+class WrappingInterceptor(MethodInterceptor):
+ def invoke(self, invocation):
+ print "Caught a tag change while calling %s %s %s" % (invocation.method_name, str(invocation.args), invocation.kwargs)
+ results = invocation.proceed()
+ return results
+
def setupCharacters(gs, gui, kb):
rendererNPC = RendererCharacter(Constants.npcColorName, Constants.flashColorName)
rendererPC = RendererCharacter(Constants.pcColorName, Constants.flashColorName)
@@ -71,8 +82,16 @@
brain = BrainPC(percepts, kb)
else:
brain = setupNPCBrain("wander", percepts)
- c = Character(Circle(Constants.radius), brain)
+
+
+ tagAdvisor = WrappingInterceptor()
+ pointcutAdvisor = RegexpMethodPointcutAdvisor(advice = [tagAdvisor],
+ patterns = [".*getPosition"])
+
+ c = ProxyFactoryComponent(target = Character(Circle(Constants.radius), brain),
+ interceptors = pointcutAdvisor)
c.setPosition(vec.randomVec1(gs.worldDim, c.getPosition(), tmp))
+
if i == 0:
c.setRenderer(rendererPC)
else:
@@ -128,6 +147,15 @@
gui.destroyWindow()
if __name__ == "__main__":
+ logger = logging.getLogger("springpython")
+ loggingLevel = logging.INFO
+ logger.setLevel(loggingLevel)
+ ch = logging.StreamHandler()
+ ch.setLevel(loggingLevel)
+ formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
+ ch.setFormatter(formatter)
+ logger.addHandler(ch)
+
main()
# TODO:
Hope that gets you going!
Again, thanks for finding these issues.