tests/coverage.py
changeset 5760 0145f9afb0e7
parent 5594 8623debad845
child 5915 d0576d065993
equal deleted inserted replaced
5759:027264e720aa 5760:0145f9afb0e7
   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 
   169         # like "global a").
   169         # like "global a").
   170         if node.__class__.__name__ != 'Stmt':
   170         if node.__class__.__name__ != 'Stmt':
   171             return self.recordLine(self.getFirstLine(node))
   171             return self.recordLine(self.getFirstLine(node))
   172         else:
   172         else:
   173             return 0
   173             return 0
   174     
   174 
   175     def recordLine(self, lineno):
   175     def recordLine(self, lineno):
   176         # Returns a bool, whether the line is included or excluded.
   176         # Returns a bool, whether the line is included or excluded.
   177         if lineno:
   177         if lineno:
   178             # Multi-line tests introducing suites have to get charged to their
   178             # Multi-line tests introducing suites have to get charged to their
   179             # keyword.
   179             # keyword.
   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 
   281     cache_env = "COVERAGE_FILE"
   281     cache_env = "COVERAGE_FILE"
   282 
   282 
   283     # A dictionary with an entry for (Python source file name, line number
   283     # A dictionary with an entry for (Python source file name, line number
   284     # in that file) if that line has been executed.
   284     # in that file) if that line has been executed.
   285     c = {}
   285     c = {}
   286     
   286 
   287     # A map from canonical Python source file name to a dictionary in
   287     # A map from canonical Python source file name to a dictionary in
   288     # which there's an entry for each line number that has been
   288     # which there's an entry for each line number that has been
   289     # executed.
   289     # executed.
   290     cexecuted = {}
   290     cexecuted = {}
   291 
   291 
   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()
   399             execfile(sys.argv[0], __main__.__dict__)
   399             execfile(sys.argv[0], __main__.__dict__)
   400         if settings.get('collect'):
   400         if settings.get('collect'):
   401             self.collect()
   401             self.collect()
   402         if not args:
   402         if not args:
   403             args = self.cexecuted.keys()
   403             args = self.cexecuted.keys()
   404         
   404 
   405         ignore_errors = settings.get('ignore-errors')
   405         ignore_errors = settings.get('ignore-errors')
   406         show_missing = settings.get('show-missing')
   406         show_missing = settings.get('show-missing')
   407         directory = settings.get('directory=')
   407         directory = settings.get('directory=')
   408 
   408 
   409         omit = settings.get('omit=')
   409         omit = settings.get('omit=')
   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.)
  1031 #
  1031 #
  1032 # 2004-12-31 NMB Allow for keyword arguments in the module global functions.
  1032 # 2004-12-31 NMB Allow for keyword arguments in the module global functions.
  1033 # Thanks, Allen.
  1033 # Thanks, Allen.
  1034 #
  1034 #
  1035 # 2005-12-02 NMB Call threading.settrace so that all threads are measured.
  1035 # 2005-12-02 NMB Call threading.settrace so that all threads are measured.
  1036 # Thanks Martin Fuzzey. Add a file argument to report so that reports can be 
  1036 # Thanks Martin Fuzzey. Add a file argument to report so that reports can be
  1037 # captured to a different destination.
  1037 # captured to a different destination.
  1038 #
  1038 #
  1039 # 2005-12-03 NMB coverage.py can now measure itself.
  1039 # 2005-12-03 NMB coverage.py can now measure itself.
  1040 #
  1040 #
  1041 # 2005-12-04 NMB Adapted Greg Rogers' patch for using relative filenames,
  1041 # 2005-12-04 NMB Adapted Greg Rogers' patch for using relative filenames,