wrapping all class methods

Carl Banks imbosol at vt.edu
Fri Nov 15 15:40:30 EST 2002


Robin Becker wrote:
> In a partially successful attempt at wrapping all methods in a
> module I used the appended code. It does add a traceback wrapper to
> each method, but the tracebacks don't continue back very far ie I
> typically see something like
> 
> [Application.__getattr__]
> Traceback (most recent call last):
>  File "<string>", line 3, in __getattr__
>  File "C:\Python\devel\anygui\lib\anygui\Attribs.py", line 67, in __getattr__
>    raise AttributeError, name
> AttributeError: wrapper

Can't tell what's going on here.  My best guess is there's a bug in
your exec'd string, and it is trying to call an method that doesn't
exist.


> ie the traceback seems to get stuck in the wrapper method. What's
> the 'proper' pythonic way to do this kind of global wrapping?

Metaclasses.  What else?  :-) Ok, it looks like you want to do this on
the fly, and I don't think you can set a class's type after it's been
defined.  (Maybe you can assign to it's __class__ attribute, but I
recall that assigning to __class__ is being phased out.)


> ##### attempt at wrapping all methods
> n = 0
> from inspect import isclass, ismethod, getmembers
> def makeTBWrap(C,methodname):
>        import new
>        global n
>        oldmethodname = '_%d_tbWrap_%s' % (n,methodname)
>        oldmethod = getattr(C,methodname)
>        S = []
>        s = S.append
>        s('def %s(self,*A,**K):' % methodname)
>        s('\ttry:')
>        s('\t\treturn self.%s(*A,**K)' % oldmethodname)
>        s('\texcept:')
>        s('\t\timport traceback')
>        s('\t\tprint \'[%s.%s]\'' % (C.__name__,methodname))
>        s('\t\ttraceback.print_exc()')
>        s('\t\traise')
>        s('setattr(C,oldmethodname,oldmethod)')
>        s('setattr(C,methodname,new.instancemethod(%s,None,C))' % methodname)
>        exec '\n'.join(S) + '\n' in locals()
>        n += 1

Mega-Ugh.  Using exec for stuff like this is hella-uncool.  That's is
what nested scopes are for:

def makeTBWrap(C,methodname):
        oldmethod = getattr(C,methodname)
	def wrapper(self,*A,**K):
	    try:
	        return oldmethod(self,*A,**K)
	    except:
	        import traceback
		print "[%s.%s]" % (C.__name__,methodname)
		traceback.print_exc()
		raise
	setattr(C,methodname,wrapper)

Maybe you're using a version where there are no nested scopes.  And in
this case, you can't use a callable class, because we're replacing a
method.  You can set C, oldmethod, and methodname as optional
arguments to wrapper, using odd names that you hope will never be
arguments to the method you're wrapping--the risk of variable capture
is probably worth it to avoid having to use exec.

Also, note that you don't have to set the oldmethod as an attribute of
the class to call it.  You can call a method simply by calling it with
the instance as the first argument, as I did.  This is why
class.method(instance,args) is equivalent to instance.method(args).


-- 
CARL BANKS



More information about the Python-list mailing list