1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 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 87 88 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 117 118 119 120 121 122 123 124
|
"""
Support for a tag that allows skipping over functions while debugging.
"""
import linecache
import re
# To suppress tracing a method, add the tag @DontTrace
# to a comment either preceding or on the same line as
# the method definition
#
# E.g.:
# #@DontTrace
# def test1():
# pass
#
# ... or ...
#
# def test2(): #@DontTrace
# pass
DONT_TRACE_TAG = "@DontTrace"
# Regular expression to match a decorator (at the beginning
# of a line).
RE_DECORATOR = re.compile(r"^\s*@")
# Mapping from code object to bool.
# If the key exists, the value is the cached result of should_trace_hook
_filename_to_ignored_lines = {}
def default_should_trace_hook(code, absolute_filename):
"""
Return True if this frame should be traced, False if tracing should be blocked.
"""
# First, check whether this code object has a cached value
ignored_lines = _filename_to_ignored_lines.get(absolute_filename)
if ignored_lines is None:
# Now, look up that line of code and check for a @DontTrace
# preceding or on the same line as the method.
# E.g.:
# #@DontTrace
# def test():
# pass
# ... or ...
# def test(): #@DontTrace
# pass
ignored_lines = {}
lines = linecache.getlines(absolute_filename)
for i_line, line in enumerate(lines):
j = line.find("#")
if j >= 0:
comment = line[j:]
if DONT_TRACE_TAG in comment:
ignored_lines[i_line] = 1
# Note: when it's found in the comment, mark it up and down for the decorator lines found.
k = i_line - 1
while k >= 0:
if RE_DECORATOR.match(lines[k]):
ignored_lines[k] = 1
k -= 1
else:
break
k = i_line + 1
while k <= len(lines):
if RE_DECORATOR.match(lines[k]):
ignored_lines[k] = 1
k += 1
else:
break
_filename_to_ignored_lines[absolute_filename] = ignored_lines
func_line = code.co_firstlineno - 1 # co_firstlineno is 1-based, so -1 is needed
return not (
func_line - 1 in ignored_lines # -1 to get line before method
or func_line in ignored_lines
) # method line
should_trace_hook = None
def clear_trace_filter_cache():
"""
Clear the trace filter cache.
Call this after reloading.
"""
global should_trace_hook
try:
# Need to temporarily disable a hook because otherwise
# _filename_to_ignored_lines.clear() will never complete.
old_hook = should_trace_hook
should_trace_hook = None
# Clear the linecache
linecache.clearcache()
_filename_to_ignored_lines.clear()
finally:
should_trace_hook = old_hook
def trace_filter(mode):
"""
Set the trace filter mode.
mode: Whether to enable the trace hook.
True: Trace filtering on (skipping methods tagged @DontTrace)
False: Trace filtering off (trace methods tagged @DontTrace)
None/default: Toggle trace filtering.
"""
global should_trace_hook
if mode is None:
mode = should_trace_hook is None
if mode:
should_trace_hook = default_should_trace_hook
else:
should_trace_hook = None
return mode
|