1import pytest 2import sys 3from pydev_tests_python.test_debugger import IS_PY26, IS_PY34 4from _pydevd_bundle.pydevd_constants import NO_FTRACE 5from pydev_tests_python.debugger_unittest import IS_JYTHON 6 7 8class Tracer(object): 9 10 def __init__(self): 11 self.call_calls = 0 12 self.line_calls = 0 13 14 def trace_func(self, frame, event, arg): 15 if event == 'call': 16 self.call_calls += 1 17 return self.on_return() 18 19 elif event == 'line': 20 self.line_calls += 1 21 # Should disable tracing when None is returned (but doesn't). 22 return self.on_line(frame, event, arg) 23 24 else: 25 return self.trace_func 26 27 def on_return(self): 28 return self.trace_func 29 30 def on_line(self, frame, event, arg): 31 return None 32 33 34class TracerSettingNone(Tracer): 35 36 def on_line(self, frame, event, arg): 37 frame.f_trace = NO_FTRACE 38 return NO_FTRACE 39 40 41class TracerChangeToOtherTracing(Tracer): 42 43 def on_line(self, frame, event, arg): 44 # Does NOT change to another tracing even if != None is returned 45 # (unless it's python 2.6). 46 return NO_FTRACE 47 48 49class TracerDisableOnCall(Tracer): 50 51 def on_return(self): 52 return None 53 54 55def test_tracing_gotchas(): 56 ''' 57 Summary of the gotchas tested: 58 59 If 'call' is used, the return value is used for the tracing. Afterwards the return may or may 60 not be ignored depending on the Python version, so, frame.f_trace should be set in that case. 61 62 Also, on Python 2.6, the trace function may not be set to None as it'll give an error 63 afterwards (it needs to be set to an empty tracing function). 64 65 Note: according to: https://docs.python.org/3/library/sys.html#sys.settrace the behavior 66 does not follow the spec (but we have to work with it nonetheless). 67 68 Note: Jython seems to do what's written in the docs. 69 ''' 70 71 def method(): 72 _a = 1 73 _b = 1 74 _c = 1 75 _d = 1 76 77 for tracer, line_events in ( 78 (Tracer(), 1 if IS_JYTHON else 4), 79 (TracerSettingNone(), 1), 80 (TracerChangeToOtherTracing(), 4 if not IS_PY26 and not IS_PY34 and not IS_JYTHON else 1), 81 (TracerDisableOnCall(), 0), 82 ): 83 curr_trace_func = sys.gettrace() 84 try: 85 sys.settrace(tracer.trace_func) 86 87 method() 88 89 if tracer.call_calls != 1: 90 pytest.fail('Expected a single call event. Found: %s' % (tracer.call_calls)) 91 92 if tracer.line_calls != line_events: 93 pytest.fail('Expected %s line events. Found: %s. Tracer: %s' % (line_events, tracer.line_calls, tracer)) 94 finally: 95 sys.settrace(curr_trace_func) 96 97 98if __name__ == '__main__': 99 pytest.main(['-k', 'test_tracing_gotchas']) 100