gramex.debug

Debugging and profiling tools for Gramex

Timer(msg='', level=logging.WARNING)

Find how long a code blocks takes to execute. Wrap any code block like this:

Examples:

>>> from gramex.debug import Timer
>>> with Timer('optional message'):
>>>     slow_running_code()
WARNING:gramex:1.000s optional message [<file>:<func>:line]
Source code in gramex\debug.py
34
35
36
def __init__(self, msg='', level=logging.WARNING):
    self.msg = msg
    self.level = logging.WARNING

print(args, kwargs)

A replacement for the print function that also logs the (file, function, line, msg) from where it is called. For example:

Examples:

>>> from gramex.debug import print              # import print function
>>> print('hello world')                        # It works like the print function
<file>(line).<function>: hello world
>>> print(x=1, y=2)                             # Use kwargs to print variable names
<file>(line).<function>:
 .. x = 1
 .. y = 2

It automatically pretty-prints complex variables.

Source code in gramex\debug.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
def print(*args, **kwargs):  # noqa
    '''
    A replacement for the `print` function that also logs the (file, function,
    line, msg) from where it is called. For example:

    Examples:
        >>> from gramex.debug import print              # import print function
        >>> print('hello world')                        # It works like the print function
        <file>(line).<function>: hello world
        >>> print(x=1, y=2)                             # Use kwargs to print variable names
        <file>(line).<function>:
         .. x = 1
         .. y = 2

    It automatically pretty-prints complex variables.
    '''
    stream = kwargs.pop('stream', sys.stdout)
    parent = inspect.getouterframes(inspect.currentframe())[1]
    file, line, function = parent[1:4]
    if len(args) == 1 and not kwargs:
        stream.write('{}({}).{}: {}\n'.format(file, line, function, args[0]))
    else:
        stream.write('\n{}({}).{}:\n'.format(file, line, function))
        for val in args:
            _write(val, stream=stream)
        for key, val in kwargs.items():
            _write(val, key, stream=stream)
        stream.write('\n')

trace(trace=True, exclude=None, kwargs)

Decorator to trace line execution. Usage:

@trace()
def method(...):
    ...

When method() is called, every line of execution is traced.

Source code in gramex\debug.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
def trace(trace=True, exclude=None, **kwargs):
    '''
    Decorator to trace line execution. Usage:

    ```python
    @trace()
    def method(...):
        ...
    ```

    When `method()` is called, every line of execution is traced.
    '''
    if exclude is None:
        ignoredirs = (sys.prefix,)
    elif isinstance(exclude, str):
        ignoredirs = (sys.prefix, os.path.abspath(exclude))
    elif isinstance(exclude, (list, tuple)):
        ignoredirs = [sys.prefix] + [os.path.abspath(path) for path in exclude]
    tracer = Trace(trace=trace, ignoredirs=ignoredirs, **kwargs)

    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            return tracer.runfunc(func, *args, **kwargs)

        return wrapper

    return decorator

lineprofile(func)

A decorator that prints the time taken for each line of a function every time it is called. This example prints each line’s performance:

Examples:

>>> from gramex.debug import lineprofile
>>> @lineprofile
>>> def calc():
>>>     ...
Source code in gramex\debug.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def lineprofile(func):
    '''
    A decorator that prints the time taken for each line of a function every
    time it is called. This example prints each line's performance:

    Examples:
        >>> from gramex.debug import lineprofile
        >>> @lineprofile
        >>> def calc():
        >>>     ...
    '''
    try:
        import line_profiler
    except ImportError:
        app_log.warning('@lineprofile requires line_profiler module')
        return func

    profile = line_profiler.LineProfiler(func)

    @functools.wraps(func)
    def wrapper(*args, **kwds):
        profile.enable_by_count()
        try:
            result = func(*args, **kwds)
        finally:
            profile.disable_by_count()
        profile.print_stats(stripzeros=True)
        return result

    return wrapper