comparison tests/coverage.py @ 10282:08a0f04b56bd

many, many trivial check-code fixups
author Matt Mackall <mpm@selenic.com>
date Mon, 25 Jan 2010 00:05:27 -0600
parents 277c91fe8384
children 86a6bc519592
comparison
equal deleted inserted replaced
10281:e7d3b509af8b 10282:08a0f04b56bd
104 compiler.visitor.ASTVisitor.__init__(self) 104 compiler.visitor.ASTVisitor.__init__(self)
105 self.statements = statements 105 self.statements = statements
106 self.excluded = excluded 106 self.excluded = excluded
107 self.suite_spots = suite_spots 107 self.suite_spots = suite_spots
108 self.excluding_suite = 0 108 self.excluding_suite = 0
109 109
110 def doRecursive(self, node): 110 def doRecursive(self, node):
111 for n in node.getChildNodes(): 111 for n in node.getChildNodes():
112 self.dispatch(n) 112 self.dispatch(n)
113 113
114 visitStmt = visitModule = doRecursive 114 visitStmt = visitModule = doRecursive
115 115
116 def doCode(self, node): 116 def doCode(self, node):
117 if hasattr(node, 'decorators') and node.decorators: 117 if hasattr(node, 'decorators') and node.decorators:
118 self.dispatch(node.decorators) 118 self.dispatch(node.decorators)
119 self.recordAndDispatch(node.code) 119 self.recordAndDispatch(node.code)
120 else: 120 else:
121 self.doSuite(node, node.code) 121 self.doSuite(node, node.code)
122 122
123 visitFunction = visitClass = doCode 123 visitFunction = visitClass = doCode
124 124
125 def getFirstLine(self, node): 125 def getFirstLine(self, node):
126 # Find the first line in the tree node. 126 # Find the first line in the tree node.
127 lineno = node.lineno 127 lineno = node.lineno
137 # Find the first line in the tree node. 137 # Find the first line in the tree node.
138 lineno = node.lineno 138 lineno = node.lineno
139 for n in node.getChildNodes(): 139 for n in node.getChildNodes():
140 lineno = max(lineno, self.getLastLine(n)) 140 lineno = max(lineno, self.getLastLine(n))
141 return lineno 141 return lineno
142 142
143 def doStatement(self, node): 143 def doStatement(self, node):
144 self.recordLine(self.getFirstLine(node)) 144 self.recordLine(self.getFirstLine(node))
145 145
146 visitAssert = visitAssign = visitAssTuple = visitPrint = \ 146 visitAssert = visitAssign = visitAssTuple = visitPrint = \
147 visitPrintnl = visitRaise = visitSubscript = visitDecorators = \ 147 visitPrintnl = visitRaise = visitSubscript = visitDecorators = \
148 doStatement 148 doStatement
149 149
150 def visitPass(self, node): 150 def visitPass(self, node):
151 # Pass statements have weird interactions with docstrings. If this 151 # Pass statements have weird interactions with docstrings. If
152 # pass statement is part of one of those pairs, claim that the statement 152 # this pass statement is part of one of those pairs, claim
153 # is on the later of the two lines. 153 # that the statement is on the later of the two lines.
154 l = node.lineno 154 l = node.lineno
155 if l: 155 if l:
156 lines = self.suite_spots.get(l, [l,l]) 156 lines = self.suite_spots.get(l, [l, l])
157 self.statements[lines[1]] = 1 157 self.statements[lines[1]] = 1
158 158
159 def visitDiscard(self, node): 159 def visitDiscard(self, node):
160 # Discard nodes are statements that execute an expression, but then 160 # Discard nodes are statements that execute an expression, but then
161 # discard the results. This includes function calls, so we can't 161 # discard the results. This includes function calls, so we can't
162 # ignore them all. But if the expression is a constant, the statement 162 # ignore them all. But if the expression is a constant, the statement
163 # won't be "executed", so don't count it now. 163 # won't be "executed", so don't count it now.
164 if node.expr.__class__.__name__ != 'Const': 164 if node.expr.__class__.__name__ != 'Const':
165 self.doStatement(node) 165 self.doStatement(node)
166 166
170 # like "global a"). 170 # like "global a").
171 if node.__class__.__name__ != 'Stmt': 171 if node.__class__.__name__ != 'Stmt':
172 return self.recordLine(self.getFirstLine(node)) 172 return self.recordLine(self.getFirstLine(node))
173 else: 173 else:
174 return 0 174 return 0
175 175
176 def recordLine(self, lineno): 176 def recordLine(self, lineno):
177 # Returns a bool, whether the line is included or excluded. 177 # Returns a bool, whether the line is included or excluded.
178 if lineno: 178 if lineno:
179 # Multi-line tests introducing suites have to get charged to their 179 # Multi-line tests introducing suites have to get charged to their
180 # keyword. 180 # keyword.
194 # Otherwise, this is an executable line. 194 # Otherwise, this is an executable line.
195 else: 195 else:
196 self.statements[lineno] = 1 196 self.statements[lineno] = 1
197 return 1 197 return 1
198 return 0 198 return 0
199 199
200 default = recordNodeLine 200 default = recordNodeLine
201 201
202 def recordAndDispatch(self, node): 202 def recordAndDispatch(self, node):
203 self.recordNodeLine(node) 203 self.recordNodeLine(node)
204 self.dispatch(node) 204 self.dispatch(node)
205 205
206 def doSuite(self, intro, body, exclude=0): 206 def doSuite(self, intro, body, exclude=0):
207 exsuite = self.excluding_suite 207 exsuite = self.excluding_suite
208 if exclude or (intro and not self.recordNodeLine(intro)): 208 if exclude or (intro and not self.recordNodeLine(intro)):
209 self.excluding_suite = 1 209 self.excluding_suite = 1
210 self.recordAndDispatch(body) 210 self.recordAndDispatch(body)
211 self.excluding_suite = exsuite 211 self.excluding_suite = exsuite
212 212
213 def doPlainWordSuite(self, prevsuite, suite): 213 def doPlainWordSuite(self, prevsuite, suite):
214 # Finding the exclude lines for else's is tricky, because they aren't 214 # Finding the exclude lines for else's is tricky, because they aren't
215 # present in the compiler parse tree. Look at the previous suite, 215 # present in the compiler parse tree. Look at the previous suite,
216 # and find its last line. If any line between there and the else's 216 # and find its last line. If any line between there and the else's
217 # first line are excluded, then we exclude the else. 217 # first line are excluded, then we exclude the else.
218 lastprev = self.getLastLine(prevsuite) 218 lastprev = self.getLastLine(prevsuite)
219 firstelse = self.getFirstLine(suite) 219 firstelse = self.getFirstLine(suite)
220 for l in range(lastprev+1, firstelse): 220 for l in range(lastprev + 1, firstelse):
221 if self.suite_spots.has_key(l): 221 if self.suite_spots.has_key(l):
222 self.doSuite(None, suite, exclude=self.excluded.has_key(l)) 222 self.doSuite(None, suite, exclude=self.excluded.has_key(l))
223 break 223 break
224 else: 224 else:
225 self.doSuite(None, suite) 225 self.doSuite(None, suite)
226 226
227 def doElse(self, prevsuite, node): 227 def doElse(self, prevsuite, node):
228 if node.else_: 228 if node.else_:
229 self.doPlainWordSuite(prevsuite, node.else_) 229 self.doPlainWordSuite(prevsuite, node.else_)
230 230
231 def visitFor(self, node): 231 def visitFor(self, node):
232 self.doSuite(node, node.body) 232 self.doSuite(node, node.body)
233 self.doElse(node.body, node) 233 self.doElse(node.body, node)
234 234
235 visitWhile = visitFor 235 visitWhile = visitFor
248 for i in range(len(node.handlers)): 248 for i in range(len(node.handlers)):
249 a, b, h = node.handlers[i] 249 a, b, h = node.handlers[i]
250 if not a: 250 if not a:
251 # It's a plain "except:". Find the previous suite. 251 # It's a plain "except:". Find the previous suite.
252 if i > 0: 252 if i > 0:
253 prev = node.handlers[i-1][2] 253 prev = node.handlers[i - 1][2]
254 else: 254 else:
255 prev = node.body 255 prev = node.body
256 self.doPlainWordSuite(prev, h) 256 self.doPlainWordSuite(prev, h)
257 else: 257 else:
258 self.doSuite(a, h) 258 self.doSuite(a, h)
259 self.doElse(node.handlers[-1][2], node) 259 self.doElse(node.handlers[-1][2], node)
260 260
261 def visitTryFinally(self, node): 261 def visitTryFinally(self, node):
262 self.doSuite(node, node.body) 262 self.doSuite(node, node.body)
263 self.doPlainWordSuite(node.body, node.final) 263 self.doPlainWordSuite(node.body, node.final)
264 264
265 def visitWith(self, node): 265 def visitWith(self, node):
266 self.doSuite(node, node.body) 266 self.doSuite(node, node.body)
267 267
268 def visitGlobal(self, node): 268 def visitGlobal(self, node):
269 # "global" statements don't execute like others (they don't call the 269 # "global" statements don't execute like others (they don't call the
270 # trace function), so don't record their line numbers. 270 # trace function), so don't record their line numbers.
271 pass 271 pass
272 272
283 cache_env = "COVERAGE_FILE" 283 cache_env = "COVERAGE_FILE"
284 284
285 # A dictionary with an entry for (Python source file name, line number 285 # A dictionary with an entry for (Python source file name, line number
286 # in that file) if that line has been executed. 286 # in that file) if that line has been executed.
287 c = {} 287 c = {}
288 288
289 # A map from canonical Python source file name to a dictionary in 289 # A map from canonical Python source file name to a dictionary in
290 # which there's an entry for each line number that has been 290 # which there's an entry for each line number that has been
291 # executed. 291 # executed.
292 cexecuted = {} 292 cexecuted = {}
293 293
311 self.cstack = [] 311 self.cstack = []
312 self.xstack = [] 312 self.xstack = []
313 self.relative_dir = self.abs_file(os.curdir)+os.sep 313 self.relative_dir = self.abs_file(os.curdir)+os.sep
314 self.exclude('# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]') 314 self.exclude('# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]')
315 315
316 # t(f, x, y). This method is passed to sys.settrace as a trace function. 316 # t(f, x, y). This method is passed to sys.settrace as a trace function.
317 # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and 317 # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and
318 # the arguments and return value of the trace function. 318 # the arguments and return value of the trace function.
319 # See [van Rossum 2001-07-20a, 3.2] for a description of frame and code 319 # See [van Rossum 2001-07-20a, 3.2] for a description of frame and code
320 # objects. 320 # objects.
321 321 def t(self, f, w, unused): #pragma: no cover
322 def t(self, f, w, unused): #pragma: no cover
323 if w == 'line': 322 if w == 'line':
324 self.c[(f.f_code.co_filename, f.f_lineno)] = 1 323 self.c[(f.f_code.co_filename, f.f_lineno)] = 1
325 #-for c in self.cstack: 324 #-for c in self.cstack:
326 #- c[(f.f_code.co_filename, f.f_lineno)] = 1 325 #- c[(f.f_code.co_filename, f.f_lineno)] = 1
327 return self.t 326 return self.t
328 327
329 def help(self, error=None): #pragma: no cover 328 def help(self, error=None): #pragma: no cover
330 if error: 329 if error:
331 print error 330 print error
332 print 331 print
333 print __doc__ 332 print __doc__
361 elif o[2:] in long_opts: 360 elif o[2:] in long_opts:
362 settings[o[2:]] = 1 361 settings[o[2:]] = 1
363 elif o[2:] + '=' in long_opts: 362 elif o[2:] + '=' in long_opts:
364 settings[o[2:]+'='] = a 363 settings[o[2:]+'='] = a
365 else: #pragma: no cover 364 else: #pragma: no cover
366 pass # Can't get here, because getopt won't return anything unknown. 365 # Can't get here, because getopt won't return anything unknown.
366 pass
367 367
368 if settings.get('help'): 368 if settings.get('help'):
369 help_fn() 369 help_fn()
370 370
371 for i in ['erase', 'execute']: 371 for i in ['erase', 'execute']:
375 "options at the same time." % (i, j)) 375 "options at the same time." % (i, j))
376 376
377 args_needed = (settings.get('execute') 377 args_needed = (settings.get('execute')
378 or settings.get('annotate') 378 or settings.get('annotate')
379 or settings.get('report')) 379 or settings.get('report'))
380 action = (settings.get('erase') 380 action = (settings.get('erase')
381 or settings.get('collect') 381 or settings.get('collect')
382 or args_needed) 382 or args_needed)
383 if not action: 383 if not action:
384 help_fn("You must specify at least one of -e, -x, -c, -r, or -a.") 384 help_fn("You must specify at least one of -e, -x, -c, -r, or -a.")
385 if not args_needed and args: 385 if not args_needed and args:
386 help_fn("Unexpected arguments: %s" % " ".join(args)) 386 help_fn("Unexpected arguments: %s" % " ".join(args))
387 387
388 self.parallel_mode = settings.get('parallel-mode') 388 self.parallel_mode = settings.get('parallel-mode')
389 self.get_ready() 389 self.get_ready()
390 390
391 if settings.get('erase'): 391 if settings.get('erase'):
392 self.erase() 392 self.erase()
400 execfile(sys.argv[0], __main__.__dict__) 400 execfile(sys.argv[0], __main__.__dict__)
401 if settings.get('collect'): 401 if settings.get('collect'):
402 self.collect() 402 self.collect()
403 if not args: 403 if not args:
404 args = self.cexecuted.keys() 404 args = self.cexecuted.keys()
405 405
406 ignore_errors = settings.get('ignore-errors') 406 ignore_errors = settings.get('ignore-errors')
407 show_missing = settings.get('show-missing') 407 show_missing = settings.get('show-missing')
408 directory = settings.get('directory=') 408 directory = settings.get('directory=')
409 409
410 omit = settings.get('omit=') 410 omit = settings.get('omit=')
411 if omit is not None: 411 if omit is not None:
412 omit = [self.abs_file(p) for p in omit.split(',')] 412 omit = [self.abs_file(p) for p in omit.split(',')]
413 else: 413 else:
414 omit = [] 414 omit = []
415 415
416 if settings.get('report'): 416 if settings.get('report'):
417 self.report(args, show_missing, ignore_errors, omit_prefixes=omit) 417 self.report(args, show_missing, ignore_errors, omit_prefixes=omit)
418 if settings.get('annotate'): 418 if settings.get('annotate'):
419 self.annotate(args, directory, ignore_errors, omit_prefixes=omit) 419 self.annotate(args, directory, ignore_errors, omit_prefixes=omit)
420 420
421 def use_cache(self, usecache, cache_file=None): 421 def use_cache(self, usecache, cache_file=None):
422 self.usecache = usecache 422 self.usecache = usecache
423 if cache_file and not self.cache: 423 if cache_file and not self.cache:
424 self.cache_default = cache_file 424 self.cache_default = cache_file
425 425
426 def get_ready(self, parallel_mode=False): 426 def get_ready(self, parallel_mode=False):
427 if self.usecache and not self.cache: 427 if self.usecache and not self.cache:
428 self.cache = os.environ.get(self.cache_env, self.cache_default) 428 self.cache = os.environ.get(self.cache_env, self.cache_default)
429 if self.parallel_mode: 429 if self.parallel_mode:
430 self.cache += "." + gethostname() + "." + str(os.getpid()) 430 self.cache += "." + gethostname() + "." + str(os.getpid())
431 self.restore() 431 self.restore()
432 self.analysis_cache = {} 432 self.analysis_cache = {}
433 433
434 def start(self, parallel_mode=False): 434 def start(self, parallel_mode=False):
435 self.get_ready() 435 self.get_ready()
436 if self.nesting == 0: #pragma: no cover 436 if self.nesting == 0: #pragma: no cover
437 sys.settrace(self.t) 437 sys.settrace(self.t)
438 if hasattr(threading, 'settrace'): 438 if hasattr(threading, 'settrace'):
439 threading.settrace(self.t) 439 threading.settrace(self.t)
440 self.nesting += 1 440 self.nesting += 1
441 441
442 def stop(self): 442 def stop(self):
443 self.nesting -= 1 443 self.nesting -= 1
444 if self.nesting == 0: #pragma: no cover 444 if self.nesting == 0: #pragma: no cover
445 sys.settrace(None) 445 sys.settrace(None)
446 if hasattr(threading, 'settrace'): 446 if hasattr(threading, 'settrace'):
460 self.exclude_re += "(" + re + ")" 460 self.exclude_re += "(" + re + ")"
461 461
462 def begin_recursive(self): 462 def begin_recursive(self):
463 self.cstack.append(self.c) 463 self.cstack.append(self.c)
464 self.xstack.append(self.exclude_re) 464 self.xstack.append(self.exclude_re)
465 465
466 def end_recursive(self): 466 def end_recursive(self):
467 self.c = self.cstack.pop() 467 self.c = self.cstack.pop()
468 self.exclude_re = self.xstack.pop() 468 self.exclude_re = self.xstack.pop()
469 469
470 # save(). Save coverage data to the coverage cache. 470 # save(). Save coverage data to the coverage cache.
566 break 566 break
567 cf = self.abs_file(f) 567 cf = self.abs_file(f)
568 self.canonical_filename_cache[filename] = cf 568 self.canonical_filename_cache[filename] = cf
569 return self.canonical_filename_cache[filename] 569 return self.canonical_filename_cache[filename]
570 570
571 # canonicalize_filenames(). Copy results from "c" to "cexecuted", 571 # canonicalize_filenames(). Copy results from "c" to "cexecuted",
572 # canonicalizing filenames on the way. Clear the "c" map. 572 # canonicalizing filenames on the way. Clear the "c" map.
573 573
574 def canonicalize_filenames(self): 574 def canonicalize_filenames(self):
575 for filename, lineno in self.c.keys(): 575 for filename, lineno in self.c.keys():
576 if filename == '<string>': 576 if filename == '<string>':
596 # Otherwise, return a tuple of (1) the canonical filename of the 596 # Otherwise, return a tuple of (1) the canonical filename of the
597 # source code for the module, (2) a list of lines of statements 597 # source code for the module, (2) a list of lines of statements
598 # in the source code, (3) a list of lines of excluded statements, 598 # in the source code, (3) a list of lines of excluded statements,
599 # and (4), a map of line numbers to multi-line line number ranges, for 599 # and (4), a map of line numbers to multi-line line number ranges, for
600 # statements that cross lines. 600 # statements that cross lines.
601
602 def analyze_morf(self, morf): 601 def analyze_morf(self, morf):
603 if self.analysis_cache.has_key(morf): 602 if self.analysis_cache.has_key(morf):
604 return self.analysis_cache[morf] 603 return self.analysis_cache[morf]
605 filename = self.morf_filename(morf) 604 filename = self.morf_filename(morf)
606 ext = os.path.splitext(filename)[1] 605 ext = os.path.splitext(filename)[1]
634 def first_line_of_tree(self, tree): 633 def first_line_of_tree(self, tree):
635 while True: 634 while True:
636 if len(tree) == 3 and type(tree[2]) == type(1): 635 if len(tree) == 3 and type(tree[2]) == type(1):
637 return tree[2] 636 return tree[2]
638 tree = tree[1] 637 tree = tree[1]
639 638
640 def last_line_of_tree(self, tree): 639 def last_line_of_tree(self, tree):
641 while True: 640 while True:
642 if len(tree) == 3 and type(tree[2]) == type(1): 641 if len(tree) == 3 and type(tree[2]) == type(1):
643 return tree[2] 642 return tree[2]
644 tree = tree[-1] 643 tree = tree[-1]
645 644
646 def find_docstring_pass_pair(self, tree, spots): 645 def find_docstring_pass_pair(self, tree, spots):
647 for i in range(1, len(tree)): 646 for i in range(1, len(tree)):
648 if self.is_string_constant(tree[i]) and self.is_pass_stmt(tree[i+1]): 647 if (self.is_string_constant(tree[i]) and
648 self.is_pass_stmt(tree[i + 1]):
649 first_line = self.first_line_of_tree(tree[i]) 649 first_line = self.first_line_of_tree(tree[i])
650 last_line = self.last_line_of_tree(tree[i+1]) 650 last_line = self.last_line_of_tree(tree[i + 1])
651 self.record_multiline(spots, first_line, last_line) 651 self.record_multiline(spots, first_line, last_line)
652 652
653 def is_string_constant(self, tree): 653 def is_string_constant(self, tree):
654 try: 654 try:
655 return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.expr_stmt 655 return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.expr_stmt
656 except: 656 except:
657 return False 657 return False
658 658
659 def is_pass_stmt(self, tree): 659 def is_pass_stmt(self, tree):
660 try: 660 try:
661 return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.pass_stmt 661 return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.pass_stmt
662 except: 662 except:
663 return False 663 return False
664 664
665 def record_multiline(self, spots, i, j): 665 def record_multiline(self, spots, i, j):
666 for l in range(i, j+1): 666 for l in range(i, j + 1):
667 spots[l] = (i, j) 667 spots[l] = (i, j)
668 668
669 def get_suite_spots(self, tree, spots): 669 def get_suite_spots(self, tree, spots):
670 """ Analyze a parse tree to find suite introducers which span a number 670 """ Analyze a parse tree to find suite introducers which span a number
671 of lines. 671 of lines.
672 """ 672 """
673 for i in range(1, len(tree)): 673 for i in range(1, len(tree)):
674 if type(tree[i]) == type(()): 674 if type(tree[i]) == type(()):
675 if tree[i][0] == symbol.suite: 675 if tree[i][0] == symbol.suite:
676 # Found a suite, look back for the colon and keyword. 676 # Found a suite, look back for the colon and keyword.
677 lineno_colon = lineno_word = None 677 lineno_colon = lineno_word = None
678 for j in range(i-1, 0, -1): 678 for j in range(i - 1, 0, -1):
679 if tree[j][0] == token.COLON: 679 if tree[j][0] == token.COLON:
680 # Colons are never executed themselves: we want the 680 # Colons are never executed themselves: we want the
681 # line number of the last token before the colon. 681 # line number of the last token before the colon.
682 lineno_colon = self.last_line_of_tree(tree[j-1]) 682 lineno_colon = self.last_line_of_tree(tree[j - 1])
683 elif tree[j][0] == token.NAME: 683 elif tree[j][0] == token.NAME:
684 if tree[j][1] == 'elif': 684 if tree[j][1] == 'elif':
685 # Find the line number of the first non-terminal 685 # Find the line number of the first
686 # after the keyword. 686 # non-terminal after the keyword.
687 t = tree[j+1] 687 t = tree[j + 1]
688 while t and token.ISNONTERMINAL(t[0]): 688 while t and token.ISNONTERMINAL(t[0]):
689 t = t[1] 689 t = t[1]
690 if t: 690 if t:
691 lineno_word = t[2] 691 lineno_word = t[2]
692 else: 692 else:
693 lineno_word = tree[j][2] 693 lineno_word = tree[j][2]
694 break 694 break
695 elif tree[j][0] == symbol.except_clause: 695 elif tree[j][0] == symbol.except_clause:
696 # "except" clauses look like: 696 # "except" clauses look like:
697 # ('except_clause', ('NAME', 'except', lineno), ...) 697 # ('except_clause', ('NAME', 'except', lineno),...)
698 if tree[j][1][0] == token.NAME: 698 if tree[j][1][0] == token.NAME:
699 lineno_word = tree[j][1][2] 699 lineno_word = tree[j][1][2]
700 break 700 break
701 if lineno_colon and lineno_word: 701 if lineno_colon and lineno_word:
702 # Found colon and keyword, mark all the lines 702 # Found colon and keyword, mark all the lines
703 # between the two with the two line numbers. 703 # between the two with the two line numbers.
704 self.record_multiline(spots, lineno_word, lineno_colon) 704 self.record_multiline(spots, lineno_word, lineno_colon)
705 705
706 # "pass" statements are tricky: different versions of Python 706 # "pass" statements are tricky: different versions
707 # treat them differently, especially in the common case of a 707 # of Python treat them differently, especially in
708 # function with a doc string and a single pass statement. 708 # the common case of a function with a doc string
709 # and a single pass statement.
709 self.find_docstring_pass_pair(tree[i], spots) 710 self.find_docstring_pass_pair(tree[i], spots)
710
711 elif tree[i][0] == symbol.simple_stmt: 711 elif tree[i][0] == symbol.simple_stmt:
712 first_line = self.first_line_of_tree(tree[i]) 712 first_line = self.first_line_of_tree(tree[i])
713 last_line = self.last_line_of_tree(tree[i]) 713 last_line = self.last_line_of_tree(tree[i])
714 if first_line != last_line: 714 if first_line != last_line:
715 self.record_multiline(spots, first_line, last_line) 715 self.record_multiline(spots, first_line, last_line)
722 if exclude: 722 if exclude:
723 reExclude = re.compile(exclude) 723 reExclude = re.compile(exclude)
724 lines = text.split('\n') 724 lines = text.split('\n')
725 for i in range(len(lines)): 725 for i in range(len(lines)):
726 if reExclude.search(lines[i]): 726 if reExclude.search(lines[i]):
727 excluded[i+1] = 1 727 excluded[i + 1] = 1
728 728
729 # Parse the code and analyze the parse tree to find out which statements 729 # Parse the code and analyze the parse tree to find out which statements
730 # are multiline, and where suites begin and end. 730 # are multiline, and where suites begin and end.
731 import parser 731 import parser
732 tree = parser.suite(text+'\n\n').totuple(1) 732 tree = parser.suite(text+'\n\n').totuple(1)
733 self.get_suite_spots(tree, suite_spots) 733 self.get_suite_spots(tree, suite_spots)
734 #print "Suite spots:", suite_spots 734 #print "Suite spots:", suite_spots
735 735
736 # Use the compiler module to parse the text and find the executable 736 # Use the compiler module to parse the text and find the executable
737 # statements. We add newlines to be impervious to final partial lines. 737 # statements. We add newlines to be impervious to final partial lines.
738 statements = {} 738 statements = {}
739 ast = compiler.parse(text+'\n\n') 739 ast = compiler.parse(text+'\n\n')
740 visitor = StatementFindingAstVisitor(statements, excluded, suite_spots) 740 visitor = StatementFindingAstVisitor(statements, excluded, suite_spots)
829 return filtered_morfs 829 return filtered_morfs
830 830
831 def morf_name_compare(self, x, y): 831 def morf_name_compare(self, x, y):
832 return cmp(self.morf_name(x), self.morf_name(y)) 832 return cmp(self.morf_name(x), self.morf_name(y))
833 833
834 def report(self, morfs, show_missing=1, ignore_errors=0, file=None, omit_prefixes=[]): 834 def report(self, morfs, show_missing=1, ignore_errors=0, file=None,
835 omit_prefixes=[]):
835 if not isinstance(morfs, types.ListType): 836 if not isinstance(morfs, types.ListType):
836 morfs = [morfs] 837 morfs = [morfs]
837 # On windows, the shell doesn't expand wildcards. Do it here. 838 # On windows, the shell doesn't expand wildcards. Do it here.
838 globbed = [] 839 globbed = []
839 for morf in morfs: 840 for morf in morfs:
840 if isinstance(morf, strclass): 841 if isinstance(morf, strclass):
841 globbed.extend(glob.glob(morf)) 842 globbed.extend(glob.glob(morf))
842 else: 843 else:
843 globbed.append(morf) 844 globbed.append(morf)
844 morfs = globbed 845 morfs = globbed
845 846
846 morfs = self.filter_by_prefix(morfs, omit_prefixes) 847 morfs = self.filter_by_prefix(morfs, omit_prefixes)
847 morfs.sort(self.morf_name_compare) 848 morfs.sort(self.morf_name_compare)
848 849
849 max_name = max([5,] + map(len, map(self.morf_name, morfs))) 850 max_name = max([5,] + map(len, map(self.morf_name, morfs)))
850 fmt_name = "%%- %ds " % max_name 851 fmt_name = "%%- %ds " % max_name
854 if show_missing: 855 if show_missing:
855 header = header + " Missing" 856 header = header + " Missing"
856 fmt_coverage = fmt_coverage + " %s" 857 fmt_coverage = fmt_coverage + " %s"
857 if not file: 858 if not file:
858 file = sys.stdout 859 file = sys.stdout
859 print >>file, header 860 print >> file, header
860 print >>file, "-" * len(header) 861 print >> file, "-" * len(header)
861 total_statements = 0 862 total_statements = 0
862 total_executed = 0 863 total_executed = 0
863 for morf in morfs: 864 for morf in morfs:
864 name = self.morf_name(morf) 865 name = self.morf_name(morf)
865 try: 866 try:
901 def annotate(self, morfs, directory=None, ignore_errors=0, omit_prefixes=[]): 902 def annotate(self, morfs, directory=None, ignore_errors=0, omit_prefixes=[]):
902 morfs = self.filter_by_prefix(morfs, omit_prefixes) 903 morfs = self.filter_by_prefix(morfs, omit_prefixes)
903 for morf in morfs: 904 for morf in morfs:
904 try: 905 try:
905 filename, statements, excluded, missing, _ = self.analysis2(morf) 906 filename, statements, excluded, missing, _ = self.analysis2(morf)
906 self.annotate_file(filename, statements, excluded, missing, directory) 907 self.annotate_file(filename, statements, excluded, missing,
908 directory)
907 except KeyboardInterrupt: 909 except KeyboardInterrupt:
908 raise 910 raise
909 except: 911 except:
910 if not ignore_errors: 912 if not ignore_errors:
911 raise 913 raise
912 914
913 def annotate_file(self, filename, statements, excluded, missing, directory=None): 915 def annotate_file(self, filename, statements, excluded, missing,
916 directory=None):
914 source = open(filename, 'r') 917 source = open(filename, 'r')
915 if directory: 918 if directory:
916 dest_file = os.path.join(directory, 919 dest_file = os.path.join(directory,
917 os.path.basename(filename) 920 os.path.basename(filename)
918 + ',cover') 921 + ',cover')
935 if i < len(statements) and statements[i] == lineno: 938 if i < len(statements) and statements[i] == lineno:
936 covered = j >= len(missing) or missing[j] > lineno 939 covered = j >= len(missing) or missing[j] > lineno
937 if self.blank_re.match(line): 940 if self.blank_re.match(line):
938 dest.write(' ') 941 dest.write(' ')
939 elif self.else_re.match(line): 942 elif self.else_re.match(line):
940 # Special logic for lines containing only 'else:'. 943 # Special logic for lines containing only 'else:'.
941 # See [GDR 2001-12-04b, 3.2]. 944 # See [GDR 2001-12-04b, 3.2].
942 if i >= len(statements) and j >= len(missing): 945 if i >= len(statements) and j >= len(missing):
943 dest.write('! ') 946 dest.write('! ')
944 elif i >= len(statements) or j >= len(missing): 947 elif i >= len(statements) or j >= len(missing):
945 dest.write('> ') 948 dest.write('> ')
959 962
960 # Singleton object. 963 # Singleton object.
961 the_coverage = coverage() 964 the_coverage = coverage()
962 965
963 # Module functions call methods in the singleton object. 966 # Module functions call methods in the singleton object.
964 def use_cache(*args, **kw): 967 def use_cache(*args, **kw):
965 return the_coverage.use_cache(*args, **kw) 968 return the_coverage.use_cache(*args, **kw)
966 969
967 def start(*args, **kw): 970 def start(*args, **kw):
968 return the_coverage.start(*args, **kw) 971 return the_coverage.start(*args, **kw)
969 972
970 def stop(*args, **kw): 973 def stop(*args, **kw):
971 return the_coverage.stop(*args, **kw) 974 return the_coverage.stop(*args, **kw)
972 975
973 def erase(*args, **kw): 976 def erase(*args, **kw):
974 return the_coverage.erase(*args, **kw) 977 return the_coverage.erase(*args, **kw)
975 978
976 def begin_recursive(*args, **kw): 979 def begin_recursive(*args, **kw):
977 return the_coverage.begin_recursive(*args, **kw) 980 return the_coverage.begin_recursive(*args, **kw)
978 981
979 def end_recursive(*args, **kw): 982 def end_recursive(*args, **kw):
980 return the_coverage.end_recursive(*args, **kw) 983 return the_coverage.end_recursive(*args, **kw)
981 984
982 def exclude(*args, **kw): 985 def exclude(*args, **kw):
983 return the_coverage.exclude(*args, **kw) 986 return the_coverage.exclude(*args, **kw)
984 987
985 def analysis(*args, **kw): 988 def analysis(*args, **kw):
986 return the_coverage.analysis(*args, **kw) 989 return the_coverage.analysis(*args, **kw)
987 990
988 def analysis2(*args, **kw): 991 def analysis2(*args, **kw):
989 return the_coverage.analysis2(*args, **kw) 992 return the_coverage.analysis2(*args, **kw)
990 993
991 def report(*args, **kw): 994 def report(*args, **kw):
992 return the_coverage.report(*args, **kw) 995 return the_coverage.report(*args, **kw)
993 996
994 def annotate(*args, **kw): 997 def annotate(*args, **kw):
995 return the_coverage.annotate(*args, **kw) 998 return the_coverage.annotate(*args, **kw)
996 999
997 def annotate_file(*args, **kw): 1000 def annotate_file(*args, **kw):
998 return the_coverage.annotate_file(*args, **kw) 1001 return the_coverage.annotate_file(*args, **kw)
999 1002
1000 # Save coverage data when Python exits. (The atexit module wasn't 1003 # Save coverage data when Python exits. (The atexit module wasn't
1001 # introduced until Python 2.0, so use sys.exitfunc when it's not 1004 # introduced until Python 2.0, so use sys.exitfunc when it's not
1002 # available.) 1005 # available.)
1006 except ImportError: 1009 except ImportError:
1007 sys.exitfunc = the_coverage.save 1010 sys.exitfunc = the_coverage.save
1008 1011
1009 def main(): 1012 def main():
1010 the_coverage.command_line(sys.argv[1:]) 1013 the_coverage.command_line(sys.argv[1:])
1011 1014
1012 # Command-line interface. 1015 # Command-line interface.
1013 if __name__ == '__main__': 1016 if __name__ == '__main__':
1014 main() 1017 main()
1015 1018
1016 1019
1070 # 1073 #
1071 # 2004-12-31 NMB Allow for keyword arguments in the module global functions. 1074 # 2004-12-31 NMB Allow for keyword arguments in the module global functions.
1072 # Thanks, Allen. 1075 # Thanks, Allen.
1073 # 1076 #
1074 # 2005-12-02 NMB Call threading.settrace so that all threads are measured. 1077 # 2005-12-02 NMB Call threading.settrace so that all threads are measured.
1075 # Thanks Martin Fuzzey. Add a file argument to report so that reports can be 1078 # Thanks Martin Fuzzey. Add a file argument to report so that reports can be
1076 # captured to a different destination. 1079 # captured to a different destination.
1077 # 1080 #
1078 # 2005-12-03 NMB coverage.py can now measure itself. 1081 # 2005-12-03 NMB coverage.py can now measure itself.
1079 # 1082 #
1080 # 2005-12-04 NMB Adapted Greg Rogers' patch for using relative filenames, 1083 # 2005-12-04 NMB Adapted Greg Rogers' patch for using relative filenames,