Indeed, the 2.0 code is very different, and got this case right.
I'm a little surprised no one is arguing that changing this code now
could break some applications. Maybe the fact no one noticed the docs
were wrong proves that no one ever tried returning None from a local
trace function.
--Ned.
On 4/30/2011 8:43 PM, Guido van Rossum wrote:
I think you need to go back farther in time. :-) In Python 2.0 the
call_trace function in ceval.c has a completely different signature
(but the docs are the same). I haven't checked all history but
somewhere between 2.0 and 2.3, SET_LINENO-less tracing was added, and
that's where the implementation must have gone wrong. So I think we
should fix the code.
--Guido
On Sat, Apr 30, 2011 at 3:49 PM, Ned Batchelder<n...@nedbatchelder.com> wrote:
This week I learned something new about trace functions (how to write a C
trace function that survives a sys.settrace(sys.gettrace()) round-trip), and
while writing up what I learned, I was surprised to discover that trace
functions don't behave the way I thought, or the way the docs say they
behave.
The docs say:
The trace function is invoked (with event set to 'call') whenever a new
local scope is entered; it should return a reference to a local trace
function to be used that scope, or None if the scope shouldn’t be traced.
The local trace function should return a reference to itself (or to another
function for further tracing in that scope), or None to turn off tracing in
that scope.
It's that last part that's wrong: returning None from the trace function
only has an effect on the first call in a new frame. Once the trace
function returns a function for a frame, returning None from subsequent
calls is ignored. A "local trace function" can't turn off tracing in its
scope.
To demonstrate:
import sys
UPTO_LINE = 1
def t(frame, event, arg):
num = frame.f_lineno
print("line %d" % num)
if num< UPTO_LINE:
return t
def try_it():
print("twelve")
print("thirteen")
print("fourteen")
print("fifteen")
UPTO_LINE = 1
sys.settrace(t)
try_it()
UPTO_LINE = 13
sys.settrace(t)
try_it()
Produces:
line 11
twelve
thirteen
fourteen
fifteen
line 11
line 12
twelve
line 13
thirteen
line 14
fourteen
line 15
fifteen
line 15
The first call to try_it() returns None immediately, preventing tracing for
the rest of the function. The second call returns None at line 13, but the
rest of the function is traced anyway. This behavior is the same in all
versions from 2.3 to 3.2, in fact, the 100 lines of code in sysmodule.c
responsible for Python tracing functions are completely unchanged through
those versions. (A deeper mystery that I haven't looked into yet is why
Python 3.x intersperses all of these lines with "line 18" interjections.)
I'm writing this email because I'm not sure whether this is a behavior bug
or a doc bug. One of them is wrong, since they disagree. The documented
behavior makes sense, and is what people have all along thought the trace
function did. The actual behavior is a bit more complicated to explain, but
is what people have actually been experiencing. FWIW, PyPy implements the
documented behavior.
Should we fix the code or the docs? I'd be glad to supply a patch for
either.
--Ned.
_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe:
http://mail.python.org/mailman/options/python-dev/guido%40python.org
_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe:
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com