Having been writing a fairly comprehensive code and path coverage tool
(incomplete - currently working on determining what *is* an executable line
... think I need to work with the AST)
I've done this recently as well, and I'm now on my third iteration of
the work-out-which-lines-are-executable code (!)

The first version used the AST, but there are several holes in that -
'else' lines, for instance, are identified by the parser as executable
lines, but the trace function doesn't always get called for them. I
then wrote a system that found all the SET_LINENO instructions, and
that gave perfect results (as long as you weren't using -O) but then I
found out that SET_LINENO is going away in Python 2.3...

My current system (which I think works 8-) uses co_lnotab, which is a
delightful data structure that describes the relationship between
bytecodes and line numbers. Here's the relevant piece of code, which
builds in 'lineNumbers' a list of the execuable line number in the
code object 'code':

# Derive the line numbers from co_lnotab; that's a list of pairs of
# increments, one for the bytecode address and one for the line
# number (see compile.c in the Python sources).
lineNumbers = []
lnotab = code.co_lnotab
previousAddress = -1
previousLineNo = -1
lineNo = code.co_firstlineno
byteCodeAddress = 0
for i in range( 0, len( lnotab ), 2 ):
byteCodeAddress = byteCodeAddress + ord( lnotab[ i ] )
lineNo = lineNo + ord( lnotab[ i+1 ] )

# When the compiler wants to increment the line number by more
# than 255 in one go, it increments it by 255 as many times as it
# needs and then the remainder. Fair enough, but there's a bug
# whereby the byte code address gets incremented along
# with the *first* 255, not the last. Hence you get a line that
# claims to have code on it when in fact it doesn't. We detect
# that here.
if byteCodeAddress != previousAddress and not \
( lineNo - previousLineNo == 255 and
len( lnotab ) > i+2 and
ord( lnotab[ i+2 ] ) == 0 ):
lineNumbers.append( lineNo )
previousAddress = byteCodeAddress
previousLineNo = lineNo

Hope that helps. The coverage tool that this belongs to is finished
but unreleased (mostly because of lack of thorough testing, but I'm
using it myself with no problems) - if you'd like a copy, drop me an

Richie Hindle
richie at entrian.com

Search Discussions

Discussion Posts


Follow ups

Related Discussions

Discussion Navigation
viewthread | post
posts ‹ prev | 15 of 18 | next ›
Discussion Overview
grouppython-list @
postedSep 19, '02 at 2:54a
activeSep 22, '02 at 11:51p



site design / logo © 2022 Grokbase