| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | #!/usr/bin/env python | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # portions copyright 2001, Autonomous Zones Industries, Inc., all rights... | 
					
						
							|  |  |  | # err...  reserved and offered to the public under the terms of the | 
					
						
							|  |  |  | # Python 2.2 license. | 
					
						
							|  |  |  | # Author: Zooko O'Whielacronx | 
					
						
							|  |  |  | # http://zooko.com/ | 
					
						
							|  |  |  | # mailto:zooko@zooko.com | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Copyright 2000, Mojam Media, Inc., all rights reserved. | 
					
						
							|  |  |  | # Author: Skip Montanaro | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Copyright 1999, Bioreason, Inc., all rights reserved. | 
					
						
							|  |  |  | # Author: Andrew Dalke | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Copyright 1995-1997, Automatrix, Inc., all rights reserved. | 
					
						
							|  |  |  | # Author: Skip Montanaro | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Copyright 1991-1995, Stichting Mathematisch Centrum, all rights reserved. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Permission to use, copy, modify, and distribute this Python software and | 
					
						
							|  |  |  | # its associated documentation for any purpose without fee is hereby | 
					
						
							|  |  |  | # granted, provided that the above copyright notice appears in all copies, | 
					
						
							|  |  |  | # and that both that copyright notice and this permission notice appear in | 
					
						
							|  |  |  | # supporting documentation, and that the name of neither Automatrix, | 
					
						
							|  |  |  | # Bioreason or Mojam Media be used in advertising or publicity pertaining to | 
					
						
							|  |  |  | # distribution of the software without specific, written prior permission. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | """program/module to trace Python program or function execution
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Sample use, command line: | 
					
						
							|  |  |  |   trace.py -c -f counts --ignore-dir '$prefix' spam.py eggs | 
					
						
							|  |  |  |   trace.py -t --ignore-dir '$prefix' spam.py eggs | 
					
						
							| 
									
										
										
										
											2004-04-10 16:29:58 +00:00
										 |  |  |   trace.py --trackcalls spam.py eggs | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | Sample use, programmatically | 
					
						
							| 
									
										
										
										
											2006-04-23 19:32:14 +00:00
										 |  |  |   import sys | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # create a Trace object, telling it what to ignore, and whether to | 
					
						
							|  |  |  |   # do tracing or line-counting or both. | 
					
						
							|  |  |  |   tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,], trace=0, | 
					
						
							| 
									
										
										
										
											2006-04-24 22:45:13 +00:00
										 |  |  |                     count=1) | 
					
						
							| 
									
										
										
										
											2006-04-23 19:32:14 +00:00
										 |  |  |   # run the new command using the given tracer | 
					
						
							|  |  |  |   tracer.run('main()') | 
					
						
							|  |  |  |   # make a report, placing output in /tmp | 
					
						
							|  |  |  |   r = tracer.results() | 
					
						
							|  |  |  |   r.write_results(show_missing=True, coverdir="/tmp") | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  | import linecache | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import re | 
					
						
							|  |  |  | import sys | 
					
						
							| 
									
										
										
										
											2003-06-26 14:56:17 +00:00
										 |  |  | import threading | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  | import token | 
					
						
							|  |  |  | import tokenize | 
					
						
							|  |  |  | import types | 
					
						
							| 
									
										
										
										
											2004-04-10 16:29:58 +00:00
										 |  |  | import gc | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | try: | 
					
						
							|  |  |  |     import cPickle | 
					
						
							|  |  |  |     pickle = cPickle | 
					
						
							|  |  |  | except ImportError: | 
					
						
							|  |  |  |     import pickle | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def usage(outfile): | 
					
						
							|  |  |  |     outfile.write("""Usage: %s [OPTIONS] <file> [ARGS]
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Meta-options: | 
					
						
							|  |  |  | --help                Display this help then exit. | 
					
						
							|  |  |  | --version             Output version information then exit. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Otherwise, exactly one of the following three options must be given: | 
					
						
							|  |  |  | -t, --trace           Print each line to sys.stdout before it is executed. | 
					
						
							|  |  |  | -c, --count           Count the number of times each line is executed | 
					
						
							|  |  |  |                       and write the counts to <module>.cover for each | 
					
						
							|  |  |  |                       module executed, in the module's directory. | 
					
						
							|  |  |  |                       See also `--coverdir', `--file', `--no-report' below. | 
					
						
							| 
									
										
										
										
											2003-06-27 19:09:33 +00:00
										 |  |  | -l, --listfuncs       Keep track of which functions are executed at least | 
					
						
							| 
									
										
										
										
											2003-06-27 19:22:11 +00:00
										 |  |  |                       once and write the results to sys.stdout after the | 
					
						
							| 
									
										
										
										
											2003-06-27 19:09:33 +00:00
										 |  |  |                       program exits. | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  | -T, --trackcalls      Keep track of caller/called pairs and write the | 
					
						
							|  |  |  |                       results to sys.stdout after the program exits. | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | -r, --report          Generate a report from a counts file; do not execute | 
					
						
							|  |  |  |                       any code.  `--file' must specify the results file to | 
					
						
							|  |  |  |                       read, which must have been created in a previous run | 
					
						
							|  |  |  |                       with `--count --file=FILE'. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Modifiers: | 
					
						
							|  |  |  | -f, --file=<file>     File to accumulate counts over several runs. | 
					
						
							|  |  |  | -R, --no-report       Do not generate the coverage report files. | 
					
						
							|  |  |  |                       Useful if you want to accumulate over several runs. | 
					
						
							|  |  |  | -C, --coverdir=<dir>  Directory where the report files.  The coverage | 
					
						
							|  |  |  |                       report for <package>.<module> is written to file | 
					
						
							|  |  |  |                       <dir>/<package>/<module>.cover. | 
					
						
							|  |  |  | -m, --missing         Annotate executable lines that were not executed | 
					
						
							|  |  |  |                       with '>>>>>> '. | 
					
						
							|  |  |  | -s, --summary         Write a brief summary on stdout for each file. | 
					
						
							|  |  |  |                       (Can only be used with --count or --report.) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Filters, may be repeated multiple times: | 
					
						
							|  |  |  | --ignore-module=<mod> Ignore the given module and its submodules | 
					
						
							|  |  |  |                       (if it is a package). | 
					
						
							|  |  |  | --ignore-dir=<dir>    Ignore files in the given directory (multiple | 
					
						
							|  |  |  |                       directories can be joined by os.pathsep). | 
					
						
							|  |  |  | """ % sys.argv[0])
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  | PRAGMA_NOCOVER = "#pragma NO COVER" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Simple rx to find lines with no code. | 
					
						
							|  |  |  | rx_blank = re.compile(r'^\s*(#.*)?$') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | class Ignore: | 
					
						
							|  |  |  |     def __init__(self, modules = None, dirs = None): | 
					
						
							|  |  |  |         self._mods = modules or [] | 
					
						
							|  |  |  |         self._dirs = dirs or [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._dirs = map(os.path.normpath, self._dirs) | 
					
						
							|  |  |  |         self._ignore = { '<string>': 1 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def names(self, filename, modulename): | 
					
						
							|  |  |  |         if self._ignore.has_key(modulename): | 
					
						
							|  |  |  |             return self._ignore[modulename] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # haven't seen this one before, so see if the module name is | 
					
						
							|  |  |  |         # on the ignore list.  Need to take some care since ignoring | 
					
						
							|  |  |  |         # "cmp" musn't mean ignoring "cmpcache" but ignoring | 
					
						
							|  |  |  |         # "Spam" must also mean ignoring "Spam.Eggs". | 
					
						
							|  |  |  |         for mod in self._mods: | 
					
						
							|  |  |  |             if mod == modulename:  # Identical names, so ignore | 
					
						
							|  |  |  |                 self._ignore[modulename] = 1 | 
					
						
							|  |  |  |                 return 1 | 
					
						
							|  |  |  |             # check if the module is a proper submodule of something on | 
					
						
							|  |  |  |             # the ignore list | 
					
						
							|  |  |  |             n = len(mod) | 
					
						
							|  |  |  |             # (will not overflow since if the first n characters are the | 
					
						
							| 
									
										
										
										
											2005-10-28 14:39:47 +00:00
										 |  |  |             # same and the name has not already occurred, then the size | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |             # of "name" is greater than that of "mod") | 
					
						
							|  |  |  |             if mod == modulename[:n] and modulename[n] == '.': | 
					
						
							|  |  |  |                 self._ignore[modulename] = 1 | 
					
						
							|  |  |  |                 return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Now check that __file__ isn't in one of the directories | 
					
						
							|  |  |  |         if filename is None: | 
					
						
							|  |  |  |             # must be a built-in, so we must ignore | 
					
						
							|  |  |  |             self._ignore[modulename] = 1 | 
					
						
							|  |  |  |             return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Ignore a file when it contains one of the ignorable paths | 
					
						
							|  |  |  |         for d in self._dirs: | 
					
						
							|  |  |  |             # The '+ os.sep' is to ensure that d is a parent directory, | 
					
						
							|  |  |  |             # as compared to cases like: | 
					
						
							|  |  |  |             #  d = "/usr/local" | 
					
						
							|  |  |  |             #  filename = "/usr/local.py" | 
					
						
							|  |  |  |             # or | 
					
						
							|  |  |  |             #  d = "/usr/local.py" | 
					
						
							|  |  |  |             #  filename = "/usr/local.py" | 
					
						
							|  |  |  |             if filename.startswith(d + os.sep): | 
					
						
							|  |  |  |                 self._ignore[modulename] = 1 | 
					
						
							|  |  |  |                 return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Tried the different ways, so we don't ignore this module | 
					
						
							|  |  |  |         self._ignore[modulename] = 0 | 
					
						
							|  |  |  |         return 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  | def modname(path): | 
					
						
							|  |  |  |     """Return a plausible module name for the patch.""" | 
					
						
							| 
									
										
										
										
											2003-04-21 22:49:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |     base = os.path.basename(path) | 
					
						
							|  |  |  |     filename, ext = os.path.splitext(base) | 
					
						
							|  |  |  |     return filename | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:49:17 +00:00
										 |  |  | def fullmodname(path): | 
					
						
							| 
									
										
										
										
											2003-04-22 15:35:51 +00:00
										 |  |  |     """Return a plausible module name for the path.""" | 
					
						
							| 
									
										
										
										
											2003-04-21 22:49:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # If the file 'path' is part of a package, then the filename isn't | 
					
						
							|  |  |  |     # enough to uniquely identify it.  Try to do the right thing by | 
					
						
							|  |  |  |     # looking in sys.path for the longest matching prefix.  We'll | 
					
						
							|  |  |  |     # assume that the rest is the package name. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-08-14 21:55:28 +00:00
										 |  |  |     comparepath = os.path.normcase(path) | 
					
						
							| 
									
										
										
										
											2003-04-21 22:49:17 +00:00
										 |  |  |     longest = "" | 
					
						
							|  |  |  |     for dir in sys.path: | 
					
						
							| 
									
										
										
										
											2006-08-14 21:55:28 +00:00
										 |  |  |         dir = os.path.normcase(dir) | 
					
						
							|  |  |  |         if comparepath.startswith(dir) and comparepath[len(dir)] == os.sep: | 
					
						
							| 
									
										
										
										
											2003-04-21 22:49:17 +00:00
										 |  |  |             if len(dir) > len(longest): | 
					
						
							|  |  |  |                 longest = dir | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-10-10 23:02:01 +00:00
										 |  |  |     if longest: | 
					
						
							|  |  |  |         base = path[len(longest) + 1:] | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         base = path | 
					
						
							| 
									
										
										
										
											2004-02-19 19:16:50 +00:00
										 |  |  |     base = base.replace(os.sep, ".") | 
					
						
							|  |  |  |     if os.altsep: | 
					
						
							|  |  |  |         base = base.replace(os.altsep, ".") | 
					
						
							| 
									
										
										
										
											2003-04-21 22:49:17 +00:00
										 |  |  |     filename, ext = os.path.splitext(base) | 
					
						
							|  |  |  |     return filename | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | class CoverageResults: | 
					
						
							|  |  |  |     def __init__(self, counts=None, calledfuncs=None, infile=None, | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |                  callers=None, outfile=None): | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         self.counts = counts | 
					
						
							|  |  |  |         if self.counts is None: | 
					
						
							|  |  |  |             self.counts = {} | 
					
						
							|  |  |  |         self.counter = self.counts.copy() # map (filename, lineno) to count | 
					
						
							|  |  |  |         self.calledfuncs = calledfuncs | 
					
						
							|  |  |  |         if self.calledfuncs is None: | 
					
						
							|  |  |  |             self.calledfuncs = {} | 
					
						
							|  |  |  |         self.calledfuncs = self.calledfuncs.copy() | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |         self.callers = callers | 
					
						
							|  |  |  |         if self.callers is None: | 
					
						
							|  |  |  |             self.callers = {} | 
					
						
							|  |  |  |         self.callers = self.callers.copy() | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         self.infile = infile | 
					
						
							|  |  |  |         self.outfile = outfile | 
					
						
							|  |  |  |         if self.infile: | 
					
						
							| 
									
										
										
										
											2003-07-07 16:08:47 +00:00
										 |  |  |             # Try to merge existing counts file. | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |             try: | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |                 counts, calledfuncs, callers = \ | 
					
						
							|  |  |  |                         pickle.load(open(self.infile, 'rb')) | 
					
						
							|  |  |  |                 self.update(self.__class__(counts, calledfuncs, callers)) | 
					
						
							| 
									
										
										
										
											2003-07-07 16:08:47 +00:00
										 |  |  |             except (IOError, EOFError, ValueError), err: | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |                 print >> sys.stderr, ("Skipping counts file %r: %s" | 
					
						
							|  |  |  |                                       % (self.infile, err)) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def update(self, other): | 
					
						
							|  |  |  |         """Merge in the data from another CoverageResults""" | 
					
						
							|  |  |  |         counts = self.counts | 
					
						
							|  |  |  |         calledfuncs = self.calledfuncs | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |         callers = self.callers | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         other_counts = other.counts | 
					
						
							|  |  |  |         other_calledfuncs = other.calledfuncs | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |         other_callers = other.callers | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for key in other_counts.keys(): | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |             counts[key] = counts.get(key, 0) + other_counts[key] | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for key in other_calledfuncs.keys(): | 
					
						
							|  |  |  |             calledfuncs[key] = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |         for key in other_callers.keys(): | 
					
						
							|  |  |  |             callers[key] = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |     def write_results(self, show_missing=True, summary=False, coverdir=None): | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         @param coverdir | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |         if self.calledfuncs: | 
					
						
							| 
									
										
										
										
											2004-04-10 16:29:58 +00:00
										 |  |  |             print | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |             print "functions called:" | 
					
						
							|  |  |  |             calls = self.calledfuncs.keys() | 
					
						
							|  |  |  |             calls.sort() | 
					
						
							|  |  |  |             for filename, modulename, funcname in calls: | 
					
						
							|  |  |  |                 print ("filename: %s, modulename: %s, funcname: %s" | 
					
						
							|  |  |  |                        % (filename, modulename, funcname)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self.callers: | 
					
						
							| 
									
										
										
										
											2004-04-10 16:29:58 +00:00
										 |  |  |             print | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |             print "calling relationships:" | 
					
						
							|  |  |  |             calls = self.callers.keys() | 
					
						
							|  |  |  |             calls.sort() | 
					
						
							|  |  |  |             lastfile = lastcfile = "" | 
					
						
							|  |  |  |             for ((pfile, pmod, pfunc), (cfile, cmod, cfunc)) in calls: | 
					
						
							|  |  |  |                 if pfile != lastfile: | 
					
						
							| 
									
										
										
										
											2004-04-10 16:29:58 +00:00
										 |  |  |                     print | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |                     print "***", pfile, "***" | 
					
						
							|  |  |  |                     lastfile = pfile | 
					
						
							|  |  |  |                     lastcfile = "" | 
					
						
							|  |  |  |                 if cfile != pfile and lastcfile != cfile: | 
					
						
							|  |  |  |                     print "  -->", cfile | 
					
						
							|  |  |  |                     lastcfile = cfile | 
					
						
							|  |  |  |                 print "    %s.%s -> %s.%s" % (pmod, pfunc, cmod, cfunc) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # turn the counts data ("(filename, lineno) = count") into something | 
					
						
							|  |  |  |         # accessible on a per-file basis | 
					
						
							|  |  |  |         per_file = {} | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |         for filename, lineno in self.counts.keys(): | 
					
						
							|  |  |  |             lines_hit = per_file[filename] = per_file.get(filename, {}) | 
					
						
							|  |  |  |             lines_hit[lineno] = self.counts[(filename, lineno)] | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # accumulate summary info, if needed | 
					
						
							|  |  |  |         sums = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |         for filename, count in per_file.iteritems(): | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |             # skip some "files" we don't care about... | 
					
						
							|  |  |  |             if filename == "<string>": | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2007-11-23 17:08:35 +00:00
										 |  |  |             if filename.startswith("<doctest "): | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-09 20:43:48 +00:00
										 |  |  |             if filename.endswith((".pyc", ".pyo")): | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |                 filename = filename[:-1] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |             if coverdir is None: | 
					
						
							|  |  |  |                 dir = os.path.dirname(os.path.abspath(filename)) | 
					
						
							| 
									
										
										
										
											2003-04-22 15:35:51 +00:00
										 |  |  |                 modulename = modname(filename) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |                 dir = coverdir | 
					
						
							|  |  |  |                 if not os.path.exists(dir): | 
					
						
							|  |  |  |                     os.makedirs(dir) | 
					
						
							| 
									
										
										
										
											2003-04-22 15:35:51 +00:00
										 |  |  |                 modulename = fullmodname(filename) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # If desired, get a list of the line numbers which represent | 
					
						
							|  |  |  |             # executable content (returned as a dict for better lookup speed) | 
					
						
							|  |  |  |             if show_missing: | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |                 lnotab = find_executable_linenos(filename) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |                 lnotab = {} | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |             source = linecache.getlines(filename) | 
					
						
							|  |  |  |             coverpath = os.path.join(dir, modulename + ".cover") | 
					
						
							|  |  |  |             n_hits, n_lines = self.write_results_file(coverpath, source, | 
					
						
							|  |  |  |                                                       lnotab, count) | 
					
						
							| 
									
										
										
										
											2003-04-24 16:02:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |             if summary and n_lines: | 
					
						
							|  |  |  |                 percent = int(100 * n_hits / n_lines) | 
					
						
							|  |  |  |                 sums[modulename] = n_lines, percent, modulename, filename | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self.outfile: | 
					
						
							|  |  |  |             # try and store counts and module info into self.outfile | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |                 pickle.dump((self.counts, self.calledfuncs, self.callers), | 
					
						
							| 
									
										
										
										
											2003-10-14 20:12:06 +00:00
										 |  |  |                             open(self.outfile, 'wb'), 1) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |             except IOError, err: | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |                 print >> sys.stderr, "Can't save counts files because %s" % err | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def write_results_file(self, path, lines, lnotab, lines_hit): | 
					
						
							|  |  |  |         """Return a coverage results file in path.""" | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             outfile = open(path, "w") | 
					
						
							|  |  |  |         except IOError, err: | 
					
						
							|  |  |  |             print >> sys.stderr, ("trace: Could not open %r for writing: %s" | 
					
						
							|  |  |  |                                   "- skipping" % (path, err)) | 
					
						
							| 
									
										
										
										
											2004-02-19 19:16:50 +00:00
										 |  |  |             return 0, 0 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         n_lines = 0 | 
					
						
							|  |  |  |         n_hits = 0 | 
					
						
							|  |  |  |         for i, line in enumerate(lines): | 
					
						
							|  |  |  |             lineno = i + 1 | 
					
						
							|  |  |  |             # do the blank/comment match to try to mark more lines | 
					
						
							|  |  |  |             # (help the reader find stuff that hasn't been covered) | 
					
						
							|  |  |  |             if lineno in lines_hit: | 
					
						
							|  |  |  |                 outfile.write("%5d: " % lines_hit[lineno]) | 
					
						
							|  |  |  |                 n_hits += 1 | 
					
						
							|  |  |  |                 n_lines += 1 | 
					
						
							|  |  |  |             elif rx_blank.match(line): | 
					
						
							| 
									
										
										
										
											2003-07-15 10:34:02 +00:00
										 |  |  |                 outfile.write("       ") | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 # lines preceded by no marks weren't hit | 
					
						
							|  |  |  |                 # Highlight them if so indicated, unless the line contains | 
					
						
							|  |  |  |                 # #pragma: NO COVER | 
					
						
							|  |  |  |                 if lineno in lnotab and not PRAGMA_NOCOVER in lines[i]: | 
					
						
							|  |  |  |                     outfile.write(">>>>>> ") | 
					
						
							| 
									
										
										
										
											2003-06-26 14:56:17 +00:00
										 |  |  |                     n_lines += 1 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     outfile.write("       ") | 
					
						
							|  |  |  |             outfile.write(lines[i].expandtabs(8)) | 
					
						
							|  |  |  |         outfile.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return n_hits, n_lines | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def find_lines_from_code(code, strs): | 
					
						
							|  |  |  |     """Return dict where keys are lines in the line number table.""" | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |     linenos = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     line_increments = [ord(c) for c in code.co_lnotab[1::2]] | 
					
						
							|  |  |  |     table_length = len(line_increments) | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |     docstring = False | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     lineno = code.co_firstlineno | 
					
						
							|  |  |  |     for li in line_increments: | 
					
						
							|  |  |  |         lineno += li | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |         if lineno not in strs: | 
					
						
							|  |  |  |             linenos[lineno] = 1 | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return linenos | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  | def find_lines(code, strs): | 
					
						
							|  |  |  |     """Return lineno dict for all code objects reachable from code.""" | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |     # get all of the lineno information from the code of this scope level | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |     linenos = find_lines_from_code(code, strs) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # and check the constants for references to other code objects | 
					
						
							|  |  |  |     for c in code.co_consts: | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |         if isinstance(c, types.CodeType): | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |             # find another code object, so recurse into it | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |             linenos.update(find_lines(c, strs)) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |     return linenos | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  | def find_strings(filename): | 
					
						
							|  |  |  |     """Return a dict of possible docstring positions.
 | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |     The dict maps line numbers to strings.  There is an entry for | 
					
						
							|  |  |  |     line that contains only a string or a part of a triple-quoted | 
					
						
							|  |  |  |     string. | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |     d = {} | 
					
						
							|  |  |  |     # If the first token is a string, then it's the module docstring. | 
					
						
							|  |  |  |     # Add this special case so that the test in the loop passes. | 
					
						
							|  |  |  |     prev_ttype = token.INDENT | 
					
						
							|  |  |  |     f = open(filename) | 
					
						
							|  |  |  |     for ttype, tstr, start, end, line in tokenize.generate_tokens(f.readline): | 
					
						
							|  |  |  |         if ttype == token.STRING: | 
					
						
							|  |  |  |             if prev_ttype == token.INDENT: | 
					
						
							|  |  |  |                 sline, scol = start | 
					
						
							|  |  |  |                 eline, ecol = end | 
					
						
							|  |  |  |                 for i in range(sline, eline + 1): | 
					
						
							|  |  |  |                     d[i] = 1 | 
					
						
							|  |  |  |         prev_ttype = ttype | 
					
						
							|  |  |  |     f.close() | 
					
						
							|  |  |  |     return d | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  | def find_executable_linenos(filename): | 
					
						
							|  |  |  |     """Return dict where keys are line numbers in the line number table.""" | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2004-04-16 03:28:19 +00:00
										 |  |  |         prog = open(filename, "rU").read() | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |     except IOError, err: | 
					
						
							|  |  |  |         print >> sys.stderr, ("Not printing coverage data for %r: %s" | 
					
						
							|  |  |  |                               % (filename, err)) | 
					
						
							|  |  |  |         return {} | 
					
						
							|  |  |  |     code = compile(prog, filename, "exec") | 
					
						
							|  |  |  |     strs = find_strings(filename) | 
					
						
							|  |  |  |     return find_lines(code, strs) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Trace: | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |     def __init__(self, count=1, trace=1, countfuncs=0, countcallers=0, | 
					
						
							|  |  |  |                  ignoremods=(), ignoredirs=(), infile=None, outfile=None): | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         @param count true iff it should count number of times each | 
					
						
							| 
									
										
										
										
											2003-02-19 02:35:07 +00:00
										 |  |  |                      line is executed | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         @param trace true iff it should print out each line that is | 
					
						
							| 
									
										
										
										
											2003-02-19 02:35:07 +00:00
										 |  |  |                      being counted | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         @param countfuncs true iff it should just output a list of | 
					
						
							|  |  |  |                      (filename, modulename, funcname,) for functions | 
					
						
							|  |  |  |                      that were called at least once;  This overrides | 
					
						
							| 
									
										
										
										
											2003-02-19 02:35:07 +00:00
										 |  |  |                      `count' and `trace' | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         @param ignoremods a list of the names of modules to ignore | 
					
						
							|  |  |  |         @param ignoredirs a list of the names of directories to ignore | 
					
						
							| 
									
										
										
										
											2003-02-19 02:35:07 +00:00
										 |  |  |                      all of the (recursive) contents of | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         @param infile file from which to read stored counts to be | 
					
						
							| 
									
										
										
										
											2003-02-19 02:35:07 +00:00
										 |  |  |                      added into the results | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         @param outfile file in which to write the results | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self.infile = infile | 
					
						
							|  |  |  |         self.outfile = outfile | 
					
						
							|  |  |  |         self.ignore = Ignore(ignoremods, ignoredirs) | 
					
						
							|  |  |  |         self.counts = {}   # keys are (filename, linenumber) | 
					
						
							|  |  |  |         self.blabbed = {} # for debugging | 
					
						
							|  |  |  |         self.pathtobasename = {} # for memoizing os.path.basename | 
					
						
							|  |  |  |         self.donothing = 0 | 
					
						
							|  |  |  |         self.trace = trace | 
					
						
							|  |  |  |         self._calledfuncs = {} | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |         self._callers = {} | 
					
						
							| 
									
										
										
										
											2004-04-10 16:29:58 +00:00
										 |  |  |         self._caller_cache = {} | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |         if countcallers: | 
					
						
							|  |  |  |             self.globaltrace = self.globaltrace_trackcallers | 
					
						
							|  |  |  |         elif countfuncs: | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |             self.globaltrace = self.globaltrace_countfuncs | 
					
						
							|  |  |  |         elif trace and count: | 
					
						
							|  |  |  |             self.globaltrace = self.globaltrace_lt | 
					
						
							|  |  |  |             self.localtrace = self.localtrace_trace_and_count | 
					
						
							|  |  |  |         elif trace: | 
					
						
							|  |  |  |             self.globaltrace = self.globaltrace_lt | 
					
						
							|  |  |  |             self.localtrace = self.localtrace_trace | 
					
						
							|  |  |  |         elif count: | 
					
						
							|  |  |  |             self.globaltrace = self.globaltrace_lt | 
					
						
							|  |  |  |             self.localtrace = self.localtrace_count | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # Ahem -- do nothing?  Okay. | 
					
						
							|  |  |  |             self.donothing = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def run(self, cmd): | 
					
						
							|  |  |  |         import __main__ | 
					
						
							|  |  |  |         dict = __main__.__dict__ | 
					
						
							|  |  |  |         if not self.donothing: | 
					
						
							|  |  |  |             sys.settrace(self.globaltrace) | 
					
						
							| 
									
										
										
										
											2003-06-26 14:56:17 +00:00
										 |  |  |             threading.settrace(self.globaltrace) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             exec cmd in dict, dict | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             if not self.donothing: | 
					
						
							|  |  |  |                 sys.settrace(None) | 
					
						
							| 
									
										
										
										
											2003-06-26 14:56:17 +00:00
										 |  |  |                 threading.settrace(None) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def runctx(self, cmd, globals=None, locals=None): | 
					
						
							|  |  |  |         if globals is None: globals = {} | 
					
						
							|  |  |  |         if locals is None: locals = {} | 
					
						
							|  |  |  |         if not self.donothing: | 
					
						
							|  |  |  |             sys.settrace(self.globaltrace) | 
					
						
							| 
									
										
										
										
											2003-06-26 14:56:17 +00:00
										 |  |  |             threading.settrace(self.globaltrace) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             exec cmd in globals, locals | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             if not self.donothing: | 
					
						
							|  |  |  |                 sys.settrace(None) | 
					
						
							| 
									
										
										
										
											2003-06-26 14:56:17 +00:00
										 |  |  |                 threading.settrace(None) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def runfunc(self, func, *args, **kw): | 
					
						
							|  |  |  |         result = None | 
					
						
							|  |  |  |         if not self.donothing: | 
					
						
							|  |  |  |             sys.settrace(self.globaltrace) | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2003-02-27 20:14:51 +00:00
										 |  |  |             result = func(*args, **kw) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         finally: | 
					
						
							|  |  |  |             if not self.donothing: | 
					
						
							|  |  |  |                 sys.settrace(None) | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-04-10 16:29:58 +00:00
										 |  |  |     def file_module_function_of(self, frame): | 
					
						
							|  |  |  |         code = frame.f_code | 
					
						
							|  |  |  |         filename = code.co_filename | 
					
						
							|  |  |  |         if filename: | 
					
						
							|  |  |  |             modulename = modname(filename) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             modulename = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         funcname = code.co_name | 
					
						
							|  |  |  |         clsname = None | 
					
						
							|  |  |  |         if code in self._caller_cache: | 
					
						
							|  |  |  |             if self._caller_cache[code] is not None: | 
					
						
							|  |  |  |                 clsname = self._caller_cache[code] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._caller_cache[code] = None | 
					
						
							|  |  |  |             ## use of gc.get_referrers() was suggested by Michael Hudson | 
					
						
							|  |  |  |             # all functions which refer to this code object | 
					
						
							|  |  |  |             funcs = [f for f in gc.get_referrers(code) | 
					
						
							|  |  |  |                          if hasattr(f, "func_doc")] | 
					
						
							|  |  |  |             # require len(func) == 1 to avoid ambiguity caused by calls to | 
					
						
							|  |  |  |             # new.function(): "In the face of ambiguity, refuse the | 
					
						
							|  |  |  |             # temptation to guess." | 
					
						
							|  |  |  |             if len(funcs) == 1: | 
					
						
							|  |  |  |                 dicts = [d for d in gc.get_referrers(funcs[0]) | 
					
						
							|  |  |  |                              if isinstance(d, dict)] | 
					
						
							|  |  |  |                 if len(dicts) == 1: | 
					
						
							|  |  |  |                     classes = [c for c in gc.get_referrers(dicts[0]) | 
					
						
							|  |  |  |                                    if hasattr(c, "__bases__")] | 
					
						
							|  |  |  |                     if len(classes) == 1: | 
					
						
							|  |  |  |                         # ditto for new.classobj() | 
					
						
							|  |  |  |                         clsname = str(classes[0]) | 
					
						
							|  |  |  |                         # cache the result - assumption is that new.* is | 
					
						
							|  |  |  |                         # not called later to disturb this relationship | 
					
						
							|  |  |  |                         # _caller_cache could be flushed if functions in | 
					
						
							|  |  |  |                         # the new module get called. | 
					
						
							|  |  |  |                         self._caller_cache[code] = clsname | 
					
						
							|  |  |  |         if clsname is not None: | 
					
						
							|  |  |  |             # final hack - module name shows up in str(cls), but we've already | 
					
						
							|  |  |  |             # computed module name, so remove it | 
					
						
							|  |  |  |             clsname = clsname.split(".")[1:] | 
					
						
							|  |  |  |             clsname = ".".join(clsname) | 
					
						
							|  |  |  |             funcname = "%s.%s" % (clsname, funcname) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return filename, modulename, funcname | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |     def globaltrace_trackcallers(self, frame, why, arg): | 
					
						
							|  |  |  |         """Handler for call events.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Adds information about who called who to the self._callers dict. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if why == 'call': | 
					
						
							|  |  |  |             # XXX Should do a better job of identifying methods | 
					
						
							| 
									
										
										
										
											2004-04-10 16:29:58 +00:00
										 |  |  |             this_func = self.file_module_function_of(frame) | 
					
						
							|  |  |  |             parent_func = self.file_module_function_of(frame.f_back) | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |             self._callers[(parent_func, this_func)] = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |     def globaltrace_countfuncs(self, frame, why, arg): | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |         """Handler for call events.
 | 
					
						
							| 
									
										
										
										
											2003-04-24 16:02:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |         Adds (filename, modulename, funcname) to the self._calledfuncs dict. | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         if why == 'call': | 
					
						
							| 
									
										
										
										
											2004-04-10 16:29:58 +00:00
										 |  |  |             this_func = self.file_module_function_of(frame) | 
					
						
							|  |  |  |             self._calledfuncs[this_func] = 1 | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def globaltrace_lt(self, frame, why, arg): | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |         """Handler for call events.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If the code block being entered is to be ignored, returns `None', | 
					
						
							|  |  |  |         else returns self.localtrace. | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         if why == 'call': | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |             code = frame.f_code | 
					
						
							| 
									
										
										
										
											2007-02-11 18:24:37 +00:00
										 |  |  |             filename = frame.f_globals.get('__file__', None) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |             if filename: | 
					
						
							| 
									
										
										
										
											2003-04-21 22:49:17 +00:00
										 |  |  |                 # XXX modname() doesn't work right for packages, so | 
					
						
							|  |  |  |                 # the ignore support won't work right for packages | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |                 modulename = modname(filename) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |                 if modulename is not None: | 
					
						
							|  |  |  |                     ignore_it = self.ignore.names(filename, modulename) | 
					
						
							|  |  |  |                     if not ignore_it: | 
					
						
							|  |  |  |                         if self.trace: | 
					
						
							|  |  |  |                             print (" --- modulename: %s, funcname: %s" | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |                                    % (modulename, code.co_name)) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |                         return self.localtrace | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def localtrace_trace_and_count(self, frame, why, arg): | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |         if why == "line": | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |             # record the file name and line number of every trace | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |             filename = frame.f_code.co_filename | 
					
						
							|  |  |  |             lineno = frame.f_lineno | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |             key = filename, lineno | 
					
						
							|  |  |  |             self.counts[key] = self.counts.get(key, 0) + 1 | 
					
						
							| 
									
										
										
										
											2003-02-19 02:35:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |             bname = os.path.basename(filename) | 
					
						
							|  |  |  |             print "%s(%d): %s" % (bname, lineno, | 
					
						
							|  |  |  |                                   linecache.getline(filename, lineno)), | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         return self.localtrace | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def localtrace_trace(self, frame, why, arg): | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |         if why == "line": | 
					
						
							|  |  |  |             # record the file name and line number of every trace | 
					
						
							|  |  |  |             filename = frame.f_code.co_filename | 
					
						
							|  |  |  |             lineno = frame.f_lineno | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             bname = os.path.basename(filename) | 
					
						
							|  |  |  |             print "%s(%d): %s" % (bname, lineno, | 
					
						
							|  |  |  |                                   linecache.getline(filename, lineno)), | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         return self.localtrace | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def localtrace_count(self, frame, why, arg): | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |         if why == "line": | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |             filename = frame.f_code.co_filename | 
					
						
							|  |  |  |             lineno = frame.f_lineno | 
					
						
							|  |  |  |             key = filename, lineno | 
					
						
							|  |  |  |             self.counts[key] = self.counts.get(key, 0) + 1 | 
					
						
							|  |  |  |         return self.localtrace | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def results(self): | 
					
						
							|  |  |  |         return CoverageResults(self.counts, infile=self.infile, | 
					
						
							|  |  |  |                                outfile=self.outfile, | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |                                calledfuncs=self._calledfuncs, | 
					
						
							|  |  |  |                                callers=self._callers) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def _err_exit(msg): | 
					
						
							|  |  |  |     sys.stderr.write("%s: %s\n" % (sys.argv[0], msg)) | 
					
						
							|  |  |  |     sys.exit(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def main(argv=None): | 
					
						
							|  |  |  |     import getopt | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if argv is None: | 
					
						
							|  |  |  |         argv = sys.argv | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |         opts, prog_argv = getopt.getopt(argv[1:], "tcrRf:d:msC:lT", | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |                                         ["help", "version", "trace", "count", | 
					
						
							|  |  |  |                                          "report", "no-report", "summary", | 
					
						
							|  |  |  |                                          "file=", "missing", | 
					
						
							|  |  |  |                                          "ignore-module=", "ignore-dir=", | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |                                          "coverdir=", "listfuncs", | 
					
						
							|  |  |  |                                          "trackcalls"]) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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]) | 
					
						
							|  |  |  |         sys.exit(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     trace = 0 | 
					
						
							|  |  |  |     count = 0 | 
					
						
							|  |  |  |     report = 0 | 
					
						
							|  |  |  |     no_report = 0 | 
					
						
							|  |  |  |     counts_file = None | 
					
						
							|  |  |  |     missing = 0 | 
					
						
							|  |  |  |     ignore_modules = [] | 
					
						
							|  |  |  |     ignore_dirs = [] | 
					
						
							|  |  |  |     coverdir = None | 
					
						
							|  |  |  |     summary = 0 | 
					
						
							|  |  |  |     listfuncs = False | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |     countcallers = False | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for opt, val in opts: | 
					
						
							|  |  |  |         if opt == "--help": | 
					
						
							|  |  |  |             usage(sys.stdout) | 
					
						
							|  |  |  |             sys.exit(0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if opt == "--version": | 
					
						
							|  |  |  |             sys.stdout.write("trace 2.0\n") | 
					
						
							|  |  |  |             sys.exit(0) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |         if opt == "-T" or opt == "--trackcalls": | 
					
						
							|  |  |  |             countcallers = True | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         if opt == "-l" or opt == "--listfuncs": | 
					
						
							|  |  |  |             listfuncs = True | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if opt == "-t" or opt == "--trace": | 
					
						
							|  |  |  |             trace = 1 | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if opt == "-c" or opt == "--count": | 
					
						
							|  |  |  |             count = 1 | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if opt == "-r" or opt == "--report": | 
					
						
							|  |  |  |             report = 1 | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if opt == "-R" or opt == "--no-report": | 
					
						
							|  |  |  |             no_report = 1 | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if opt == "-f" or opt == "--file": | 
					
						
							|  |  |  |             counts_file = val | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if opt == "-m" or opt == "--missing": | 
					
						
							|  |  |  |             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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if opt == "--ignore-dir": | 
					
						
							|  |  |  |             for s in val.split(os.pathsep): | 
					
						
							|  |  |  |                 s = os.path.expandvars(s) | 
					
						
							|  |  |  |                 # should I also call expanduser? (after all, could use $HOME) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 s = s.replace("$prefix", | 
					
						
							|  |  |  |                               os.path.join(sys.prefix, "lib", | 
					
						
							|  |  |  |                                            "python" + sys.version[:3])) | 
					
						
							|  |  |  |                 s = s.replace("$exec_prefix", | 
					
						
							|  |  |  |                               os.path.join(sys.exec_prefix, "lib", | 
					
						
							|  |  |  |                                            "python" + sys.version[:3])) | 
					
						
							|  |  |  |                 s = os.path.normpath(s) | 
					
						
							|  |  |  |                 ignore_dirs.append(s) | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         assert 0, "Should never get here" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if listfuncs and (count or trace): | 
					
						
							|  |  |  |         _err_exit("cannot specify both --listfuncs and (--trace or --count)") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |     if not (count or trace or report or listfuncs or countcallers): | 
					
						
							|  |  |  |         _err_exit("must specify one of --trace, --count, --report, " | 
					
						
							|  |  |  |                   "--listfuncs, or --trackcalls") | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if report and no_report: | 
					
						
							|  |  |  |         _err_exit("cannot specify both --report and --no-report") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if report and not counts_file: | 
					
						
							|  |  |  |         _err_exit("--report requires a --file") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if no_report and len(prog_argv) == 0: | 
					
						
							|  |  |  |         _err_exit("missing name of file to run") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # everything is ready | 
					
						
							|  |  |  |     if report: | 
					
						
							|  |  |  |         results = CoverageResults(infile=counts_file, outfile=counts_file) | 
					
						
							|  |  |  |         results.write_results(missing, summary=summary, coverdir=coverdir) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         sys.argv = prog_argv | 
					
						
							|  |  |  |         progname = prog_argv[0] | 
					
						
							|  |  |  |         sys.path[0] = os.path.split(progname)[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         t = Trace(count, trace, countfuncs=listfuncs, | 
					
						
							| 
									
										
										
										
											2004-04-07 15:46:05 +00:00
										 |  |  |                   countcallers=countcallers, ignoremods=ignore_modules, | 
					
						
							|  |  |  |                   ignoredirs=ignore_dirs, infile=counts_file, | 
					
						
							|  |  |  |                   outfile=counts_file) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |             t.run('execfile(%r)' % (progname,)) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         except IOError, err: | 
					
						
							| 
									
										
										
										
											2003-04-21 22:04:46 +00:00
										 |  |  |             _err_exit("Cannot run file %r because: %s" % (sys.argv[0], err)) | 
					
						
							| 
									
										
										
										
											2003-02-18 15:06:17 +00:00
										 |  |  |         except SystemExit: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         results = t.results() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not no_report: | 
					
						
							|  |  |  |             results.write_results(missing, summary=summary, coverdir=coverdir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__=='__main__': | 
					
						
							|  |  |  |     main() |