Finn,
(A bit long, two subjects, a maybe bug and a likely bug.)
I wrote:
> Finn,
>
> >[Ype Kingma]
> >
> >>Hello jythoneers,
> >>
> >>I've been using pyunit for a while in jython and would like to
> > >have code coverage reports from some of the tests.
>
> <...>
>
> >But if all you need to know is whether a line have some executable
> >python code, then the line number table in the $py.class file can answer
> >that with the same certainty as the setline() method calls can.
>
> It would probably be a lot easier to parse, so I'll give it a try.
>
> >Each time a call to PyFrame.setline() is generated, we also add a line
> >number entry to the line number table. See the Code.setline() for
> >details.
>
> Ok.
>
> >
>
> >Line tracing is implemented in dynamic compilation. Unless you have
> >discovered a bug of course. This works for me:
>
My class file parser does not find a linenumber table in java $py.class
(21a3) files. It finds is an UTF8 'LineNumberTable' in the constant pool,
but no attribute seems to be using that name.
(Is this a bug?).
In fact the only attributes it finds are an 'UTF8' 'SourceFile'
attribute with the file name in the constant pool and
an 'UTF8' 'org.python.APIVersion' with a 4byte of value 9.
This coincides with Module.java in the compiler package.
Method write() (line 550) does the attributes above,
but no LineNumberTable. Adding it to a $py.class file
would take 4 bytes per 'executable' line.
The line number table is completely
redundant given the setline() calls in the code.
The same parser for $py.class files can also fish for all
the setline() arguments.
The parser is written in jython, but it
is ugly (it ends with an exception because it is confused
about u4 bytes read already from the DataInputStream,
even though readInt() works fine reading directly.)
Also it is quite untested, and the internal workings
are, uhh, not write to home about (Dutchism).
If anyone is interested I'll gladly provide a copy.
>
> >
> >
> >def tracer(frame, why, arg):
> > print why, frame.f_lineno, arg
> > return tracer
> >
> >def foo():
> > for i in range(10):
> > a = "abc"
> > a = "Done"
> >
> >import sys
> >sys.settrace(tracer)
> >foo()
>
> I'll give it a try,
>
This works fine when called interactively.
However when I use it via execfile() i get a:
NameError: tracer
on the "return tracer" statement.
The following works when called via execfile(),
the interesting difference is probably the fact
that the tracer function is now not in a module
name space when returned for line tracing,
so I think there is a bug related to the new nested
namespaces in execfile():
class LineInfoCollector:
def __init__(self):
self.lineInfos = {}
def addLineInfo(self, lineInfo):
self.lineInfos[lineInfo] = 1
def getLineInfos(self):
infos = self.lineInfos.keys()
infos.sort()
return infos
def tracer(self, frame, why, arg):
self.addLineInfo((frame.f_code.co_filename,
frame.f_code.co_name,
frame.f_lineno,
why, arg))
return self.tracer
def foo():
for i in range(2):
a = "abc"
a = "Done"
if __name__ == '__main__':
import sys
ltrac = LineInfoCollector()
sys.settrace(ltrac.tracer)
try: foo()
finally: sys.settrace(None)
print 'Lines covered:'
for lineInfo in map(str, ltrac.getLineInfos()):
print lineInfo
Regards,
Ype
|