summaryrefslogtreecommitdiff
path: root/python/helpers/pydev/pydevd_dont_trace.py
blob: 2d5ad959f7b007f7f7eb6839fa0595932500a204 (plain)
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
125
126
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