Alex
Alex

Reputation: 44395

How can I built in a trace ability to python calls?

Suppose I have some python code, e.g. some class defined somewhere, which cannot be modified

class MyClass(object):

    def __init__(self, arg1, arg2):
        do_something...
    def foo(self):
        do_something

Now I want to add a trace capability, e.g. some mechanism from outside that traces each and every method call for the above class. I want to be able to print out when e.g, __init__ has been called, or foo or even the __del__ method of MyClass.

Is this possible to do, and how is this done best?

Upvotes: 3

Views: 299

Answers (4)

Greg
Greg

Reputation: 5588

Get the code for OnlinePythonTutor from github.com/pgbovine/OnlinePythonTutor/tree/master/v3.

You don't need to bother with all the JS stuff. Extract the files into some directory. You can run your scripts using python /path/to/my/OnlinePythonTutor-master/v3/generate_json_trace my_script.py

This will give you basically everything your program is doing in a step by step manner. It will probably be overkill so if you want look into the source code and the underlying source in bdb http://docs.python.org/2/library/bdb.html. The docs for bdb are horrible so I'm having trouble figuring out what exactly is going on but I think this is a pretty cool problem, good luck.

Upvotes: 0

barracel
barracel

Reputation: 1829

You can create a trace decorator and attach it to all the methods of the class instance or class definition as shown in decorate_methods function.

import functools
import inspect
import types

class TestClass(object):
    def func1(self):
        pass

    def func2(self, a, b):
        pass

def trace(func):
    @functools.wraps(func)
    def decorator(*args, **kwargs):
        print "TRACE:", func.__name__, args, kwargs
        return func(*args, **kwargs)
    return decorator

def decorate_methods(obj, decorator):
    for name, func in inspect.getmembers(obj):
        if isinstance(func, types.MethodType):
            setattr(obj, name, decorator(func))

# Apply the decorator to a class instance
test1 = TestClass()
decorate_methods(test1, trace)
test1.func1()
test1.func2('bar1', b='bar2')

# Apply the decorator to the class definition
decorate_methods(TestClass, trace)
test2 = TestClass()
test2.func1()
test2.func2('bar1', b='bar2')

The output of the script will be:

TRACE: func1 () {}
TRACE: func2 ('bar1',) {'b': 'bar2'}
TRACE: func1 (<__main__.TestClass object at 0x7f5a8d888150>,) {}
TRACE: func2 (<__main__.TestClass object at 0x7f5a8d888150>, 'bar1') {'b': 'bar2'}

Upvotes: 2

rajpy
rajpy

Reputation: 2476

Use decorator as shown below:

def call_trace(orig_func):

    def decorated_func(*args, **kwargs):
        print "========>In function: " + orig_func.__name__ + "<========"
        orig_func(*args, **kwargs)
    return decorated_func

Apply this decorator to trace the function. It prints function name before entering the function.

Ex:

@call_trace
def foo(self):
    do_something

Hope it helps.

[Update]: You can use metaclass, only thing you got to change is to add "metaclass" parameter to your class as shown below. As you can see, below code applies "call_trace" decorator to every function in the class "ExBase".

I tried this out yesterday, it worked fine. I am also new to python.:)

def call_trace(orig_func):

    def inner_func(*args, **kwargs):
        print ("function name:" + str(orig_func.__name__))
        orig_func(*args, **kwargs)
    return inner_func

class ExMeta(type):

    def __new__(cls, name, bases, attrs):

        for attr in attrs:
            if hasattr(attrs[attr], '__call__'):
                attrs[attr] = call_trace(attrs[attr])
        return type.__new__(cls, name, bases, attrs)

class ExBase(metaclass=ExMeta):

    x = "x"
    y = "y"
    def __init__(self):
        self.__name = "name"

    def getname(self):
        return self.__name

b = ExBase()
b.getname()

Upvotes: 1

Raymond Hettinger
Raymond Hettinger

Reputation: 226664

Create a proxy class that wraps the original class and then delegates the work after printing a trace:

 class MyClassProxy(object):

      def __init__(*args, **kwds):
           print 'Initializing'
           self.o = MyClass(*args, **kwds)

      def foo(self):
           print 'Fooing'
           return self.o.foo()

Upvotes: 2

Related Questions