0001 #!/usr/bin/env python 0002 0003 # portions copyright 2001, Autonomous Zones Industries, Inc., all rights... 0004 # err... reserved and offered to the public under the terms of the 0005 # Python 2.2 license. 0006 # Author: Zooko O'Whielacronx 0007 # http://zooko.com/ 0008 # mailto:zooko@zooko.com 0009 # 0010 # Copyright 2000, Mojam Media, Inc., all rights reserved. 0011 # Author: Skip Montanaro 0012 # 0013 # Copyright 1999, Bioreason, Inc., all rights reserved. 0014 # Author: Andrew Dalke 0015 # 0016 # Copyright 1995-1997, Automatrix, Inc., all rights reserved. 0017 # Author: Skip Montanaro 0018 # 0019 # Copyright 1991-1995, Stichting Mathematisch Centrum, all rights reserved. 0020 # 0021 # 0022 # Permission to use, copy, modify, and distribute this Python software and 0023 # its associated documentation for any purpose without fee is hereby 0024 # granted, provided that the above copyright notice appears in all copies, 0025 # and that both that copyright notice and this permission notice appear in 0026 # supporting documentation, and that the name of neither Automatrix, 0027 # Bioreason or Mojam Media be used in advertising or publicity pertaining to 0028 # distribution of the software without specific, written prior permission. 0029 # 0030 """program/module to trace Python program or function execution 0031 0032 Sample use, command line: 0033 trace.py -c -f counts --ignore-dir '$prefix' spam.py eggs 0034 trace.py -t --ignore-dir '$prefix' spam.py eggs 0035 trace.py --trackcalls spam.py eggs 0036 0037 Sample use, programmatically 0038 # create a Trace object, telling it what to ignore, and whether to 0039 # do tracing or line-counting or both. 0040 trace = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,], trace=0, 0041 count=1) 0042 # run the new command using the given trace 0043 trace.run('main()') 0044 # make a report, telling it where you want output 0045 r = trace.results() 0046 r.write_results(show_missing=True) 0047 """ 0048 0049 import linecache 0050 import os 0051 import re 0052 import sys 0053 import threading 0054 import token 0055 import tokenize 0056 import types 0057 import gc 0058 0059 try: 0060 import cPickle 0061 pickle = cPickle 0062 except ImportError: 0063 import pickle 0064 0065 def usage(outfile): 0066 outfile.write("""Usage: %s [OPTIONS] <file> [ARGS] 0067 0068 Meta-options: 0069 --help Display this help then exit. 0070 --version Output version information then exit. 0071 0072 Otherwise, exactly one of the following three options must be given: 0073 -t, --trace Print each line to sys.stdout before it is executed. 0074 -c, --count Count the number of times each line is executed 0075 and write the counts to <module>.cover for each 0076 module executed, in the module's directory. 0077 See also `--coverdir', `--file', `--no-report' below. 0078 -l, --listfuncs Keep track of which functions are executed at least 0079 once and write the results to sys.stdout after the 0080 program exits. 0081 -T, --trackcalls Keep track of caller/called pairs and write the 0082 results to sys.stdout after the program exits. 0083 -r, --report Generate a report from a counts file; do not execute 0084 any code. `--file' must specify the results file to 0085 read, which must have been created in a previous run 0086 with `--count --file=FILE'. 0087 0088 Modifiers: 0089 -f, --file=<file> File to accumulate counts over several runs. 0090 -R, --no-report Do not generate the coverage report files. 0091 Useful if you want to accumulate over several runs. 0092 -C, --coverdir=<dir> Directory where the report files. The coverage 0093 report for <package>.<module> is written to file 0094 <dir>/<package>/<module>.cover. 0095 -m, --missing Annotate executable lines that were not executed 0096 with '>>>>>> '. 0097 -s, --summary Write a brief summary on stdout for each file. 0098 (Can only be used with --count or --report.) 0099 0100 Filters, may be repeated multiple times: 0101 --ignore-module=<mod> Ignore the given module and its submodules 0102 (if it is a package). 0103 --ignore-dir=<dir> Ignore files in the given directory (multiple 0104 directories can be joined by os.pathsep). 0105 """ % sys.argv[0]) 0106 0107 PRAGMA_NOCOVER = "#pragma NO COVER" 0108 0109 # Simple rx to find lines with no code. 0110 rx_blank = re.compile(r'^\s*(#.*)?$') 0111 0112 class Ignore: 0113 def __init__(self, modules = None, dirs = None): 0114 self._mods = modules or [] 0115 self._dirs = dirs or [] 0116 0117 self._dirs = map(os.path.normpath, self._dirs) 0118 self._ignore = { '<string>': 1 } 0119 0120 def names(self, filename, modulename): 0121 if self._ignore.has_key(modulename): 0122 return self._ignore[modulename] 0123 0124 # haven't seen this one before, so see if the module name is 0125 # on the ignore list. Need to take some care since ignoring 0126 # "cmp" musn't mean ignoring "cmpcache" but ignoring 0127 # "Spam" must also mean ignoring "Spam.Eggs". 0128 for mod in self._mods: 0129 if mod == modulename: # Identical names, so ignore 0130 self._ignore[modulename] = 1 0131 return 1 0132 # check if the module is a proper submodule of something on 0133 # the ignore list 0134 n = len(mod) 0135 # (will not overflow since if the first n characters are the 0136 # same and the name has not already occured, then the size 0137 # of "name" is greater than that of "mod") 0138 if mod == modulename[:n] and modulename[n] == '.': 0139 self._ignore[modulename] = 1 0140 return 1 0141 0142 # Now check that __file__ isn't in one of the directories 0143 if filename is None: 0144 # must be a built-in, so we must ignore 0145 self._ignore[modulename] = 1 0146 return 1 0147 0148 # Ignore a file when it contains one of the ignorable paths 0149 for d in self._dirs: 0150 # The '+ os.sep' is to ensure that d is a parent directory, 0151 # as compared to cases like: 0152 # d = "/usr/local" 0153 # filename = "/usr/local.py" 0154 # or 0155 # d = "/usr/local.py" 0156 # filename = "/usr/local.py" 0157 if filename.startswith(d + os.sep): 0158 self._ignore[modulename] = 1 0159 return 1 0160 0161 # Tried the different ways, so we don't ignore this module 0162 self._ignore[modulename] = 0 0163 return 0 0164 0165 def modname(path): 0166 """Return a plausible module name for the patch.""" 0167 0168 base = os.path.basename(path) 0169 filename, ext = os.path.splitext(base) 0170 return filename 0171 0172 def fullmodname(path): 0173 """Return a plausible module name for the path.""" 0174 0175 # If the file 'path' is part of a package, then the filename isn't 0176 # enough to uniquely identify it. Try to do the right thing by 0177 # looking in sys.path for the longest matching prefix. We'll 0178 # assume that the rest is the package name. 0179 0180 longest = "" 0181 for dir in sys.path: 0182 if path.startswith(dir) and path[len(dir)] == os.path.sep: 0183 if len(dir) > len(longest): 0184 longest = dir 0185 0186 if longest: 0187 base = path[len(longest) + 1:] 0188 else: 0189 base = path 0190 base = base.replace(os.sep, ".") 0191 if os.altsep: 0192 base = base.replace(os.altsep, ".") 0193 filename, ext = os.path.splitext(base) 0194 return filename 0195 0196 class CoverageResults: 0197 def __init__(self, counts=None, calledfuncs=None, infile=None, 0198 callers=None, outfile=None): 0199 self.counts = counts 0200 if self.counts is None: 0201 self.counts = {} 0202 self.counter = self.counts.copy() # map (filename, lineno) to count 0203 self.calledfuncs = calledfuncs 0204 if self.calledfuncs is None: 0205 self.calledfuncs = {} 0206 self.calledfuncs = self.calledfuncs.copy() 0207 self.callers = callers 0208 if self.callers is None: 0209 self.callers = {} 0210 self.callers = self.callers.copy() 0211 self.infile = infile 0212 self.outfile = outfile 0213 if self.infile: 0214 # Try to merge existing counts file. 0215 try: 0216 counts, calledfuncs, callers = \ 0217 pickle.load(open(self.infile, 'rb')) 0218 self.update(self.__class__(counts, calledfuncs, callers)) 0219 except (IOError, EOFError, ValueError), err: 0220 print >> sys.stderr, ("Skipping counts file %r: %s" 0221 % (self.infile, err)) 0222 0223 def update(self, other): 0224 """Merge in the data from another CoverageResults""" 0225 counts = self.counts 0226 calledfuncs = self.calledfuncs 0227 callers = self.callers 0228 other_counts = other.counts 0229 other_calledfuncs = other.calledfuncs 0230 other_callers = other.callers 0231 0232 for key in other_counts.keys(): 0233 counts[key] = counts.get(key, 0) + other_counts[key] 0234 0235 for key in other_calledfuncs.keys(): 0236 calledfuncs[key] = 1 0237 0238 for key in other_callers.keys(): 0239 callers[key] = 1 0240 0241 def write_results(self, show_missing=True, summary=False, coverdir=None): 0242 """ 0243 @param coverdir 0244 """ 0245 if self.calledfuncs: 0246 print 0247 print "functions called:" 0248 calls = self.calledfuncs.keys() 0249 calls.sort() 0250 for filename, modulename, funcname in calls: 0251 print ("filename: %s, modulename: %s, funcname: %s" 0252 % (filename, modulename, funcname)) 0253 0254 if self.callers: 0255 print 0256 print "calling relationships:" 0257 calls = self.callers.keys() 0258 calls.sort() 0259 lastfile = lastcfile = "" 0260 for ((pfile, pmod, pfunc), (cfile, cmod, cfunc)) in calls: 0261 if pfile != lastfile: 0262 print 0263 print "***", pfile, "***" 0264 lastfile = pfile 0265 lastcfile = "" 0266 if cfile != pfile and lastcfile != cfile: 0267 print " -->", cfile 0268 lastcfile = cfile 0269 print " %s.%s -> %s.%s" % (pmod, pfunc, cmod, cfunc) 0270 0271 # turn the counts data ("(filename, lineno) = count") into something 0272 # accessible on a per-file basis 0273 per_file = {} 0274 for filename, lineno in self.counts.keys(): 0275 lines_hit = per_file[filename] = per_file.get(filename, {}) 0276 lines_hit[lineno] = self.counts[(filename, lineno)] 0277 0278 # accumulate summary info, if needed 0279 sums = {} 0280 0281 for filename, count in per_file.iteritems(): 0282 # skip some "files" we don't care about... 0283 if filename == "<string>": 0284 continue 0285 0286 if filename.endswith(".pyc") or filename.endswith(".pyo"): 0287 filename = filename[:-1] 0288 0289 if coverdir is None: 0290 dir = os.path.dirname(os.path.abspath(filename)) 0291 modulename = modname(filename) 0292 else: 0293 dir = coverdir 0294 if not os.path.exists(dir): 0295 os.makedirs(dir) 0296 modulename = fullmodname(filename) 0297 0298 # If desired, get a list of the line numbers which represent 0299 # executable content (returned as a dict for better lookup speed) 0300 if show_missing: 0301 lnotab = find_executable_linenos(filename) 0302 else: 0303 lnotab = {} 0304 0305 source = linecache.getlines(filename) 0306 coverpath = os.path.join(dir, modulename + ".cover") 0307 n_hits, n_lines = self.write_results_file(coverpath, source, 0308 lnotab, count) 0309 0310 if summary and n_lines: 0311 percent = int(100 * n_hits / n_lines) 0312 sums[modulename] = n_lines, percent, modulename, filename 0313 0314 if summary and sums: 0315 mods = sums.keys() 0316 mods.sort() 0317 print "lines cov% module (path)" 0318 for m in mods: 0319 n_lines, percent, modulename, filename = sums[m] 0320 print "%5d %3d%% %s (%s)" % sums[m] 0321 0322 if self.outfile: 0323 # try and store counts and module info into self.outfile 0324 try: 0325 pickle.dump((self.counts, self.calledfuncs, self.callers), 0326 open(self.outfile, 'wb'), 1) 0327 except IOError, err: 0328 print >> sys.stderr, "Can't save counts files because %s" % err 0329 0330 def write_results_file(self, path, lines, lnotab, lines_hit): 0331 """Return a coverage results file in path.""" 0332 0333 try: 0334 outfile = open(path, "w") 0335 except IOError, err: 0336 print >> sys.stderr, ("trace: Could not open %r for writing: %s" 0337 "- skipping" % (path, err)) 0338 return 0, 0 0339 0340 n_lines = 0 0341 n_hits = 0 0342 for i, line in enumerate(lines): 0343 lineno = i + 1 0344 # do the blank/comment match to try to mark more lines 0345 # (help the reader find stuff that hasn't been covered) 0346 if lineno in lines_hit: 0347 outfile.write("%5d: " % lines_hit[lineno]) 0348 n_hits += 1 0349 n_lines += 1 0350 elif rx_blank.match(line): 0351 outfile.write(" ") 0352 else: 0353 # lines preceded by no marks weren't hit 0354 # Highlight them if so indicated, unless the line contains 0355 # #pragma: NO COVER 0356 if lineno in lnotab and not PRAGMA_NOCOVER in lines[i]: 0357 outfile.write(">>>>>> ") 0358 n_lines += 1 0359 else: 0360 outfile.write(" ") 0361 outfile.write(lines[i].expandtabs(8)) 0362 outfile.close() 0363 0364 return n_hits, n_lines 0365 0366 def find_lines_from_code(code, strs): 0367 """Return dict where keys are lines in the line number table.""" 0368 linenos = {} 0369 0370 line_increments = [ord(c) for c in code.co_lnotab[1::2]] 0371 table_length = len(line_increments) 0372 docstring = False 0373 0374 lineno = code.co_firstlineno 0375 for li in line_increments: 0376 lineno += li 0377 if lineno not in strs: 0378 linenos[lineno] = 1 0379 0380 return linenos 0381 0382 def find_lines(code, strs): 0383 """Return lineno dict for all code objects reachable from code.""" 0384 # get all of the lineno information from the code of this scope level 0385 linenos = find_lines_from_code(code, strs) 0386 0387 # and check the constants for references to other code objects 0388 for c in code.co_consts: 0389 if isinstance(c, types.CodeType): 0390 # find another code object, so recurse into it 0391 linenos.update(find_lines(c, strs)) 0392 return linenos 0393 0394 def find_strings(filename): 0395 """Return a dict of possible docstring positions. 0396 0397 The dict maps line numbers to strings. There is an entry for 0398 line that contains only a string or a part of a triple-quoted 0399 string. 0400 """ 0401 d = {} 0402 # If the first token is a string, then it's the module docstring. 0403 # Add this special case so that the test in the loop passes. 0404 prev_ttype = token.INDENT 0405 f = open(filename) 0406 for ttype, tstr, start, end, line in tokenize.generate_tokens(f.readline): 0407 if ttype == token.STRING: 0408 if prev_ttype == token.INDENT: 0409 sline, scol = start 0410 eline, ecol = end 0411 for i in range(sline, eline + 1): 0412 d[i] = 1 0413 prev_ttype = ttype 0414 f.close() 0415 return d 0416 0417 def find_executable_linenos(filename): 0418 """Return dict where keys are line numbers in the line number table.""" 0419 assert filename.endswith('.py') 0420 try: 0421 prog = open(filename, "rU").read() 0422 except IOError, err: 0423 print >> sys.stderr, ("Not printing coverage data for %r: %s" 0424 % (filename, err)) 0425 return {} 0426 code = compile(prog, filename, "exec") 0427 strs = find_strings(filename) 0428 return find_lines(code, strs) 0429 0430 class Trace: 0431 def __init__(self, count=1, trace=1, countfuncs=0, countcallers=0, 0432 ignoremods=(), ignoredirs=(), infile=None, outfile=None): 0433 """ 0434 @param count true iff it should count number of times each 0435 line is executed 0436 @param trace true iff it should print out each line that is 0437 being counted 0438 @param countfuncs true iff it should just output a list of 0439 (filename, modulename, funcname,) for functions 0440 that were called at least once; This overrides 0441 `count' and `trace' 0442 @param ignoremods a list of the names of modules to ignore 0443 @param ignoredirs a list of the names of directories to ignore 0444 all of the (recursive) contents of 0445 @param infile file from which to read stored counts to be 0446 added into the results 0447 @param outfile file in which to write the results 0448 """ 0449 self.infile = infile 0450 self.outfile = outfile 0451 self.ignore = Ignore(ignoremods, ignoredirs) 0452 self.counts = {} # keys are (filename, linenumber) 0453 self.blabbed = {} # for debugging 0454 self.pathtobasename = {} # for memoizing os.path.basename 0455 self.donothing = 0 0456 self.trace = trace 0457 self._calledfuncs = {} 0458 self._callers = {} 0459 self._caller_cache = {} 0460 if countcallers: 0461 self.globaltrace = self.globaltrace_trackcallers 0462 elif countfuncs: 0463 self.globaltrace = self.globaltrace_countfuncs 0464 elif trace and count: 0465 self.globaltrace = self.globaltrace_lt 0466 self.localtrace = self.localtrace_trace_and_count 0467 elif trace: 0468 self.globaltrace = self.globaltrace_lt 0469 self.localtrace = self.localtrace_trace 0470 elif count: 0471 self.globaltrace = self.globaltrace_lt 0472 self.localtrace = self.localtrace_count 0473 else: 0474 # Ahem -- do nothing? Okay. 0475 self.donothing = 1 0476 0477 def run(self, cmd): 0478 import __main__ 0479 dict = __main__.__dict__ 0480 if not self.donothing: 0481 sys.settrace(self.globaltrace) 0482 threading.settrace(self.globaltrace) 0483 try: 0484 exec cmd in dict, dict 0485 finally: 0486 if not self.donothing: 0487 sys.settrace(None) 0488 threading.settrace(None) 0489 0490 def runctx(self, cmd, globals=None, locals=None): 0491 if globals is None: globals = {} 0492 if locals is None: locals = {} 0493 if not self.donothing: 0494 sys.settrace(self.globaltrace) 0495 threading.settrace(self.globaltrace) 0496 try: 0497 exec cmd in globals, locals 0498 finally: 0499 if not self.donothing: 0500 sys.settrace(None) 0501 threading.settrace(None) 0502 0503 def runfunc(self, func, *args, **kw): 0504 result = None 0505 if not self.donothing: 0506 sys.settrace(self.globaltrace) 0507 try: 0508 result = func(*args, **kw) 0509 finally: 0510 if not self.donothing: 0511 sys.settrace(None) 0512 return result 0513 0514 def file_module_function_of(self, frame): 0515 code = frame.f_code 0516 filename = code.co_filename 0517 if filename: 0518 modulename = modname(filename) 0519 else: 0520 modulename = None 0521 0522 funcname = code.co_name 0523 clsname = None 0524 if code in self._caller_cache: 0525 if self._caller_cache[code] is not None: 0526 clsname = self._caller_cache[code] 0527 else: 0528 self._caller_cache[code] = None 0529 ## use of gc.get_referrers() was suggested by Michael Hudson 0530 # all functions which refer to this code object 0531 funcs = [f for f in gc.get_referrers(code) 0532 if hasattr(f, "func_doc")] 0533 # require len(func) == 1 to avoid ambiguity caused by calls to 0534 # new.function(): "In the face of ambiguity, refuse the 0535 # temptation to guess." 0536 if len(funcs) == 1: 0537 dicts = [d for d in gc.get_referrers(funcs[0]) 0538 if isinstance(d, dict)] 0539 if len(dicts) == 1: 0540 classes = [c for c in gc.get_referrers(dicts[0]) 0541 if hasattr(c, "__bases__")] 0542 if len(classes) == 1: 0543 # ditto for new.classobj() 0544 clsname = str(classes[0]) 0545 # cache the result - assumption is that new.* is 0546 # not called later to disturb this relationship 0547 # _caller_cache could be flushed if functions in 0548 # the new module get called. 0549 self._caller_cache[code] = clsname 0550 if clsname is not None: 0551 # final hack - module name shows up in str(cls), but we've already 0552 # computed module name, so remove it 0553 clsname = clsname.split(".")[1:] 0554 clsname = ".".join(clsname) 0555 funcname = "%s.%s" % (clsname, funcname) 0556 0557 return filename, modulename, funcname 0558 0559 def globaltrace_trackcallers(self, frame, why, arg): 0560 """Handler for call events. 0561 0562 Adds information about who called who to the self._callers dict. 0563 """ 0564 if why == 'call': 0565 # XXX Should do a better job of identifying methods 0566 this_func = self.file_module_function_of(frame) 0567 parent_func = self.file_module_function_of(frame.f_back) 0568 self._callers[(parent_func, this_func)] = 1 0569 0570 def globaltrace_countfuncs(self, frame, why, arg): 0571 """Handler for call events. 0572 0573 Adds (filename, modulename, funcname) to the self._calledfuncs dict. 0574 """ 0575 if why == 'call': 0576 this_func = self.file_module_function_of(frame) 0577 self._calledfuncs[this_func] = 1 0578 0579 def globaltrace_lt(self, frame, why, arg): 0580 """Handler for call events. 0581 0582 If the code block being entered is to be ignored, returns `None', 0583 else returns self.localtrace. 0584 """ 0585 if why == 'call': 0586 code = frame.f_code 0587 filename = code.co_filename 0588 if filename: 0589 # XXX modname() doesn't work right for packages, so 0590 # the ignore support won't work right for packages 0591 modulename = modname(filename) 0592 if modulename is not None: 0593 ignore_it = self.ignore.names(filename, modulename) 0594 if not ignore_it: 0595 if self.trace: 0596 print (" --- modulename: %s, funcname: %s" 0597 % (modulename, code.co_name)) 0598 return self.localtrace 0599 else: 0600 return None 0601 0602 def localtrace_trace_and_count(self, frame, why, arg): 0603 if why == "line": 0604 # record the file name and line number of every trace 0605 filename = frame.f_code.co_filename 0606 lineno = frame.f_lineno 0607 key = filename, lineno 0608 self.counts[key] = self.counts.get(key, 0) + 1 0609 0610 bname = os.path.basename(filename) 0611 print "%s(%d): %s" % (bname, lineno, 0612 linecache.getline(filename, lineno)), 0613 return self.localtrace 0614 0615 def localtrace_trace(self, frame, why, arg): 0616 if why == "line": 0617 # record the file name and line number of every trace 0618 filename = frame.f_code.co_filename 0619 lineno = frame.f_lineno 0620 0621 bname = os.path.basename(filename) 0622 print "%s(%d): %s" % (bname, lineno, 0623 linecache.getline(filename, lineno)), 0624 return self.localtrace 0625 0626 def localtrace_count(self, frame, why, arg): 0627 if why == "line": 0628 filename = frame.f_code.co_filename 0629 lineno = frame.f_lineno 0630 key = filename, lineno 0631 self.counts[key] = self.counts.get(key, 0) + 1 0632 return self.localtrace 0633 0634 def results(self): 0635 return CoverageResults(self.counts, infile=self.infile, 0636 outfile=self.outfile, 0637 calledfuncs=self._calledfuncs, 0638 callers=self._callers) 0639 0640 def _err_exit(msg): 0641 sys.stderr.write("%s: %s\n" % (sys.argv[0], msg)) 0642 sys.exit(1) 0643 0644 def main(argv=None): 0645 import getopt 0646 0647 if argv is None: 0648 argv = sys.argv 0649 try: 0650 opts, prog_argv = getopt.getopt(argv[1:], "tcrRf:d:msC:lT", 0651 ["help", "version", "trace", "count", 0652 "report", "no-report", "summary", 0653 "file=", "missing", 0654 "ignore-module=", "ignore-dir=", 0655 "coverdir=", "listfuncs", 0656 "trackcalls"]) 0657 0658 except getopt.error, msg: 0659 sys.stderr.write("%s: %s\n" % (sys.argv[0], msg)) 0660 sys.stderr.write("Try `%s --help' for more information\n" 0661 % sys.argv[0]) 0662 sys.exit(1) 0663 0664 trace = 0 0665 count = 0 0666 report = 0 0667 no_report = 0 0668 counts_file = None 0669 missing = 0 0670 ignore_modules = [] 0671 ignore_dirs = [] 0672 coverdir = None 0673 summary = 0 0674 listfuncs = False 0675 countcallers = False 0676 0677 for opt, val in opts: 0678 if opt == "--help": 0679 usage(sys.stdout) 0680 sys.exit(0) 0681 0682 if opt == "--version": 0683 sys.stdout.write("trace 2.0\n") 0684 sys.exit(0) 0685 0686 if opt == "-T" or opt == "--trackcalls": 0687 countcallers = True 0688 continue 0689 0690 if opt == "-l" or opt == "--listfuncs": 0691 listfuncs = True 0692 continue 0693 0694 if opt == "-t" or opt == "--trace": 0695 trace = 1 0696 continue 0697 0698 if opt == "-c" or opt == "--count": 0699 count = 1 0700 continue 0701 0702 if opt == "-r" or opt == "--report": 0703 report = 1 0704 continue 0705 0706 if opt == "-R" or opt == "--no-report": 0707 no_report = 1 0708 continue 0709 0710 if opt == "-f" or opt == "--file": 0711 counts_file = val 0712 continue 0713 0714 if opt == "-m" or opt == "--missing": 0715 missing = 1 0716 continue 0717 0718 if opt == "-C" or opt == "--coverdir": 0719 coverdir = val 0720 continue 0721 0722 if opt == "-s" or opt == "--summary": 0723 summary = 1 0724 continue 0725 0726 if opt == "--ignore-module": 0727 ignore_modules.append(val) 0728 continue 0729 0730 if opt == "--ignore-dir": 0731 for s in val.split(os.pathsep): 0732 s = os.path.expandvars(s) 0733 # should I also call expanduser? (after all, could use $HOME) 0734 0735 s = s.replace("$prefix", 0736 os.path.join(sys.prefix, "lib", 0737 "python" + sys.version[:3])) 0738 s = s.replace("$exec_prefix", 0739 os.path.join(sys.exec_prefix, "lib", 0740 "python" + sys.version[:3])) 0741 s = os.path.normpath(s) 0742 ignore_dirs.append(s) 0743 continue 0744 0745 assert 0, "Should never get here" 0746 0747 if listfuncs and (count or trace): 0748 _err_exit("cannot specify both --listfuncs and (--trace or --count)") 0749 0750 if not (count or trace or report or listfuncs or countcallers): 0751 _err_exit("must specify one of --trace, --count, --report, " 0752 "--listfuncs, or --trackcalls") 0753 0754 if report and no_report: 0755 _err_exit("cannot specify both --report and --no-report") 0756 0757 if report and not counts_file: 0758 _err_exit("--report requires a --file") 0759 0760 if no_report and len(prog_argv) == 0: 0761 _err_exit("missing name of file to run") 0762 0763 # everything is ready 0764 if report: 0765 results = CoverageResults(infile=counts_file, outfile=counts_file) 0766 results.write_results(missing, summary=summary, coverdir=coverdir) 0767 else: 0768 sys.argv = prog_argv 0769 progname = prog_argv[0] 0770 sys.path[0] = os.path.split(progname)[0] 0771 0772 t = Trace(count, trace, countfuncs=listfuncs, 0773 countcallers=countcallers, ignoremods=ignore_modules, 0774 ignoredirs=ignore_dirs, infile=counts_file, 0775 outfile=counts_file) 0776 try: 0777 t.run('execfile(%r)' % (progname,)) 0778 except IOError, err: 0779 _err_exit("Cannot run file %r because: %s" % (sys.argv[0], err)) 0780 except SystemExit: 0781 pass 0782 0783 results = t.results() 0784 0785 if not no_report: 0786 results.write_results(missing, summary=summary, coverdir=coverdir) 0787 0788 if __name__=='__main__': 0789 main() 0790
Generated by PyXR 0.9.4