wrapping a method function call?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • mh@pixar.com

    wrapping a method function call?

    I am instantiating a class A (which I am importing from somebody
    else, so I can't modify it) into my class X.

    Is there a way I can intercept or wrape calls to methods in A?
    I.e., in the code below can I call

    x.a.p1()

    and get the output

    X.pre
    A.p1
    X.post

    Many TIA!
    Mark


    class A:
    # in my real application, this is an imported class
    # that I cannot modify
    def p1(self): print 'A.p1'

    class X:
    def __init__(self):
    self.a=A()
    def pre(self): print 'X.pre'
    def post(self): print 'X.post'

    x=X()
    x.a.p1()

    --
    Mark Harrison
    Pixar Animation Studios
  • Diez B. Roggisch

    #2
    Re: wrapping a method function call?

    mh@pixar.com schrieb:
    I am instantiating a class A (which I am importing from somebody
    else, so I can't modify it) into my class X.
    >
    Is there a way I can intercept or wrape calls to methods in A?
    I.e., in the code below can I call
    >
    x.a.p1()
    >
    and get the output
    >
    X.pre
    A.p1
    X.post
    >
    Many TIA!
    Mark
    >
    >
    class A:
    # in my real application, this is an imported class
    # that I cannot modify
    def p1(self): print 'A.p1'
    >
    class X:
    def __init__(self):
    self.a=A()
    def pre(self): print 'X.pre'
    def post(self): print 'X.post'
    >
    x=X()
    x.a.p1()
    There are a few ways to accompish this. First of all, Python allows
    monkey-patching. That means you *can* modify it:
    import sys


    class X(object):
    def foo(self):
    print "foo"


    X.foo = lambda self: sys.stdout.writ e("more than foo")

    x = X()
    x.foo()


    You can of course wrap the old foo instead of replacing it, with
    something like

    def foowrapper(old_ foo):

    def _w(self, *args, **kwargs):
    print "wrapped"
    return old_foo(self, *args, **kwargs)

    return _w

    X.foo = foowrapper(X.fo o)


    Alternatively, you can wrap the whole of X into a proxy, based on
    __getattr__ that will allow you do intercept all delegate calls.


    class Wrapper(object) :
    def __init__(self, delegate):
    self._delegate = delegate

    def __getattr__(sel f, name):
    print "accessing" , name
    return getattr(self._d elegate, name)


    Diez



    Comment

    • Steven D'Aprano

      #3
      Re: wrapping a method function call?

      On Mon, 03 Nov 2008 08:17:02 +0000, mh wrote:
      I am instantiating a class A (which I am importing from somebody else,
      so I can't modify it) into my class X.
      >
      Is there a way I can intercept or wrape calls to methods in A? I.e., in
      the code below can I call
      >
      x.a.p1()
      >
      and get the output
      >
      X.pre
      A.p1
      X.post

      Possibly you can do it with some metaclass magic. You might like to
      search for Eiffel-style pre- and post-conditions using a metaclass,
      although I warn you, many people consider metaclasses deep voodoo.

      Here's one way using decorators:

      # Define two decorator factories.
      def precall(pre):
      def decorator(f):
      def newf(*args, **kwargs):
      pre()
      return f(*args, **kwargs)
      return newf
      return decorator

      def postcall(post):
      def decorator(f):
      def newf(*args, **kwargs):
      x = f(*args, **kwargs)
      post()
      return x
      return newf
      return decorator


      Now you can monkey patch class A if you want. It's probably not a great
      idea to do this in production code, as it will effect class A everywhere.

      def pre(): print 'X.pre'
      def post(): print 'X.post'

      from module import A

      A.p1 = precall(pre)(po stcall(post)(A. p1))


      Here's another way:

      class A:
      # in my real application, this is an imported class
      # that I cannot modify
      def p1(self): print 'A.p1'

      class WrapA:
      def __init__(self, ainstance, xinstance):
      self._a = ainstance
      self._x = xinstance
      def p1(self):
      # Delegate calls as needed.
      self._x.pre()
      self._a.p1()
      self._x.post()

      class X:
      def __init__(self):
      self.a = WrapA(A(), self)
      def pre(self):
      print 'X.pre'
      def post(self):
      print 'X.post'


      There are probably many other ways to accomplish the same thing,
      depending on your exact requirements. You should be able to combine the
      decorator technique and the delegation technique to leave class A
      untouched outside of class X, but wrapped inside of X.



      --
      Steven

      Comment

      • mh@pixar.com

        #4
        Re: wrapping a method function call?

        Steven D'Aprano <steven@remove. this.cybersourc e.com.auwrote:
        Now you can monkey patch class A if you want. It's probably not a great
        idea to do this in production code, as it will effect class A everywhere.
        >
        This is perfect for me. The code in question is basically a protocol
        translator... it receives requests over the network, makes some calls,
        and returns the result translated back to the original protocol, so there's
        a single instance of each A,B, etc.
        A.p1 = precall(pre)(po stcall(post)(A. p1))
        Is there a way to do this for all callable methods of A? e.g.

        for x in callable_method s(A):
        x = precall(pre)(po stcall(post)(x) )

        Thanks!
        Mark

        --
        Mark Harrison
        Pixar Animation Studios

        Comment

        • Chris Rebert

          #5
          Re: wrapping a method function call?

          On Mon, Nov 3, 2008 at 1:57 AM, <mh@pixar.comwr ote:
          Steven D'Aprano <steven@remove. this.cybersourc e.com.auwrote:
          >Now you can monkey patch class A if you want. It's probably not a great
          >idea to do this in production code, as it will effect class A everywhere.
          >>
          >
          This is perfect for me. The code in question is basically a protocol
          translator... it receives requests over the network, makes some calls,
          and returns the result translated back to the original protocol, so there's
          a single instance of each A,B, etc.
          >
          >A.p1 = precall(pre)(po stcall(post)(A. p1))
          >
          Is there a way to do this for all callable methods of A? e.g.
          >
          for x in callable_method s(A):
          x = precall(pre)(po stcall(post)(x) )
          for name, attr in A.__dict__.iter items():
          if callable(attr):
          A.__dict__[name] = precall(pre)(po stcall(post)(at tr))

          Cheers,
          Chris
          --
          Follow the path of the Iguana...

          >
          Thanks!
          Mark
          >
          --
          Mark Harrison
          Pixar Animation Studios
          --

          >

          Comment

          • Aaron Brady

            #6
            Re: wrapping a method function call?

            On Nov 3, 3:57 am, m...@pixar.com wrote:
            Steven D'Aprano <ste...@remove. this.cybersourc e.com.auwrote:
            Now you can monkey patch class A if you want. It's probably not a great
            idea to do this in production code, as it will effect class A everywhere.
            >
            This is perfect for me.  The code in question is basically a protocol
            translator... it receives requests over the network, makes some calls,
            and returns the result translated back to the original protocol, so there's
            a single instance of each A,B, etc.
            >
            A.p1 = precall(pre)(po stcall(post)(A. p1))
            >
            Is there a way to do this for all callable methods of A? e.g.
            >
                for x in callable_method s(A):
                    x = precall(pre)(po stcall(post)(x) )
            >
            Thanks!
            Mark
            >
            --
            Mark Harrison
            Pixar Animation Studios
            Hi, that sounds like metaclasses.

            from types import *
            def pre( self, *ar, **kwar ):
            print 'in pre'
            def post( self, *ar, **kwar ):
            print 'in post'
            class metacls(type):
            def __new__(mcs, name, bases, dict):
            for k, x in dict.items():
            if isinstance( x, FunctionType ):
            def modx( f ):
            def _mod( *arg, **kwarg ):
            pre( *arg, **kwarg )
            retval= f( *arg, **kwarg )
            post( *arg, **kwarg )
            return retval
            return _mod
            dict[ k ]= modx( x )
            return type.__new__(mc s, name, bases, dict)

            class A( object ):
            __metaclass__= metacls
            def f( self ):
            print 'in f'

            a= A()
            a.f()

            /Output:

            in pre
            in f
            in post

            Comment

            • Goldfish

              #7
              Re: wrapping a method function call?

              Spring Python provides an AOP solution (http://
              springpython.we bfactional.com/reference/html/aop.html). You can define
              regexp patterns of what you want to intercept.

              Imagine having this service:
              class SampleService:
              def method(self, data):
              return "You sent me '%s'" % data
              def do_something(se lf):
              return "Okay, I'm doing something"

              You can write a simple interceptor that wraps the results:

              from springpython.ao p import *
              class WrappingInterce ptor(MethodInte rceptor):
              """Intercep tor that is called before the real method, and has
              access afterwards to the results
              def invoke(self, invocation):
              print "BEFORE..."
              results = "<Wrapped>" + invocation.proc eed() + "</Wrapped>"
              print "AFTER...."
              return results

              Simply creating an instance of your base class acts as you would
              expect:
              service = SampleService()
              print service.method( "something" )
              >>"You sent me 'something'"
              Change one line, and your interceptor is plugged in:
              service = ProxyFactoryCom ponent(target = SampleService() , interceptors
              = [WrappingInterce ptor()])
              print service.method( "something" )
              >>"<Wrapped>Y ou sent me 'something'</Wrapped>"
              Visit the website at http://springpython.webfactional.com, and read
              about AOP, along with the other features provided by this library.

              Comment

              Working...