@@ -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
7880I/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
8992Selection: 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
283289def 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
489529def _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
493533def 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