run-time construction of methods

C

Carlo v. Dango

Hello all.

I am in the need of wrapping certain objects at run-time. My initial
approach was:

import inspect, new
def make_wrapper(obj, methodName):
cls = obj.__class__
wrapmth = getattr(obj, methodName)
print "************ ", methodName
def wrapper(self, *args, **kwargs):
print "**before ", methodName
return wrapmth(*args, **kwargs)
return wrapper

class Person(object):
def __init__(self, age):
super(Person, self).__init__()
self.age = age

def getAge(self):
return self.age

def setAge(self, newage):
"""sets the age of the person"""
self.age = newage

p = Person(33)
setattr(p, "setAge", new.instancemethod(make_wrapper(p,"setAge"), p,
p.__class__))
p.setAge(22)
print "age is ", p.getAge()



However, reflectional information is gone, such as the __doc__ string, and
in an interactive environment, when typing a method call of setAge() the
arguments are specified as "(..., ***)" rather than "(newage)"

I have thus attempted to replace the make_wrapper function with a version
where the inner function "wrapper" is a string which gets translated into
a function with an identical signature as the method to wrap.. my closest
attempt to a real solution is


def make_wrapper(obj, methodName):
cls = obj.__class__
wrapmth = getattr(obj, methodName)
print "************ ", methodName
wrapperstr = """def wrapper(self, *args, **kwargs):
print "**before ", methodName
return wrapmth(*args, **kwargs)"""
exec(wrapperstr, globals(), locals())
return wrapper

but I get the error

NameError: global name 'methodName' is not defined

which I don't know how to deal with... inspecting the locals() a
'methodName' is defined.. Note that my above 'solution' is far from the
product I want, I just figured I needed to get this to work, before
fidling with the signature stuff...


does anyone have an idea on how to approach this?


-Carlo
 
L

Lenard Lindstrom

Carlo v. Dango said:
Hello all.

I am in the need of wrapping certain objects at run-time. My initial
approach was:

import inspect, new
def make_wrapper(obj, methodName):
cls = obj.__class__
wrapmth = getattr(obj, methodName)
print "************ ", methodName
def wrapper(self, *args, **kwargs):
print "**before ", methodName
return wrapmth(*args, **kwargs)
return wrapper

class Person(object):
def __init__(self, age):
super(Person, self).__init__()
self.age = age

def getAge(self):
return self.age

def setAge(self, newage):
"""sets the age of the person"""
self.age = newage

p = Person(33)
setattr(p, "setAge", new.instancemethod(make_wrapper(p,"setAge"), p,
p.__class__))
p.setAge(22)
print "age is ", p.getAge()
I like the approach of using a bound method to p to create a
new instance specific method for p.
...
def make_wrapper(obj, methodName):
cls = obj.__class__
wrapmth = getattr(obj, methodName)
print "************ ", methodName
wrapperstr = """def wrapper(self, *args, **kwargs):
print "**before ", methodName
return wrapmth(*args, **kwargs)"""
exec(wrapperstr, globals(), locals())
return wrapper

but I get the error

NameError: global name 'methodName' is not defined
exec does not create a closure within the function calling it.
That is its downside. So everything is either local to wrapmth,
eg. parameters and variables created within the function, or
global. Fortunately you can still create a closure with exec
by declaring a function within a function. Here's my rewrite.
I hope it works for you.

def make_wrapper(obj, methodName):
cls = obj.__class__
wrapmth = getattr(obj, methodName)
print "********** ", methodName
wrapperstr = """\
def _closure(_wrapmth):
def wrapper(self, *args, **kwds):
"I wrap method %(methodName)s"
print "**before %(methodName)s"
return _wrapmth(*args, **kwds)
return wrapper
_wrapper = _closure(wrapmth)
""" % {'methodName': methodName} # Insert strings directly into code
locs = {'wrapmth': wrapmth} # Keep namespace clean to avoid conflicts
exec(wrapperstr, {}, locs)
return locs['_wrapper'] # and here is the result

See how the argument to _closure is the 'local' you want to keep
around. Also I don't use globals() or locals() directly since
they are cluttered. When you start trying to make the parameters
of wrapper meaningful you will want to minimize the chance
the parameter names conflict with other names within the scope
of the exec. That is also why _closure and _wrapmth begin
with underscores. You might want to choose even more cryptic names.
Also, inlining strings such as methodName instead of passing them
as variables into the exec scope reduces the chance of conflict.

Lenard Lindstrom
<[email protected]>
 

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

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top