SX Spring Python
  1. SX Spring Python
  2. SESPRINGPYTHONPY-134

AOPProxy throws an error unless target.__class__.__name__ == "instance"

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Complete
    • Affects Version/s: 1.0.0.GA
    • Fix Version/s: 1.2.0.RC1, 1.1.1
    • Component/s: AOP
    • Labels:
      None
    • Environment:
      python 2.6 linux 32 bit virtual environment

      Description

      see this code...

      #
      from springpython.config import PythonConfig, Object as SpringObject
      from springpython.context import scope
      from springpython.context import ApplicationContext
      from springpython.aop import MethodInterceptor, ProxyFactory

      class WrappingInterceptor(MethodInterceptor):

      def invoke(self, invocation):
      return "<wrapped>" + invocation.proceed() + "</wrapped>"

      class BogusLister(object):

      def _getitem_(self, key):
      return key

      def fuckingDuh(self):
      return "Fucking Duh"

      class BogusFinder(object):

      def _init_(self, filename):
      self._filename = filename

      class BogusApplicationContext(PythonConfig):

      def _init_(self):
      super(BogusApplicationContext, self)._init_()

      @SpringObject(scope.PROTOTYPE)
      def BogusLister(self):
      factory = self.aopFactory()
      lister = BogusLister()
      lister.finder = self.BogusFinder()
      lister.description = self.SingletonString()
      self.logger.debug("Description=%s" % lister.description)
      factory.target=lister
      factory.target._class.name_ = "instance" #wtf?
      return factory.getProxy()

      @SpringObject(scope.SINGLETON)
      def BogusFinder(self):
      return BogusFinder(filename = "movies.txt")

      @SpringObject(scope.SINGLETON)
      def SingletonString(self):
      return "this is the only string"

      @SpringObject(scope.PROTOTYPE)
      def aopFactory(self):
      factory = ProxyFactory()
      factory.interceptors.append(WrappingInterceptor())
      return factory

      context = ApplicationContext(BogusApplicationContext())

      #---------offending source springpython/aop/_init_/py----------#
      169 def _init_(self, target, interceptors):
      170 if type(target)._name_ != "instance":
      171 -> raise Exception("Target attribute must be an instance.")

      i can't imagine why this conditional is necessary, but I'm not an aop expert either.

        Activity

        Hide
        Tom Willis added a comment -

        Perhaps this example would better illustrate the problem. What you'll notice is that the difference between SampleService and SampleServiceNew is that SampleService is regarded as an classic class and SampleServiceNew is regarded as a new-style class. It is a very subtle distinction I will admit.

        http://docs.python.org/reference/datamodel.html#new-style-and-classic-classes

        class MyClass: # classic class == tolerated
        pass

        class MyClass(object): # new-style class == good
        pass

        The problem is, AOPProxy is hard coded to only work with classic classes where I believe that those are discouraged in favor of new-style classes, nonetheless
        http://springpython.webfactional.com/1.1.x/reference/html/aop.html makes no mention of the this limitation or implied recommendation to favor old-style classes.

        Below is the entire output from my console starting with creating a fresh virtualenv so as to eliminate the possibility of other dependencies interfering. You may notice that easy_install (which will be how some will find/try springpython) install 1.1.0M2 maybe that's another bug.

        I took gregs example pastebin and added a definition for SampleServiceNew which is the same as SampleService except that it is defined as a new-style class, other than that they are handled identically and the AOPProxy works on the classic class, but not the new-style class. Hopefully this removes any doubt as to what the problem is.

        twillis@twillis-vm:~$ virtualenv --no-site-packages springTst.env
        Using real prefix '/usr'
        New python executable in springTst.env/bin/python
        Installing setuptools............done.
        twillis@twillis-vm:~$ cd springTst.env/
        twillis@twillis-vm:~/springTst.env$ source bin/activate
        (springTst.env)twillis@twillis-vm:~/springTst.env$ easy_install springpython
        Searching for springpython
        Reading http://pypi.python.org/simple/springpython/
        Reading http://springpython.webfactional.com
        Reading http://www.springsource.com/download/community?project=Spring%20Extensions
        Reading http://s3browse.com/explore/dist.springframework.org/release/EXT/se-springpython-py/
        Best match: springpython 1.1.0.M2
        Downloading http://s3.amazonaws.com/dist.springframework.org/milestone/EXTPY/springpython-1.1.0.M2.tar.gz
        Processing springpython-1.1.0.M2.tar.gz
        Running springpython-1.1.0.M2/setup.py -q bdist_egg --dist-dir /tmp/easy_install-C7QTPL/springpython-1.1.0.M2/egg-dist-tmp-bwoHU3
        zip_safe flag not set; analyzing archive contents...
        springpython.security._init: module references __file_
        Adding springpython 1.1.0.M2 to easy-install.pth file
        Installing coily script to /home/twillis/springTst.env/bin

        Installed /home/twillis/springTst.env/lib/python2.6/site-packages/springpython-1.1.0.M2-py2.6.egg
        Processing dependencies for springpython
        Finished processing dependencies for springpython
        (springTst.env)twillis@twillis-vm:~/springTst.env$ python
        Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56)
        [GCC 4.4.3] on linux2
        Type "help", "copyright", "credits" or "license" for more information.
        >>> import springpython
        >>> class SampleService:
        ... def method(self, data):
        ... return "You sent me %s" % str(data)
        ... def doSomething(self):
        ... return "Okay, I'm doing something"
        ...
        >>> class SampleServiceNew(object):
        ... def method(self, data):
        ... return "You sent me %s" % str(data)
        ... def doSomething(self):
        ... return "Okay, I'm doing something"
        ...
        >>> service = SampleService()
        >>> service.method("xxx")
        'You sent me xxx'
        >>> service.doSomething()
        "Okay, I'm doing something"
        >>> service2= SampleServiceNew()
        >>> service2.method("xxx")
        'You sent me xxx'
        >>> service2.doSomething()
        "Okay, I'm doing something"
        >>> from springpython.aop import *
        >>> class WrappingInterceptor(MethodInterceptor):
        ... def invoke(self, invocation):
        ... results = "<w>%s</w>" % str(invocation.proceed())
        ... return results
        ...
        >>> factory = ProxyFactory()
        >>> factory.target = SampleService()
        >>> factory.interceptors.append(WrappingInterceptor())
        >>> service=factory.getProxy()
        >>> service.method("xxx")
        '<w>You sent me xxx</w>'
        >>> service.doSomething()
        "<w>Okay, I'm doing something</w>"
        >>> factory.ProxyFactory()
        Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        AttributeError: 'ProxyFactory' object has no attribute 'ProxyFactory'
        >>> factory=ProxyFactory()
        >>> factory.target = SampleServiceNew()
        >>> factory.interceptors.append(WrappingInterceptor())
        >>> service2 = factory.getProxy()
        Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        File "/home/twillis/springTst.env/lib/python2.6/site-packages/springpython-1.1.0.M2-py2.6.egg/springpython/aop/_init_.py", line 220, in getProxy
        return AopProxy(self.target, self.interceptors)
        File "/home/twillis/springTst.env/lib/python2.6/site-packages/springpython-1.1.0.M2-py2.6.egg/springpython/aop/_init.py", line 171, in __init_
        raise Exception("Target attribute must be an instance.")
        Exception: Target attribute must be an instance.
        >>>

        Show
        Tom Willis added a comment - Perhaps this example would better illustrate the problem. What you'll notice is that the difference between SampleService and SampleServiceNew is that SampleService is regarded as an classic class and SampleServiceNew is regarded as a new-style class. It is a very subtle distinction I will admit. http://docs.python.org/reference/datamodel.html#new-style-and-classic-classes class MyClass: # classic class == tolerated pass class MyClass(object): # new-style class == good pass The problem is, AOPProxy is hard coded to only work with classic classes where I believe that those are discouraged in favor of new-style classes, nonetheless http://springpython.webfactional.com/1.1.x/reference/html/aop.html makes no mention of the this limitation or implied recommendation to favor old-style classes. Below is the entire output from my console starting with creating a fresh virtualenv so as to eliminate the possibility of other dependencies interfering. You may notice that easy_install (which will be how some will find/try springpython) install 1.1.0M2 maybe that's another bug. I took gregs example pastebin and added a definition for SampleServiceNew which is the same as SampleService except that it is defined as a new-style class, other than that they are handled identically and the AOPProxy works on the classic class, but not the new-style class. Hopefully this removes any doubt as to what the problem is. twillis@twillis-vm:~$ virtualenv --no-site-packages springTst.env Using real prefix '/usr' New python executable in springTst.env/bin/python Installing setuptools............done. twillis@twillis-vm:~$ cd springTst.env/ twillis@twillis-vm:~/springTst.env$ source bin/activate (springTst.env)twillis@twillis-vm:~/springTst.env$ easy_install springpython Searching for springpython Reading http://pypi.python.org/simple/springpython/ Reading http://springpython.webfactional.com Reading http://www.springsource.com/download/community?project=Spring%20Extensions Reading http://s3browse.com/explore/dist.springframework.org/release/EXT/se-springpython-py/ Best match: springpython 1.1.0.M2 Downloading http://s3.amazonaws.com/dist.springframework.org/milestone/EXTPY/springpython-1.1.0.M2.tar.gz Processing springpython-1.1.0.M2.tar.gz Running springpython-1.1.0.M2/setup.py -q bdist_egg --dist-dir /tmp/easy_install-C7QTPL/springpython-1.1.0.M2/egg-dist-tmp-bwoHU3 zip_safe flag not set; analyzing archive contents... springpython.security._ init : module references __file _ Adding springpython 1.1.0.M2 to easy-install.pth file Installing coily script to /home/twillis/springTst.env/bin Installed /home/twillis/springTst.env/lib/python2.6/site-packages/springpython-1.1.0.M2-py2.6.egg Processing dependencies for springpython Finished processing dependencies for springpython (springTst.env)twillis@twillis-vm:~/springTst.env$ python Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56) [GCC 4.4.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import springpython >>> class SampleService: ... def method(self, data): ... return "You sent me %s" % str(data) ... def doSomething(self): ... return "Okay, I'm doing something" ... >>> class SampleServiceNew(object): ... def method(self, data): ... return "You sent me %s" % str(data) ... def doSomething(self): ... return "Okay, I'm doing something" ... >>> service = SampleService() >>> service.method("xxx") 'You sent me xxx' >>> service.doSomething() "Okay, I'm doing something" >>> service2= SampleServiceNew() >>> service2.method("xxx") 'You sent me xxx' >>> service2.doSomething() "Okay, I'm doing something" >>> from springpython.aop import * >>> class WrappingInterceptor(MethodInterceptor): ... def invoke(self, invocation): ... results = "<w>%s</w>" % str(invocation.proceed()) ... return results ... >>> factory = ProxyFactory() >>> factory.target = SampleService() >>> factory.interceptors.append(WrappingInterceptor()) >>> service=factory.getProxy() >>> service.method("xxx") '<w>You sent me xxx</w>' >>> service.doSomething() "<w>Okay, I'm doing something</w>" >>> factory.ProxyFactory() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'ProxyFactory' object has no attribute 'ProxyFactory' >>> factory=ProxyFactory() >>> factory.target = SampleServiceNew() >>> factory.interceptors.append(WrappingInterceptor()) >>> service2 = factory.getProxy() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/twillis/springTst.env/lib/python2.6/site-packages/springpython-1.1.0.M2-py2.6.egg/springpython/aop/_ init _.py", line 220, in getProxy return AopProxy(self.target, self.interceptors) File "/home/twillis/springTst.env/lib/python2.6/site-packages/springpython-1.1.0.M2-py2.6.egg/springpython/aop/_ init .py", line 171, in __init _ raise Exception("Target attribute must be an instance.") Exception: Target attribute must be an instance. >>>
        Hide
        Greg Turnquist added a comment -

        Tom, that does look bad!

        1) I repeated your steps of creating a fresh virtualenv and using easy_install. I don't understand why it doesn't grab the 1.1.0.FINAL. I did what you did, and got 1.1.0.M2! I'm attaching some screenshots to show what is published at pypi.

        2) Tom, you did a great job repeating your problem. Spring Python should definitely not be puking on something like classic vs. new style classes. This needs fixing and probably backporting to previous releases. I just inspected the automated tests, and sure enough, SampleService is a classic class. I'm coding another automated test to expose this bug.

        Show
        Greg Turnquist added a comment - Tom, that does look bad! 1) I repeated your steps of creating a fresh virtualenv and using easy_install. I don't understand why it doesn't grab the 1.1.0.FINAL. I did what you did, and got 1.1.0.M2! I'm attaching some screenshots to show what is published at pypi. 2) Tom, you did a great job repeating your problem. Spring Python should definitely not be puking on something like classic vs. new style classes. This needs fixing and probably backporting to previous releases. I just inspected the automated tests, and sure enough, SampleService is a classic class. I'm coding another automated test to expose this bug.
        Hide
        Greg Turnquist added a comment - - edited

        Okay, I just created an automated test to reproduce this error:

        (sp)gturnquist-mbp:spring-python gturnquist$ python build.py --clean --test
        Reading property file springpython.properties
        Removing 'target' directory
        Running checkin tests...
        .2010-05-20 09:39:26,932 - springpython.context.ApplicationContext - ERROR - Object 'foo_root1' has no definition!
        2010-05-20 09:39:26,933 - springpython.context.ApplicationContext - ERROR - Object 'foo_root2' has no definition!
        2010-05-20 09:39:26,933 - springpython.context.ApplicationContext - ERROR - Object 'foo_root1' has no definition!
        2010-05-20 09:39:26,934 - springpython.context.ApplicationContext - ERROR - Object 'foo_root2' has no definition!
        .......................E.......................................................2010-05-20 09:39:27,290 - springpython.security.web.HttpSessionContextIntegrationFilter - WARNING - SPRINGPYTHON_SECURITY_CONTEXT_KEY did not contain a SecurityContext but contained: 'Bad credentials''; are you improperly modifying the HttpSession directly (you should always use SecurityContextHolder) or using the HttpSession attribute reserved for this class? - new SecurityContext instance associated with SecurityContextHolder
        ............................................................................................................................................2010-05-20 09:39:28,838 - springpython.config.YamlConfig - WARNING - No matching type found for object

        {'object': 'MyObject'}

        /Users/gturnquist/src/spring-python/test/springpythontest/contextTestCases.py:1041: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
        self.assertEqual(e.message, "class")
        ...
        ======================================================================
        ERROR: testCreatingAopProxyFactoryAndAddingInterceptorToNewStyleClassProgammatically (checkin.AopProxyTestCase)
        ----------------------------------------------------------------------
        Traceback (most recent call last):
        File "/Users/gturnquist/src/spring-python/test/springpythontest/aopTestCases.py", line 63, in testCreatingAopProxyFactoryAndAddingInterceptorToNewStyleClassProgammatically
        service = factory.getProxy()
        File "/Users/gturnquist/src/spring-python/src/springpython/aop/_init_.py", line 220, in getProxy
        return AopProxy(self.target, self.interceptors)
        File "/Users/gturnquist/src/spring-python/src/springpython/aop/_init.py", line 171, in __init_
        raise Exception("Target attribute must be an instance.")
        Exception: Target attribute must be an instance.

        ----------------------------------------------------------------------
        Ran 223 tests in 2.012s

        Show
        Greg Turnquist added a comment - - edited Okay, I just created an automated test to reproduce this error: (sp)gturnquist-mbp:spring-python gturnquist$ python build.py --clean --test Reading property file springpython.properties Removing 'target' directory Running checkin tests... .2010-05-20 09:39:26,932 - springpython.context.ApplicationContext - ERROR - Object 'foo_root1' has no definition! 2010-05-20 09:39:26,933 - springpython.context.ApplicationContext - ERROR - Object 'foo_root2' has no definition! 2010-05-20 09:39:26,933 - springpython.context.ApplicationContext - ERROR - Object 'foo_root1' has no definition! 2010-05-20 09:39:26,934 - springpython.context.ApplicationContext - ERROR - Object 'foo_root2' has no definition! .......................E.......................................................2010-05-20 09:39:27,290 - springpython.security.web.HttpSessionContextIntegrationFilter - WARNING - SPRINGPYTHON_SECURITY_CONTEXT_KEY did not contain a SecurityContext but contained: 'Bad credentials''; are you improperly modifying the HttpSession directly (you should always use SecurityContextHolder) or using the HttpSession attribute reserved for this class? - new SecurityContext instance associated with SecurityContextHolder ............................................................................................................................................2010-05-20 09:39:28,838 - springpython.config.YamlConfig - WARNING - No matching type found for object {'object': 'MyObject'} /Users/gturnquist/src/spring-python/test/springpythontest/contextTestCases.py:1041: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6 self.assertEqual(e.message, "class") ... ====================================================================== ERROR: testCreatingAopProxyFactoryAndAddingInterceptorToNewStyleClassProgammatically (checkin.AopProxyTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/gturnquist/src/spring-python/test/springpythontest/aopTestCases.py", line 63, in testCreatingAopProxyFactoryAndAddingInterceptorToNewStyleClassProgammatically service = factory.getProxy() File "/Users/gturnquist/src/spring-python/src/springpython/aop/_ init _.py", line 220, in getProxy return AopProxy(self.target, self.interceptors) File "/Users/gturnquist/src/spring-python/src/springpython/aop/_ init .py", line 171, in __init _ raise Exception("Target attribute must be an instance.") Exception: Target attribute must be an instance. ---------------------------------------------------------------------- Ran 223 tests in 2.012s
        Hide
        Greg Turnquist added a comment -

        Okay, I have now coded a solution. This is the output, with some extra debug stuff dumping out the type.

        (sp)gturnquist-mbp:spring-python gturnquist$ python
        Python 2.6.1 (r261:67515, Feb 11 2010, 00:51:29)
        [GCC 4.2.1 (Apple Inc. build 5646)] on darwin
        Type "help", "copyright", "credits" or "license" for more information.
        >>> import springpython
        >>> from springpython.aop import *
        >>> class SampleService:
        ... def doSomething(self):
        ... return "Hey!"
        ...
        >>> class NewSampleService(object):
        ... def doSomething(self):
        ... return "Hey in a new way!"
        ...
        >>> factory = ProxyFactory()
        >>> factory.target = SampleService()
        >>> class WrappingInterceptor(MethodInterceptor):
        ... def invoke(self, invocation):
        ... return "[Wrapped]" + invocation.proceed() + "[/Wrapped]"
        ...
        >>> factory.interceptors = [WrappingInterceptor()]
        >>> factory.getProxy().doSomething()
        The type of the target is instance
        '[Wrapped]Hey![/Wrapped]'
        >>> factory2 = ProxyFactory()
        >>> factory2.target = NewSampleService()
        >>> factory2.interceptors = [WrappingInterceptor()]
        >>> factory2.getProxy().doSomething()
        The type of the target is NewSampleService
        '[Wrapped]Hey in a new way![/Wrapped]'
        >>> factory2.getProxy().doSomething()
        The type of the target is NewSampleService
        '[Wrapped]Hey in a new way![/Wrapped]'
        >>> ^D

        Show
        Greg Turnquist added a comment - Okay, I have now coded a solution. This is the output, with some extra debug stuff dumping out the type. (sp)gturnquist-mbp:spring-python gturnquist$ python Python 2.6.1 (r261:67515, Feb 11 2010, 00:51:29) [GCC 4.2.1 (Apple Inc. build 5646)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import springpython >>> from springpython.aop import * >>> class SampleService: ... def doSomething(self): ... return "Hey!" ... >>> class NewSampleService(object): ... def doSomething(self): ... return "Hey in a new way!" ... >>> factory = ProxyFactory() >>> factory.target = SampleService() >>> class WrappingInterceptor(MethodInterceptor): ... def invoke(self, invocation): ... return " [Wrapped] " + invocation.proceed() + " [/Wrapped] " ... >>> factory.interceptors = [WrappingInterceptor()] >>> factory.getProxy().doSomething() The type of the target is instance ' [Wrapped] Hey! [/Wrapped] ' >>> factory2 = ProxyFactory() >>> factory2.target = NewSampleService() >>> factory2.interceptors = [WrappingInterceptor()] >>> factory2.getProxy().doSomething() The type of the target is NewSampleService ' [Wrapped] Hey in a new way! [/Wrapped] ' >>> factory2.getProxy().doSomething() The type of the target is NewSampleService ' [Wrapped] Hey in a new way! [/Wrapped] ' >>> ^D
        Hide
        Greg Turnquist added a comment - - edited

        To be complete, I also tested this with ProxyFactoryObject, to make sure it also was acting correctly with old and new style classes.

        Python 2.6.1 (r261:67515, Feb 11 2010, 00:51:29)
        [GCC 4.2.1 (Apple Inc. build 5646)] on darwin
        Type "help", "copyright", "credits" or "license" for more information.
        >>> from springpython.aop import *
        >>> class SampleService:
        ... def doSomething(self):
        ... return "Alright!"
        ...
        >>> class NewSampleService(object):
        ... def doSomething(self):
        ... return "Even better!"
        ...
        >>> class WrappingInterceptor(MethodInterceptor):
        ... def invoke(self, invocation):
        ... return "<Wrapped>" + invocation.proceed() + "</Wrapped>"
        ...
        >>> proxy = ProxyFactoryObject()
        >>> proxy.target = SampleService()
        >>> proxy.interceptors = [WrappingInterceptor()]
        >>> proxy.doSomething()
        '<Wrapped>Alright!</Wrapped>'
        >>> proxy2 = ProxyFactoryObject()
        >>> proxy2.target = NewSampleService()
        >>> proxy2.interceptors = [WrappingInterceptor()]
        >>> proxy2.doSomething()
        '<Wrapped>Even better!</Wrapped>'

        Show
        Greg Turnquist added a comment - - edited To be complete, I also tested this with ProxyFactoryObject, to make sure it also was acting correctly with old and new style classes. Python 2.6.1 (r261:67515, Feb 11 2010, 00:51:29) [GCC 4.2.1 (Apple Inc. build 5646)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> from springpython.aop import * >>> class SampleService: ... def doSomething(self): ... return "Alright!" ... >>> class NewSampleService(object): ... def doSomething(self): ... return "Even better!" ... >>> class WrappingInterceptor(MethodInterceptor): ... def invoke(self, invocation): ... return "<Wrapped>" + invocation.proceed() + "</Wrapped>" ... >>> proxy = ProxyFactoryObject() >>> proxy.target = SampleService() >>> proxy.interceptors = [WrappingInterceptor()] >>> proxy.doSomething() '<Wrapped>Alright!</Wrapped>' >>> proxy2 = ProxyFactoryObject() >>> proxy2.target = NewSampleService() >>> proxy2.interceptors = [WrappingInterceptor()] >>> proxy2.doSomething() '<Wrapped>Even better!</Wrapped>'

          People

          • Assignee:
            Greg Turnquist
            Reporter:
            Tom Willis
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: