wrapping a method function call?

M

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()
 
D

Diez B. Roggisch

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
 
S

Steven D'Aprano

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.
 
M

mh

Steven D'Aprano said:
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
 
C

Chris Rebert

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.


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
 
A

Aaron Brady

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.


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

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
 
G

Goldfish

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")

Change one line, and your interceptor is plugged in:
service = ProxyFactoryComponent(target = SampleService(), interceptors
= [WrappingInterceptor()])
print service.method("something")

Visit the website at http://springpython.webfactional.com, and read
about AOP, along with the other features provided by this library.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top