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 |
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 |
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__ |
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'): |
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 |
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') |
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.) |