summaryrefslogtreecommitdiffstats
path: root/Tools
diff options
context:
space:
mode:
Diffstat (limited to 'Tools')
-rw-r--r--Tools/scripts/trace.py122
1 files changed, 88 insertions, 34 deletions
diff --git a/Tools/scripts/trace.py b/Tools/scripts/trace.py
index 78715e0..35dc71f 100644
--- a/Tools/scripts/trace.py
+++ b/Tools/scripts/trace.py
@@ -74,6 +74,8 @@ Execution:
-r,--report Generate a report from a results file; do not
execute any code.
(One of `-t', `-c' or `-r' must be specified)
+ -s,--summary Generate a brief summary for each file. (Can only
+ be used with -c or -r.)
I/O:
-f,--file= File name for accumulating results over several runs.
@@ -85,6 +87,7 @@ I/O:
with a '>>>>>> '.
-R,--no-report Do not generate the annotated reports. Useful if
you want to accumulate several over tests.
+ -C,--coverdir= Generate .cover files in this directory
Selection: Do not trace or log lines from ...
--ignore-module=[string] modules with the given __name__, and submodules
@@ -197,8 +200,9 @@ class CoverageResults:
if modules.has_key(key):
# make sure they point to the same file
assert modules[key] == other_modules[key], \
- "Strange! filename %s has two different module names" % \
- (key, modules[key], other_module[key])
+ "Strange! filename %s has two different module " \
+ "names: %s and %s" % \
+ (key, modules[key], other_modules[key])
else:
modules[key] = other_modules[key]
@@ -254,6 +258,8 @@ def find_executable_linenos(filename):
"""
import parser
+ assert filename.endswith('.py')
+
prog = open(filename).read()
ast = parser.suite(prog)
code = parser.compileast(ast, filename)
@@ -281,7 +287,7 @@ def commonprefix(dirs):
return os.sep.join(prefix)
def create_results_log(results, dirname = ".", show_missing = 1,
- save_counts = 0):
+ save_counts = 0, summary = 0, coverdir = None):
import re
# turn the counts data ("(filename, lineno) = count") into something
# accessible on a per-file basis
@@ -302,6 +308,9 @@ def create_results_log(results, dirname = ".", show_missing = 1,
# line embedded in a multiline string.
blank = re.compile(r'^\s*(#.*)?$')
+ # accumulate summary info, if needed
+ sums = {}
+
# generate file paths for the coverage files we are going to write...
fnlist = []
tfdir = tempfile.gettempdir()
@@ -315,36 +324,38 @@ def create_results_log(results, dirname = ".", show_missing = 1,
if filename.startswith(tfdir):
continue
- # XXX this is almost certainly not portable!!!
- fndir = os.path.dirname(filename)
- if filename[:1] == os.sep:
- coverpath = os.path.join(dirname, "."+fndir)
- else:
- coverpath = os.path.join(dirname, fndir)
+ modulename = os.path.split(results.modules[key])[1]
if filename.endswith(".pyc") or filename.endswith(".pyo"):
filename = filename[:-1]
+ if coverdir:
+ listfilename = os.path.join(coverdir, modulename + ".cover")
+ else:
+ # XXX this is almost certainly not portable!!!
+ fndir = os.path.dirname(filename)
+ if os.path.isabs(filename):
+ coverpath = fndir
+ else:
+ coverpath = os.path.join(dirname, fndir)
+
+ # build list file name by appending a ".cover" to the module name
+ # and sticking it into the specified directory
+ if "." in modulename:
+ # A module in a package
+ finalname = modulename.split(".")[-1]
+ listfilename = os.path.join(coverpath, finalname + ".cover")
+ else:
+ listfilename = os.path.join(coverpath, modulename + ".cover")
+
# Get the original lines from the .py file
try:
lines = open(filename, 'r').readlines()
except IOError, err:
- sys.stderr.write("%s: Could not open %s for reading " \
- "because: %s - skipping\n" % \
- ("trace", `filename`, err.strerror))
+ print >> sys.stderr, "trace: Could not open %s for reading " \
+ "because: %s - skipping" % (`filename`, err.strerror)
continue
- modulename = os.path.split(results.modules[key])[1]
-
- # build list file name by appending a ".cover" to the module name
- # and sticking it into the specified directory
- listfilename = os.path.join(coverpath, modulename + ".cover")
- #sys.stderr.write("modulename: %(modulename)s\n"
- # "filename: %(filename)s\n"
- # "coverpath: %(coverpath)s\n"
- # "listfilename: %(listfilename)s\n"
- # "dirname: %(dirname)s\n"
- # % locals())
try:
outfile = open(listfilename, 'w')
except IOError, err:
@@ -360,6 +371,8 @@ def create_results_log(results, dirname = ".", show_missing = 1,
else:
executable_linenos = {}
+ n_lines = 0
+ n_hits = 0
lines_hit = per_file[key]
for i in range(len(lines)):
line = lines[i]
@@ -369,6 +382,8 @@ def create_results_log(results, dirname = ".", show_missing = 1,
if lines_hit.has_key(i+1):
# count precedes the lines that we captured
outfile.write('%5d: ' % lines_hit[i+1])
+ n_hits = n_hits + 1
+ n_lines = n_lines + 1
elif blank.match(line):
# blank lines and comments are preceded by dots
outfile.write(' . ')
@@ -383,10 +398,15 @@ def create_results_log(results, dirname = ".", show_missing = 1,
outfile.write('>>>>>> ')
else:
outfile.write(' '*7)
+ n_lines = n_lines + 1
outfile.write(string.expandtabs(lines[i], 8))
outfile.close()
+ if summary and n_lines:
+ percent = int(100 * n_hits / n_lines)
+ sums[modulename] = n_lines, percent, modulename, filename
+
if save_counts:
# try and store counts and module info into dirname
try:
@@ -398,6 +418,14 @@ def create_results_log(results, dirname = ".", show_missing = 1,
sys.stderr.write("cannot save counts/modules " \
"files because %s" % err.strerror)
+ if summary and sums:
+ mods = sums.keys()
+ mods.sort()
+ print "lines cov% module (path)"
+ for m in mods:
+ n_lines, percent, modulename, filename = sums[m]
+ print "%5d %3d%% %s (%s)" % sums[m]
+
# There is a lot of code shared between these two classes even though
# it is straightforward to make a super class to share code. However,
# for performance reasons (remember, this is called at every step) I
@@ -419,7 +447,12 @@ class Coverage:
filename = frame.f_globals.get("__file__", None)
if filename is None:
filename = frame.f_code.co_filename
- modulename = frame.f_globals["__name__"]
+ try:
+ modulename = frame.f_globals["__name__"]
+ except KeyError:
+ # PyRun_String() for example
+ # XXX what to do?
+ modulename = None
# We do this next block to keep from having to make methods
# calls, which also requires resetting the trace
@@ -449,12 +482,18 @@ class Trace:
self.ignore = ignore
self.ignore_names = ignore._ignore # access ignore's cache (speed hack)
- self.files = {'<string>': None} # stores lines from the .py file, or None
+ self.files = {'<string>': None} # stores lines from the .py file,
+ # or None
def trace(self, frame, why, arg):
if why == 'line':
filename = frame.f_code.co_filename
- modulename = frame.f_globals["__name__"]
+ try:
+ modulename = frame.f_globals["__name__"]
+ except KeyError:
+ # PyRun_String() for example
+ # XXX what to do?
+ modulename = None
# We do this next block to keep from having to make methods
# calls, which also requires resetting the trace
@@ -474,7 +513,8 @@ class Trace:
# If you want to see filenames (the original behaviour), try:
# modulename = filename
- # or, prettier but confusing when several files have the same name
+ # or, prettier but confusing when several files have the
+ # same name
# modulename = os.path.basename(filename)
if files[filename] != None:
@@ -487,7 +527,7 @@ class Trace:
def _err_exit(msg):
- sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
+ print >> sys.stderr, "%s: %s" % (sys.argv[0], msg)
sys.exit(1)
def main(argv = None):
@@ -496,15 +536,17 @@ def main(argv = None):
if argv is None:
argv = sys.argv
try:
- opts, prog_argv = getopt.getopt(argv[1:], "tcrRf:d:m",
+ opts, prog_argv = getopt.getopt(argv[1:], "tcrRf:d:msC:",
["help", "version", "trace", "count",
"report", "no-report",
"file=", "logdir=", "missing",
- "ignore-module=", "ignore-dir="])
+ "ignore-module=", "ignore-dir=",
+ "coverdir="])
except getopt.error, msg:
- sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
- sys.stderr.write("Try `%s --help' for more information\n" % sys.argv[0])
+ print >> sys.stderr, "%s: %s" % (sys.argv[0], msg)
+ print >> sys.stderr, "Try `%s --help' for more information" \
+ % sys.argv[0]
sys.exit(1)
trace = 0
@@ -516,6 +558,8 @@ def main(argv = None):
missing = 0
ignore_modules = []
ignore_dirs = []
+ coverdir = None
+ summary = 0
for opt, val in opts:
if opt == "--help":
@@ -554,6 +598,14 @@ def main(argv = None):
missing = 1
continue
+ if opt == "-C" or opt == "--coverdir":
+ coverdir = val
+ continue
+
+ if opt == "-s" or opt == "--summary":
+ summary = 1
+ continue
+
if opt == "--ignore-module":
ignore_modules.append(val)
continue
@@ -638,7 +690,8 @@ def main(argv = None):
results.update(CoverageResults(old_counts, old_modules))
if not no_report:
- create_results_log(results, logdir, missing)
+ create_results_log(results, logdir, missing,
+ summary=summary, coverdir=coverdir)
if counts_file:
try:
@@ -651,7 +704,8 @@ def main(argv = None):
elif report:
old_counts, old_modules = marshal.load(open(counts_file, 'rb'))
results = CoverageResults(old_counts, old_modules)
- create_results_log(results, logdir, missing)
+ create_results_log(results, logdir, missing,
+ summary=summary, coverdir=coverdir)
else:
assert 0, "Should never get here"