summaryrefslogtreecommitdiff
path: root/python/helpers/pydev/pydevd_dont_trace.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/helpers/pydev/pydevd_dont_trace.py')
-rw-r--r--python/helpers/pydev/pydevd_dont_trace.py127
1 files changed, 127 insertions, 0 deletions
diff --git a/python/helpers/pydev/pydevd_dont_trace.py b/python/helpers/pydev/pydevd_dont_trace.py
new file mode 100644
index 000000000000..2d5ad959f7b0
--- /dev/null
+++ b/python/helpers/pydev/pydevd_dont_trace.py
@@ -0,0 +1,127 @@
+'''
+Support for a tag that allows skipping over functions while debugging.
+'''
+import linecache
+import re
+from pydevd_constants import DictContains
+
+# 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(frame, 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(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(filename)
+ i_line = 0 # Could use enumerate, but not there on all versions...
+ for line in 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
+
+ i_line += 1
+
+
+ _filename_to_ignored_lines[filename] = ignored_lines
+
+ func_line = frame.f_code.co_firstlineno - 1 # co_firstlineno is 1-based, so -1 is needed
+ return not (
+ DictContains(ignored_lines, func_line - 1) or #-1 to get line before method
+ DictContains(ignored_lines, func_line)) #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
+