diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2011-04-27 09:49:23 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2011-04-27 09:49:23 -0400 |
commit | e91c97da931e54b27a838106d25d6ae5529256ae (patch) | |
tree | 352b9857bd6fbee4f6f07dfd80a85b8d5b286a3c | |
parent | f1d4dcbe9e5b8b8b335521f6aa806f8438fde917 (diff) | |
parent | a243a6a2b34eb9ef5cba3346796160e06ba77930 (diff) | |
download | python-coveragepy-git-e91c97da931e54b27a838106d25d6ae5529256ae.tar.gz |
Merge bug_123 work into default.
-rw-r--r-- | AUTHORS.txt | 1 | ||||
-rw-r--r-- | CHANGES.txt | 7 | ||||
-rw-r--r-- | coverage/tracer.c | 34 | ||||
-rw-r--r-- | test/farm/run/run_timid.py | 5 | ||||
-rw-r--r-- | test/farm/run/src/showtrace.py | 9 | ||||
-rw-r--r-- | test/test_oddball.py | 34 |
6 files changed, 80 insertions, 10 deletions
diff --git a/AUTHORS.txt b/AUTHORS.txt index e6a9ed78..0f921841 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -15,6 +15,7 @@ Martin Fuzzey Imri Goldberg Bill Hart Christian Heimes +Devin Jeanpierre Ross Lawley Edward Loper Sandra Martocchia diff --git a/CHANGES.txt b/CHANGES.txt index 6d0664ed..4d9bf1e2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -37,6 +37,10 @@ Version 3.5 coverage.py will issue a warning, at least alerting you to the problem. Closes `issue 93`_. Thanks to Marius Gedminas for the idea. +- The C-based trace function now behaves properly when saved and restored + with ``sys.gettrace()`` and ``sys.settrace()``. This fixes `issue 125`_ + and `issue 123`_. Thanks, Devin Jeanpierre. + - Source files are now opened with Python 3.2's ``tokenize.open()`` where possible, to get the best handling of Python source files with encodings. Closes `issue 107`_, thanks, Brett Cannon. @@ -61,7 +65,8 @@ Version 3.5 .. _issue 107: https://bitbucket.org/ned/coveragepy/issue/107/codeparser-not-opening-source-files-with .. _issue 115: https://bitbucket.org/ned/coveragepy/issue/115/fail-gracefully-when-reporting-on-file .. _issue 121: https://bitbucket.org/ned/coveragepy/issue/121/filename-patterns-are-applied-stupidly - +.. _issue 123: https://bitbucket.org/ned/coveragepy/issue/123/pyeval_settrace-used-in-way-that-breaks +.. _issue 125: https://bitbucket.org/ned/coveragepy/issue/125/coverage-removes-decoratortoolss-tracing Version 3.4 --- 19 September 2010 --------------------------------- diff --git a/coverage/tracer.c b/coverage/tracer.c index e046596a..1017c9d4 100644 --- a/coverage/tracer.c +++ b/coverage/tracer.c @@ -365,6 +365,9 @@ Tracer_trace(Tracer *self, PyFrameObject *frame, int what, PyObject *arg_unused) } } self->cur_file_data = file_data; + /* Make the frame right in case settrace(gettrace()) happens. */ + Py_INCREF(self); + frame->f_trace = (PyObject*)self; SHOWLOG(self->depth, frame->f_lineno, filename, "traced"); } else { @@ -462,7 +465,7 @@ Tracer_pytrace(Tracer *self, PyObject *args) { PyFrameObject *frame; PyObject *what_str; - PyObject *arg_unused; + PyObject *arg; int what; static char *what_names[] = { "call", "exception", "line", "return", @@ -470,8 +473,12 @@ Tracer_pytrace(Tracer *self, PyObject *args) NULL }; + #if WHAT_LOG + printf("pytrace\n"); + #endif + if (!PyArg_ParseTuple(args, "O!O!O:Tracer_pytrace", - &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg_unused)) { + &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg)) { goto done; } @@ -484,7 +491,7 @@ Tracer_pytrace(Tracer *self, PyObject *args) } /* Invoke the C function, and return ourselves. */ - if (Tracer_trace(self, frame, what, arg_unused) == RET_OK) { + if (Tracer_trace(self, frame, what, arg) == RET_OK) { return PyObject_GetAttrString((PyObject*)self, "pytrace"); } @@ -492,6 +499,24 @@ done: return NULL; } +/* + * Python has two ways to set the trace function: sys.settrace(fn), which + * takes a Python callable, and PyEval_SetTrace(func, obj), which takes + * a C function and a Python object. The way these work together is that + * sys.settrace(pyfn) calls PyEval_SetTrace(builtin_func, pyfn), using the + * Python callable as the object in PyEval_SetTrace. So sys.gettrace() + * simply returns the Python object used as the second argument to + * PyEval_SetTrace. So sys.gettrace() will return our self parameter, which + * means it must be callable to be used in sys.settrace(). + * + * So we make our self callable, equivalent to invoking our trace function. + */ +static PyObject * +Tracer_call(Tracer *self, PyObject *args, PyObject *kwds_unused) +{ + return Tracer_pytrace(self, args); +} + static PyObject * Tracer_start(Tracer *self, PyObject *args_unused) { @@ -590,7 +615,7 @@ TracerType = { 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ - 0, /*tp_call*/ + (ternaryfunc)Tracer_call, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ @@ -678,3 +703,4 @@ inittracer(void) } #endif /* Py3k */ + diff --git a/test/farm/run/run_timid.py b/test/farm/run/run_timid.py index 3810e6db..19651a1c 100644 --- a/test/farm/run/run_timid.py +++ b/test/farm/run/run_timid.py @@ -20,9 +20,8 @@ contains("out/showtraceout.txt", "timid PyTracer") if os.environ.get('COVERAGE_TEST_TRACER', 'c') == 'c': # If the C trace function is being tested, then regular running should have - # the C function (shown as None in f_trace since it isn't a Python - # function). - contains("out/showtraceout.txt", "regular None") + # the C function, which registers itself as f_trace. + contains("out/showtraceout.txt", "regular Tracer") else: # If the Python trace function is being tested, then regular running will # also show the Python function. diff --git a/test/farm/run/src/showtrace.py b/test/farm/run/src/showtrace.py index c3b4356c..e97412e0 100644 --- a/test/farm/run/src/showtrace.py +++ b/test/farm/run/src/showtrace.py @@ -4,7 +4,7 @@ import sys # Show what the trace function is. If a C-based function is used, then f_trace -# is None. +# may be None. trace_fn = sys._getframe(0).f_trace if trace_fn is None: trace_name = "None" @@ -13,6 +13,11 @@ else: try: trace_name = trace_fn.im_class.__name__ except AttributeError: - trace_name = trace_fn.__self__.__class__.__name__ + try: + trace_name = trace_fn.__self__.__class__.__name__ + except AttributeError: + # A C-based function could also manifest as an f_trace value + # which doesn't have im_class or __self__. + trace_name = trace_fn.__class__.__name__ print("%s %s" % (sys.argv[1], trace_name)) diff --git a/test/test_oddball.py b/test/test_oddball.py index e94e2bad..859648fa 100644 --- a/test/test_oddball.py +++ b/test/test_oddball.py @@ -348,3 +348,37 @@ if sys.version_info >= (2, 5): doctest.testmod(sys.modules[__name__]) # we're not __main__ :( ''', [1,11,12,14,16,17], "") + + +if hasattr(sys, 'gettrace'): + class GettraceTest(CoverageTest): + """Tests that we work properly with `sys.gettrace()`.""" + def test_round_trip(self): + self.check_coverage('''\ + import sys + def foo(n): + return 3*n + def bar(n): + return 5*n + a = foo(6) + sys.settrace(sys.gettrace()) + a = bar(8) + ''', + [1,2,3,4,5,6,7,8], "") + + def test_multi_layers(self): + self.check_coverage('''\ + import sys + def level1(): + a = 3 + level2() + b = 5 + def level2(): + c = 7 + sys.settrace(sys.gettrace()) + d = 9 + e = 10 + level1() + f = 12 + ''', + [1,2,3,4,5,6,7,8,9,10,11,12], "") |