Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 66a7e57

Browse files
committed
Fix several bugs and add two features.
Assertion error message had typos in arguments to string format. .cover files for modules in packages are now put in the right place. The code that generate .cover files seemed to prepend a "./" to many absolute paths, causing them to fail. The code now checks explicitly for absolute paths and leaves them alone. In trace/coverage code, recover from case where module has no __name__ attribute, when e.g. it is executed by PyRun_String(). In this case, assign modulename to None and hope for the best. There isn't anywhere to write out coverage data for this code anyway. Also, replace several sys.stderr.writes with print >> sys.stderr. New features: -C/--coverdir dir: Generate .cover files in specified directory instead of in the directory where the .py file is. -s: Print a short summary of files coverred (# lines, % coverage, name)
1 parent 9c90105 commit 66a7e57

1 file changed

Lines changed: 88 additions & 34 deletions

File tree

Tools/scripts/trace.py

Lines changed: 88 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ def usage(outfile):
7474
-r,--report Generate a report from a results file; do not
7575
execute any code.
7676
(One of `-t', `-c' or `-r' must be specified)
77+
-s,--summary Generate a brief summary for each file. (Can only
78+
be used with -c or -r.)
7779
7880
I/O:
7981
-f,--file= File name for accumulating results over several runs.
@@ -85,6 +87,7 @@ def usage(outfile):
8587
with a '>>>>>> '.
8688
-R,--no-report Do not generate the annotated reports. Useful if
8789
you want to accumulate several over tests.
90+
-C,--coverdir= Generate .cover files in this directory
8891
8992
Selection: Do not trace or log lines from ...
9093
--ignore-module=[string] modules with the given __name__, and submodules
@@ -197,8 +200,9 @@ def update(self, other):
197200
if modules.has_key(key):
198201
# make sure they point to the same file
199202
assert modules[key] == other_modules[key], \
200-
"Strange! filename %s has two different module names" % \
201-
(key, modules[key], other_module[key])
203+
"Strange! filename %s has two different module " \
204+
"names: %s and %s" % \
205+
(key, modules[key], other_modules[key])
202206
else:
203207
modules[key] = other_modules[key]
204208

@@ -254,6 +258,8 @@ def find_executable_linenos(filename):
254258
"""
255259
import parser
256260

261+
assert filename.endswith('.py')
262+
257263
prog = open(filename).read()
258264
ast = parser.suite(prog)
259265
code = parser.compileast(ast, filename)
@@ -281,7 +287,7 @@ def commonprefix(dirs):
281287
return os.sep.join(prefix)
282288

283289
def create_results_log(results, dirname = ".", show_missing = 1,
284-
save_counts = 0):
290+
save_counts = 0, summary = 0, coverdir = None):
285291
import re
286292
# turn the counts data ("(filename, lineno) = count") into something
287293
# accessible on a per-file basis
@@ -302,6 +308,9 @@ def create_results_log(results, dirname = ".", show_missing = 1,
302308
# line embedded in a multiline string.
303309
blank = re.compile(r'^\s*(#.*)?$')
304310

311+
# accumulate summary info, if needed
312+
sums = {}
313+
305314
# generate file paths for the coverage files we are going to write...
306315
fnlist = []
307316
tfdir = tempfile.gettempdir()
@@ -315,36 +324,38 @@ def create_results_log(results, dirname = ".", show_missing = 1,
315324
if filename.startswith(tfdir):
316325
continue
317326

318-
# XXX this is almost certainly not portable!!!
319-
fndir = os.path.dirname(filename)
320-
if filename[:1] == os.sep:
321-
coverpath = os.path.join(dirname, "."+fndir)
322-
else:
323-
coverpath = os.path.join(dirname, fndir)
327+
modulename = os.path.split(results.modules[key])[1]
324328

325329
if filename.endswith(".pyc") or filename.endswith(".pyo"):
326330
filename = filename[:-1]
327331

332+
if coverdir:
333+
listfilename = os.path.join(coverdir, modulename + ".cover")
334+
else:
335+
# XXX this is almost certainly not portable!!!
336+
fndir = os.path.dirname(filename)
337+
if os.path.isabs(filename):
338+
coverpath = fndir
339+
else:
340+
coverpath = os.path.join(dirname, fndir)
341+
342+
# build list file name by appending a ".cover" to the module name
343+
# and sticking it into the specified directory
344+
if "." in modulename:
345+
# A module in a package
346+
finalname = modulename.split(".")[-1]
347+
listfilename = os.path.join(coverpath, finalname + ".cover")
348+
else:
349+
listfilename = os.path.join(coverpath, modulename + ".cover")
350+
328351
# Get the original lines from the .py file
329352
try:
330353
lines = open(filename, 'r').readlines()
331354
except IOError, err:
332-
sys.stderr.write("%s: Could not open %s for reading " \
333-
"because: %s - skipping\n" % \
334-
("trace", `filename`, err.strerror))
355+
print >> sys.stderr, "trace: Could not open %s for reading " \
356+
"because: %s - skipping" % (`filename`, err.strerror)
335357
continue
336358

337-
modulename = os.path.split(results.modules[key])[1]
338-
339-
# build list file name by appending a ".cover" to the module name
340-
# and sticking it into the specified directory
341-
listfilename = os.path.join(coverpath, modulename + ".cover")
342-
#sys.stderr.write("modulename: %(modulename)s\n"
343-
# "filename: %(filename)s\n"
344-
# "coverpath: %(coverpath)s\n"
345-
# "listfilename: %(listfilename)s\n"
346-
# "dirname: %(dirname)s\n"
347-
# % locals())
348359
try:
349360
outfile = open(listfilename, 'w')
350361
except IOError, err:
@@ -360,6 +371,8 @@ def create_results_log(results, dirname = ".", show_missing = 1,
360371
else:
361372
executable_linenos = {}
362373

374+
n_lines = 0
375+
n_hits = 0
363376
lines_hit = per_file[key]
364377
for i in range(len(lines)):
365378
line = lines[i]
@@ -369,6 +382,8 @@ def create_results_log(results, dirname = ".", show_missing = 1,
369382
if lines_hit.has_key(i+1):
370383
# count precedes the lines that we captured
371384
outfile.write('%5d: ' % lines_hit[i+1])
385+
n_hits = n_hits + 1
386+
n_lines = n_lines + 1
372387
elif blank.match(line):
373388
# blank lines and comments are preceded by dots
374389
outfile.write(' . ')
@@ -383,10 +398,15 @@ def create_results_log(results, dirname = ".", show_missing = 1,
383398
outfile.write('>>>>>> ')
384399
else:
385400
outfile.write(' '*7)
401+
n_lines = n_lines + 1
386402
outfile.write(string.expandtabs(lines[i], 8))
387403

388404
outfile.close()
389405

406+
if summary and n_lines:
407+
percent = int(100 * n_hits / n_lines)
408+
sums[modulename] = n_lines, percent, modulename, filename
409+
390410
if save_counts:
391411
# try and store counts and module info into dirname
392412
try:
@@ -398,6 +418,14 @@ def create_results_log(results, dirname = ".", show_missing = 1,
398418
sys.stderr.write("cannot save counts/modules " \
399419
"files because %s" % err.strerror)
400420

421+
if summary and sums:
422+
mods = sums.keys()
423+
mods.sort()
424+
print "lines cov% module (path)"
425+
for m in mods:
426+
n_lines, percent, modulename, filename = sums[m]
427+
print "%5d %3d%% %s (%s)" % sums[m]
428+
401429
# There is a lot of code shared between these two classes even though
402430
# it is straightforward to make a super class to share code. However,
403431
# for performance reasons (remember, this is called at every step) I
@@ -419,7 +447,12 @@ def trace(self, frame, why, arg):
419447
filename = frame.f_globals.get("__file__", None)
420448
if filename is None:
421449
filename = frame.f_code.co_filename
422-
modulename = frame.f_globals["__name__"]
450+
try:
451+
modulename = frame.f_globals["__name__"]
452+
except KeyError:
453+
# PyRun_String() for example
454+
# XXX what to do?
455+
modulename = None
423456

424457
# We do this next block to keep from having to make methods
425458
# calls, which also requires resetting the trace
@@ -449,12 +482,18 @@ def __init__(self, ignore = Ignore()):
449482
self.ignore = ignore
450483
self.ignore_names = ignore._ignore # access ignore's cache (speed hack)
451484

452-
self.files = {'<string>': None} # stores lines from the .py file, or None
485+
self.files = {'<string>': None} # stores lines from the .py file,
486+
# or None
453487

454488
def trace(self, frame, why, arg):
455489
if why == 'line':
456490
filename = frame.f_code.co_filename
457-
modulename = frame.f_globals["__name__"]
491+
try:
492+
modulename = frame.f_globals["__name__"]
493+
except KeyError:
494+
# PyRun_String() for example
495+
# XXX what to do?
496+
modulename = None
458497

459498
# We do this next block to keep from having to make methods
460499
# calls, which also requires resetting the trace
@@ -474,7 +513,8 @@ def trace(self, frame, why, arg):
474513

475514
# If you want to see filenames (the original behaviour), try:
476515
# modulename = filename
477-
# or, prettier but confusing when several files have the same name
516+
# or, prettier but confusing when several files have the
517+
# same name
478518
# modulename = os.path.basename(filename)
479519

480520
if files[filename] != None:
@@ -487,7 +527,7 @@ def trace(self, frame, why, arg):
487527

488528

489529
def _err_exit(msg):
490-
sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
530+
print >> sys.stderr, "%s: %s" % (sys.argv[0], msg)
491531
sys.exit(1)
492532

493533
def main(argv = None):
@@ -496,15 +536,17 @@ def main(argv = None):
496536
if argv is None:
497537
argv = sys.argv
498538
try:
499-
opts, prog_argv = getopt.getopt(argv[1:], "tcrRf:d:m",
539+
opts, prog_argv = getopt.getopt(argv[1:], "tcrRf:d:msC:",
500540
["help", "version", "trace", "count",
501541
"report", "no-report",
502542
"file=", "logdir=", "missing",
503-
"ignore-module=", "ignore-dir="])
543+
"ignore-module=", "ignore-dir=",
544+
"coverdir="])
504545

505546
except getopt.error, msg:
506-
sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
507-
sys.stderr.write("Try `%s --help' for more information\n" % sys.argv[0])
547+
print >> sys.stderr, "%s: %s" % (sys.argv[0], msg)
548+
print >> sys.stderr, "Try `%s --help' for more information" \
549+
% sys.argv[0]
508550
sys.exit(1)
509551

510552
trace = 0
@@ -516,6 +558,8 @@ def main(argv = None):
516558
missing = 0
517559
ignore_modules = []
518560
ignore_dirs = []
561+
coverdir = None
562+
summary = 0
519563

520564
for opt, val in opts:
521565
if opt == "--help":
@@ -554,6 +598,14 @@ def main(argv = None):
554598
missing = 1
555599
continue
556600

601+
if opt == "-C" or opt == "--coverdir":
602+
coverdir = val
603+
continue
604+
605+
if opt == "-s" or opt == "--summary":
606+
summary = 1
607+
continue
608+
557609
if opt == "--ignore-module":
558610
ignore_modules.append(val)
559611
continue
@@ -638,7 +690,8 @@ def main(argv = None):
638690
results.update(CoverageResults(old_counts, old_modules))
639691

640692
if not no_report:
641-
create_results_log(results, logdir, missing)
693+
create_results_log(results, logdir, missing,
694+
summary=summary, coverdir=coverdir)
642695

643696
if counts_file:
644697
try:
@@ -651,7 +704,8 @@ def main(argv = None):
651704
elif report:
652705
old_counts, old_modules = marshal.load(open(counts_file, 'rb'))
653706
results = CoverageResults(old_counts, old_modules)
654-
create_results_log(results, logdir, missing)
707+
create_results_log(results, logdir, missing,
708+
summary=summary, coverdir=coverdir)
655709

656710
else:
657711
assert 0, "Should never get here"

0 commit comments

Comments
 (0)