| Home | Trees | Indices | Help |
|
|---|
|
|
1 #!/usr/bin/python 2 # 3 # Perforce Defect Tracking Integration Project 4 # <http://www.ravenbrook.com/project/p4dti/> 5 # 6 # COVERAGE.PY -- COVERAGE TESTING 7 # 8 # Gareth Rees, Ravenbrook Limited, 2001-12-04 9 # Ned Batchelder, 2004-12-12 10 # http://nedbatchelder.com/code/modules/coverage.html 11 # 12 # 13 # 1. INTRODUCTION 14 # 15 # This module provides coverage testing for Python code. 16 # 17 # The intended readership is all Python developers. 18 # 19 # This document is not confidential. 20 # 21 # See [GDR 2001-12-04a] for the command-line interface, programmatic 22 # interface and limitations. See [GDR 2001-12-04b] for requirements and 23 # design. 24 25 r"""Usage: 26 27 coverage.py -x [-p] MODULE.py [ARG1 ARG2 ...] 28 Execute module, passing the given command-line arguments, collecting 29 coverage data. With the -p option, write to a temporary file containing 30 the machine name and process ID. 31 32 coverage.py -e 33 Erase collected coverage data. 34 35 coverage.py -c 36 Collect data from multiple coverage files (as created by -p option above) 37 and store it into a single file representing the union of the coverage. 38 39 coverage.py -r [-m] [-o dir1,dir2,...] FILE1 FILE2 ... 40 Report on the statement coverage for the given files. With the -m 41 option, show line numbers of the statements that weren't executed. 42 43 coverage.py -a [-d dir] [-o dir1,dir2,...] FILE1 FILE2 ... 44 Make annotated copies of the given files, marking statements that 45 are executed with > and statements that are missed with !. With 46 the -d option, make the copies in that directory. Without the -d 47 option, make each copy in the same directory as the original. 48 49 -o dir,dir2,... 50 Omit reporting or annotating files when their filename path starts with 51 a directory listed in the omit list. 52 e.g. python coverage.py -i -r -o c:\python23,lib\enthought\traits 53 54 Coverage data is saved in the file .coverage by default. Set the 55 COVERAGE_FILE environment variable to save it somewhere else.""" 56 57 __docformat__ = 'plaintext en' 58 __version__ = "2.77.20070729" # see detailed history at the end of this file. 59 60 import compiler 61 import compiler.visitor 62 import glob 63 import os 64 import re 65 import string 66 import symbol 67 import sys 68 import threading 69 import token 70 import types 71 from socket import gethostname 72 73 # Python version compatibility 74 try: 75 strclass = basestring # new to 2.3 76 except: 77 strclass = str78 79 # 2. IMPLEMENTATION 80 # 81 # This uses the "singleton" pattern. 82 # 83 # The word "morf" means a module object (from which the source file can 84 # be deduced by suitable manipulation of the __file__ attribute) or a 85 # filename. 86 # 87 # When we generate a coverage report we have to canonicalize every 88 # filename in the coverage dictionary just in case it refers to the 89 # module we are reporting on. It seems a shame to throw away this 90 # information so the data in the coverage dictionary is transferred to 91 # the 'cexecuted' dictionary under the canonical filenames. 92 # 93 # The coverage dictionary is called "c" and the trace function "t". The 94 # reason for these short names is that Python looks up variables by name 95 # at runtime and so execution time depends on the length of variables! 96 # In the bottleneck of this application it's appropriate to abbreviate 97 # names to increase speed. 98 99 -class DefInfo:100 FUNC = 'func' 101 CLASS = 'class'121103 self.typ = self.type_for(node) 104 self.name = node.name 105 self.defstart = defstart 106 self.codestart = codestart 107 self.end = end 108 self.coverage = coverage109111 return ('DefInfo(%s, %s, %s, %s, %s, %s)' % 112 (self.typ, self.name, self.defstart, self.codestart, 113 self.end, self.coverage))114 115 @staticmethod123 """ A visitor for a parsed Abstract Syntax Tree which finds executable 124 statements. 125 """308 309 the_coverage = None 312127 compiler.visitor.ASTVisitor.__init__(self) 128 self.statements = statements 129 self.excluded = excluded 130 self.suite_spots = suite_spots 131 self.excluding_suite = 0 132 self.definfo = definfo133 137 138 visitStmt = visitModule = doRecursive 139141 if hasattr(node, 'decorators') and node.decorators: 142 self.dispatch(node.decorators) 143 self.recordAndDispatch(node.code) 144 else: 145 self.doSuite(node, node.code) 146 147 defstart = self.getFirstLine(node) 148 codestart = self.getFirstLine(node.code) 149 end = self.getLastLine(node.code) 150 self.definfo.append(DefInfo(node, defstart, codestart, end)) 151 # When we exit a block, build up dotted names. 152 for i in range(len(self.definfo)-2, -1, -1): 153 if self.definfo[i].defstart < defstart: break 154 self.definfo[i].name = '%s.%s' % (node.name, self.definfo[i].name) 155 # When we exit a func block, hide any contained objects. 156 if len(self.definfo)>1 and self.definfo[-1].typ == DefInfo.FUNC: 157 del self.definfo[i+1:-1]158 159 visitFunction = visitClass = doCode 160162 # Find the first line in the tree node. 163 lineno = node.lineno 164 for n in node.getChildNodes(): 165 f = self.getFirstLine(n) 166 if lineno and f: 167 lineno = min(lineno, f) 168 else: 169 lineno = lineno or f 170 return lineno171173 # Find the first line in the tree node. 174 lineno = node.lineno 175 for n in node.getChildNodes(): 176 lineno = max(lineno, self.getLastLine(n)) 177 return lineno178 181 182 visitAssert = visitAssign = visitAssTuple = visitPrint = \ 183 visitPrintnl = visitRaise = visitSubscript = visitDecorators = \ 184 doStatement 185187 # Pass statements have weird interactions with docstrings. If this 188 # pass statement is part of one of those pairs, claim that the statement 189 # is on the later of the two lines. 190 l = node.lineno 191 if l: 192 lines = self.suite_spots.get(l, [l,l]) 193 self.statements[lines[1]] = 1194196 # Discard nodes are statements that execute an expression, but then 197 # discard the results. This includes function calls, so we can't 198 # ignore them all. But if the expression is a constant, the statement 199 # won't be "executed", so don't count it now. 200 if node.expr.__class__.__name__ != 'Const': 201 self.doStatement(node)202204 # Stmt nodes often have None, but shouldn't claim the first line of 205 # their children (because the first child might be an ignorable line 206 # like "global a"). 207 if node.__class__.__name__ != 'Stmt': 208 return self.recordLine(self.getFirstLine(node)) 209 else: 210 return 0211213 # Returns a bool, whether the line is included or excluded. 214 if lineno: 215 # Multi-line tests introducing suites have to get charged to their 216 # keyword. 217 if lineno in self.suite_spots: 218 lineno = self.suite_spots[lineno][0] 219 # If we're inside an excluded suite, record that this line was 220 # excluded. 221 if self.excluding_suite: 222 self.excluded[lineno] = 1 223 return 0 224 # If this line is excluded, or suite_spots maps this line to 225 # another line that is exlcuded, then we're excluded. 226 elif self.excluded.has_key(lineno) or \ 227 self.suite_spots.has_key(lineno) and \ 228 self.excluded.has_key(self.suite_spots[lineno][1]): 229 return 0 230 # Otherwise, this is an executable line. 231 else: 232 self.statements[lineno] = 1 233 return 1 234 return 0235 236 default = recordNodeLine 237 241243 exsuite = self.excluding_suite 244 if exclude or (intro and not self.recordNodeLine(intro)): 245 self.excluding_suite = 1 246 self.recordAndDispatch(body) 247 self.excluding_suite = exsuite248250 # Finding the exclude lines for else's is tricky, because they aren't 251 # present in the compiler parse tree. Look at the previous suite, 252 # and find its last line. If any line between there and the else's 253 # first line are excluded, then we exclude the else. 254 lastprev = self.getLastLine(prevsuite) 255 firstelse = self.getFirstLine(suite) 256 for l in range(lastprev+1, firstelse): 257 if self.suite_spots.has_key(l): 258 self.doSuite(None, suite, exclude=self.excluded.has_key(l)) 259 break 260 else: 261 self.doSuite(None, suite)262 266 270 271 visitWhile = visitFor 272274 # The first test has to be handled separately from the rest. 275 # The first test is credited to the line with the "if", but the others 276 # are credited to the line with the test for the elif. 277 self.doSuite(node, node.tests[0][1]) 278 for t, n in node.tests[1:]: 279 self.doSuite(t, n) 280 self.doElse(node.tests[-1][1], node)281283 self.doSuite(node, node.body) 284 for i in range(len(node.handlers)): 285 a, b, h = node.handlers[i] 286 if not a: 287 # It's a plain "except:". Find the previous suite. 288 if i > 0: 289 prev = node.handlers[i-1][2] 290 else: 291 prev = node.body 292 self.doPlainWordSuite(prev, h) 293 else: 294 self.doSuite(a, h) 295 self.doElse(node.handlers[-1][2], node)296 300 303314 # Name of the cache file (unless environment variable is set). 315 cache_default = ".coverage" 316 317 # Environment variable naming the cache file. 318 cache_env = "COVERAGE_FILE" 319 320 # A dictionary with an entry for (Python source file name, line number 321 # in that file) if that line has been executed. 322 c = {} 323 324 # A map from canonical Python source file name to a dictionary in 325 # which there's an entry for each line number that has been 326 # executed. 327 cexecuted = {} 328 329 # Cache of results of calling the analysis2() method, so that you can 330 # specify both -r and -a without doing double work. 331 analysis_cache = {} 332 333 # Cache of results of calling the canonical_filename() method, to 334 # avoid duplicating work. 335 canonical_filename_cache = {} 336364 366 if error: 367 print error 368 print 369 print __doc__ 370 sys.exit(1) 371338 global the_coverage 339 if the_coverage: 340 raise CoverageException, "Only one coverage object allowed." 341 self.usecache = 1 342 self.cache = None 343 self.parallel_mode = False 344 self.exclude_re = '' 345 self.nesting = 0 346 self.cstack = [] 347 self.xstack = [] 348 self.relative_dir = os.path.normcase(os.path.abspath(os.curdir)+os.sep) 349 self.exclude('# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]')350 351 # t(f, x, y). This method is passed to sys.settrace as a trace function. 352 # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and 353 # the arguments and return value of the trace function. 354 # See [van Rossum 2001-07-20a, 3.2] for a description of frame and code 355 # objects. 356 358 if w == 'line': 359 #print "Executing %s @ %d" % (f.f_code.co_filename, f.f_lineno) 360 self.c[(f.f_code.co_filename, f.f_lineno)] = 1 361 for c in self.cstack: 362 c[(f.f_code.co_filename, f.f_lineno)] = 1 363 return self.t373 import getopt 374 help_fn = help_fn or self.help 375 settings = {} 376 optmap = { 377 '-a': 'annotate', 378 '-c': 'collect', 379 '-d:': 'directory=', 380 '-e': 'erase', 381 '-h': 'help', 382 '-i': 'ignore-errors', 383 '-m': 'show-missing', 384 '-p': 'parallel-mode', 385 '-r': 'report', 386 '-x': 'execute', 387 '-o:': 'omit=', 388 } 389 short_opts = string.join(map(lambda o: o[1:], optmap.keys()), '') 390 long_opts = optmap.values() 391 options, args = getopt.getopt(argv, short_opts, long_opts) 392 for o, a in options: 393 if optmap.has_key(o): 394 settings[optmap[o]] = 1 395 elif optmap.has_key(o + ':'): 396 settings[optmap[o + ':']] = a 397 elif o[2:] in long_opts: 398 settings[o[2:]] = 1 399 elif o[2:] + '=' in long_opts: 400 settings[o[2:]+'='] = a 401 else: #pragma: no cover 402 pass # Can't get here, because getopt won't return anything unknown. 403 404 if settings.get('help'): 405 help_fn() 406 407 for i in ['erase', 'execute']: 408 for j in ['annotate', 'report', 'collect']: 409 if settings.get(i) and settings.get(j): 410 help_fn("You can't specify the '%s' and '%s' " 411 "options at the same time." % (i, j)) 412 413 args_needed = (settings.get('execute') 414 or settings.get('annotate') 415 or settings.get('report')) 416 action = (settings.get('erase') 417 or settings.get('collect') 418 or args_needed) 419 if not action: 420 help_fn("You must specify at least one of -e, -x, -c, -r, or -a.") 421 if not args_needed and args: 422 help_fn("Unexpected arguments: %s" % " ".join(args)) 423 424 self.parallel_mode = settings.get('parallel-mode') 425 self.get_ready() 426 427 if settings.get('erase'): 428 self.erase() 429 if settings.get('execute'): 430 if not args: 431 help_fn("Nothing to do.") 432 sys.argv = args 433 self.start() 434 import __main__ 435 sys.path[0] = os.path.dirname(sys.argv[0]) 436 execfile(sys.argv[0], __main__.__dict__) 437 if settings.get('collect'): 438 self.collect() 439 if not args: 440 args = self.cexecuted.keys() 441 442 ignore_errors = settings.get('ignore-errors') 443 show_missing = settings.get('show-missing') 444 directory = settings.get('directory=') 445 446 omit = settings.get('omit=') 447 if omit is not None: 448 omit = omit.split(',') 449 else: 450 omit = [] 451 452 if settings.get('report'): 453 self.report(args, show_missing, ignore_errors, omit_prefixes=omit) 454 if settings.get('annotate'): 455 self.annotate(args, directory, ignore_errors, omit_prefixes=omit)456458 self.usecache = usecache 459 if cache_file and not self.cache: 460 self.cache_default = cache_file461463 if self.usecache and not self.cache: 464 self.cache = os.environ.get(self.cache_env, self.cache_default) 465 if self.parallel_mode: 466 self.cache += "." + gethostname() + "." + str(os.getpid()) 467 self.restore() 468 self.analysis_cache = {}469471 self.get_ready() 472 if self.nesting == 0: #pragma: no cover 473 sys.settrace(self.t) 474 if hasattr(threading, 'settrace'): 475 threading.settrace(self.t) 476 self.nesting += 1477479 self.nesting -= 1 480 if self.nesting == 0: #pragma: no cover 481 sys.settrace(None) 482 if hasattr(threading, 'settrace'): 483 threading.settrace(None)484486 self.get_ready() 487 self.c = {} 488 self.analysis_cache = {} 489 self.cexecuted = {} 490 if self.cache and os.path.exists(self.cache): 491 os.remove(self.cache)492 497 501 505 506 # save(). Save coverage data to the coverage cache. 507509 if self.usecache and self.cache: 510 self.canonicalize_filenames() 511 cache = open(self.cache, 'wb') 512 import marshal 513 marshal.dump(self.cexecuted, cache) 514 cache.close()515 516 # restore(). Restore coverage data from the coverage cache (if it exists). 517519 self.c = {} 520 self.cexecuted = {} 521 assert self.usecache 522 if os.path.exists(self.cache): 523 self.cexecuted = self.restore_file(self.cache)524526 try: 527 cache = open(file_name, 'rb') 528 import marshal 529 cexecuted = marshal.load(cache) 530 cache.close() 531 if isinstance(cexecuted, types.DictType): 532 return cexecuted 533 else: 534 return {} 535 except: 536 return {}537 538 # collect(). Collect data in multiple files produced by parallel mode 539541 cache_dir, local = os.path.split(self.cache) 542 for f in os.listdir(cache_dir or '.'): 543 if not f.startswith(local): 544 continue 545 546 full_path = os.path.join(cache_dir, f) 547 cexecuted = self.restore_file(full_path) 548 self.merge_data(cexecuted)549551 for file_name, file_data in new_data.items(): 552 if self.cexecuted.has_key(file_name): 553 self.merge_file_data(self.cexecuted[file_name], file_data) 554 else: 555 self.cexecuted[file_name] = file_data556558 for line_number in new_data.keys(): 559 if not cache_data.has_key(line_number): 560 cache_data[line_number] = new_data[line_number]561 562 # canonical_filename(filename). Return a canonical filename for the 563 # file (that is, an absolute path with no redundant components and 564 # normalized case). See [GDR 2001-12-04b, 3.3]. 565567 if not self.canonical_filename_cache.has_key(filename): 568 f = filename 569 if os.path.isabs(f) and not os.path.exists(f): 570 f = os.path.basename(f) 571 if not os.path.isabs(f): 572 for path in [os.curdir] + sys.path: 573 g = os.path.join(path, f) 574 if os.path.exists(g): 575 f = g 576 break 577 cf = os.path.normcase(os.path.abspath(f)) 578 self.canonical_filename_cache[filename] = cf 579 return self.canonical_filename_cache[filename]580 581 # canonicalize_filenames(). Copy results from "c" to "cexecuted", 582 # canonicalizing filenames on the way. Clear the "c" map. 583585 for filename, lineno in self.c.keys(): 586 if filename == '<string>': 587 # Can't do anything useful with exec'd strings, so skip them. 588 continue 589 f = self.canonical_filename(filename) 590 if not self.cexecuted.has_key(f): 591 self.cexecuted[f] = {} 592 self.cexecuted[f][lineno] = 1 593 self.c = {}594 595 # morf_filename(morf). Return the filename for a module or file. 596598 if isinstance(morf, types.ModuleType): 599 if not hasattr(morf, '__file__'): 600 raise CoverageException, "Module has no __file__ attribute." 601 f = morf.__file__ 602 else: 603 f = morf 604 return self.canonical_filename(f)605 606 # analyze_morf(morf). Analyze the module or filename passed as 607 # the argument. If the source code can't be found, raise an error. 608 # Otherwise, return a tuple of (1) the canonical filename of the 609 # source code for the module, (2) a list of lines of statements 610 # in the source code, (3) a list of lines of excluded statements, 611 # and (4), a map of line numbers to multi-line line number ranges, for 612 # statements that cross lines. 613615 return self.analyze_morf(morf)[:-1]616618 if self.analysis_cache.has_key(morf): 619 return self.analysis_cache[morf] 620 filename = self.morf_filename(morf) 621 ext = os.path.splitext(filename)[1] 622 if ext == '.pyc': 623 if not os.path.exists(filename[0:-1]): 624 raise CoverageException, ("No source for compiled code '%s'." 625 % filename) 626 filename = filename[0:-1] 627 elif ext != '.py': 628 raise CoverageException, "File '%s' not Python source." % filename 629 source = open(filename, 'rU') 630 lines, excluded_lines, line_map, definfo = \ 631 self.find_executable_statements2(source.read(), 632 exclude=self.exclude_re) 633 source.close() 634 result = filename, lines, excluded_lines, line_map, definfo 635 self.analysis_cache[morf] = result 636 return result637639 while True: 640 if len(tree) == 3 and type(tree[2]) == type(1): 641 return tree[2] 642 tree = tree[1]643645 while True: 646 if len(tree) == 3 and type(tree[2]) == type(1): 647 return tree[2] 648 tree = tree[-1]649651 for i in range(1, len(tree)): 652 if self.is_string_constant(tree[i]) and self.is_pass_stmt(tree[i+1]): 653 first_line = self.first_line_of_tree(tree[i]) 654 last_line = self.last_line_of_tree(tree[i+1]) 655 self.record_multiline(spots, first_line, last_line)656658 try: 659 return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.expr_stmt 660 except: 661 return False662664 try: 665 return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.pass_stmt 666 except: 667 return False668 672674 """ Analyze a parse tree to find suite introducers which span a number 675 of lines. 676 """ 677 for i in range(1, len(tree)): 678 if type(tree[i]) == type(()): 679 if tree[i][0] == symbol.suite: 680 # Found a suite, look back for the colon and keyword. 681 lineno_colon = lineno_word = None 682 for j in range(i-1, 0, -1): 683 if tree[j][0] == token.COLON: 684 # Colons are never executed themselves: we want the 685 # line number of the last token before the colon. 686 lineno_colon = self.last_line_of_tree(tree[j-1]) 687 elif tree[j][0] == token.NAME: 688 if tree[j][1] == 'elif': 689 # Find the line number of the first non-terminal 690 # after the keyword. 691 t = tree[j+1] 692 while t and token.ISNONTERMINAL(t[0]): 693 t = t[1] 694 if t: 695 lineno_word = t[2] 696 else: 697 lineno_word = tree[j][2] 698 break 699 elif tree[j][0] == symbol.except_clause: 700 # "except" clauses look like: 701 # ('except_clause', ('NAME', 'except', lineno), ...) 702 if tree[j][1][0] == token.NAME: 703 lineno_word = tree[j][1][2] 704 break 705 if lineno_colon and lineno_word: 706 # Found colon and keyword, mark all the lines 707 # between the two with the two line numbers. 708 self.record_multiline(spots, lineno_word, lineno_colon) 709 710 # "pass" statements are tricky: different versions of Python 711 # treat them differently, especially in the common case of a 712 # function with a doc string and a single pass statement. 713 self.find_docstring_pass_pair(tree[i], spots) 714 715 elif tree[i][0] == symbol.simple_stmt: 716 first_line = self.first_line_of_tree(tree[i]) 717 last_line = self.last_line_of_tree(tree[i]) 718 if first_line != last_line: 719 self.record_multiline(spots, first_line, last_line) 720 self.get_suite_spots(tree[i], spots)721 724726 # Find lines which match an exclusion pattern. 727 excluded = {} 728 suite_spots = {} 729 if exclude: 730 reExclude = re.compile(exclude) 731 lines = text.split('\n') 732 for i in range(len(lines)): 733 if reExclude.search(lines[i]): 734 excluded[i+1] = 1 735 736 # Parse the code and analyze the parse tree to find out which statements 737 # are multiline, and where suites begin and end. 738 import parser 739 tree = parser.suite(text+'\n\n').totuple(1) 740 self.get_suite_spots(tree, suite_spots) 741 #print "Suite spots:", suite_spots 742 743 # Mapping from name of func/class -> (start, end). 744 definfo = [] 745 746 # Use the compiler module to parse the text and find the executable 747 # statements. We add newlines to be impervious to final partial lines. 748 statements = {} 749 ast = compiler.parse(text+'\n\n') 750 visitor = StatementFindingAstVisitor(statements, excluded, 751 suite_spots, definfo) 752 compiler.walk(ast, visitor, walker=visitor) 753 754 lines = statements.keys() 755 lines.sort() 756 excluded_lines = excluded.keys() 757 excluded_lines.sort() 758 return lines, excluded_lines, suite_spots, definfo759 760 # format_lines(statements, lines). Format a list of line numbers 761 # for printing by coalescing groups of lines as long as the lines 762 # represent consecutive statements. This will coalesce even if 763 # there are gaps between statements, so if statements = 764 # [1,2,3,4,5,10,11,12,13,14] and lines = [1,2,5,10,11,13,14] then 765 # format_lines will return "1-2, 5-11, 13-14". 766768 pairs = [] 769 i = 0 770 j = 0 771 start = None 772 pairs = [] 773 while i < len(statements) and j < len(lines): 774 if statements[i] == lines[j]: 775 if start == None: 776 start = lines[j] 777 end = lines[j] 778 j = j + 1 779 elif start: 780 pairs.append((start, end)) 781 start = None 782 i = i + 1 783 if start: 784 pairs.append((start, end)) 785 def stringify(pair): 786 start, end = pair 787 if start == end: 788 return "%d" % start 789 else: 790 return "%d-%d" % (start, end)791 ret = string.join(map(stringify, pairs), ", ") 792 return ret 793 794 # Backward compatibility with version 1. 798 802804 filename, statements, excluded, line_map, definfo = \ 805 self.analyze_morf2(morf) 806 self.canonicalize_filenames() 807 if not self.cexecuted.has_key(filename): 808 self.cexecuted[filename] = {} 809 missing = [] 810 for line in statements: 811 lines = line_map.get(line, [line, line]) 812 for l in range(lines[0], lines[1]+1): 813 if self.cexecuted[filename].has_key(l): 814 break 815 else: 816 missing.append(line) 817 self.find_def_coverage(morf, statements, missing, definfo) 818 return (filename, statements, excluded, missing, 819 self.format_lines(statements, missing), definfo)820822 """Return mapping from function name to coverage. 823 """ 824 def_coverage = {} 825 root = self.morf_name(morf) 826 statements = set(statements) 827 missing = set(missing) 828 for info in definfo: 829 if info.codestart is None: 830 info.coverage = 1 831 else: 832 lines = set(range(info.codestart, info.end+1)) 833 stmt = len(lines.intersection(statements)) 834 miss = len(lines.intersection(missing)) 835 if miss == 0: info.cover = 1 836 else: info.coverage = (1.0 - float(miss)/float(stmt))837839 """ Convert filename to relative filename from self.relative_dir. 840 """ 841 return filename.replace(self.relative_dir, "")842844 """ Return the name of morf as used in report. 845 """ 846 if isinstance(morf, types.ModuleType): 847 return morf.__name__ 848 else: 849 return self.relative_filename(os.path.splitext(morf)[0])850852 """ Return list of morfs where the morf name does not begin 853 with any one of the omit_prefixes. 854 """ 855 filtered_morfs = [] 856 for morf in morfs: 857 for prefix in omit_prefixes: 858 if self.morf_name(morf).startswith(prefix): 859 break 860 else: 861 filtered_morfs.append(morf) 862 863 return filtered_morfs864 867869 if not isinstance(morfs, types.ListType): 870 morfs = [morfs] 871 # On windows, the shell doesn't expand wildcards. Do it here. 872 globbed = [] 873 for morf in morfs: 874 if isinstance(morf, strclass): 875 globbed.extend(glob.glob(morf)) 876 else: 877 globbed.append(morf) 878 morfs = globbed 879 880 morfs = self.filter_by_prefix(morfs, omit_prefixes) 881 morfs.sort(self.morf_name_compare) 882 883 max_name = max([5,] + map(len, map(self.morf_name, morfs))) 884 fmt_name = "%%- %ds " % max_name 885 fmt_err = fmt_name + "%s: %s" 886 header = fmt_name % "Name" + " Stmts Exec Cover" 887 fmt_coverage = fmt_name + "% 6d % 6d % 5d%%" 888 if show_missing: 889 header = header + " Missing" 890 fmt_coverage = fmt_coverage + " %s" 891 if not file: 892 file = sys.stdout 893 print >>file, header 894 print >>file, "-" * len(header) 895 total_statements = 0 896 total_executed = 0 897 for morf in morfs: 898 name = self.morf_name(morf) 899 try: 900 _, statements, _, missing, readable = self.analysis2(morf) 901 n = len(statements) 902 m = n - len(missing) 903 if n > 0: 904 pc = 100.0 * m / n 905 else: 906 pc = 100.0 907 args = (name, n, m, pc) 908 if show_missing: 909 args = args + (readable,) 910 print >>file, fmt_coverage % args 911 total_statements = total_statements + n 912 total_executed = total_executed + m 913 except KeyboardInterrupt: #pragma: no cover 914 raise 915 except: 916 if not ignore_errors: 917 typ, msg = sys.exc_info()[0:2] 918 print >>file, fmt_err % (name, typ, msg) 919 if len(morfs) > 1: 920 print >>file, "-" * len(header) 921 if total_statements > 0: 922 pc = 100.0 * total_executed / total_statements 923 else: 924 pc = 100.0 925 args = ("TOTAL", total_statements, total_executed, pc) 926 if show_missing: 927 args = args + ("",) 928 print >>file, fmt_coverage % args929 930 # annotate(morfs, ignore_errors). 931 932 blank_re = re.compile(r"\s*(#|$)") 933 else_re = re.compile(r"\s*else\s*:\s*(#|$)") 934936 morfs = self.filter_by_prefix(morfs, omit_prefixes) 937 for morf in morfs: 938 try: 939 filename, statements, excluded, missing, _ = self.analysis2(morf) 940 self.annotate_file(filename, statements, excluded, missing, directory) 941 except KeyboardInterrupt: 942 raise 943 except: 944 if not ignore_errors: 945 raise946948 source = open(filename, 'rU') 949 if directory: 950 dest_file = os.path.join(directory, 951 os.path.basename(filename) 952 + ',cover') 953 else: 954 dest_file = filename + ',cover' 955 dest = open(dest_file, 'w') 956 lineno = 0 957 i = 0 958 j = 0 959 covered = 1 960 while 1: 961 line = source.readline() 962 if line == '': 963 break 964 lineno = lineno + 1 965 while i < len(statements) and statements[i] < lineno: 966 i = i + 1 967 while j < len(missing) and missing[j] < lineno: 968 j = j + 1 969 if i < len(statements) and statements[i] == lineno: 970 covered = j >= len(missing) or missing[j] > lineno 971 if self.blank_re.match(line): 972 dest.write(' ') 973 elif self.else_re.match(line): 974 # Special logic for lines containing only 'else:'. 975 # See [GDR 2001-12-04b, 3.2]. 976 if i >= len(statements) and j >= len(missing): 977 dest.write('! ') 978 elif i >= len(statements) or j >= len(missing): 979 dest.write('> ') 980 elif statements[i] == missing[j]: 981 dest.write('! ') 982 else: 983 dest.write('> ') 984 elif lineno in excluded: 985 dest.write('- ') 986 elif covered: 987 dest.write('> ') 988 else: 989 dest.write('! ') 990 dest.write(line) 991 source.close() 992 dest.close()993 994 # Singleton object. 995 the_coverage = coverage() 999 return the_coverage.use_cache(*args, **kw) 1000 1002 return the_coverage.start(*args, **kw) 1003 1005 return the_coverage.stop(*args, **kw) 1006 1008 return the_coverage.erase(*args, **kw) 1009 1011 return the_coverage.begin_recursive(*args, **kw) 1012 1014 return the_coverage.end_recursive(*args, **kw) 1015 1017 return the_coverage.exclude(*args, **kw) 1018 1020 return the_coverage.analysis(*args, **kw) 1021 1023 return the_coverage.analysis2(*args, **kw) 1024 1026 return the_coverage.analysis3(*args, **kw) 1027 1029 return the_coverage.report(*args, **kw) 1030 1032 return the_coverage.annotate(*args, **kw) 1033 1035 return the_coverage.annotate_file(*args, **kw) 1036 1037 # Save coverage data when Python exits. (The atexit module wasn't 1038 # introduced until Python 2.0, so use sys.exitfunc when it's not 1039 # available.) 1040 try: 1041 import atexit 1042 atexit.register(the_coverage.save) 1043 except ImportError: 1044 sys.exitfunc = the_coverage.save 1045 1046 # Command-line interface. 1047 if __name__ == '__main__': 1048 the_coverage.command_line(sys.argv[1:]) 1049 1050 1051 # A. REFERENCES 1052 # 1053 # [GDR 2001-12-04a] "Statement coverage for Python"; Gareth Rees; 1054 # Ravenbrook Limited; 2001-12-04; 1055 # <http://www.nedbatchelder.com/code/modules/rees-coverage.html>. 1056 # 1057 # [GDR 2001-12-04b] "Statement coverage for Python: design and 1058 # analysis"; Gareth Rees; Ravenbrook Limited; 2001-12-04; 1059 # <http://www.nedbatchelder.com/code/modules/rees-design.html>. 1060 # 1061 # [van Rossum 2001-07-20a] "Python Reference Manual (releae 2.1.1)"; 1062 # Guide van Rossum; 2001-07-20; 1063 # <http://www.python.org/doc/2.1.1/ref/ref.html>. 1064 # 1065 # [van Rossum 2001-07-20b] "Python Library Reference"; Guido van Rossum; 1066 # 2001-07-20; <http://www.python.org/doc/2.1.1/lib/lib.html>. 1067 # 1068 # 1069 # B. DOCUMENT HISTORY 1070 # 1071 # 2001-12-04 GDR Created. 1072 # 1073 # 2001-12-06 GDR Added command-line interface and source code 1074 # annotation. 1075 # 1076 # 2001-12-09 GDR Moved design and interface to separate documents. 1077 # 1078 # 2001-12-10 GDR Open cache file as binary on Windows. Allow 1079 # simultaneous -e and -x, or -a and -r. 1080 # 1081 # 2001-12-12 GDR Added command-line help. Cache analysis so that it 1082 # only needs to be done once when you specify -a and -r. 1083 # 1084 # 2001-12-13 GDR Improved speed while recording. Portable between 1085 # Python 1.5.2 and 2.1.1. 1086 # 1087 # 2002-01-03 GDR Module-level functions work correctly. 1088 # 1089 # 2002-01-07 GDR Update sys.path when running a file with the -x option, 1090 # so that it matches the value the program would get if it were run on 1091 # its own. 1092 # 1093 # 2004-12-12 NMB Significant code changes. 1094 # - Finding executable statements has been rewritten so that docstrings and 1095 # other quirks of Python execution aren't mistakenly identified as missing 1096 # lines. 1097 # - Lines can be excluded from consideration, even entire suites of lines. 1098 # - The filesystem cache of covered lines can be disabled programmatically. 1099 # - Modernized the code. 1100 # 1101 # 2004-12-14 NMB Minor tweaks. Return 'analysis' to its original behavior 1102 # and add 'analysis2'. Add a global for 'annotate', and factor it, adding 1103 # 'annotate_file'. 1104 # 1105 # 2004-12-31 NMB Allow for keyword arguments in the module global functions. 1106 # Thanks, Allen. 1107 # 1108 # 2005-12-02 NMB Call threading.settrace so that all threads are measured. 1109 # Thanks Martin Fuzzey. Add a file argument to report so that reports can be 1110 # captured to a different destination. 1111 # 1112 # 2005-12-03 NMB coverage.py can now measure itself. 1113 # 1114 # 2005-12-04 NMB Adapted Greg Rogers' patch for using relative filenames, 1115 # and sorting and omitting files to report on. 1116 # 1117 # 2006-07-23 NMB Applied Joseph Tate's patch for function decorators. 1118 # 1119 # 2006-08-21 NMB Applied Sigve Tjora and Mark van der Wal's fixes for argument 1120 # handling. 1121 # 1122 # 2006-08-22 NMB Applied Geoff Bache's parallel mode patch. 1123 # 1124 # 2006-08-23 NMB Refactorings to improve testability. Fixes to command-line 1125 # logic for parallel mode and collect. 1126 # 1127 # 2006-08-25 NMB "#pragma: nocover" is excluded by default. 1128 # 1129 # 2006-09-10 NMB Properly ignore docstrings and other constant expressions that 1130 # appear in the middle of a function, a problem reported by Tim Leslie. 1131 # Minor changes to avoid lint warnings. 1132 # 1133 # 2006-09-17 NMB coverage.erase() shouldn't clobber the exclude regex. 1134 # Change how parallel mode is invoked, and fix erase() so that it erases the 1135 # cache when called programmatically. 1136 # 1137 # 2007-07-21 NMB In reports, ignore code executed from strings, since we can't 1138 # do anything useful with it anyway. 1139 # Better file handling on Linux, thanks Guillaume Chazarain. 1140 # Better shell support on Windows, thanks Noel O'Boyle. 1141 # Python 2.2 support maintained, thanks Catherine Proulx. 1142 # 1143 # 2007-07-22 NMB Python 2.5 now fully supported. The method of dealing with 1144 # multi-line statements is now less sensitive to the exact line that Python 1145 # reports during execution. Pass statements are handled specially so that their 1146 # disappearance during execution won't throw off the measurement. 1147 # 1148 # 2007-07-23 NMB Now Python 2.5 is *really* fully supported: the body of the 1149 # new with statement is counted as executable. 1150 # 1151 # 2007-07-29 NMB Better packaging. 1152 # 1153 # 2007-09-13 EDL Open Python files with 'rU' mode. 1154 # 1155 # 2007-09-20 EDL Added DefInfo, which gives fine-grained info about 1156 # coverage (and location) of functions & classes. 1157 1158 # C. COPYRIGHT AND LICENCE 1159 # 1160 # Copyright 2001 Gareth Rees. All rights reserved. 1161 # Copyright 2004-2007 Ned Batchelder. All rights reserved. 1162 # 1163 # Redistribution and use in source and binary forms, with or without 1164 # modification, are permitted provided that the following conditions are 1165 # met: 1166 # 1167 # 1. Redistributions of source code must retain the above copyright 1168 # notice, this list of conditions and the following disclaimer. 1169 # 1170 # 2. Redistributions in binary form must reproduce the above copyright 1171 # notice, this list of conditions and the following disclaimer in the 1172 # documentation and/or other materials provided with the 1173 # distribution. 1174 # 1175 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1176 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1177 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1178 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1179 # HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 1180 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 1181 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 1182 # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 1183 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 1184 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 1185 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 1186 # DAMAGE. 1187 # 1188 # $Id: coverage.py 74 2007-07-29 22:28:35Z nedbat $ 1189
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Apr 11 14:40:16 2011 | http://epydoc.sourceforge.net |