We have a tracing decorator that automatically logs enter/exits to/from
functions and methods and it also figures out by itself the function
call arguments values and the class or module the function/method is
defined on. Finding the name of the class where the method we just
entered was defined in is a bit tricky.
Here's a snippet of the test code:
class Base:
@tracelevel(1)
def foomethod(self, x, y=5, **kwds):
return x+y
class TClass(Base):
@tracelevel(1)
def foomethod(self, x, y=1, **kwds):
return Base.foomethod( self, x, **kwds) + x + y
t = TClass()
t.foomethod(4, d=1)
return
The output format is irrelevant at this point but it should be
something like:
TClass:foometho d ...
Base:foomethod ...
correctly showing the name of the class where the entered method was
defined on.
We are also using the proposed decorator function in the future
functools module, which looks like this:
def _update_wrapper (decorated, func, deco_func):
# Support naive introspection
decorated.__mod ule__ = func.__module__
decorated.__nam e__ = func.__name__
decorated.__doc __ = func.__doc__
decorated.__dic t__.update(func .__dict__)
decorated.__dec orator__ = deco_func
decorated.__dec orates__ = func
def decorator(deco_ func):
"""Wrap a function as an introspection friendly decorator
function"""
def wrapper(func):
decorated = deco_func(func)
if decorated is func:
return func
_update_wrapper (decorated, func, deco_func)
return decorated
# Manually make this decorator introspection friendly
_update_wrapper (wrapper, deco_func, functools_decor ator)
return wrapper
In our decorator, the part that figures out the name of the class where
the wrapped method was defined in has to start with the assumption that
the first argument is self and then find the defining class from it.
This code fragment is in the wrapper function of the decorator:
if numargs > 0:
# at definition time, class methods are not methods
# yet because the class doesn't exist when the
# decorators get called and thus, we have to figure
# out classname at runtime via self
#
# assume first arg is self, see if f.__name__ is there
# as a method and if so, then grab it's class name
#
self = args[0]
if type(self) == types.InstanceT ype:
# getattr will find the method anywhere in the
# class tree so start from the top
bases = list(inspect.ge tmro(self.__cla ss__))
bases.reverse()
for c in bases:
# f was given to us in the deco_func
meth = getattr(c, f.__name__, None)
# we found a method with that name, which
# it's probably this same wrapper function
# we used to wrap the original method with.
ofunc = getattr(meth, '__decorates__' , False)
if ofunc and ofunc.func_code == f.func_code:
# got it
clsname = meth.im_class._ _name__
break
Is there a way to do this without the __decorates__ attribute?
--
Luis P Caamano
Atlanta, GA, USA
functions and methods and it also figures out by itself the function
call arguments values and the class or module the function/method is
defined on. Finding the name of the class where the method we just
entered was defined in is a bit tricky.
Here's a snippet of the test code:
class Base:
@tracelevel(1)
def foomethod(self, x, y=5, **kwds):
return x+y
class TClass(Base):
@tracelevel(1)
def foomethod(self, x, y=1, **kwds):
return Base.foomethod( self, x, **kwds) + x + y
t = TClass()
t.foomethod(4, d=1)
return
The output format is irrelevant at this point but it should be
something like:
TClass:foometho d ...
Base:foomethod ...
correctly showing the name of the class where the entered method was
defined on.
We are also using the proposed decorator function in the future
functools module, which looks like this:
def _update_wrapper (decorated, func, deco_func):
# Support naive introspection
decorated.__mod ule__ = func.__module__
decorated.__nam e__ = func.__name__
decorated.__doc __ = func.__doc__
decorated.__dic t__.update(func .__dict__)
decorated.__dec orator__ = deco_func
decorated.__dec orates__ = func
def decorator(deco_ func):
"""Wrap a function as an introspection friendly decorator
function"""
def wrapper(func):
decorated = deco_func(func)
if decorated is func:
return func
_update_wrapper (decorated, func, deco_func)
return decorated
# Manually make this decorator introspection friendly
_update_wrapper (wrapper, deco_func, functools_decor ator)
return wrapper
In our decorator, the part that figures out the name of the class where
the wrapped method was defined in has to start with the assumption that
the first argument is self and then find the defining class from it.
This code fragment is in the wrapper function of the decorator:
if numargs > 0:
# at definition time, class methods are not methods
# yet because the class doesn't exist when the
# decorators get called and thus, we have to figure
# out classname at runtime via self
#
# assume first arg is self, see if f.__name__ is there
# as a method and if so, then grab it's class name
#
self = args[0]
if type(self) == types.InstanceT ype:
# getattr will find the method anywhere in the
# class tree so start from the top
bases = list(inspect.ge tmro(self.__cla ss__))
bases.reverse()
for c in bases:
# f was given to us in the deco_func
meth = getattr(c, f.__name__, None)
# we found a method with that name, which
# it's probably this same wrapper function
# we used to wrap the original method with.
ofunc = getattr(meth, '__decorates__' , False)
if ofunc and ofunc.func_code == f.func_code:
# got it
clsname = meth.im_class._ _name__
break
Is there a way to do this without the __decorates__ attribute?
--
Luis P Caamano
Atlanta, GA, USA
Comment