Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Decorator for printing every line executed by a function

I want to, for debugging purposes, print out something pertaining to each and every line executed in a python method.

For example if there was some assignment in the line, i want to print what value was assigned for that variable, and if there was a function call, i want to print out the value returned by the function, etc.

So, for example if i were to use a decorator, applied on function/method such as :

@some_decorator
def testing() : 
    a = 10
    b = 20
    c = a + b
    e = test_function()

the function testing when called, should print the following :

a = 10
b = 20  
c = 30
e = some_value

Is there some way to achieve this? More fundamentally, i want to know whether i can write a code that can go through some other code line by line, check what type of an instruction it is, etc. Or maybe like we can get a dictionary for finding out all the variables in a class, can i get a dictionary like datastructure for getting every instruction in a function, which is as good a metaprogram can get.

Hence, I am particularly looking a solution using decorators, as I am curious if one can have a decorator that can go through an entire function line by line, and decorate it line by line, but any and all solutions are welcome.

Thanks in advance.

like image 774
ironstein Avatar asked Aug 23 '15 05:08

ironstein


People also ask

How do I print a decorator in Python?

Decorators in Python are the design pattern that allows the users to add new functionalities to an existing object without the need to modify its structure. Decorators are generally called before defining a function the user wants to decorate.

What is __ wrapped __ in Python?

__wrapped__ in Python decorators As we can see from the code of the functools module 1, when decorating an object, there is an attribute named __wrapped__ that holds the reference to the original one. So now if we use this, we can access it directly without having to resort to the old quirks.

Can decorators return values?

Return values from decorated functions don't get returned by default unless the decorator allows it. In this lesson, you'll see how to get return values out of decorated functions by making a small change to the decorator.

How many decorators can be applied to a function in Python?

Python allows us to implement more than one decorator to a function. It makes decorators useful for reusable building blocks as it accumulates several effects together. It is also known as nested decorators in Python.


1 Answers

How about something like this? Would this work for you?

Debug Context:

import sys

class debug_context():
    """ Debug context to trace any function calls inside the context """

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

    def __enter__(self):
        print('Entering Debug Decorated func')
        # Set the trace function to the trace_calls function
        # So all events are now traced
        sys.settrace(self.trace_calls)

    def __exit__(self, *args, **kwargs):
        # Stop tracing all events
        sys.settrace = None

    def trace_calls(self, frame, event, arg): 
        # We want to only trace our call to the decorated function
        if event != 'call':
            return
        elif frame.f_code.co_name != self.name:
            return
        # return the trace function to use when you go into that 
        # function call
        return self.trace_lines

    def trace_lines(self, frame, event, arg):
        # If you want to print local variables each line
        # keep the check for the event 'line'
        # If you want to print local variables only on return
        # check only for the 'return' event
        if event not in ['line', 'return']:
            return
        co = frame.f_code
        func_name = co.co_name
        line_no = frame.f_lineno
        filename = co.co_filename
        local_vars = frame.f_locals
        print ('  {0} {1} {2} locals: {3}'.format(func_name, 
                                                  event,
                                                  line_no, 
                                                  local_vars))

Debug Decorator:

def debug_decorator(func):
    """ Debug decorator to call the function within the debug context """
    def decorated_func(*args, **kwargs):
        with debug_context(func.__name__):
            return_value = func(*args, **kwargs)
        return return_value
    return decorated_func

Usage

@debug_decorator
def testing() : 
    a = 10
    b = 20
    c = a + b

testing()

Output

###########################################################
#output:
#   Entering Debug Decorated func
#     testing line 44 locals: {}
#     testing line 45 locals: {'a': 10}
#     testing line 46 locals: {'a': 10, 'b': 20}
#     testing return 46 locals: {'a': 10, 'b': 20, 'c': 30}
###########################################################
like image 55
ashwinjv Avatar answered Sep 23 '22 18:09

ashwinjv