By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
455,587 Members | 1,774 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 455,587 IT Pros & Developers. It's quick & easy.

wrapping a method function call?

P: n/a
mh
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 '08 #1
Share this Question
Share on Google+
6 Replies


P: n/a
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.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

Nov 3 '08 #2

P: n/a
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

Nov 3 '08 #3

P: n/a
mh
Steven D'Aprano <st****@remove.this.cybersource.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)(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 '08 #4

P: n/a
On Mon, Nov 3, 2008 at 1:57 AM, <mh@pixar.comwrote:
Steven D'Aprano <st****@remove.this.cybersource.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)(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
Nov 3 '08 #5

P: n/a
On Nov 3, 3:57*am, m...@pixar.com wrote:
Steven D'Aprano <ste...@remove.this.cybersource.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)(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
Nov 3 '08 #6

P: n/a
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.
Nov 4 '08 #7

This discussion thread is closed

Replies have been disabled for this discussion.