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
28 coverage.py -x [-p] MODULE.py [ARG1 ARG2 ...]
29 Execute module, passing the given command-line arguments, collecting
30 coverage data. With the -p option, write to a temporary file containing
31 the machine name and process ID.
34 Erase collected coverage data.
37 Collect data from multiple coverage files (as created by -p option above)
38 and store it into a single file representing the union of the coverage.
40 coverage.py -r [-m] [-o dir1,dir2,...] FILE1 FILE2 ...
41 Report on the statement coverage for the given files. With the -m
42 option, show line numbers of the statements that weren't executed.
44 coverage.py -a [-d dir] [-o dir1,dir2,...] FILE1 FILE2 ...
45 Make annotated copies of the given files, marking statements that
46 are executed with > and statements that are missed with !. With
47 the -d option, make the copies in that directory. Without the -d
48 option, make each copy in the same directory as the original.
51 Omit reporting or annotating files when their filename path starts with
52 a directory listed in the omit list.
53 e.g. python coverage.py -i -r -o c:\python23,lib\enthought\traits
55 Coverage data is saved in the file .coverage by default. Set the
56 COVERAGE_FILE environment variable to save it somewhere else."""
58 __version__
= "2.75.20070722" # see detailed history at the end of this file.
61 import compiler
.visitor
71 from socket
import gethostname
73 # Python version compatibility
75 strclass
= basestring
# new to 2.3
81 # This uses the "singleton" pattern.
83 # The word "morf" means a module object (from which the source file can
84 # be deduced by suitable manipulation of the __file__ attribute) or a
87 # When we generate a coverage report we have to canonicalize every
88 # filename in the coverage dictionary just in case it refers to the
89 # module we are reporting on. It seems a shame to throw away this
90 # information so the data in the coverage dictionary is transferred to
91 # the 'cexecuted' dictionary under the canonical filenames.
93 # The coverage dictionary is called "c" and the trace function "t". The
94 # reason for these short names is that Python looks up variables by name
95 # at runtime and so execution time depends on the length of variables!
96 # In the bottleneck of this application it's appropriate to abbreviate
97 # names to increase speed.
99 class StatementFindingAstVisitor(compiler
.visitor
.ASTVisitor
):
100 """ A visitor for a parsed Abstract Syntax Tree which finds executable
103 def __init__(self
, statements
, excluded
, suite_spots
):
104 compiler
.visitor
.ASTVisitor
.__init
__(self
)
105 self
.statements
= statements
106 self
.excluded
= excluded
107 self
.suite_spots
= suite_spots
108 self
.excluding_suite
= 0
110 def doRecursive(self
, node
):
111 for n
in node
.getChildNodes():
114 visitStmt
= visitModule
= doRecursive
116 def doCode(self
, node
):
117 if hasattr(node
, 'decorators') and node
.decorators
:
118 self
.dispatch(node
.decorators
)
119 self
.recordAndDispatch(node
.code
)
121 self
.doSuite(node
, node
.code
)
123 visitFunction
= visitClass
= doCode
125 def getFirstLine(self
, node
):
126 # Find the first line in the tree node.
128 for n
in node
.getChildNodes():
129 f
= self
.getFirstLine(n
)
131 lineno
= min(lineno
, f
)
136 def getLastLine(self
, node
):
137 # Find the first line in the tree node.
139 for n
in node
.getChildNodes():
140 lineno
= max(lineno
, self
.getLastLine(n
))
143 def doStatement(self
, node
):
144 self
.recordLine(self
.getFirstLine(node
))
146 visitAssert
= visitAssign
= visitAssTuple
= visitPrint
= \
147 visitPrintnl
= visitRaise
= visitSubscript
= visitDecorators
= \
150 def visitPass(self
, node
):
151 # Pass statements have weird interactions with docstrings. If this
152 # pass statement is part of one of those pairs, claim that the statement
153 # is on the later of the two lines.
156 lines
= self
.suite_spots
.get(l
, [l
,l
])
157 self
.statements
[lines
[1]] = 1
159 def visitDiscard(self
, node
):
160 # Discard nodes are statements that execute an expression, but then
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
163 # won't be "executed", so don't count it now.
164 if node
.expr
.__class
__.__name
__ != 'Const':
165 self
.doStatement(node
)
167 def recordNodeLine(self
, node
):
168 # Stmt nodes often have None, but shouldn't claim the first line of
169 # their children (because the first child might be an ignorable line
171 if node
.__class
__.__name
__ != 'Stmt':
172 return self
.recordLine(self
.getFirstLine(node
))
176 def recordLine(self
, lineno
):
177 # Returns a bool, whether the line is included or excluded.
179 # Multi-line tests introducing suites have to get charged to their
181 if lineno
in self
.suite_spots
:
182 lineno
= self
.suite_spots
[lineno
][0]
183 # If we're inside an excluded suite, record that this line was
185 if self
.excluding_suite
:
186 self
.excluded
[lineno
] = 1
188 # If this line is excluded, or suite_spots maps this line to
189 # another line that is exlcuded, then we're excluded.
190 elif self
.excluded
.has_key(lineno
) or \
191 self
.suite_spots
.has_key(lineno
) and \
192 self
.excluded
.has_key(self
.suite_spots
[lineno
][1]):
194 # Otherwise, this is an executable line.
196 self
.statements
[lineno
] = 1
200 default
= recordNodeLine
202 def recordAndDispatch(self
, node
):
203 self
.recordNodeLine(node
)
206 def doSuite(self
, intro
, body
, exclude
=0):
207 exsuite
= self
.excluding_suite
208 if exclude
or (intro
and not self
.recordNodeLine(intro
)):
209 self
.excluding_suite
= 1
210 self
.recordAndDispatch(body
)
211 self
.excluding_suite
= exsuite
213 def doPlainWordSuite(self
, prevsuite
, suite
):
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,
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.
218 lastprev
= self
.getLastLine(prevsuite
)
219 firstelse
= self
.getFirstLine(suite
)
220 for l
in range(lastprev
+1, firstelse
):
221 if self
.suite_spots
.has_key(l
):
222 self
.doSuite(None, suite
, exclude
=self
.excluded
.has_key(l
))
225 self
.doSuite(None, suite
)
227 def doElse(self
, prevsuite
, node
):
229 self
.doPlainWordSuite(prevsuite
, node
.else_
)
231 def visitFor(self
, node
):
232 self
.doSuite(node
, node
.body
)
233 self
.doElse(node
.body
, node
)
235 visitWhile
= visitFor
237 def visitIf(self
, node
):
238 # The first test has to be handled separately from the rest.
239 # The first test is credited to the line with the "if", but the others
240 # are credited to the line with the test for the elif.
241 self
.doSuite(node
, node
.tests
[0][1])
242 for t
, n
in node
.tests
[1:]:
244 self
.doElse(node
.tests
[-1][1], node
)
246 def visitTryExcept(self
, node
):
247 self
.doSuite(node
, node
.body
)
248 for i
in range(len(node
.handlers
)):
249 a
, b
, h
= node
.handlers
[i
]
251 # It's a plain "except:". Find the previous suite.
253 prev
= node
.handlers
[i
-1][2]
256 self
.doPlainWordSuite(prev
, h
)
259 self
.doElse(node
.handlers
[-1][2], node
)
261 def visitTryFinally(self
, node
):
262 self
.doSuite(node
, node
.body
)
263 self
.doPlainWordSuite(node
.body
, node
.final
)
265 def visitGlobal(self
, node
):
266 # "global" statements don't execute like others (they don't call the
267 # trace function), so don't record their line numbers.
272 class CoverageException(Exception): pass
275 # Name of the cache file (unless environment variable is set).
276 cache_default
= ".coverage"
278 # Environment variable naming the cache file.
279 cache_env
= "COVERAGE_FILE"
281 # A dictionary with an entry for (Python source file name, line number
282 # in that file) if that line has been executed.
285 # A map from canonical Python source file name to a dictionary in
286 # which there's an entry for each line number that has been
290 # Cache of results of calling the analysis2() method, so that you can
291 # specify both -r and -a without doing double work.
294 # Cache of results of calling the canonical_filename() method, to
295 # avoid duplicating work.
296 canonical_filename_cache
= {}
301 raise CoverageException
, "Only one coverage object allowed."
304 self
.parallel_mode
= False
309 self
.relative_dir
= os
.path
.normcase(os
.path
.abspath(os
.curdir
)+os
.sep
)
310 self
.exclude('# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]')
312 # t(f, x, y). This method is passed to sys.settrace as a trace function.
313 # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and
314 # the arguments and return value of the trace function.
315 # See [van Rossum 2001-07-20a, 3.2] for a description of frame and code
318 def t(self
, f
, w
, unused
): #pragma: no cover
320 #print "Executing %s @ %d" % (f.f_code.co_filename, f.f_lineno)
321 self
.c
[(f
.f_code
.co_filename
, f
.f_lineno
)] = 1
322 for c
in self
.cstack
:
323 c
[(f
.f_code
.co_filename
, f
.f_lineno
)] = 1
326 def help(self
, error
=None): #pragma: no cover
333 def command_line(self
, argv
, help_fn
=None):
335 help_fn
= help_fn
or self
.help
343 '-i': 'ignore-errors',
344 '-m': 'show-missing',
345 '-p': 'parallel-mode',
350 short_opts
= string
.join(map(lambda o
: o
[1:], optmap
.keys()), '')
351 long_opts
= optmap
.values()
352 options
, args
= getopt
.getopt(argv
, short_opts
, long_opts
)
354 if optmap
.has_key(o
):
355 settings
[optmap
[o
]] = 1
356 elif optmap
.has_key(o
+ ':'):
357 settings
[optmap
[o
+ ':']] = a
358 elif o
[2:] in long_opts
:
360 elif o
[2:] + '=' in long_opts
:
361 settings
[o
[2:]+'='] = a
362 else: #pragma: no cover
363 pass # Can't get here, because getopt won't return anything unknown.
365 if settings
.get('help'):
368 for i
in ['erase', 'execute']:
369 for j
in ['annotate', 'report', 'collect']:
370 if settings
.get(i
) and settings
.get(j
):
371 help_fn("You can't specify the '%s' and '%s' "
372 "options at the same time." % (i
, j
))
374 args_needed
= (settings
.get('execute')
375 or settings
.get('annotate')
376 or settings
.get('report'))
377 action
= (settings
.get('erase')
378 or settings
.get('collect')
381 help_fn("You must specify at least one of -e, -x, -c, -r, or -a.")
382 if not args_needed
and args
:
383 help_fn("Unexpected arguments: %s" % " ".join(args
))
385 self
.parallel_mode
= settings
.get('parallel-mode')
388 if settings
.get('erase'):
390 if settings
.get('execute'):
392 help_fn("Nothing to do.")
396 sys
.path
[0] = os
.path
.dirname(sys
.argv
[0])
397 execfile(sys
.argv
[0], __main__
.__dict
__)
398 if settings
.get('collect'):
401 args
= self
.cexecuted
.keys()
403 ignore_errors
= settings
.get('ignore-errors')
404 show_missing
= settings
.get('show-missing')
405 directory
= settings
.get('directory=')
407 omit
= settings
.get('omit=')
409 omit
= omit
.split(',')
413 if settings
.get('report'):
414 self
.report(args
, show_missing
, ignore_errors
, omit_prefixes
=omit
)
415 if settings
.get('annotate'):
416 self
.annotate(args
, directory
, ignore_errors
, omit_prefixes
=omit
)
418 def use_cache(self
, usecache
, cache_file
=None):
419 self
.usecache
= usecache
420 if cache_file
and not self
.cache
:
421 self
.cache_default
= cache_file
423 def get_ready(self
, parallel_mode
=False):
424 if self
.usecache
and not self
.cache
:
425 self
.cache
= os
.environ
.get(self
.cache_env
, self
.cache_default
)
426 if self
.parallel_mode
:
427 self
.cache
+= "." + gethostname() + "." + str(os
.getpid())
429 self
.analysis_cache
= {}
431 def start(self
, parallel_mode
=False):
433 if self
.nesting
== 0: #pragma: no cover
435 if hasattr(threading
, 'settrace'):
436 threading
.settrace(self
.t
)
441 if self
.nesting
== 0: #pragma: no cover
443 if hasattr(threading
, 'settrace'):
444 threading
.settrace(None)
449 self
.analysis_cache
= {}
451 if self
.cache
and os
.path
.exists(self
.cache
):
452 os
.remove(self
.cache
)
454 def exclude(self
, re
):
456 self
.exclude_re
+= "|"
457 self
.exclude_re
+= "(" + re
+ ")"
459 def begin_recursive(self
):
460 self
.cstack
.append(self
.c
)
461 self
.xstack
.append(self
.exclude_re
)
463 def end_recursive(self
):
464 self
.c
= self
.cstack
.pop()
465 self
.exclude_re
= self
.xstack
.pop()
467 # save(). Save coverage data to the coverage cache.
470 if self
.usecache
and self
.cache
:
471 self
.canonicalize_filenames()
472 cache
= open(self
.cache
, 'wb')
474 marshal
.dump(self
.cexecuted
, cache
)
477 # restore(). Restore coverage data from the coverage cache (if it exists).
483 if os
.path
.exists(self
.cache
):
484 self
.cexecuted
= self
.restore_file(self
.cache
)
486 def restore_file(self
, file_name
):
488 cache
= open(file_name
, 'rb')
490 cexecuted
= marshal
.load(cache
)
492 if isinstance(cexecuted
, types
.DictType
):
499 # collect(). Collect data in multiple files produced by parallel mode
502 cache_dir
, local
= os
.path
.split(self
.cache
)
503 for f
in os
.listdir(cache_dir
or '.'):
504 if not f
.startswith(local
):
507 full_path
= os
.path
.join(cache_dir
, f
)
508 cexecuted
= self
.restore_file(full_path
)
509 self
.merge_data(cexecuted
)
511 def merge_data(self
, new_data
):
512 for file_name
, file_data
in new_data
.items():
513 if self
.cexecuted
.has_key(file_name
):
514 self
.merge_file_data(self
.cexecuted
[file_name
], file_data
)
516 self
.cexecuted
[file_name
] = file_data
518 def merge_file_data(self
, cache_data
, new_data
):
519 for line_number
in new_data
.keys():
520 if not cache_data
.has_key(line_number
):
521 cache_data
[line_number
] = new_data
[line_number
]
523 # canonical_filename(filename). Return a canonical filename for the
524 # file (that is, an absolute path with no redundant components and
525 # normalized case). See [GDR 2001-12-04b, 3.3].
527 def canonical_filename(self
, filename
):
528 if not self
.canonical_filename_cache
.has_key(filename
):
530 if os
.path
.isabs(f
) and not os
.path
.exists(f
):
531 f
= os
.path
.basename(f
)
532 if not os
.path
.isabs(f
):
533 for path
in [os
.curdir
] + sys
.path
:
534 g
= os
.path
.join(path
, f
)
535 if os
.path
.exists(g
):
538 cf
= os
.path
.normcase(os
.path
.abspath(f
))
539 self
.canonical_filename_cache
[filename
] = cf
540 return self
.canonical_filename_cache
[filename
]
542 # canonicalize_filenames(). Copy results from "c" to "cexecuted",
543 # canonicalizing filenames on the way. Clear the "c" map.
545 def canonicalize_filenames(self
):
546 for filename
, lineno
in self
.c
.keys():
547 if filename
== '<string>':
548 # Can't do anything useful with exec'd strings, so skip them.
550 f
= self
.canonical_filename(filename
)
551 if not self
.cexecuted
.has_key(f
):
552 self
.cexecuted
[f
] = {}
553 self
.cexecuted
[f
][lineno
] = 1
556 # morf_filename(morf). Return the filename for a module or file.
558 def morf_filename(self
, morf
):
559 if isinstance(morf
, types
.ModuleType
):
560 if not hasattr(morf
, '__file__'):
561 raise CoverageException
, "Module has no __file__ attribute."
565 return self
.canonical_filename(f
)
567 # analyze_morf(morf). Analyze the module or filename passed as
568 # the argument. If the source code can't be found, raise an error.
569 # Otherwise, return a tuple of (1) the canonical filename of the
570 # source code for the module, (2) a list of lines of statements
571 # in the source code, (3) a list of lines of excluded statements,
572 # and (4), a map of line numbers to multi-line line number ranges, for
573 # statements that cross lines.
575 def analyze_morf(self
, morf
):
576 if self
.analysis_cache
.has_key(morf
):
577 return self
.analysis_cache
[morf
]
578 filename
= self
.morf_filename(morf
)
579 ext
= os
.path
.splitext(filename
)[1]
581 if not os
.path
.exists(filename
[0:-1]):
582 raise CoverageException
, ("No source for compiled code '%s'."
584 filename
= filename
[0:-1]
586 raise CoverageException
, "File '%s' not Python source." % filename
587 source
= open(filename
, 'r')
588 lines
, excluded_lines
, line_map
= self
.find_executable_statements(
589 source
.read(), exclude
=self
.exclude_re
592 result
= filename
, lines
, excluded_lines
, line_map
593 self
.analysis_cache
[morf
] = result
596 def first_line_of_tree(self
, tree
):
598 if len(tree
) == 3 and type(tree
[2]) == type(1):
602 def last_line_of_tree(self
, tree
):
604 if len(tree
) == 3 and type(tree
[2]) == type(1):
608 def find_docstring_pass_pair(self
, tree
, spots
):
609 for i
in range(1, len(tree
)):
610 if self
.is_string_constant(tree
[i
]) and self
.is_pass_stmt(tree
[i
+1]):
611 first_line
= self
.first_line_of_tree(tree
[i
])
612 last_line
= self
.last_line_of_tree(tree
[i
+1])
613 self
.record_multiline(spots
, first_line
, last_line
)
615 def is_string_constant(self
, tree
):
617 return tree
[0] == symbol
.stmt
and tree
[1][1][1][0] == symbol
.expr_stmt
621 def is_pass_stmt(self
, tree
):
623 return tree
[0] == symbol
.stmt
and tree
[1][1][1][0] == symbol
.pass_stmt
627 def record_multiline(self
, spots
, i
, j
):
628 for l
in range(i
, j
+1):
631 def get_suite_spots(self
, tree
, spots
):
632 """ Analyze a parse tree to find suite introducers which span a number
635 for i
in range(1, len(tree
)):
636 if type(tree
[i
]) == type(()):
637 if tree
[i
][0] == symbol
.suite
:
638 # Found a suite, look back for the colon and keyword.
639 lineno_colon
= lineno_word
= None
640 for j
in range(i
-1, 0, -1):
641 if tree
[j
][0] == token
.COLON
:
642 # Colons are never executed themselves: we want the
643 # line number of the last token before the colon.
644 lineno_colon
= self
.last_line_of_tree(tree
[j
-1])
645 elif tree
[j
][0] == token
.NAME
:
646 if tree
[j
][1] == 'elif':
647 # Find the line number of the first non-terminal
650 while t
and token
.ISNONTERMINAL(t
[0]):
655 lineno_word
= tree
[j
][2]
657 elif tree
[j
][0] == symbol
.except_clause
:
658 # "except" clauses look like:
659 # ('except_clause', ('NAME', 'except', lineno), ...)
660 if tree
[j
][1][0] == token
.NAME
:
661 lineno_word
= tree
[j
][1][2]
663 if lineno_colon
and lineno_word
:
664 # Found colon and keyword, mark all the lines
665 # between the two with the two line numbers.
666 self
.record_multiline(spots
, lineno_word
, lineno_colon
)
668 # "pass" statements are tricky: different versions of Python
669 # treat them differently, especially in the common case of a
670 # function with a doc string and a single pass statement.
671 self
.find_docstring_pass_pair(tree
[i
], spots
)
673 elif tree
[i
][0] == symbol
.simple_stmt
:
674 first_line
= self
.first_line_of_tree(tree
[i
])
675 last_line
= self
.last_line_of_tree(tree
[i
])
676 if first_line
!= last_line
:
677 self
.record_multiline(spots
, first_line
, last_line
)
678 self
.get_suite_spots(tree
[i
], spots
)
680 def find_executable_statements(self
, text
, exclude
=None):
681 # Find lines which match an exclusion pattern.
685 reExclude
= re
.compile(exclude
)
686 lines
= text
.split('\n')
687 for i
in range(len(lines
)):
688 if reExclude
.search(lines
[i
]):
691 # Parse the code and analyze the parse tree to find out which statements
692 # are multiline, and where suites begin and end.
694 tree
= parser
.suite(text
+'\n\n').totuple(1)
695 self
.get_suite_spots(tree
, suite_spots
)
696 #print "Suite spots:", suite_spots
698 # Use the compiler module to parse the text and find the executable
699 # statements. We add newlines to be impervious to final partial lines.
701 ast
= compiler
.parse(text
+'\n\n')
702 visitor
= StatementFindingAstVisitor(statements
, excluded
, suite_spots
)
703 compiler
.walk(ast
, visitor
, walker
=visitor
)
705 lines
= statements
.keys()
707 excluded_lines
= excluded
.keys()
708 excluded_lines
.sort()
709 return lines
, excluded_lines
, suite_spots
711 # format_lines(statements, lines). Format a list of line numbers
712 # for printing by coalescing groups of lines as long as the lines
713 # represent consecutive statements. This will coalesce even if
714 # there are gaps between statements, so if statements =
715 # [1,2,3,4,5,10,11,12,13,14] and lines = [1,2,5,10,11,13,14] then
716 # format_lines will return "1-2, 5-11, 13-14".
718 def format_lines(self
, statements
, lines
):
724 while i
< len(statements
) and j
< len(lines
):
725 if statements
[i
] == lines
[j
]:
731 pairs
.append((start
, end
))
735 pairs
.append((start
, end
))
741 return "%d-%d" % (start
, end
)
742 ret
= string
.join(map(stringify
, pairs
), ", ")
745 # Backward compatibility with version 1.
746 def analysis(self
, morf
):
747 f
, s
, _
, m
, mf
= self
.analysis2(morf
)
750 def analysis2(self
, morf
):
751 filename
, statements
, excluded
, line_map
= self
.analyze_morf(morf
)
752 self
.canonicalize_filenames()
753 if not self
.cexecuted
.has_key(filename
):
754 self
.cexecuted
[filename
] = {}
756 for line
in statements
:
757 lines
= line_map
.get(line
, [line
, line
])
758 for l
in range(lines
[0], lines
[1]+1):
759 if self
.cexecuted
[filename
].has_key(l
):
763 return (filename
, statements
, excluded
, missing
,
764 self
.format_lines(statements
, missing
))
766 def relative_filename(self
, filename
):
767 """ Convert filename to relative filename from self.relative_dir.
769 return filename
.replace(self
.relative_dir
, "")
771 def morf_name(self
, morf
):
772 """ Return the name of morf as used in report.
774 if isinstance(morf
, types
.ModuleType
):
777 return self
.relative_filename(os
.path
.splitext(morf
)[0])
779 def filter_by_prefix(self
, morfs
, omit_prefixes
):
780 """ Return list of morfs where the morf name does not begin
781 with any one of the omit_prefixes.
785 for prefix
in omit_prefixes
:
786 if self
.morf_name(morf
).startswith(prefix
):
789 filtered_morfs
.append(morf
)
791 return filtered_morfs
793 def morf_name_compare(self
, x
, y
):
794 return cmp(self
.morf_name(x
), self
.morf_name(y
))
796 def report(self
, morfs
, show_missing
=1, ignore_errors
=0, file=None, omit_prefixes
=[]):
797 if not isinstance(morfs
, types
.ListType
):
799 # On windows, the shell doesn't expand wildcards. Do it here.
802 if isinstance(morf
, strclass
):
803 globbed
.extend(glob
.glob(morf
))
808 morfs
= self
.filter_by_prefix(morfs
, omit_prefixes
)
809 morfs
.sort(self
.morf_name_compare
)
811 max_name
= max([5,] + map(len, map(self
.morf_name
, morfs
)))
812 fmt_name
= "%%- %ds " % max_name
813 fmt_err
= fmt_name
+ "%s: %s"
814 header
= fmt_name
% "Name" + " Stmts Exec Cover"
815 fmt_coverage
= fmt_name
+ "% 6d % 6d % 5d%%"
817 header
= header
+ " Missing"
818 fmt_coverage
= fmt_coverage
+ " %s"
822 print >>file, "-" * len(header
)
826 name
= self
.morf_name(morf
)
828 _
, statements
, _
, missing
, readable
= self
.analysis2(morf
)
835 args
= (name
, n
, m
, pc
)
837 args
= args
+ (readable
,)
838 print >>file, fmt_coverage
% args
839 total_statements
= total_statements
+ n
840 total_executed
= total_executed
+ m
841 except KeyboardInterrupt: #pragma: no cover
844 if not ignore_errors
:
845 typ
, msg
= sys
.exc_info()[0:2]
846 print >>file, fmt_err
% (name
, typ
, msg
)
848 print >>file, "-" * len(header
)
849 if total_statements
> 0:
850 pc
= 100.0 * total_executed
/ total_statements
853 args
= ("TOTAL", total_statements
, total_executed
, pc
)
856 print >>file, fmt_coverage
% args
858 # annotate(morfs, ignore_errors).
860 blank_re
= re
.compile(r
"\s*(#|$)")
861 else_re
= re
.compile(r
"\s*else\s*:\s*(#|$)")
863 def annotate(self
, morfs
, directory
=None, ignore_errors
=0, omit_prefixes
=[]):
864 morfs
= self
.filter_by_prefix(morfs
, omit_prefixes
)
867 filename
, statements
, excluded
, missing
, _
= self
.analysis2(morf
)
868 self
.annotate_file(filename
, statements
, excluded
, missing
, directory
)
869 except KeyboardInterrupt:
872 if not ignore_errors
:
875 def annotate_file(self
, filename
, statements
, excluded
, missing
, directory
=None):
876 source
= open(filename
, 'r')
878 dest_file
= os
.path
.join(directory
,
879 os
.path
.basename(filename
)
882 dest_file
= filename
+ ',cover'
883 dest
= open(dest_file
, 'w')
889 line
= source
.readline()
893 while i
< len(statements
) and statements
[i
] < lineno
:
895 while j
< len(missing
) and missing
[j
] < lineno
:
897 if i
< len(statements
) and statements
[i
] == lineno
:
898 covered
= j
>= len(missing
) or missing
[j
] > lineno
899 if self
.blank_re
.match(line
):
901 elif self
.else_re
.match(line
):
902 # Special logic for lines containing only 'else:'.
903 # See [GDR 2001-12-04b, 3.2].
904 if i
>= len(statements
) and j
>= len(missing
):
906 elif i
>= len(statements
) or j
>= len(missing
):
908 elif statements
[i
] == missing
[j
]:
912 elif lineno
in excluded
:
923 the_coverage
= coverage()
925 # Module functions call methods in the singleton object.
926 def use_cache(*args
, **kw
):
927 return the_coverage
.use_cache(*args
, **kw
)
929 def start(*args
, **kw
):
930 return the_coverage
.start(*args
, **kw
)
932 def stop(*args
, **kw
):
933 return the_coverage
.stop(*args
, **kw
)
935 def erase(*args
, **kw
):
936 return the_coverage
.erase(*args
, **kw
)
938 def begin_recursive(*args
, **kw
):
939 return the_coverage
.begin_recursive(*args
, **kw
)
941 def end_recursive(*args
, **kw
):
942 return the_coverage
.end_recursive(*args
, **kw
)
944 def exclude(*args
, **kw
):
945 return the_coverage
.exclude(*args
, **kw
)
947 def analysis(*args
, **kw
):
948 return the_coverage
.analysis(*args
, **kw
)
950 def analysis2(*args
, **kw
):
951 return the_coverage
.analysis2(*args
, **kw
)
953 def report(*args
, **kw
):
954 return the_coverage
.report(*args
, **kw
)
956 def annotate(*args
, **kw
):
957 return the_coverage
.annotate(*args
, **kw
)
959 def annotate_file(*args
, **kw
):
960 return the_coverage
.annotate_file(*args
, **kw
)
962 # Save coverage data when Python exits. (The atexit module wasn't
963 # introduced until Python 2.0, so use sys.exitfunc when it's not
967 atexit
.register(the_coverage
.save
)
969 sys
.exitfunc
= the_coverage
.save
971 # Command-line interface.
972 if __name__
== '__main__':
973 the_coverage
.command_line(sys
.argv
[1:])
978 # [GDR 2001-12-04a] "Statement coverage for Python"; Gareth Rees;
979 # Ravenbrook Limited; 2001-12-04;
980 # <http://www.nedbatchelder.com/code/modules/rees-coverage.html>.
982 # [GDR 2001-12-04b] "Statement coverage for Python: design and
983 # analysis"; Gareth Rees; Ravenbrook Limited; 2001-12-04;
984 # <http://www.nedbatchelder.com/code/modules/rees-design.html>.
986 # [van Rossum 2001-07-20a] "Python Reference Manual (releae 2.1.1)";
987 # Guide van Rossum; 2001-07-20;
988 # <http://www.python.org/doc/2.1.1/ref/ref.html>.
990 # [van Rossum 2001-07-20b] "Python Library Reference"; Guido van Rossum;
991 # 2001-07-20; <http://www.python.org/doc/2.1.1/lib/lib.html>.
994 # B. DOCUMENT HISTORY
996 # 2001-12-04 GDR Created.
998 # 2001-12-06 GDR Added command-line interface and source code
1001 # 2001-12-09 GDR Moved design and interface to separate documents.
1003 # 2001-12-10 GDR Open cache file as binary on Windows. Allow
1004 # simultaneous -e and -x, or -a and -r.
1006 # 2001-12-12 GDR Added command-line help. Cache analysis so that it
1007 # only needs to be done once when you specify -a and -r.
1009 # 2001-12-13 GDR Improved speed while recording. Portable between
1010 # Python 1.5.2 and 2.1.1.
1012 # 2002-01-03 GDR Module-level functions work correctly.
1014 # 2002-01-07 GDR Update sys.path when running a file with the -x option,
1015 # so that it matches the value the program would get if it were run on
1018 # 2004-12-12 NMB Significant code changes.
1019 # - Finding executable statements has been rewritten so that docstrings and
1020 # other quirks of Python execution aren't mistakenly identified as missing
1022 # - Lines can be excluded from consideration, even entire suites of lines.
1023 # - The filesystem cache of covered lines can be disabled programmatically.
1024 # - Modernized the code.
1026 # 2004-12-14 NMB Minor tweaks. Return 'analysis' to its original behavior
1027 # and add 'analysis2'. Add a global for 'annotate', and factor it, adding
1030 # 2004-12-31 NMB Allow for keyword arguments in the module global functions.
1033 # 2005-12-02 NMB Call threading.settrace so that all threads are measured.
1034 # Thanks Martin Fuzzey. Add a file argument to report so that reports can be
1035 # captured to a different destination.
1037 # 2005-12-03 NMB coverage.py can now measure itself.
1039 # 2005-12-04 NMB Adapted Greg Rogers' patch for using relative filenames,
1040 # and sorting and omitting files to report on.
1042 # 2006-07-23 NMB Applied Joseph Tate's patch for function decorators.
1044 # 2006-08-21 NMB Applied Sigve Tjora and Mark van der Wal's fixes for argument
1047 # 2006-08-22 NMB Applied Geoff Bache's parallel mode patch.
1049 # 2006-08-23 NMB Refactorings to improve testability. Fixes to command-line
1050 # logic for parallel mode and collect.
1052 # 2006-08-25 NMB "#pragma: nocover" is excluded by default.
1054 # 2006-09-10 NMB Properly ignore docstrings and other constant expressions that
1055 # appear in the middle of a function, a problem reported by Tim Leslie.
1056 # Minor changes to avoid lint warnings.
1058 # 2006-09-17 NMB coverage.erase() shouldn't clobber the exclude regex.
1059 # Change how parallel mode is invoked, and fix erase() so that it erases the
1060 # cache when called programmatically.
1062 # 2007-07-21 NMB In reports, ignore code executed from strings, since we can't
1063 # do anything useful with it anyway.
1064 # Better file handling on Linux, thanks Guillaume Chazarain.
1065 # Better shell support on Windows, thanks Noel O'Boyle.
1066 # Python 2.2 support maintained, thanks Catherine Proulx.
1068 # 2007-07-22 NMB Python 2.5 now fully supported. The method of dealing with
1069 # multi-line statements is now less sensitive to the exact line that Python
1070 # reports during execution. Pass statements are handled specially so that their
1071 # disappearance during execution won't throw off the measurement.
1073 # C. COPYRIGHT AND LICENCE
1075 # Copyright 2001 Gareth Rees. All rights reserved.
1076 # Copyright 2004-2007 Ned Batchelder. All rights reserved.
1078 # Redistribution and use in source and binary forms, with or without
1079 # modification, are permitted provided that the following conditions are
1082 # 1. Redistributions of source code must retain the above copyright
1083 # notice, this list of conditions and the following disclaimer.
1085 # 2. Redistributions in binary form must reproduce the above copyright
1086 # notice, this list of conditions and the following disclaimer in the
1087 # documentation and/or other materials provided with the
1090 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1091 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1092 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1093 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1094 # HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1095 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1096 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
1097 # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1098 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
1099 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1100 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1103 # $Id: coverage.py 67 2007-07-21 19:51:07Z nedbat $