Index: pyconquer.py =================================================================== --- pyconquer.py (revision 44) +++ pyconquer.py (revision 45) @@ -46,4 +46,6 @@ def __init__(self, fileregex=".*", events=('call', 'return', 'exception')): + self.active = False + self.columns = {} self.currentcol = Column(-1) @@ -63,35 +65,31 @@ self.watches = {} - def _get_events(self): - return self._events - def _set_events(self, newvalue): - if 'line' in newvalue: - for e in c_events: - if e in newvalue: - raise ValueError("Tracer.events cannot contain both " - "'line' and 'c_*' events") - self._events = newvalue - events = property(_get_events, _set_events) - def start(self): self.out.write("\n\n________ pyconquer tracing started ________\n") self.write_watches() + self._using_profile_hook = False for e in c_events: if e in self.events: - # Must use the profile hook, since only it supports C events. - threading.setprofile(self.hook) - sys.setprofile(self.hook) - self._using_profile_hook = False + # Must also use the profile hook, + # since tracing does not support C events. + threading.setprofile(self.profhook) + sys.setprofile(self.profhook) + self._using_profile_hook = True break - else: - # Use the trace hook, since it is easier to clean up - threading.settrace(self.hook) - sys.settrace(self.hook) + + # Set the trace hook for all events other than C events. + # (it's easier to clean up nicely). + threading.settrace(self.hook) + sys.settrace(self.hook) + + self.active = True def stop(self): + self.active = False + if threading._trace_hook == self.hook: threading.settrace(None) - if threading._profile_hook == self.hook: + if threading._profile_hook == self.profhook: threading.setprofile(None) @@ -106,5 +104,12 @@ self.out.write("\n\n________ pyconquer tracing stopped ________\n") + def profhook(self, frame, event, arg): + if not event.startswith("c_"): + return + return self.hook(frame, event, arg) + def hook(self, frame, event, arg): + if not self.active: + return self._mutex.acquire(True) try: @@ -112,4 +117,6 @@ if event in self.events: filename = frame.f_code.co_filename + + # Deny tracing this module itself. if r"\pyconquer.py" in filename: return self.hook @@ -168,5 +175,11 @@ self.write_col("> %s (%s:%s)" % (frame.f_code.co_name, scope_name(frame), frame.f_lineno)) - process_c_call = process_call + + def process_c_call(self, frame, arg): + # arg should be the called C function + self.currentcol.indent += 1 + self.write_col(">>%s (%s:%s): %s" % (frame.f_code.co_name, + scope_name(frame), + frame.f_lineno, arg)) def process_return(self, frame, arg): @@ -178,5 +191,11 @@ frame.f_lineno, retval)) self.currentcol.indent -= 1 - process_c_return = process_return + + def process_c_return(self, frame, arg): + # arg should be the called C function + self.write_col("<<%s (%s:%s): %s" % (frame.f_code.co_name, + scope_name(frame), + frame.f_lineno, arg)) + self.currentcol.indent -= 1 def process_exception(self, frame, arg): @@ -189,5 +208,10 @@ scope_name(frame), frame.f_lineno, retval)) - process_c_exception = process_exception + + def process_c_exception(self, frame, arg): + # arg should be the called C function + self.write_col("EE%s (%s:%s): %s" % (frame.f_code.co_name, + scope_name(frame), + frame.f_lineno, arg)) def process_line(self, frame, arg): Index: test_pyconquer.py =================================================================== --- test_pyconquer.py (revision 44) +++ test_pyconquer.py (revision 45) @@ -7,4 +7,5 @@ except ImportError: from StringIO import StringIO +import sys import threading import unittest @@ -45,29 +46,61 @@ [ MainThread ] -* testNormalFlow (test_pyconquer:35) New Thread: Thread-1 +* testNormalFlow (test_pyconquer:36) New Thread: Thread-1 [ Thread-1 ] - > inc (test_pyconquer:31) + > inc (test_pyconquer:32) = c.value: 1 - < inc (test_pyconquer:33) + < inc (test_pyconquer:34) X Thread terminated: Thread-1 [ MainThread ] -* testNormalFlow (test_pyconquer:35) New Thread: Thread-2 +* testNormalFlow (test_pyconquer:36) New Thread: Thread-2 [ Thread-2 ] - > inc (test_pyconquer:31) + > inc (test_pyconquer:32) = c.value: 2 - < inc (test_pyconquer:33) + < inc (test_pyconquer:34) X Thread terminated: Thread-2 [ MainThread ] -* testNormalFlow (test_pyconquer:35) New Thread: Thread-3 +* testNormalFlow (test_pyconquer:36) New Thread: Thread-3 [ Thread-3 ] - > inc (test_pyconquer:31) + > inc (test_pyconquer:32) = c.value: 3 - < inc (test_pyconquer:33) + < inc (test_pyconquer:34) X Thread terminated: Thread-3 + +________ pyconquer tracing stopped ________ +""") + + def testCEvents(self): + c = Counter() + tr = pyconquer.Tracer(events=('call', 'return', 'exception', + 'c_call', 'c_return', 'c_exception', + )) + tr.out = StringIO() + try: + tr.start() + # Trace c_call and c_return + c.value = sys.getrecursionlimit() + # Raise a c_exception + try: + c.value = chr('a') + except TypeError: + pass + finally: + tr.stop() + + self.assertEqual(tr.out.getvalue(), """ + +________ pyconquer tracing started ________ + + +[ MainThread ] +>>testCEvents (test_pyconquer:86): +< +>>testCEvents (test_pyconquer:89): +EEtestCEvents (test_pyconquer:89): ________ pyconquer tracing stopped ________