Finding defining class in a decorator

Discussion in 'Python' started by lcaamano, May 9, 2006.

  1. lcaamano

    lcaamano Guest

    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:foomethod ...
    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.__module__ = func.__module__
    decorated.__name__ = func.__name__
    decorated.__doc__ = func.__doc__
    decorated.__dict__.update(func.__dict__)
    decorated.__decorator__ = deco_func
    decorated.__decorates__ = 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_decorator)
    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.InstanceType:
    # getattr will find the method anywhere in the
    # class tree so start from the top
    bases = list(inspect.getmro(self.__class__))
    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
     
    lcaamano, May 9, 2006
    #1
    1. Advertising

  2. lcaamano wrote:
    > 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.


    [snipped]

    You might find this helpful:

    import sys

    def tracer(func):
    """
    A decorator that prints the name of the class from which it was
    called.

    The name is determined at class creation time. This works
    only in CPython, since it relies on the sys._getframe()
    function. The assumption is that it can only be called
    from a class statement. The name of the class is deduced
    from the code object name.
    """
    classframe = sys._getframe(1)
    print classframe.f_code.co_name
    return func


    if __name__ == '__main__':

    # this should print Test1

    class Test1(object):

    @tracer
    def spam(self):
    pass

    # this should print Test2

    class Test2(Test1):

    @tracer
    def spam(self):
    pass


    > --
    > Luis P Caamano
    > Atlanta, GA, USA


    Hope this helps,
    Ziga
     
    Ziga Seilnacht, May 9, 2006
    #2
    1. Advertising

  3. lcaamano

    lcaamano Guest

    Nice. I had to call _getframe(2) to account for the wrapper but the
    idea seems to work OK, that is, save the name of the decorator's
    original caller at definition time while creating the wrapper function.
    Much better than doing that at runtime.

    Thanks

    --
    Luis P Caamano
    Atlanta, GA USA
     
    lcaamano, May 9, 2006
    #3
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. johny smith
    Replies:
    8
    Views:
    425
    Peter Koch Larsen
    Jul 2, 2004
  2. Xiangliang Meng
    Replies:
    5
    Views:
    820
    ThosRTanner
    Apr 12, 2005
  3. glomde
    Replies:
    5
    Views:
    540
    glomde
    Mar 29, 2007
  4. Thomas
    Replies:
    4
    Views:
    176
    Thomas
    Jun 7, 2005
  5. Abhas Bhattacharya

    Finding the name of a function while defining it

    Abhas Bhattacharya, Dec 26, 2012, in forum: Python
    Replies:
    28
    Views:
    296
    Jussi Piitulainen
    Dec 29, 2012
Loading...

Share This Page