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

Wrapping method calls with metaclasses

P: n/a
I've never used metaclasses in real life before and while searching through
the online Cookbook I found this gorgeous example:

"Wrapping method calls (meta-class example)"
http://aspn.activestate.com/ASPN/Coo.../Recipe/198078

What I want to do in my app is to log all method calls and seems that the
metaclass example above do this fine but it fails when it's needed to
instantiate the class I'm in in a method. So if I do:

class Test(object):
__metaclass__ = LogTheMethods
def foo(self):
return Test()

this generate a Runtime Error cause recursion never stops :(
Any hint to enhance the example?
--
Lawrence - http://www.oluyede.org/blog
"Anyone can freely use whatever he wants but the light at the end
of the tunnel for most of his problems is Python"
Dec 6 '05 #1
Share this Question
Share on Google+
4 Replies


P: n/a
Lawrence Oluyede <ra***@dot.com> wrote:
I've never used metaclasses in real life before and while searching through
the online Cookbook I found this gorgeous example:

"Wrapping method calls (meta-class example)"
http://aspn.activestate.com/ASPN/Coo.../Recipe/198078

What I want to do in my app is to log all method calls and seems that the
metaclass example above do this fine but it fails when it's needed to
instantiate the class I'm in in a method. So if I do:

class Test(object):
__metaclass__ = LogTheMethods
def foo(self):
return Test()

this generate a Runtime Error cause recursion never stops :(
Any hint to enhance the example?


I can't reproduce the infinite recursion you observe, by merging said
recipe and your definition of class Test, w/Python 2.4.2 on Mac OS 10.4.

Can you please supply a minimal complete example, e.g. simplifying
function logthemethod to just emit s/thing to stdout etc, that does
exhibit the runaway recursion problem you observe?
Alex
Dec 7 '05 #2

P: n/a
Il 2005-12-07, Alex Martelli <al***@mail.comcast.net> ha scritto:
I can't reproduce the infinite recursion you observe, by merging said
recipe and your definition of class Test, w/Python 2.4.2 on Mac OS 10.4.


It seemed that the problem arose because I was not using super() in a new
style class based hierarchy.

class Test(object):
__metaclass__ = LogTheMethods
logMatch = '.*'

def __init__(self, foo):
self.foo = foo

def test(self):
return "test"

class TestChild(Test):
def __init__(self):
# with super this works
Test.__init__(self, "foo")

def test(self):
return "child"

d = {'test': Test,
'child': TestChild}

class MainTest(object):
__metaclass__ = LogTheMethods
logMatch = '.*'

def create_test(self, key):
return d[key]()

if __name__ == '__main__':
l = MainTest()
print l.create_test("child").test()
look in TestChild's __init__(). Not using super() fails with a

"""
File "/home/rhymes/downloads/simple_logger.py", line 55, in __init__
Test.__init__(self, "foo")
File "/home/rhymes/downloads/simple_logger.py", line 24, in _method
returnval = getattr(self,'_H_%s' % methodname)(*argl,**argd)
TypeError: __init__() takes exactly 1 argument (2 given)
"""

In my project I'm doing deeply nested recursion things and it exceeds the
maximum recursion limit for this problem this way.

I don't get to fully work the metaclass anyway for other weird reasons
(using super() I lose an attribute around?!) I'm gonna fix this.
--
Lawrence - http://www.oluyede.org/blog
"Anyone can freely use whatever he wants but the light at the end
of the tunnel for most of his problems is Python"
Dec 7 '05 #3

P: n/a
Il 2005-12-07, Lawrence Oluyede <ra***@dot.com> ha scritto:
I don't get to fully work the metaclass anyway for other weird reasons
(using super() I lose an attribute around?!) I'm gonna fix this.


It was a bug of one of the super() in the chain.
Still suffer from deep recursion limit error with that metaclass.
Keep trying.
--
Lawrence - http://www.oluyede.org/blog
"Anyone can freely use whatever he wants but the light at the end
of the tunnel for most of his problems is Python"
Dec 7 '05 #4

P: n/a
Lawrence Oluyede wrote:
look in TestChild's __init__(). Not using super() fails with a

"""
File*"/home/rhymes/downloads/simple_logger.py",*line*55,*in*__init__
Test.__init__(self,*"foo")
File*"/home/rhymes/downloads/simple_logger.py",*line*24,*in*_method
returnval*=*getattr(self,'_H_%s'*%*methodname)(*ar gl,**argd)
TypeError: __init__() takes exactly 1 argument (2 given)
"""


The problem with the recipe seems to be that the mangled _H_XXX() method is
always looked up in the child class. One fix may be to store the original
method in the wrapper instead of the class:

def logmethod(methodname, method):
def _method(self,*argl,**argd):
global indent

#parse the arguments and create a string representation
args = []
for item in argl:
args.append('%s' % str(item))
for key,item in argd.items():
args.append('%s=%s' % (key,str(item)))
argstr = ','.join(args)
print >> log,"%s%s.%s(%s) " %
(indStr*indent,str(self),methodname,argstr)
indent += 1
# do the actual method call
returnval = method(self, *argl,**argd)
indent -= 1
print >> log,'%s:'% (indStr*indent), str(returnval)
return returnval

return _method
class LogTheMethods(type):
def __new__(cls,classname,bases,classdict):
logmatch = re.compile(classdict.get('logMatch','.*'))

for attr,item in classdict.items():
if callable(item) and logmatch.match(attr):
classdict[attr] = logmethod(attr, item) # replace method by
wrapper

return type.__new__(cls,classname,bases,classdict)

Be warned that I did not thoroughly test that.

Peter

Dec 7 '05 #5

This discussion thread is closed

Replies have been disabled for this discussion.