wrapping a method function call?

Discussion in 'Python' started by mh@pixar.com, Nov 3, 2008.

  1. Guest

    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
     
    , Nov 3, 2008
    #1
    1. Advertising

  2. 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.write("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.foo)


    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__(self, name):
    print "accessing", name
    return getattr(self._delegate, name)


    Diez
     
    Diez B. Roggisch, Nov 3, 2008
    #2
    1. Advertising

  3. 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)(postcall(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
     
    Steven D'Aprano, Nov 3, 2008
    #3
  4. Guest

    Steven D'Aprano <> wrote:
    > 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)(postcall(post)(A.p1))


    Is there a way to do this for all callable methods of A? e.g.

    for x in callable_methods(A):
    x = precall(pre)(postcall(post)(x))

    Thanks!
    Mark

    --
    Mark Harrison
    Pixar Animation Studios
     
    , Nov 3, 2008
    #4
  5. Chris Rebert Guest

    On Mon, Nov 3, 2008 at 1:57 AM, <> wrote:
    > Steven D'Aprano <> wrote:
    >> 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)(postcall(post)(A.p1))

    >
    > Is there a way to do this for all callable methods of A? e.g.
    >
    > for x in callable_methods(A):
    > x = precall(pre)(postcall(post)(x))


    for name, attr in A.__dict__.iteritems():
    if callable(attr):
    A.__dict__[name] = precall(pre)(postcall(post)(attr))

    Cheers,
    Chris
    --
    Follow the path of the Iguana...
    http://rebertia.com

    >
    > Thanks!
    > Mark
    >
    > --
    > Mark Harrison
    > Pixar Animation Studios
    > --
    > http://mail.python.org/mailman/listinfo/python-list
    >
     
    Chris Rebert, Nov 3, 2008
    #5
  6. Aaron Brady Guest

    On Nov 3, 3:57 am, wrote:
    > Steven D'Aprano <> wrote:
    > > 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)(postcall(post)(A.p1))

    >
    > Is there a way to do this for all callable methods of A? e.g.
    >
    >     for x in callable_methods(A):
    >         x = precall(pre)(postcall(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__(mcs, 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
     
    Aaron Brady, Nov 3, 2008
    #6
  7. Goldfish Guest

    Spring Python provides an AOP solution (http://
    springpython.webfactional.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(self):
    return "Okay, I'm doing something"

    You can write a simple interceptor that wraps the results:

    from springpython.aop import *
    class WrappingInterceptor(MethodInterceptor):
    """Interceptor that is called before the real method, and has
    access afterwards to the results
    def invoke(self, invocation):
    print "BEFORE..."
    results = "<Wrapped>" + invocation.proceed() + "</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 = ProxyFactoryComponent(target = SampleService(), interceptors
    = [WrappingInterceptor()])
    print service.method("something")

    >>> "<Wrapped>You 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.
     
    Goldfish, Nov 4, 2008
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. grbgooglefan
    Replies:
    2
    Views:
    433
    Pascal Bourguignon
    Jan 30, 2008
  2. grbgooglefan
    Replies:
    4
    Views:
    449
    Kenny McCormack
    Jan 30, 2008
  3. grbgooglefan
    Replies:
    0
    Views:
    404
    grbgooglefan
    Jan 30, 2008
  4. Alok
    Replies:
    3
    Views:
    258
  5. THAKUR PRASHANT SINGH

    Class Function call vs Normal Function call

    THAKUR PRASHANT SINGH, Feb 26, 2010, in forum: Ruby
    Replies:
    7
    Views:
    197
    THAKUR PRASHANT SINGH
    Feb 27, 2010
Loading...

Share This Page