3 # Perforce Defect Tracking Integration Project
4 # <http://www.ravenbrook.com/project/p4dti/>
6 # COVERAGE.PY -- COVERAGE TESTING
8 # Gareth Rees, Ravenbrook Limited, 2001-12-04
9 # Ned Batchelder, 2004-12-12
10 # http://nedbatchelder.com/code/modules/coverage.html
15 # This module provides coverage testing for Python code.
17 # The intended readership is all Python developers.
19 # This document is not confidential.
21 # See [GDR 2001-12-04a] for the command-line interface, programmatic
22 # interface and limitations. See [GDR 2001-12-04b] for requirements and
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.
33 Erase collected coverage data.
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.
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.
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.
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
54 Coverage data is saved in the file .coverage by default. Set the
55 COVERAGE_FILE environment variable to save it somewhere else."""
57 __version__
= "2.6.20060823" # see detailed history at the end of this file.
60 import compiler
.visitor
67 from socket
import gethostname
71 # This uses the "singleton" pattern.
73 # The word "morf" means a module object (from which the source file can
74 # be deduced by suitable manipulation of the __file__ attribute) or a
77 # When we generate a coverage report we have to canonicalize every
78 # filename in the coverage dictionary just in case it refers to the
79 # module we are reporting on. It seems a shame to throw away this
80 # information so the data in the coverage dictionary is transferred to
81 # the 'cexecuted' dictionary under the canonical filenames.
83 # The coverage dictionary is called "c" and the trace function "t". The
84 # reason for these short names is that Python looks up variables by name
85 # at runtime and so execution time depends on the length of variables!
86 # In the bottleneck of this application it's appropriate to abbreviate
87 # names to increase speed.
89 class StatementFindingAstVisitor(compiler
.visitor
.ASTVisitor
):
90 def __init__(self
, statements
, excluded
, suite_spots
):
91 compiler
.visitor
.ASTVisitor
.__init
__(self
)
92 self
.statements
= statements
93 self
.excluded
= excluded
94 self
.suite_spots
= suite_spots
95 self
.excluding_suite
= 0
97 def doRecursive(self
, node
):
98 self
.recordNodeLine(node
)
99 for n
in node
.getChildNodes():
102 visitStmt
= visitModule
= doRecursive
104 def doCode(self
, node
):
105 if hasattr(node
, 'decorators') and node
.decorators
:
106 self
.dispatch(node
.decorators
)
107 self
.recordAndDispatch(node
.code
)
109 self
.doSuite(node
, node
.code
)
111 visitFunction
= visitClass
= doCode
113 def getFirstLine(self
, node
):
114 # Find the first line in the tree node.
116 for n
in node
.getChildNodes():
117 f
= self
.getFirstLine(n
)
119 lineno
= min(lineno
, f
)
124 def getLastLine(self
, node
):
125 # Find the first line in the tree node.
127 for n
in node
.getChildNodes():
128 lineno
= max(lineno
, self
.getLastLine(n
))
131 def doStatement(self
, node
):
132 self
.recordLine(self
.getFirstLine(node
))
134 visitAssert
= visitAssign
= visitAssTuple
= visitDiscard
= visitPrint
= \
135 visitPrintnl
= visitRaise
= visitSubscript
= visitDecorators
= \
138 def recordNodeLine(self
, node
):
139 return self
.recordLine(node
.lineno
)
141 def recordLine(self
, lineno
):
142 # Returns a bool, whether the line is included or excluded.
144 # Multi-line tests introducing suites have to get charged to their
146 if lineno
in self
.suite_spots
:
147 lineno
= self
.suite_spots
[lineno
][0]
148 # If we're inside an exluded suite, record that this line was
150 if self
.excluding_suite
:
151 self
.excluded
[lineno
] = 1
153 # If this line is excluded, or suite_spots maps this line to
154 # another line that is exlcuded, then we're excluded.
155 elif self
.excluded
.has_key(lineno
) or \
156 self
.suite_spots
.has_key(lineno
) and \
157 self
.excluded
.has_key(self
.suite_spots
[lineno
][1]):
159 # Otherwise, this is an executable line.
161 self
.statements
[lineno
] = 1
165 default
= recordNodeLine
167 def recordAndDispatch(self
, node
):
168 self
.recordNodeLine(node
)
171 def doSuite(self
, intro
, body
, exclude
=0):
172 exsuite
= self
.excluding_suite
173 if exclude
or (intro
and not self
.recordNodeLine(intro
)):
174 self
.excluding_suite
= 1
175 self
.recordAndDispatch(body
)
176 self
.excluding_suite
= exsuite
178 def doPlainWordSuite(self
, prevsuite
, suite
):
179 # Finding the exclude lines for else's is tricky, because they aren't
180 # present in the compiler parse tree. Look at the previous suite,
181 # and find its last line. If any line between there and the else's
182 # first line are excluded, then we exclude the else.
183 lastprev
= self
.getLastLine(prevsuite
)
184 firstelse
= self
.getFirstLine(suite
)
185 for l
in range(lastprev
+1, firstelse
):
186 if self
.suite_spots
.has_key(l
):
187 self
.doSuite(None, suite
, exclude
=self
.excluded
.has_key(l
))
190 self
.doSuite(None, suite
)
192 def doElse(self
, prevsuite
, node
):
194 self
.doPlainWordSuite(prevsuite
, node
.else_
)
196 def visitFor(self
, node
):
197 self
.doSuite(node
, node
.body
)
198 self
.doElse(node
.body
, node
)
200 def visitIf(self
, node
):
201 # The first test has to be handled separately from the rest.
202 # The first test is credited to the line with the "if", but the others
203 # are credited to the line with the test for the elif.
204 self
.doSuite(node
, node
.tests
[0][1])
205 for t
, n
in node
.tests
[1:]:
207 self
.doElse(node
.tests
[-1][1], node
)
209 def visitWhile(self
, node
):
210 self
.doSuite(node
, node
.body
)
211 self
.doElse(node
.body
, node
)
213 def visitTryExcept(self
, node
):
214 self
.doSuite(node
, node
.body
)
215 for i
in range(len(node
.handlers
)):
216 a
, b
, h
= node
.handlers
[i
]
218 # It's a plain "except:". Find the previous suite.
220 prev
= node
.handlers
[i
-1][2]
223 self
.doPlainWordSuite(prev
, h
)
226 self
.doElse(node
.handlers
[-1][2], node
)
228 def visitTryFinally(self
, node
):
229 self
.doSuite(node
, node
.body
)
230 self
.doPlainWordSuite(node
.body
, node
.final
)
232 def visitGlobal(self
, node
):
233 # "global" statements don't execute like others (they don't call the
234 # trace function), so don't record their line numbers.
239 class CoverageException(Exception): pass
242 # Name of the cache file (unless environment variable is set).
243 cache_default
= ".coverage"
245 # Environment variable naming the cache file.
246 cache_env
= "COVERAGE_FILE"
248 # A dictionary with an entry for (Python source file name, line number
249 # in that file) if that line has been executed.
252 # A map from canonical Python source file name to a dictionary in
253 # which there's an entry for each line number that has been
257 # Cache of results of calling the analysis2() method, so that you can
258 # specify both -r and -a without doing double work.
261 # Cache of results of calling the canonical_filename() method, to
262 # avoid duplicating work.
263 canonical_filename_cache
= {}
268 raise CoverageException
, "Only one coverage object allowed."
275 self
.relative_dir
= os
.path
.normcase(os
.path
.abspath(os
.curdir
)+os
.path
.sep
)
277 # t(f, x, y). This method is passed to sys.settrace as a trace function.
278 # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and
279 # the arguments and return value of the trace function.
280 # See [van Rossum 2001-07-20a, 3.2] for a description of frame and code
283 def t(self
, f
, w
, a
): #pragma: no cover
285 self
.c
[(f
.f_code
.co_filename
, f
.f_lineno
)] = 1
286 for c
in self
.cstack
:
287 c
[(f
.f_code
.co_filename
, f
.f_lineno
)] = 1
290 def help(self
, error
=None):
297 def command_line(self
, argv
, help=None):
299 help = help or self
.help
307 '-i': 'ignore-errors',
308 '-m': 'show-missing',
309 '-p': 'parallel-mode',
314 short_opts
= string
.join(map(lambda o
: o
[1:], optmap
.keys()), '')
315 long_opts
= optmap
.values()
316 options
, args
= getopt
.getopt(argv
, short_opts
, long_opts
)
318 if optmap
.has_key(o
):
319 settings
[optmap
[o
]] = 1
320 elif optmap
.has_key(o
+ ':'):
321 settings
[optmap
[o
+ ':']] = a
322 elif o
[2:] in long_opts
:
324 elif o
[2:] + '=' in long_opts
:
325 settings
[o
[2:]+'='] = a
326 else: #pragma: no cover
327 pass # Can't get here, because getopt won't return anything unknown.
329 if settings
.get('help'):
332 for i
in ['erase', 'execute']:
333 for j
in ['annotate', 'report', 'collect']:
334 if settings
.get(i
) and settings
.get(j
):
335 help("You can't specify the '%s' and '%s' "
336 "options at the same time." % (i
, j
))
338 args_needed
= (settings
.get('execute')
339 or settings
.get('annotate')
340 or settings
.get('report'))
341 action
= (settings
.get('erase')
342 or settings
.get('collect')
345 help("You must specify at least one of -e, -x, -c, -r, or -a.")
346 if not args_needed
and args
:
347 help("Unexpected arguments: %s" % " ".join(args
))
349 self
.get_ready(settings
.get('parallel-mode'))
350 self
.exclude('#pragma[: ]+[nN][oO] [cC][oO][vV][eE][rR]')
352 if settings
.get('erase'):
354 if settings
.get('execute'):
356 help("Nothing to do.")
360 sys
.path
[0] = os
.path
.dirname(sys
.argv
[0])
361 execfile(sys
.argv
[0], __main__
.__dict
__)
362 if settings
.get('collect'):
365 args
= self
.cexecuted
.keys()
367 ignore_errors
= settings
.get('ignore-errors')
368 show_missing
= settings
.get('show-missing')
369 directory
= settings
.get('directory=')
371 omit
= settings
.get('omit=')
373 omit
= omit
.split(',')
377 if settings
.get('report'):
378 self
.report(args
, show_missing
, ignore_errors
, omit_prefixes
=omit
)
379 if settings
.get('annotate'):
380 self
.annotate(args
, directory
, ignore_errors
, omit_prefixes
=omit
)
382 def use_cache(self
, usecache
, cache_file
=None):
383 self
.usecache
= usecache
384 if cache_file
and not self
.cache
:
385 self
.cache_default
= cache_file
387 def get_ready(self
, parallel_mode
=False):
388 if self
.usecache
and not self
.cache
:
389 self
.cache
= os
.environ
.get(self
.cache_env
, self
.cache_default
)
391 self
.cache
+= "." + gethostname() + "." + str(os
.getpid())
393 self
.analysis_cache
= {}
395 def start(self
, parallel_mode
=False):
396 self
.get_ready(parallel_mode
)
397 if self
.nesting
== 0: #pragma: no cover
399 if hasattr(threading
, 'settrace'):
400 threading
.settrace(self
.t
)
405 if self
.nesting
== 0: #pragma: no cover
407 if hasattr(threading
, 'settrace'):
408 threading
.settrace(None)
412 self
.analysis_cache
= {}
414 if self
.cache
and os
.path
.exists(self
.cache
):
415 os
.remove(self
.cache
)
418 def exclude(self
, re
):
420 self
.exclude_re
+= "|"
421 self
.exclude_re
+= "(" + re
+ ")"
423 def begin_recursive(self
):
424 self
.cstack
.append(self
.c
)
425 self
.xstack
.append(self
.exclude_re
)
427 def end_recursive(self
):
428 self
.c
= self
.cstack
.pop()
429 self
.exclude_re
= self
.xstack
.pop()
431 # save(). Save coverage data to the coverage cache.
434 if self
.usecache
and self
.cache
:
435 self
.canonicalize_filenames()
436 cache
= open(self
.cache
, 'wb')
438 marshal
.dump(self
.cexecuted
, cache
)
441 # restore(). Restore coverage data from the coverage cache (if it exists).
447 if os
.path
.exists(self
.cache
):
448 self
.cexecuted
= self
.restore_file(self
.cache
)
450 def restore_file(self
, file_name
):
452 cache
= open(file_name
, 'rb')
454 cexecuted
= marshal
.load(cache
)
456 if isinstance(cexecuted
, types
.DictType
):
463 # collect(). Collect data in multiple files produced by parallel mode
466 cache_dir
, local
= os
.path
.split(self
.cache
)
467 for file in os
.listdir(cache_dir
):
468 if not file.startswith(local
):
471 full_path
= os
.path
.join(cache_dir
, file)
472 cexecuted
= self
.restore_file(full_path
)
473 self
.merge_data(cexecuted
)
475 def merge_data(self
, new_data
):
476 for file_name
, file_data
in new_data
.items():
477 if self
.cexecuted
.has_key(file_name
):
478 self
.merge_file_data(self
.cexecuted
[file_name
], file_data
)
480 self
.cexecuted
[file_name
] = file_data
482 def merge_file_data(self
, cache_data
, new_data
):
483 for line_number
in new_data
.keys():
484 if not cache_data
.has_key(line_number
):
485 cache_data
[line_number
] = new_data
[line_number
]
487 # canonical_filename(filename). Return a canonical filename for the
488 # file (that is, an absolute path with no redundant components and
489 # normalized case). See [GDR 2001-12-04b, 3.3].
491 def canonical_filename(self
, filename
):
492 if not self
.canonical_filename_cache
.has_key(filename
):
494 if os
.path
.isabs(f
) and not os
.path
.exists(f
):
495 f
= os
.path
.basename(f
)
496 if not os
.path
.isabs(f
):
497 for path
in [os
.curdir
] + sys
.path
:
498 g
= os
.path
.join(path
, f
)
499 if os
.path
.exists(g
):
502 cf
= os
.path
.normcase(os
.path
.abspath(f
))
503 self
.canonical_filename_cache
[filename
] = cf
504 return self
.canonical_filename_cache
[filename
]
506 # canonicalize_filenames(). Copy results from "c" to "cexecuted",
507 # canonicalizing filenames on the way. Clear the "c" map.
509 def canonicalize_filenames(self
):
510 for filename
, lineno
in self
.c
.keys():
511 f
= self
.canonical_filename(filename
)
512 if not self
.cexecuted
.has_key(f
):
513 self
.cexecuted
[f
] = {}
514 self
.cexecuted
[f
][lineno
] = 1
517 # morf_filename(morf). Return the filename for a module or file.
519 def morf_filename(self
, morf
):
520 if isinstance(morf
, types
.ModuleType
):
521 if not hasattr(morf
, '__file__'):
522 raise CoverageException
, "Module has no __file__ attribute."
526 return self
.canonical_filename(file)
528 # analyze_morf(morf). Analyze the module or filename passed as
529 # the argument. If the source code can't be found, raise an error.
530 # Otherwise, return a tuple of (1) the canonical filename of the
531 # source code for the module, (2) a list of lines of statements
532 # in the source code, and (3) a list of lines of excluded statements.
534 def analyze_morf(self
, morf
):
535 if self
.analysis_cache
.has_key(morf
):
536 return self
.analysis_cache
[morf
]
537 filename
= self
.morf_filename(morf
)
538 ext
= os
.path
.splitext(filename
)[1]
540 if not os
.path
.exists(filename
[0:-1]):
541 raise CoverageException
, ("No source for compiled code '%s'."
543 filename
= filename
[0:-1]
545 raise CoverageException
, "File '%s' not Python source." % filename
546 source
= open(filename
, 'r')
547 lines
, excluded_lines
= self
.find_executable_statements(
548 source
.read(), exclude
=self
.exclude_re
551 result
= filename
, lines
, excluded_lines
552 self
.analysis_cache
[morf
] = result
555 def get_suite_spots(self
, tree
, spots
):
557 for i
in range(1, len(tree
)):
558 if type(tree
[i
]) == type(()):
559 if tree
[i
][0] == symbol
.suite
:
560 # Found a suite, look back for the colon and keyword.
561 lineno_colon
= lineno_word
= None
562 for j
in range(i
-1, 0, -1):
563 if tree
[j
][0] == token
.COLON
:
564 lineno_colon
= tree
[j
][2]
565 elif tree
[j
][0] == token
.NAME
:
566 if tree
[j
][1] == 'elif':
567 # Find the line number of the first non-terminal
570 while t
and token
.ISNONTERMINAL(t
[0]):
575 lineno_word
= tree
[j
][2]
577 elif tree
[j
][0] == symbol
.except_clause
:
578 # "except" clauses look like:
579 # ('except_clause', ('NAME', 'except', lineno), ...)
580 if tree
[j
][1][0] == token
.NAME
:
581 lineno_word
= tree
[j
][1][2]
583 if lineno_colon
and lineno_word
:
584 # Found colon and keyword, mark all the lines
585 # between the two with the two line numbers.
586 for l
in range(lineno_word
, lineno_colon
+1):
587 spots
[l
] = (lineno_word
, lineno_colon
)
588 self
.get_suite_spots(tree
[i
], spots
)
590 def find_executable_statements(self
, text
, exclude
=None):
591 # Find lines which match an exclusion pattern.
595 reExclude
= re
.compile(exclude
)
596 lines
= text
.split('\n')
597 for i
in range(len(lines
)):
598 if reExclude
.search(lines
[i
]):
602 tree
= parser
.suite(text
+'\n\n').totuple(1)
603 self
.get_suite_spots(tree
, suite_spots
)
605 # Use the compiler module to parse the text and find the executable
606 # statements. We add newlines to be impervious to final partial lines.
608 ast
= compiler
.parse(text
+'\n\n')
609 visitor
= StatementFindingAstVisitor(statements
, excluded
, suite_spots
)
610 compiler
.walk(ast
, visitor
, walker
=visitor
)
612 lines
= statements
.keys()
614 excluded_lines
= excluded
.keys()
615 excluded_lines
.sort()
616 return lines
, excluded_lines
618 # format_lines(statements, lines). Format a list of line numbers
619 # for printing by coalescing groups of lines as long as the lines
620 # represent consecutive statements. This will coalesce even if
621 # there are gaps between statements, so if statements =
622 # [1,2,3,4,5,10,11,12,13,14] and lines = [1,2,5,10,11,13,14] then
623 # format_lines will return "1-2, 5-11, 13-14".
625 def format_lines(self
, statements
, lines
):
631 while i
< len(statements
) and j
< len(lines
):
632 if statements
[i
] == lines
[j
]:
638 pairs
.append((start
, end
))
642 pairs
.append((start
, end
))
648 return "%d-%d" % (start
, end
)
649 return string
.join(map(stringify
, pairs
), ", ")
651 # Backward compatibility with version 1.
652 def analysis(self
, morf
):
653 f
, s
, _
, m
, mf
= self
.analysis2(morf
)
656 def analysis2(self
, morf
):
657 filename
, statements
, excluded
= self
.analyze_morf(morf
)
658 self
.canonicalize_filenames()
659 if not self
.cexecuted
.has_key(filename
):
660 self
.cexecuted
[filename
] = {}
662 for line
in statements
:
663 if not self
.cexecuted
[filename
].has_key(line
):
665 return (filename
, statements
, excluded
, missing
,
666 self
.format_lines(statements
, missing
))
668 def relative_filename(self
, filename
):
669 """ Convert filename to relative filename from self.relative_dir.
671 return filename
.replace(self
.relative_dir
, "")
673 def morf_name(self
, morf
):
674 """ Return the name of morf as used in report.
676 if isinstance(morf
, types
.ModuleType
):
679 return self
.relative_filename(os
.path
.splitext(morf
)[0])
681 def filter_by_prefix(self
, morfs
, omit_prefixes
):
682 """ Return list of morfs where the morf name does not begin
683 with any one of the omit_prefixes.
687 for prefix
in omit_prefixes
:
688 if self
.morf_name(morf
).startswith(prefix
):
691 filtered_morfs
.append(morf
)
693 return filtered_morfs
695 def morf_name_compare(self
, x
, y
):
696 return cmp(self
.morf_name(x
), self
.morf_name(y
))
698 def report(self
, morfs
, show_missing
=1, ignore_errors
=0, file=None, omit_prefixes
=[]):
699 if not isinstance(morfs
, types
.ListType
):
701 morfs
= self
.filter_by_prefix(morfs
, omit_prefixes
)
702 morfs
.sort(self
.morf_name_compare
)
704 max_name
= max([5,] + map(len, map(self
.morf_name
, morfs
)))
705 fmt_name
= "%%- %ds " % max_name
706 fmt_err
= fmt_name
+ "%s: %s"
707 header
= fmt_name
% "Name" + " Stmts Exec Cover"
708 fmt_coverage
= fmt_name
+ "% 6d % 6d % 5d%%"
710 header
= header
+ " Missing"
711 fmt_coverage
= fmt_coverage
+ " %s"
715 print >>file, "-" * len(header
)
719 name
= self
.morf_name(morf
)
721 _
, statements
, _
, missing
, readable
= self
.analysis2(morf
)
728 args
= (name
, n
, m
, pc
)
730 args
= args
+ (readable
,)
731 print >>file, fmt_coverage
% args
732 total_statements
= total_statements
+ n
733 total_executed
= total_executed
+ m
734 except KeyboardInterrupt: #pragma: no cover
737 if not ignore_errors
:
738 type, msg
= sys
.exc_info()[0:2]
739 print >>file, fmt_err
% (name
, type, msg
)
741 print >>file, "-" * len(header
)
742 if total_statements
> 0:
743 pc
= 100.0 * total_executed
/ total_statements
746 args
= ("TOTAL", total_statements
, total_executed
, pc
)
749 print >>file, fmt_coverage
% args
751 # annotate(morfs, ignore_errors).
753 blank_re
= re
.compile(r
"\s*(#|$)")
754 else_re
= re
.compile(r
"\s*else\s*:\s*(#|$)")
756 def annotate(self
, morfs
, directory
=None, ignore_errors
=0, omit_prefixes
=[]):
757 morfs
= self
.filter_by_prefix(morfs
, omit_prefixes
)
760 filename
, statements
, excluded
, missing
, _
= self
.analysis2(morf
)
761 self
.annotate_file(filename
, statements
, excluded
, missing
, directory
)
762 except KeyboardInterrupt:
765 if not ignore_errors
:
768 def annotate_file(self
, filename
, statements
, excluded
, missing
, directory
=None):
769 source
= open(filename
, 'r')
771 dest_file
= os
.path
.join(directory
,
772 os
.path
.basename(filename
)
775 dest_file
= filename
+ ',cover'
776 dest
= open(dest_file
, 'w')
782 line
= source
.readline()
786 while i
< len(statements
) and statements
[i
] < lineno
:
788 while j
< len(missing
) and missing
[j
] < lineno
:
790 if i
< len(statements
) and statements
[i
] == lineno
:
791 covered
= j
>= len(missing
) or missing
[j
] > lineno
792 if self
.blank_re
.match(line
):
794 elif self
.else_re
.match(line
):
795 # Special logic for lines containing only 'else:'.
796 # See [GDR 2001-12-04b, 3.2].
797 if i
>= len(statements
) and j
>= len(missing
):
799 elif i
>= len(statements
) or j
>= len(missing
):
801 elif statements
[i
] == missing
[j
]:
805 elif lineno
in excluded
:
816 the_coverage
= coverage()
818 # Module functions call methods in the singleton object.
819 def use_cache(*args
, **kw
): return the_coverage
.use_cache(*args
, **kw
)
820 def start(*args
, **kw
): return the_coverage
.start(*args
, **kw
)
821 def stop(*args
, **kw
): return the_coverage
.stop(*args
, **kw
)
822 def erase(*args
, **kw
): return the_coverage
.erase(*args
, **kw
)
823 def begin_recursive(*args
, **kw
): return the_coverage
.begin_recursive(*args
, **kw
)
824 def end_recursive(*args
, **kw
): return the_coverage
.end_recursive(*args
, **kw
)
825 def exclude(*args
, **kw
): return the_coverage
.exclude(*args
, **kw
)
826 def analysis(*args
, **kw
): return the_coverage
.analysis(*args
, **kw
)
827 def analysis2(*args
, **kw
): return the_coverage
.analysis2(*args
, **kw
)
828 def report(*args
, **kw
): return the_coverage
.report(*args
, **kw
)
829 def annotate(*args
, **kw
): return the_coverage
.annotate(*args
, **kw
)
830 def annotate_file(*args
, **kw
): return the_coverage
.annotate_file(*args
, **kw
)
832 # Commented for pysize by Guillaume Chazarain: we don't want to save the cache
833 # # Save coverage data when Python exits. (The atexit module wasn't
834 # # introduced until Python 2.0, so use sys.exitfunc when it's not
838 # atexit.register(the_coverage.save)
839 # except ImportError:
840 # sys.exitfunc = the_coverage.save
842 # Command-line interface.
843 if __name__
== '__main__':
844 the_coverage
.command_line(sys
.argv
[1:])
849 # [GDR 2001-12-04a] "Statement coverage for Python"; Gareth Rees;
850 # Ravenbrook Limited; 2001-12-04;
851 # <http://www.nedbatchelder.com/code/modules/rees-coverage.html>.
853 # [GDR 2001-12-04b] "Statement coverage for Python: design and
854 # analysis"; Gareth Rees; Ravenbrook Limited; 2001-12-04;
855 # <http://www.nedbatchelder.com/code/modules/rees-design.html>.
857 # [van Rossum 2001-07-20a] "Python Reference Manual (releae 2.1.1)";
858 # Guide van Rossum; 2001-07-20;
859 # <http://www.python.org/doc/2.1.1/ref/ref.html>.
861 # [van Rossum 2001-07-20b] "Python Library Reference"; Guido van Rossum;
862 # 2001-07-20; <http://www.python.org/doc/2.1.1/lib/lib.html>.
865 # B. DOCUMENT HISTORY
867 # 2001-12-04 GDR Created.
869 # 2001-12-06 GDR Added command-line interface and source code
872 # 2001-12-09 GDR Moved design and interface to separate documents.
874 # 2001-12-10 GDR Open cache file as binary on Windows. Allow
875 # simultaneous -e and -x, or -a and -r.
877 # 2001-12-12 GDR Added command-line help. Cache analysis so that it
878 # only needs to be done once when you specify -a and -r.
880 # 2001-12-13 GDR Improved speed while recording. Portable between
881 # Python 1.5.2 and 2.1.1.
883 # 2002-01-03 GDR Module-level functions work correctly.
885 # 2002-01-07 GDR Update sys.path when running a file with the -x option,
886 # so that it matches the value the program would get if it were run on
889 # 2004-12-12 NMB Significant code changes.
890 # - Finding executable statements has been rewritten so that docstrings and
891 # other quirks of Python execution aren't mistakenly identified as missing
893 # - Lines can be excluded from consideration, even entire suites of lines.
894 # - The filesystem cache of covered lines can be disabled programmatically.
895 # - Modernized the code.
897 # 2004-12-14 NMB Minor tweaks. Return 'analysis' to its original behavior
898 # and add 'analysis2'. Add a global for 'annotate', and factor it, adding
901 # 2004-12-31 NMB Allow for keyword arguments in the module global functions.
904 # 2005-12-02 NMB Call threading.settrace so that all threads are measured.
905 # Thanks Martin Fuzzey. Add a file argument to report so that reports can be
906 # captured to a different destination.
908 # 2005-12-03 NMB coverage.py can now measure itself.
910 # 2005-12-04 NMB Adapted Greg Rogers' patch for using relative filenames,
911 # and sorting and omitting files to report on.
913 # 2006-07-23 NMB Applied Joseph Tate's patch for function decorators.
915 # 2006-08-21 NMB Applied Sigve Tjora and Mark van der Wal's fixes for argument
918 # 2006-08-22 NMB Applied Geoff Bache's parallel mode patch.
920 # 2006-08-23 NMB Refactorings to improve testability. Fixes to command-line
921 # logic for parallel mode and collect.
923 # C. COPYRIGHT AND LICENCE
925 # Copyright 2001 Gareth Rees. All rights reserved.
926 # Copyright 2004-2006 Ned Batchelder. All rights reserved.
928 # Redistribution and use in source and binary forms, with or without
929 # modification, are permitted provided that the following conditions are
932 # 1. Redistributions of source code must retain the above copyright
933 # notice, this list of conditions and the following disclaimer.
935 # 2. Redistributions in binary form must reproduce the above copyright
936 # notice, this list of conditions and the following disclaimer in the
937 # documentation and/or other materials provided with the
940 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
941 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
942 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
943 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
944 # HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
945 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
946 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
947 # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
948 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
949 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
950 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
953 # $Id: coverage.py 47 2006-08-24 01:08:48Z Ned $