[Python-Dev] tests for standard library modules

Vladimir Marangozov Vladimir.Marangozov@inrialpes.fr
Fri, 4 Aug 2000 00:25:38 +0200 (CEST)


Jeremy Hylton wrote:
> 
> Skip's trace.py code coverage tool is now available in Tools/script.
> You can use it to examine how much of a particular module is covered
> by existing tests.

Hmm. Glancing quickly at trace.py, I see that half of it is guessing
line numbers. The same SET_LINENO problem again. This is unfortunate.
But fortunately <wink>, here's another piece of code, modeled after
its C counterpart, that comes to Skip's rescue and that works with -O.

Example:

>>> import codeutil
>>> co = codeutil.PyCode_Line2Addr.func_code   # some code object
>>> codeutil.PyCode_GetExecLines(co)
[20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36]
>>> codeutil.PyCode_Line2Addr(co, 29)
173
>>> codeutil.PyCode_Addr2Line(co, 173)
29
>>> codeutil.PyCode_Line2Addr(co, 10)
Traceback (innermost last):
  File "<stdin>", line 1, in ?
  File "codeutil.py", line 26, in PyCode_Line2Addr
    raise IndexError, "line must be in range [%d,%d]" % (line, lastlineno)
IndexError: line must be in range [20,36]

etc...

-- 
       Vladimir MARANGOZOV          | Vladimir.Marangozov@inrialpes.fr
http://sirac.inrialpes.fr/~marangoz | tel:(+33-4)76615277 fax:76615252

------------------------------[ codeutil.py ]-------------------------
import types

def PyCode_Addr2Line(co, addrq):
    assert type(co) == types.CodeType, \
           "1st arg must be a code object, %s given" % type(co).__name__
    if addrq < 0 or addrq > len(co.co_code):
        raise IndexError, "address must be in range [0,%d]" % len(co.co_code)
    addr = 0
    line = co.co_firstlineno
    lnotab = co.co_lnotab
    for i in range(0, len(lnotab), 2):
        addr_incr = ord(lnotab[i])
        line_incr = ord(lnotab[i+1])
        addr = addr + addr_incr
        if (addr > addrq):
            break
        line = line + line_incr
    return line

def PyCode_Line2Addr(co, lineq):
    assert type(co) == types.CodeType, \
           "1st arg must be a code object, %s given" % type(co).__name__
    line = co.co_firstlineno
    lastlineno = PyCode_Addr2Line(co, len(co.co_code))
    if lineq < line or lineq > lastlineno:
        raise IndexError, "line must be in range [%d,%d]" % (line, lastlineno)
    addr = 0
    lnotab = co.co_lnotab
    for i in range(0, len(lnotab), 2):
        if line >= lineq:
            break
        addr_incr = ord(lnotab[i])
        line_incr = ord(lnotab[i+1])
        addr = addr + addr_incr
        line = line + line_incr
    return addr

def PyCode_GetExecLines(co):
    assert type(co) == types.CodeType, \
           "arg must be a code object, %s given" % type(co).__name__
    lastlineno = PyCode_Addr2Line(co, len(co.co_code))
    lines = range(co.co_firstlineno, lastlineno + 1)
    # remove void lines (w/o opcodes): comments, blank/escaped lines
    i = len(lines) - 1
    while i >= 0:
        if lines[i] != PyCode_Addr2Line(co, PyCode_Line2Addr(co, lines[i])):
            lines.pop(i)
        i = i - 1
    return lines