103 compiler.visitor.ASTVisitor.__init__(self) |
103 compiler.visitor.ASTVisitor.__init__(self) |
104 self.statements = statements |
104 self.statements = statements |
105 self.excluded = excluded |
105 self.excluded = excluded |
106 self.suite_spots = suite_spots |
106 self.suite_spots = suite_spots |
107 self.excluding_suite = 0 |
107 self.excluding_suite = 0 |
108 |
108 |
109 def doRecursive(self, node): |
109 def doRecursive(self, node): |
110 for n in node.getChildNodes(): |
110 for n in node.getChildNodes(): |
111 self.dispatch(n) |
111 self.dispatch(n) |
112 |
112 |
113 visitStmt = visitModule = doRecursive |
113 visitStmt = visitModule = doRecursive |
114 |
114 |
115 def doCode(self, node): |
115 def doCode(self, node): |
116 if hasattr(node, 'decorators') and node.decorators: |
116 if hasattr(node, 'decorators') and node.decorators: |
117 self.dispatch(node.decorators) |
117 self.dispatch(node.decorators) |
118 self.recordAndDispatch(node.code) |
118 self.recordAndDispatch(node.code) |
119 else: |
119 else: |
120 self.doSuite(node, node.code) |
120 self.doSuite(node, node.code) |
121 |
121 |
122 visitFunction = visitClass = doCode |
122 visitFunction = visitClass = doCode |
123 |
123 |
124 def getFirstLine(self, node): |
124 def getFirstLine(self, node): |
125 # Find the first line in the tree node. |
125 # Find the first line in the tree node. |
126 lineno = node.lineno |
126 lineno = node.lineno |
136 # Find the first line in the tree node. |
136 # Find the first line in the tree node. |
137 lineno = node.lineno |
137 lineno = node.lineno |
138 for n in node.getChildNodes(): |
138 for n in node.getChildNodes(): |
139 lineno = max(lineno, self.getLastLine(n)) |
139 lineno = max(lineno, self.getLastLine(n)) |
140 return lineno |
140 return lineno |
141 |
141 |
142 def doStatement(self, node): |
142 def doStatement(self, node): |
143 self.recordLine(self.getFirstLine(node)) |
143 self.recordLine(self.getFirstLine(node)) |
144 |
144 |
145 visitAssert = visitAssign = visitAssTuple = visitPrint = \ |
145 visitAssert = visitAssign = visitAssTuple = visitPrint = \ |
146 visitPrintnl = visitRaise = visitSubscript = visitDecorators = \ |
146 visitPrintnl = visitRaise = visitSubscript = visitDecorators = \ |
147 doStatement |
147 doStatement |
148 |
148 |
149 def visitPass(self, node): |
149 def visitPass(self, node): |
150 # Pass statements have weird interactions with docstrings. If this |
150 # Pass statements have weird interactions with docstrings. If this |
151 # pass statement is part of one of those pairs, claim that the statement |
151 # pass statement is part of one of those pairs, claim that the statement |
152 # is on the later of the two lines. |
152 # is on the later of the two lines. |
153 l = node.lineno |
153 l = node.lineno |
154 if l: |
154 if l: |
155 lines = self.suite_spots.get(l, [l,l]) |
155 lines = self.suite_spots.get(l, [l,l]) |
156 self.statements[lines[1]] = 1 |
156 self.statements[lines[1]] = 1 |
157 |
157 |
158 def visitDiscard(self, node): |
158 def visitDiscard(self, node): |
159 # Discard nodes are statements that execute an expression, but then |
159 # Discard nodes are statements that execute an expression, but then |
160 # discard the results. This includes function calls, so we can't |
160 # discard the results. This includes function calls, so we can't |
161 # ignore them all. But if the expression is a constant, the statement |
161 # ignore them all. But if the expression is a constant, the statement |
162 # won't be "executed", so don't count it now. |
162 # won't be "executed", so don't count it now. |
163 if node.expr.__class__.__name__ != 'Const': |
163 if node.expr.__class__.__name__ != 'Const': |
164 self.doStatement(node) |
164 self.doStatement(node) |
165 |
165 |
193 # Otherwise, this is an executable line. |
193 # Otherwise, this is an executable line. |
194 else: |
194 else: |
195 self.statements[lineno] = 1 |
195 self.statements[lineno] = 1 |
196 return 1 |
196 return 1 |
197 return 0 |
197 return 0 |
198 |
198 |
199 default = recordNodeLine |
199 default = recordNodeLine |
200 |
200 |
201 def recordAndDispatch(self, node): |
201 def recordAndDispatch(self, node): |
202 self.recordNodeLine(node) |
202 self.recordNodeLine(node) |
203 self.dispatch(node) |
203 self.dispatch(node) |
204 |
204 |
205 def doSuite(self, intro, body, exclude=0): |
205 def doSuite(self, intro, body, exclude=0): |
206 exsuite = self.excluding_suite |
206 exsuite = self.excluding_suite |
207 if exclude or (intro and not self.recordNodeLine(intro)): |
207 if exclude or (intro and not self.recordNodeLine(intro)): |
208 self.excluding_suite = 1 |
208 self.excluding_suite = 1 |
209 self.recordAndDispatch(body) |
209 self.recordAndDispatch(body) |
210 self.excluding_suite = exsuite |
210 self.excluding_suite = exsuite |
211 |
211 |
212 def doPlainWordSuite(self, prevsuite, suite): |
212 def doPlainWordSuite(self, prevsuite, suite): |
213 # Finding the exclude lines for else's is tricky, because they aren't |
213 # Finding the exclude lines for else's is tricky, because they aren't |
214 # present in the compiler parse tree. Look at the previous suite, |
214 # present in the compiler parse tree. Look at the previous suite, |
215 # and find its last line. If any line between there and the else's |
215 # and find its last line. If any line between there and the else's |
216 # first line are excluded, then we exclude the else. |
216 # first line are excluded, then we exclude the else. |
220 if self.suite_spots.has_key(l): |
220 if self.suite_spots.has_key(l): |
221 self.doSuite(None, suite, exclude=self.excluded.has_key(l)) |
221 self.doSuite(None, suite, exclude=self.excluded.has_key(l)) |
222 break |
222 break |
223 else: |
223 else: |
224 self.doSuite(None, suite) |
224 self.doSuite(None, suite) |
225 |
225 |
226 def doElse(self, prevsuite, node): |
226 def doElse(self, prevsuite, node): |
227 if node.else_: |
227 if node.else_: |
228 self.doPlainWordSuite(prevsuite, node.else_) |
228 self.doPlainWordSuite(prevsuite, node.else_) |
229 |
229 |
230 def visitFor(self, node): |
230 def visitFor(self, node): |
231 self.doSuite(node, node.body) |
231 self.doSuite(node, node.body) |
232 self.doElse(node.body, node) |
232 self.doElse(node.body, node) |
233 |
233 |
234 visitWhile = visitFor |
234 visitWhile = visitFor |
254 prev = node.body |
254 prev = node.body |
255 self.doPlainWordSuite(prev, h) |
255 self.doPlainWordSuite(prev, h) |
256 else: |
256 else: |
257 self.doSuite(a, h) |
257 self.doSuite(a, h) |
258 self.doElse(node.handlers[-1][2], node) |
258 self.doElse(node.handlers[-1][2], node) |
259 |
259 |
260 def visitTryFinally(self, node): |
260 def visitTryFinally(self, node): |
261 self.doSuite(node, node.body) |
261 self.doSuite(node, node.body) |
262 self.doPlainWordSuite(node.body, node.final) |
262 self.doPlainWordSuite(node.body, node.final) |
263 |
263 |
264 def visitWith(self, node): |
264 def visitWith(self, node): |
265 self.doSuite(node, node.body) |
265 self.doSuite(node, node.body) |
266 |
266 |
267 def visitGlobal(self, node): |
267 def visitGlobal(self, node): |
268 # "global" statements don't execute like others (they don't call the |
268 # "global" statements don't execute like others (they don't call the |
269 # trace function), so don't record their line numbers. |
269 # trace function), so don't record their line numbers. |
270 pass |
270 pass |
271 |
271 |
309 self.cstack = [] |
309 self.cstack = [] |
310 self.xstack = [] |
310 self.xstack = [] |
311 self.relative_dir = os.path.normcase(os.path.abspath(os.curdir)+os.sep) |
311 self.relative_dir = os.path.normcase(os.path.abspath(os.curdir)+os.sep) |
312 self.exclude('# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]') |
312 self.exclude('# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]') |
313 |
313 |
314 # t(f, x, y). This method is passed to sys.settrace as a trace function. |
314 # t(f, x, y). This method is passed to sys.settrace as a trace function. |
315 # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and |
315 # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and |
316 # the arguments and return value of the trace function. |
316 # the arguments and return value of the trace function. |
317 # See [van Rossum 2001-07-20a, 3.2] for a description of frame and code |
317 # See [van Rossum 2001-07-20a, 3.2] for a description of frame and code |
318 # objects. |
318 # objects. |
319 |
319 |
320 def t(self, f, w, unused): #pragma: no cover |
320 def t(self, f, w, unused): #pragma: no cover |
321 if w == 'line': |
321 if w == 'line': |
322 #print "Executing %s @ %d" % (f.f_code.co_filename, f.f_lineno) |
322 #print "Executing %s @ %d" % (f.f_code.co_filename, f.f_lineno) |
323 self.c[(f.f_code.co_filename, f.f_lineno)] = 1 |
323 self.c[(f.f_code.co_filename, f.f_lineno)] = 1 |
324 for c in self.cstack: |
324 for c in self.cstack: |
325 c[(f.f_code.co_filename, f.f_lineno)] = 1 |
325 c[(f.f_code.co_filename, f.f_lineno)] = 1 |
326 return self.t |
326 return self.t |
327 |
327 |
328 def help(self, error=None): #pragma: no cover |
328 def help(self, error=None): #pragma: no cover |
329 if error: |
329 if error: |
330 print error |
330 print error |
331 print |
331 print |
332 print __doc__ |
332 print __doc__ |
374 "options at the same time." % (i, j)) |
374 "options at the same time." % (i, j)) |
375 |
375 |
376 args_needed = (settings.get('execute') |
376 args_needed = (settings.get('execute') |
377 or settings.get('annotate') |
377 or settings.get('annotate') |
378 or settings.get('report')) |
378 or settings.get('report')) |
379 action = (settings.get('erase') |
379 action = (settings.get('erase') |
380 or settings.get('collect') |
380 or settings.get('collect') |
381 or args_needed) |
381 or args_needed) |
382 if not action: |
382 if not action: |
383 help_fn("You must specify at least one of -e, -x, -c, -r, or -a.") |
383 help_fn("You must specify at least one of -e, -x, -c, -r, or -a.") |
384 if not args_needed and args: |
384 if not args_needed and args: |
385 help_fn("Unexpected arguments: %s" % " ".join(args)) |
385 help_fn("Unexpected arguments: %s" % " ".join(args)) |
386 |
386 |
387 self.parallel_mode = settings.get('parallel-mode') |
387 self.parallel_mode = settings.get('parallel-mode') |
388 self.get_ready() |
388 self.get_ready() |
389 |
389 |
390 if settings.get('erase'): |
390 if settings.get('erase'): |
391 self.erase() |
391 self.erase() |
419 |
419 |
420 def use_cache(self, usecache, cache_file=None): |
420 def use_cache(self, usecache, cache_file=None): |
421 self.usecache = usecache |
421 self.usecache = usecache |
422 if cache_file and not self.cache: |
422 if cache_file and not self.cache: |
423 self.cache_default = cache_file |
423 self.cache_default = cache_file |
424 |
424 |
425 def get_ready(self, parallel_mode=False): |
425 def get_ready(self, parallel_mode=False): |
426 if self.usecache and not self.cache: |
426 if self.usecache and not self.cache: |
427 self.cache = os.environ.get(self.cache_env, self.cache_default) |
427 self.cache = os.environ.get(self.cache_env, self.cache_default) |
428 if self.parallel_mode: |
428 if self.parallel_mode: |
429 self.cache += "." + gethostname() + "." + str(os.getpid()) |
429 self.cache += "." + gethostname() + "." + str(os.getpid()) |
430 self.restore() |
430 self.restore() |
431 self.analysis_cache = {} |
431 self.analysis_cache = {} |
432 |
432 |
433 def start(self, parallel_mode=False): |
433 def start(self, parallel_mode=False): |
434 self.get_ready() |
434 self.get_ready() |
435 if self.nesting == 0: #pragma: no cover |
435 if self.nesting == 0: #pragma: no cover |
436 sys.settrace(self.t) |
436 sys.settrace(self.t) |
437 if hasattr(threading, 'settrace'): |
437 if hasattr(threading, 'settrace'): |
438 threading.settrace(self.t) |
438 threading.settrace(self.t) |
439 self.nesting += 1 |
439 self.nesting += 1 |
440 |
440 |
441 def stop(self): |
441 def stop(self): |
442 self.nesting -= 1 |
442 self.nesting -= 1 |
443 if self.nesting == 0: #pragma: no cover |
443 if self.nesting == 0: #pragma: no cover |
444 sys.settrace(None) |
444 sys.settrace(None) |
445 if hasattr(threading, 'settrace'): |
445 if hasattr(threading, 'settrace'): |
459 self.exclude_re += "(" + re + ")" |
459 self.exclude_re += "(" + re + ")" |
460 |
460 |
461 def begin_recursive(self): |
461 def begin_recursive(self): |
462 self.cstack.append(self.c) |
462 self.cstack.append(self.c) |
463 self.xstack.append(self.exclude_re) |
463 self.xstack.append(self.exclude_re) |
464 |
464 |
465 def end_recursive(self): |
465 def end_recursive(self): |
466 self.c = self.cstack.pop() |
466 self.c = self.cstack.pop() |
467 self.exclude_re = self.xstack.pop() |
467 self.exclude_re = self.xstack.pop() |
468 |
468 |
469 # save(). Save coverage data to the coverage cache. |
469 # save(). Save coverage data to the coverage cache. |
539 break |
539 break |
540 cf = os.path.normcase(os.path.abspath(f)) |
540 cf = os.path.normcase(os.path.abspath(f)) |
541 self.canonical_filename_cache[filename] = cf |
541 self.canonical_filename_cache[filename] = cf |
542 return self.canonical_filename_cache[filename] |
542 return self.canonical_filename_cache[filename] |
543 |
543 |
544 # canonicalize_filenames(). Copy results from "c" to "cexecuted", |
544 # canonicalize_filenames(). Copy results from "c" to "cexecuted", |
545 # canonicalizing filenames on the way. Clear the "c" map. |
545 # canonicalizing filenames on the way. Clear the "c" map. |
546 |
546 |
547 def canonicalize_filenames(self): |
547 def canonicalize_filenames(self): |
548 for filename, lineno in self.c.keys(): |
548 for filename, lineno in self.c.keys(): |
549 if filename == '<string>': |
549 if filename == '<string>': |
571 # Otherwise, return a tuple of (1) the canonical filename of the |
571 # Otherwise, return a tuple of (1) the canonical filename of the |
572 # source code for the module, (2) a list of lines of statements |
572 # source code for the module, (2) a list of lines of statements |
573 # in the source code, (3) a list of lines of excluded statements, |
573 # in the source code, (3) a list of lines of excluded statements, |
574 # and (4), a map of line numbers to multi-line line number ranges, for |
574 # and (4), a map of line numbers to multi-line line number ranges, for |
575 # statements that cross lines. |
575 # statements that cross lines. |
576 |
576 |
577 def analyze_morf(self, morf): |
577 def analyze_morf(self, morf): |
578 if self.analysis_cache.has_key(morf): |
578 if self.analysis_cache.has_key(morf): |
579 return self.analysis_cache[morf] |
579 return self.analysis_cache[morf] |
580 filename = self.morf_filename(morf) |
580 filename = self.morf_filename(morf) |
581 ext = os.path.splitext(filename)[1] |
581 ext = os.path.splitext(filename)[1] |
598 def first_line_of_tree(self, tree): |
598 def first_line_of_tree(self, tree): |
599 while True: |
599 while True: |
600 if len(tree) == 3 and type(tree[2]) == type(1): |
600 if len(tree) == 3 and type(tree[2]) == type(1): |
601 return tree[2] |
601 return tree[2] |
602 tree = tree[1] |
602 tree = tree[1] |
603 |
603 |
604 def last_line_of_tree(self, tree): |
604 def last_line_of_tree(self, tree): |
605 while True: |
605 while True: |
606 if len(tree) == 3 and type(tree[2]) == type(1): |
606 if len(tree) == 3 and type(tree[2]) == type(1): |
607 return tree[2] |
607 return tree[2] |
608 tree = tree[-1] |
608 tree = tree[-1] |
609 |
609 |
610 def find_docstring_pass_pair(self, tree, spots): |
610 def find_docstring_pass_pair(self, tree, spots): |
611 for i in range(1, len(tree)): |
611 for i in range(1, len(tree)): |
612 if self.is_string_constant(tree[i]) and self.is_pass_stmt(tree[i+1]): |
612 if self.is_string_constant(tree[i]) and self.is_pass_stmt(tree[i+1]): |
613 first_line = self.first_line_of_tree(tree[i]) |
613 first_line = self.first_line_of_tree(tree[i]) |
614 last_line = self.last_line_of_tree(tree[i+1]) |
614 last_line = self.last_line_of_tree(tree[i+1]) |
615 self.record_multiline(spots, first_line, last_line) |
615 self.record_multiline(spots, first_line, last_line) |
616 |
616 |
617 def is_string_constant(self, tree): |
617 def is_string_constant(self, tree): |
618 try: |
618 try: |
619 return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.expr_stmt |
619 return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.expr_stmt |
620 except: |
620 except: |
621 return False |
621 return False |
622 |
622 |
623 def is_pass_stmt(self, tree): |
623 def is_pass_stmt(self, tree): |
624 try: |
624 try: |
625 return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.pass_stmt |
625 return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.pass_stmt |
626 except: |
626 except: |
627 return False |
627 return False |
628 |
628 |
629 def record_multiline(self, spots, i, j): |
629 def record_multiline(self, spots, i, j): |
630 for l in range(i, j+1): |
630 for l in range(i, j+1): |
631 spots[l] = (i, j) |
631 spots[l] = (i, j) |
632 |
632 |
633 def get_suite_spots(self, tree, spots): |
633 def get_suite_spots(self, tree, spots): |
634 """ Analyze a parse tree to find suite introducers which span a number |
634 """ Analyze a parse tree to find suite introducers which span a number |
635 of lines. |
635 of lines. |
636 """ |
636 """ |
637 for i in range(1, len(tree)): |
637 for i in range(1, len(tree)): |
669 |
669 |
670 # "pass" statements are tricky: different versions of Python |
670 # "pass" statements are tricky: different versions of Python |
671 # treat them differently, especially in the common case of a |
671 # treat them differently, especially in the common case of a |
672 # function with a doc string and a single pass statement. |
672 # function with a doc string and a single pass statement. |
673 self.find_docstring_pass_pair(tree[i], spots) |
673 self.find_docstring_pass_pair(tree[i], spots) |
674 |
674 |
675 elif tree[i][0] == symbol.simple_stmt: |
675 elif tree[i][0] == symbol.simple_stmt: |
676 first_line = self.first_line_of_tree(tree[i]) |
676 first_line = self.first_line_of_tree(tree[i]) |
677 last_line = self.last_line_of_tree(tree[i]) |
677 last_line = self.last_line_of_tree(tree[i]) |
678 if first_line != last_line: |
678 if first_line != last_line: |
679 self.record_multiline(spots, first_line, last_line) |
679 self.record_multiline(spots, first_line, last_line) |
694 # are multiline, and where suites begin and end. |
694 # are multiline, and where suites begin and end. |
695 import parser |
695 import parser |
696 tree = parser.suite(text+'\n\n').totuple(1) |
696 tree = parser.suite(text+'\n\n').totuple(1) |
697 self.get_suite_spots(tree, suite_spots) |
697 self.get_suite_spots(tree, suite_spots) |
698 #print "Suite spots:", suite_spots |
698 #print "Suite spots:", suite_spots |
699 |
699 |
700 # Use the compiler module to parse the text and find the executable |
700 # Use the compiler module to parse the text and find the executable |
701 # statements. We add newlines to be impervious to final partial lines. |
701 # statements. We add newlines to be impervious to final partial lines. |
702 statements = {} |
702 statements = {} |
703 ast = compiler.parse(text+'\n\n') |
703 ast = compiler.parse(text+'\n\n') |
704 visitor = StatementFindingAstVisitor(statements, excluded, suite_spots) |
704 visitor = StatementFindingAstVisitor(statements, excluded, suite_spots) |
804 if isinstance(morf, strclass): |
804 if isinstance(morf, strclass): |
805 globbed.extend(glob.glob(morf)) |
805 globbed.extend(glob.glob(morf)) |
806 else: |
806 else: |
807 globbed.append(morf) |
807 globbed.append(morf) |
808 morfs = globbed |
808 morfs = globbed |
809 |
809 |
810 morfs = self.filter_by_prefix(morfs, omit_prefixes) |
810 morfs = self.filter_by_prefix(morfs, omit_prefixes) |
811 morfs.sort(self.morf_name_compare) |
811 morfs.sort(self.morf_name_compare) |
812 |
812 |
813 max_name = max([5,] + map(len, map(self.morf_name, morfs))) |
813 max_name = max([5,] + map(len, map(self.morf_name, morfs))) |
814 fmt_name = "%%- %ds " % max_name |
814 fmt_name = "%%- %ds " % max_name |
871 except KeyboardInterrupt: |
871 except KeyboardInterrupt: |
872 raise |
872 raise |
873 except: |
873 except: |
874 if not ignore_errors: |
874 if not ignore_errors: |
875 raise |
875 raise |
876 |
876 |
877 def annotate_file(self, filename, statements, excluded, missing, directory=None): |
877 def annotate_file(self, filename, statements, excluded, missing, directory=None): |
878 source = open(filename, 'r') |
878 source = open(filename, 'r') |
879 if directory: |
879 if directory: |
880 dest_file = os.path.join(directory, |
880 dest_file = os.path.join(directory, |
881 os.path.basename(filename) |
881 os.path.basename(filename) |
899 if i < len(statements) and statements[i] == lineno: |
899 if i < len(statements) and statements[i] == lineno: |
900 covered = j >= len(missing) or missing[j] > lineno |
900 covered = j >= len(missing) or missing[j] > lineno |
901 if self.blank_re.match(line): |
901 if self.blank_re.match(line): |
902 dest.write(' ') |
902 dest.write(' ') |
903 elif self.else_re.match(line): |
903 elif self.else_re.match(line): |
904 # Special logic for lines containing only 'else:'. |
904 # Special logic for lines containing only 'else:'. |
905 # See [GDR 2001-12-04b, 3.2]. |
905 # See [GDR 2001-12-04b, 3.2]. |
906 if i >= len(statements) and j >= len(missing): |
906 if i >= len(statements) and j >= len(missing): |
907 dest.write('! ') |
907 dest.write('! ') |
908 elif i >= len(statements) or j >= len(missing): |
908 elif i >= len(statements) or j >= len(missing): |
909 dest.write('> ') |
909 dest.write('> ') |
923 |
923 |
924 # Singleton object. |
924 # Singleton object. |
925 the_coverage = coverage() |
925 the_coverage = coverage() |
926 |
926 |
927 # Module functions call methods in the singleton object. |
927 # Module functions call methods in the singleton object. |
928 def use_cache(*args, **kw): |
928 def use_cache(*args, **kw): |
929 return the_coverage.use_cache(*args, **kw) |
929 return the_coverage.use_cache(*args, **kw) |
930 |
930 |
931 def start(*args, **kw): |
931 def start(*args, **kw): |
932 return the_coverage.start(*args, **kw) |
932 return the_coverage.start(*args, **kw) |
933 |
933 |
934 def stop(*args, **kw): |
934 def stop(*args, **kw): |
935 return the_coverage.stop(*args, **kw) |
935 return the_coverage.stop(*args, **kw) |
936 |
936 |
937 def erase(*args, **kw): |
937 def erase(*args, **kw): |
938 return the_coverage.erase(*args, **kw) |
938 return the_coverage.erase(*args, **kw) |
939 |
939 |
940 def begin_recursive(*args, **kw): |
940 def begin_recursive(*args, **kw): |
941 return the_coverage.begin_recursive(*args, **kw) |
941 return the_coverage.begin_recursive(*args, **kw) |
942 |
942 |
943 def end_recursive(*args, **kw): |
943 def end_recursive(*args, **kw): |
944 return the_coverage.end_recursive(*args, **kw) |
944 return the_coverage.end_recursive(*args, **kw) |
945 |
945 |
946 def exclude(*args, **kw): |
946 def exclude(*args, **kw): |
947 return the_coverage.exclude(*args, **kw) |
947 return the_coverage.exclude(*args, **kw) |
948 |
948 |
949 def analysis(*args, **kw): |
949 def analysis(*args, **kw): |
950 return the_coverage.analysis(*args, **kw) |
950 return the_coverage.analysis(*args, **kw) |
951 |
951 |
952 def analysis2(*args, **kw): |
952 def analysis2(*args, **kw): |
953 return the_coverage.analysis2(*args, **kw) |
953 return the_coverage.analysis2(*args, **kw) |
954 |
954 |
955 def report(*args, **kw): |
955 def report(*args, **kw): |
956 return the_coverage.report(*args, **kw) |
956 return the_coverage.report(*args, **kw) |
957 |
957 |
958 def annotate(*args, **kw): |
958 def annotate(*args, **kw): |
959 return the_coverage.annotate(*args, **kw) |
959 return the_coverage.annotate(*args, **kw) |
960 |
960 |
961 def annotate_file(*args, **kw): |
961 def annotate_file(*args, **kw): |
962 return the_coverage.annotate_file(*args, **kw) |
962 return the_coverage.annotate_file(*args, **kw) |
963 |
963 |
964 # Save coverage data when Python exits. (The atexit module wasn't |
964 # Save coverage data when Python exits. (The atexit module wasn't |
965 # introduced until Python 2.0, so use sys.exitfunc when it's not |
965 # introduced until Python 2.0, so use sys.exitfunc when it's not |
966 # available.) |
966 # available.) |