tests/coverage.py
changeset 7047 277c91fe8384
parent 6349 6aaf5b1d8f15
child 10282 08a0f04b56bd
equal deleted inserted replaced
7040:f29b674cc221 7047:277c91fe8384
    52   e.g. python coverage.py -i -r -o c:\python23,lib\enthought\traits
    52   e.g. python coverage.py -i -r -o c:\python23,lib\enthought\traits
    53 
    53 
    54 Coverage data is saved in the file .coverage by default.  Set the
    54 Coverage data is saved in the file .coverage by default.  Set the
    55 COVERAGE_FILE environment variable to save it somewhere else."""
    55 COVERAGE_FILE environment variable to save it somewhere else."""
    56 
    56 
    57 __version__ = "2.77.20070729"    # see detailed history at the end of this file.
    57 __version__ = "2.85.20080914"    # see detailed history at the end of this file.
    58 
    58 
    59 import compiler
    59 import compiler
    60 import compiler.visitor
    60 import compiler.visitor
    61 import glob
    61 import glob
    62 import os
    62 import os
    65 import symbol
    65 import symbol
    66 import sys
    66 import sys
    67 import threading
    67 import threading
    68 import token
    68 import token
    69 import types
    69 import types
       
    70 import zipimport
    70 from socket import gethostname
    71 from socket import gethostname
    71 
    72 
    72 # Python version compatibility
    73 # Python version compatibility
    73 try:
    74 try:
    74     strclass = basestring   # new to 2.3
    75     strclass = basestring   # new to 2.3
   103         compiler.visitor.ASTVisitor.__init__(self)
   104         compiler.visitor.ASTVisitor.__init__(self)
   104         self.statements = statements
   105         self.statements = statements
   105         self.excluded = excluded
   106         self.excluded = excluded
   106         self.suite_spots = suite_spots
   107         self.suite_spots = suite_spots
   107         self.excluding_suite = 0
   108         self.excluding_suite = 0
   108 
   109         
   109     def doRecursive(self, node):
   110     def doRecursive(self, node):
   110         for n in node.getChildNodes():
   111         for n in node.getChildNodes():
   111             self.dispatch(n)
   112             self.dispatch(n)
   112 
   113 
   113     visitStmt = visitModule = doRecursive
   114     visitStmt = visitModule = doRecursive
   114 
   115     
   115     def doCode(self, node):
   116     def doCode(self, node):
   116         if hasattr(node, 'decorators') and node.decorators:
   117         if hasattr(node, 'decorators') and node.decorators:
   117             self.dispatch(node.decorators)
   118             self.dispatch(node.decorators)
   118             self.recordAndDispatch(node.code)
   119             self.recordAndDispatch(node.code)
   119         else:
   120         else:
   120             self.doSuite(node, node.code)
   121             self.doSuite(node, node.code)
   121 
   122             
   122     visitFunction = visitClass = doCode
   123     visitFunction = visitClass = doCode
   123 
   124 
   124     def getFirstLine(self, node):
   125     def getFirstLine(self, node):
   125         # Find the first line in the tree node.
   126         # Find the first line in the tree node.
   126         lineno = node.lineno
   127         lineno = node.lineno
   136         # Find the first line in the tree node.
   137         # Find the first line in the tree node.
   137         lineno = node.lineno
   138         lineno = node.lineno
   138         for n in node.getChildNodes():
   139         for n in node.getChildNodes():
   139             lineno = max(lineno, self.getLastLine(n))
   140             lineno = max(lineno, self.getLastLine(n))
   140         return lineno
   141         return lineno
   141 
   142     
   142     def doStatement(self, node):
   143     def doStatement(self, node):
   143         self.recordLine(self.getFirstLine(node))
   144         self.recordLine(self.getFirstLine(node))
   144 
   145 
   145     visitAssert = visitAssign = visitAssTuple = visitPrint = \
   146     visitAssert = visitAssign = visitAssTuple = visitPrint = \
   146         visitPrintnl = visitRaise = visitSubscript = visitDecorators = \
   147         visitPrintnl = visitRaise = visitSubscript = visitDecorators = \
   147         doStatement
   148         doStatement
   148 
   149     
   149     def visitPass(self, node):
   150     def visitPass(self, node):
   150         # Pass statements have weird interactions with docstrings.  If this
   151         # Pass statements have weird interactions with docstrings.  If this
   151         # pass statement is part of one of those pairs, claim that the statement
   152         # pass statement is part of one of those pairs, claim that the statement
   152         # is on the later of the two lines.
   153         # is on the later of the two lines.
   153         l = node.lineno
   154         l = node.lineno
   154         if l:
   155         if l:
   155             lines = self.suite_spots.get(l, [l,l])
   156             lines = self.suite_spots.get(l, [l,l])
   156             self.statements[lines[1]] = 1
   157             self.statements[lines[1]] = 1
   157 
   158         
   158     def visitDiscard(self, node):
   159     def visitDiscard(self, node):
   159         # Discard nodes are statements that execute an expression, but then
   160         # Discard nodes are statements that execute an expression, but then
   160         # discard the results.  This includes function calls, so we can't
   161         # discard the results.  This includes function calls, so we can't 
   161         # ignore them all.  But if the expression is a constant, the statement
   162         # ignore them all.  But if the expression is a constant, the statement
   162         # won't be "executed", so don't count it now.
   163         # won't be "executed", so don't count it now.
   163         if node.expr.__class__.__name__ != 'Const':
   164         if node.expr.__class__.__name__ != 'Const':
   164             self.doStatement(node)
   165             self.doStatement(node)
   165 
   166 
   169         # like "global a").
   170         # like "global a").
   170         if node.__class__.__name__ != 'Stmt':
   171         if node.__class__.__name__ != 'Stmt':
   171             return self.recordLine(self.getFirstLine(node))
   172             return self.recordLine(self.getFirstLine(node))
   172         else:
   173         else:
   173             return 0
   174             return 0
   174 
   175     
   175     def recordLine(self, lineno):
   176     def recordLine(self, lineno):
   176         # Returns a bool, whether the line is included or excluded.
   177         # Returns a bool, whether the line is included or excluded.
   177         if lineno:
   178         if lineno:
   178             # Multi-line tests introducing suites have to get charged to their
   179             # Multi-line tests introducing suites have to get charged to their
   179             # keyword.
   180             # keyword.
   184             if self.excluding_suite:
   185             if self.excluding_suite:
   185                 self.excluded[lineno] = 1
   186                 self.excluded[lineno] = 1
   186                 return 0
   187                 return 0
   187             # If this line is excluded, or suite_spots maps this line to
   188             # If this line is excluded, or suite_spots maps this line to
   188             # another line that is exlcuded, then we're excluded.
   189             # another line that is exlcuded, then we're excluded.
   189             elif lineno in self.excluded or \
   190             elif self.excluded.has_key(lineno) or \
   190                  lineno in self.suite_spots and \
   191                  self.suite_spots.has_key(lineno) and \
   191                  self.suite_spots[lineno][1] in self.excluded:
   192                  self.excluded.has_key(self.suite_spots[lineno][1]):
   192                 return 0
   193                 return 0
   193             # Otherwise, this is an executable line.
   194             # Otherwise, this is an executable line.
   194             else:
   195             else:
   195                 self.statements[lineno] = 1
   196                 self.statements[lineno] = 1
   196                 return 1
   197                 return 1
   197         return 0
   198         return 0
   198 
   199     
   199     default = recordNodeLine
   200     default = recordNodeLine
   200 
   201     
   201     def recordAndDispatch(self, node):
   202     def recordAndDispatch(self, node):
   202         self.recordNodeLine(node)
   203         self.recordNodeLine(node)
   203         self.dispatch(node)
   204         self.dispatch(node)
   204 
   205 
   205     def doSuite(self, intro, body, exclude=0):
   206     def doSuite(self, intro, body, exclude=0):
   206         exsuite = self.excluding_suite
   207         exsuite = self.excluding_suite
   207         if exclude or (intro and not self.recordNodeLine(intro)):
   208         if exclude or (intro and not self.recordNodeLine(intro)):
   208             self.excluding_suite = 1
   209             self.excluding_suite = 1
   209         self.recordAndDispatch(body)
   210         self.recordAndDispatch(body)
   210         self.excluding_suite = exsuite
   211         self.excluding_suite = exsuite
   211 
   212         
   212     def doPlainWordSuite(self, prevsuite, suite):
   213     def doPlainWordSuite(self, prevsuite, suite):
   213         # Finding the exclude lines for else's is tricky, because they aren't
   214         # Finding the exclude lines for else's is tricky, because they aren't
   214         # present in the compiler parse tree.  Look at the previous suite,
   215         # 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
   216         # and find its last line.  If any line between there and the else's
   216         # first line are excluded, then we exclude the else.
   217         # first line are excluded, then we exclude the else.
   217         lastprev = self.getLastLine(prevsuite)
   218         lastprev = self.getLastLine(prevsuite)
   218         firstelse = self.getFirstLine(suite)
   219         firstelse = self.getFirstLine(suite)
   219         for l in range(lastprev+1, firstelse):
   220         for l in range(lastprev+1, firstelse):
   220             if l in self.suite_spots:
   221             if self.suite_spots.has_key(l):
   221                 self.doSuite(None, suite, exclude=l in self.excluded)
   222                 self.doSuite(None, suite, exclude=self.excluded.has_key(l))
   222                 break
   223                 break
   223         else:
   224         else:
   224             self.doSuite(None, suite)
   225             self.doSuite(None, suite)
   225 
   226         
   226     def doElse(self, prevsuite, node):
   227     def doElse(self, prevsuite, node):
   227         if node.else_:
   228         if node.else_:
   228             self.doPlainWordSuite(prevsuite, node.else_)
   229             self.doPlainWordSuite(prevsuite, node.else_)
   229 
   230     
   230     def visitFor(self, node):
   231     def visitFor(self, node):
   231         self.doSuite(node, node.body)
   232         self.doSuite(node, node.body)
   232         self.doElse(node.body, node)
   233         self.doElse(node.body, node)
   233 
   234 
   234     visitWhile = visitFor
   235     visitWhile = visitFor
   254                     prev = node.body
   255                     prev = node.body
   255                 self.doPlainWordSuite(prev, h)
   256                 self.doPlainWordSuite(prev, h)
   256             else:
   257             else:
   257                 self.doSuite(a, h)
   258                 self.doSuite(a, h)
   258         self.doElse(node.handlers[-1][2], node)
   259         self.doElse(node.handlers[-1][2], node)
   259 
   260     
   260     def visitTryFinally(self, node):
   261     def visitTryFinally(self, node):
   261         self.doSuite(node, node.body)
   262         self.doSuite(node, node.body)
   262         self.doPlainWordSuite(node.body, node.final)
   263         self.doPlainWordSuite(node.body, node.final)
   263 
   264         
   264     def visitWith(self, node):
   265     def visitWith(self, node):
   265         self.doSuite(node, node.body)
   266         self.doSuite(node, node.body)
   266 
   267         
   267     def visitGlobal(self, node):
   268     def visitGlobal(self, node):
   268         # "global" statements don't execute like others (they don't call the
   269         # "global" statements don't execute like others (they don't call the
   269         # trace function), so don't record their line numbers.
   270         # trace function), so don't record their line numbers.
   270         pass
   271         pass
   271 
   272 
   272 the_coverage = None
   273 the_coverage = None
   273 
   274 
   274 class CoverageException(Exception): pass
   275 class CoverageException(Exception):
       
   276     pass
   275 
   277 
   276 class coverage:
   278 class coverage:
   277     # Name of the cache file (unless environment variable is set).
   279     # Name of the cache file (unless environment variable is set).
   278     cache_default = ".coverage"
   280     cache_default = ".coverage"
   279 
   281 
   281     cache_env = "COVERAGE_FILE"
   283     cache_env = "COVERAGE_FILE"
   282 
   284 
   283     # A dictionary with an entry for (Python source file name, line number
   285     # A dictionary with an entry for (Python source file name, line number
   284     # in that file) if that line has been executed.
   286     # in that file) if that line has been executed.
   285     c = {}
   287     c = {}
   286 
   288     
   287     # A map from canonical Python source file name to a dictionary in
   289     # 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
   290     # which there's an entry for each line number that has been
   289     # executed.
   291     # executed.
   290     cexecuted = {}
   292     cexecuted = {}
   291 
   293 
   298     canonical_filename_cache = {}
   300     canonical_filename_cache = {}
   299 
   301 
   300     def __init__(self):
   302     def __init__(self):
   301         global the_coverage
   303         global the_coverage
   302         if the_coverage:
   304         if the_coverage:
   303             raise CoverageException, "Only one coverage object allowed."
   305             raise CoverageException("Only one coverage object allowed.")
   304         self.usecache = 1
   306         self.usecache = 1
   305         self.cache = None
   307         self.cache = None
   306         self.parallel_mode = False
   308         self.parallel_mode = False
   307         self.exclude_re = ''
   309         self.exclude_re = ''
   308         self.nesting = 0
   310         self.nesting = 0
   309         self.cstack = []
   311         self.cstack = []
   310         self.xstack = []
   312         self.xstack = []
   311         self.relative_dir = os.path.normcase(os.path.abspath(os.curdir)+os.sep)
   313         self.relative_dir = self.abs_file(os.curdir)+os.sep
   312         self.exclude('# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]')
   314         self.exclude('# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]')
   313 
   315 
   314     # t(f, x, y).  This method is passed to sys.settrace as a trace function.
   316     # t(f, x, y).  This method is passed to sys.settrace as a trace function.  
   315     # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and
   317     # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and 
   316     # the arguments and return value of the trace function.
   318     # 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
   319     # See [van Rossum 2001-07-20a, 3.2] for a description of frame and code
   318     # objects.
   320     # objects.
   319 
   321     
   320     def t(self, f, w, unused):                                   #pragma: no cover
   322     def t(self, f, w, unused):                                 #pragma: no cover
   321         if w == 'line':
   323         if w == 'line':
   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
   324             self.c[(f.f_code.co_filename, f.f_lineno)] = 1
   324             for c in self.cstack:
   325             #-for c in self.cstack:
   325                 c[(f.f_code.co_filename, f.f_lineno)] = 1
   326             #-    c[(f.f_code.co_filename, f.f_lineno)] = 1
   326         return self.t
   327         return self.t
   327 
   328     
   328     def help(self, error=None):     #pragma: no cover
   329     def help(self, error=None):     #pragma: no cover
   329         if error:
   330         if error:
   330             print error
   331             print error
   331             print
   332             print
   332         print __doc__
   333         print __doc__
   351             }
   352             }
   352         short_opts = string.join(map(lambda o: o[1:], optmap.keys()), '')
   353         short_opts = string.join(map(lambda o: o[1:], optmap.keys()), '')
   353         long_opts = optmap.values()
   354         long_opts = optmap.values()
   354         options, args = getopt.getopt(argv, short_opts, long_opts)
   355         options, args = getopt.getopt(argv, short_opts, long_opts)
   355         for o, a in options:
   356         for o, a in options:
   356             if o in optmap:
   357             if optmap.has_key(o):
   357                 settings[optmap[o]] = 1
   358                 settings[optmap[o]] = 1
   358             elif o + ':' in optmap:
   359             elif optmap.has_key(o + ':'):
   359                 settings[optmap[o + ':']] = a
   360                 settings[optmap[o + ':']] = a
   360             elif o[2:] in long_opts:
   361             elif o[2:] in long_opts:
   361                 settings[o[2:]] = 1
   362                 settings[o[2:]] = 1
   362             elif o[2:] + '=' in long_opts:
   363             elif o[2:] + '=' in long_opts:
   363                 settings[o[2:]+'='] = a
   364                 settings[o[2:]+'='] = a
   374                               "options at the same time." % (i, j))
   375                               "options at the same time." % (i, j))
   375 
   376 
   376         args_needed = (settings.get('execute')
   377         args_needed = (settings.get('execute')
   377                        or settings.get('annotate')
   378                        or settings.get('annotate')
   378                        or settings.get('report'))
   379                        or settings.get('report'))
   379         action = (settings.get('erase')
   380         action = (settings.get('erase') 
   380                   or settings.get('collect')
   381                   or settings.get('collect')
   381                   or args_needed)
   382                   or args_needed)
   382         if not action:
   383         if not action:
   383             help_fn("You must specify at least one of -e, -x, -c, -r, or -a.")
   384             help_fn("You must specify at least one of -e, -x, -c, -r, or -a.")
   384         if not args_needed and args:
   385         if not args_needed and args:
   385             help_fn("Unexpected arguments: %s" % " ".join(args))
   386             help_fn("Unexpected arguments: %s" % " ".join(args))
   386 
   387         
   387         self.parallel_mode = settings.get('parallel-mode')
   388         self.parallel_mode = settings.get('parallel-mode')
   388         self.get_ready()
   389         self.get_ready()
   389 
   390 
   390         if settings.get('erase'):
   391         if settings.get('erase'):
   391             self.erase()
   392             self.erase()
   399             execfile(sys.argv[0], __main__.__dict__)
   400             execfile(sys.argv[0], __main__.__dict__)
   400         if settings.get('collect'):
   401         if settings.get('collect'):
   401             self.collect()
   402             self.collect()
   402         if not args:
   403         if not args:
   403             args = self.cexecuted.keys()
   404             args = self.cexecuted.keys()
   404 
   405         
   405         ignore_errors = settings.get('ignore-errors')
   406         ignore_errors = settings.get('ignore-errors')
   406         show_missing = settings.get('show-missing')
   407         show_missing = settings.get('show-missing')
   407         directory = settings.get('directory=')
   408         directory = settings.get('directory=')
   408 
   409 
   409         omit = settings.get('omit=')
   410         omit = settings.get('omit=')
   410         if omit is not None:
   411         if omit is not None:
   411             omit = omit.split(',')
   412             omit = [self.abs_file(p) for p in omit.split(',')]
   412         else:
   413         else:
   413             omit = []
   414             omit = []
   414 
   415         
   415         omit = [os.path.normcase(os.path.abspath(os.path.realpath(p)))
       
   416                 for p in omit]
       
   417 
       
   418         if settings.get('report'):
   416         if settings.get('report'):
   419             self.report(args, show_missing, ignore_errors, omit_prefixes=omit)
   417             self.report(args, show_missing, ignore_errors, omit_prefixes=omit)
   420         if settings.get('annotate'):
   418         if settings.get('annotate'):
   421             self.annotate(args, directory, ignore_errors, omit_prefixes=omit)
   419             self.annotate(args, directory, ignore_errors, omit_prefixes=omit)
   422 
   420 
   423     def use_cache(self, usecache, cache_file=None):
   421     def use_cache(self, usecache, cache_file=None):
   424         self.usecache = usecache
   422         self.usecache = usecache
   425         if cache_file and not self.cache:
   423         if cache_file and not self.cache:
   426             self.cache_default = cache_file
   424             self.cache_default = cache_file
   427 
   425         
   428     def get_ready(self, parallel_mode=False):
   426     def get_ready(self, parallel_mode=False):
   429         if self.usecache and not self.cache:
   427         if self.usecache and not self.cache:
   430             self.cache = os.environ.get(self.cache_env, self.cache_default)
   428             self.cache = os.environ.get(self.cache_env, self.cache_default)
   431             if self.parallel_mode:
   429             if self.parallel_mode:
   432                 self.cache += "." + gethostname() + "." + str(os.getpid())
   430                 self.cache += "." + gethostname() + "." + str(os.getpid())
   433             self.restore()
   431             self.restore()
   434         self.analysis_cache = {}
   432         self.analysis_cache = {}
   435 
   433         
   436     def start(self, parallel_mode=False):
   434     def start(self, parallel_mode=False):
   437         self.get_ready()
   435         self.get_ready()
   438         if self.nesting == 0:                               #pragma: no cover
   436         if self.nesting == 0:                               #pragma: no cover
   439             sys.settrace(self.t)
   437             sys.settrace(self.t)
   440             if hasattr(threading, 'settrace'):
   438             if hasattr(threading, 'settrace'):
   441                 threading.settrace(self.t)
   439                 threading.settrace(self.t)
   442         self.nesting += 1
   440         self.nesting += 1
   443 
   441         
   444     def stop(self):
   442     def stop(self):
   445         self.nesting -= 1
   443         self.nesting -= 1
   446         if self.nesting == 0:                               #pragma: no cover
   444         if self.nesting == 0:                               #pragma: no cover
   447             sys.settrace(None)
   445             sys.settrace(None)
   448             if hasattr(threading, 'settrace'):
   446             if hasattr(threading, 'settrace'):
   462         self.exclude_re += "(" + re + ")"
   460         self.exclude_re += "(" + re + ")"
   463 
   461 
   464     def begin_recursive(self):
   462     def begin_recursive(self):
   465         self.cstack.append(self.c)
   463         self.cstack.append(self.c)
   466         self.xstack.append(self.exclude_re)
   464         self.xstack.append(self.exclude_re)
   467 
   465         
   468     def end_recursive(self):
   466     def end_recursive(self):
   469         self.c = self.cstack.pop()
   467         self.c = self.cstack.pop()
   470         self.exclude_re = self.xstack.pop()
   468         self.exclude_re = self.xstack.pop()
   471 
   469 
   472     # save().  Save coverage data to the coverage cache.
   470     # save().  Save coverage data to the coverage cache.
   513             cexecuted = self.restore_file(full_path)
   511             cexecuted = self.restore_file(full_path)
   514             self.merge_data(cexecuted)
   512             self.merge_data(cexecuted)
   515 
   513 
   516     def merge_data(self, new_data):
   514     def merge_data(self, new_data):
   517         for file_name, file_data in new_data.items():
   515         for file_name, file_data in new_data.items():
   518             if file_name in self.cexecuted:
   516             if self.cexecuted.has_key(file_name):
   519                 self.merge_file_data(self.cexecuted[file_name], file_data)
   517                 self.merge_file_data(self.cexecuted[file_name], file_data)
   520             else:
   518             else:
   521                 self.cexecuted[file_name] = file_data
   519                 self.cexecuted[file_name] = file_data
   522 
   520 
   523     def merge_file_data(self, cache_data, new_data):
   521     def merge_file_data(self, cache_data, new_data):
   524         for line_number in new_data.keys():
   522         for line_number in new_data.keys():
   525             if not line_number in cache_data:
   523             if not cache_data.has_key(line_number):
   526                 cache_data[line_number] = new_data[line_number]
   524                 cache_data[line_number] = new_data[line_number]
       
   525 
       
   526     def abs_file(self, filename):
       
   527         """ Helper function to turn a filename into an absolute normalized
       
   528             filename.
       
   529         """
       
   530         return os.path.normcase(os.path.abspath(os.path.realpath(filename)))
       
   531 
       
   532     def get_zip_data(self, filename):
       
   533         """ Get data from `filename` if it is a zip file path, or return None
       
   534             if it is not.
       
   535         """
       
   536         markers = ['.zip'+os.sep, '.egg'+os.sep]
       
   537         for marker in markers:
       
   538             if marker in filename:
       
   539                 parts = filename.split(marker)
       
   540                 try:
       
   541                     zi = zipimport.zipimporter(parts[0]+marker[:-1])
       
   542                 except zipimport.ZipImportError:
       
   543                     continue
       
   544                 try:
       
   545                     data = zi.get_data(parts[1])
       
   546                 except IOError:
       
   547                     continue
       
   548                 return data
       
   549         return None
   527 
   550 
   528     # canonical_filename(filename).  Return a canonical filename for the
   551     # canonical_filename(filename).  Return a canonical filename for the
   529     # file (that is, an absolute path with no redundant components and
   552     # file (that is, an absolute path with no redundant components and
   530     # normalized case).  See [GDR 2001-12-04b, 3.3].
   553     # normalized case).  See [GDR 2001-12-04b, 3.3].
   531 
   554 
   532     def canonical_filename(self, filename):
   555     def canonical_filename(self, filename):
   533         if not filename in self.canonical_filename_cache:
   556         if not self.canonical_filename_cache.has_key(filename):
   534             f = filename
   557             f = filename
   535             if os.path.isabs(f) and not os.path.exists(f):
   558             if os.path.isabs(f) and not os.path.exists(f):
   536                 f = os.path.basename(f)
   559                 if not self.get_zip_data(f):
       
   560                     f = os.path.basename(f)
   537             if not os.path.isabs(f):
   561             if not os.path.isabs(f):
   538                 for path in [os.curdir] + sys.path:
   562                 for path in [os.curdir] + sys.path:
   539                     g = os.path.join(path, f)
   563                     g = os.path.join(path, f)
   540                     if os.path.exists(g):
   564                     if os.path.exists(g):
   541                         f = g
   565                         f = g
   542                         break
   566                         break
   543             cf = os.path.normcase(os.path.abspath(os.path.realpath(f)))
   567             cf = self.abs_file(f)
   544             self.canonical_filename_cache[filename] = cf
   568             self.canonical_filename_cache[filename] = cf
   545         return self.canonical_filename_cache[filename]
   569         return self.canonical_filename_cache[filename]
   546 
   570 
   547     # canonicalize_filenames().  Copy results from "c" to "cexecuted",
   571     # canonicalize_filenames().  Copy results from "c" to "cexecuted", 
   548     # canonicalizing filenames on the way.  Clear the "c" map.
   572     # canonicalizing filenames on the way.  Clear the "c" map.
   549 
   573 
   550     def canonicalize_filenames(self):
   574     def canonicalize_filenames(self):
   551         for filename, lineno in self.c.keys():
   575         for filename, lineno in self.c.keys():
   552             if filename == '<string>':
   576             if filename == '<string>':
   553                 # Can't do anything useful with exec'd strings, so skip them.
   577                 # Can't do anything useful with exec'd strings, so skip them.
   554                 continue
   578                 continue
   555             f = self.canonical_filename(filename)
   579             f = self.canonical_filename(filename)
   556             if not f in self.cexecuted:
   580             if not self.cexecuted.has_key(f):
   557                 self.cexecuted[f] = {}
   581                 self.cexecuted[f] = {}
   558             self.cexecuted[f][lineno] = 1
   582             self.cexecuted[f][lineno] = 1
   559         self.c = {}
   583         self.c = {}
   560 
   584 
   561     # morf_filename(morf).  Return the filename for a module or file.
   585     # morf_filename(morf).  Return the filename for a module or file.
   562 
   586 
   563     def morf_filename(self, morf):
   587     def morf_filename(self, morf):
   564         if isinstance(morf, types.ModuleType):
   588         if hasattr(morf, '__file__'):
   565             if not hasattr(morf, '__file__'):
       
   566                 raise CoverageException, "Module has no __file__ attribute."
       
   567             f = morf.__file__
   589             f = morf.__file__
   568         else:
   590         else:
   569             f = morf
   591             f = morf
   570         return self.canonical_filename(f)
   592         return self.canonical_filename(f)
   571 
   593 
   574     # Otherwise, return a tuple of (1) the canonical filename of the
   596     # Otherwise, return a tuple of (1) the canonical filename of the
   575     # source code for the module, (2) a list of lines of statements
   597     # source code for the module, (2) a list of lines of statements
   576     # in the source code, (3) a list of lines of excluded statements,
   598     # in the source code, (3) a list of lines of excluded statements,
   577     # and (4), a map of line numbers to multi-line line number ranges, for
   599     # and (4), a map of line numbers to multi-line line number ranges, for
   578     # statements that cross lines.
   600     # statements that cross lines.
   579 
   601     
   580     def analyze_morf(self, morf):
   602     def analyze_morf(self, morf):
   581         if morf in self.analysis_cache:
   603         if self.analysis_cache.has_key(morf):
   582             return self.analysis_cache[morf]
   604             return self.analysis_cache[morf]
   583         filename = self.morf_filename(morf)
   605         filename = self.morf_filename(morf)
   584         ext = os.path.splitext(filename)[1]
   606         ext = os.path.splitext(filename)[1]
       
   607         source, sourcef = None, None
   585         if ext == '.pyc':
   608         if ext == '.pyc':
   586             if not os.path.exists(filename[0:-1]):
   609             if not os.path.exists(filename[:-1]):
   587                 raise CoverageException, ("No source for compiled code '%s'."
   610                 source = self.get_zip_data(filename[:-1])
   588                                    % filename)
   611                 if not source:
   589             filename = filename[0:-1]
   612                     raise CoverageException(
   590         elif ext != '.py':
   613                         "No source for compiled code '%s'." % filename
   591             raise CoverageException, "File '%s' not Python source." % filename
   614                         )
   592         source = open(filename, 'r')
   615             filename = filename[:-1]
   593         lines, excluded_lines, line_map = self.find_executable_statements(
   616         if not source:
   594             source.read(), exclude=self.exclude_re
   617             sourcef = open(filename, 'rU')
   595             )
   618             source = sourcef.read()
   596         source.close()
   619         try:
       
   620             lines, excluded_lines, line_map = self.find_executable_statements(
       
   621                 source, exclude=self.exclude_re
       
   622                 )
       
   623         except SyntaxError, synerr:
       
   624             raise CoverageException(
       
   625                 "Couldn't parse '%s' as Python source: '%s' at line %d" %
       
   626                     (filename, synerr.msg, synerr.lineno)
       
   627                 )
       
   628         if sourcef:
       
   629             sourcef.close()
   597         result = filename, lines, excluded_lines, line_map
   630         result = filename, lines, excluded_lines, line_map
   598         self.analysis_cache[morf] = result
   631         self.analysis_cache[morf] = result
   599         return result
   632         return result
   600 
   633 
   601     def first_line_of_tree(self, tree):
   634     def first_line_of_tree(self, tree):
   602         while True:
   635         while True:
   603             if len(tree) == 3 and type(tree[2]) == type(1):
   636             if len(tree) == 3 and type(tree[2]) == type(1):
   604                 return tree[2]
   637                 return tree[2]
   605             tree = tree[1]
   638             tree = tree[1]
   606 
   639     
   607     def last_line_of_tree(self, tree):
   640     def last_line_of_tree(self, tree):
   608         while True:
   641         while True:
   609             if len(tree) == 3 and type(tree[2]) == type(1):
   642             if len(tree) == 3 and type(tree[2]) == type(1):
   610                 return tree[2]
   643                 return tree[2]
   611             tree = tree[-1]
   644             tree = tree[-1]
   612 
   645     
   613     def find_docstring_pass_pair(self, tree, spots):
   646     def find_docstring_pass_pair(self, tree, spots):
   614         for i in range(1, len(tree)):
   647         for i in range(1, len(tree)):
   615             if self.is_string_constant(tree[i]) and self.is_pass_stmt(tree[i+1]):
   648             if self.is_string_constant(tree[i]) and self.is_pass_stmt(tree[i+1]):
   616                 first_line = self.first_line_of_tree(tree[i])
   649                 first_line = self.first_line_of_tree(tree[i])
   617                 last_line = self.last_line_of_tree(tree[i+1])
   650                 last_line = self.last_line_of_tree(tree[i+1])
   618                 self.record_multiline(spots, first_line, last_line)
   651                 self.record_multiline(spots, first_line, last_line)
   619 
   652         
   620     def is_string_constant(self, tree):
   653     def is_string_constant(self, tree):
   621         try:
   654         try:
   622             return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.expr_stmt
   655             return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.expr_stmt
   623         except:
   656         except:
   624             return False
   657             return False
   625 
   658         
   626     def is_pass_stmt(self, tree):
   659     def is_pass_stmt(self, tree):
   627         try:
   660         try:
   628             return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.pass_stmt
   661             return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.pass_stmt
   629         except:
   662         except:
   630             return False
   663             return False
   631 
   664 
   632     def record_multiline(self, spots, i, j):
   665     def record_multiline(self, spots, i, j):
   633         for l in range(i, j+1):
   666         for l in range(i, j+1):
   634             spots[l] = (i, j)
   667             spots[l] = (i, j)
   635 
   668             
   636     def get_suite_spots(self, tree, spots):
   669     def get_suite_spots(self, tree, spots):
   637         """ Analyze a parse tree to find suite introducers which span a number
   670         """ Analyze a parse tree to find suite introducers which span a number
   638             of lines.
   671             of lines.
   639         """
   672         """
   640         for i in range(1, len(tree)):
   673         for i in range(1, len(tree)):
   672 
   705 
   673                     # "pass" statements are tricky: different versions of Python
   706                     # "pass" statements are tricky: different versions of Python
   674                     # treat them differently, especially in the common case of a
   707                     # treat them differently, especially in the common case of a
   675                     # function with a doc string and a single pass statement.
   708                     # function with a doc string and a single pass statement.
   676                     self.find_docstring_pass_pair(tree[i], spots)
   709                     self.find_docstring_pass_pair(tree[i], spots)
   677 
   710                     
   678                 elif tree[i][0] == symbol.simple_stmt:
   711                 elif tree[i][0] == symbol.simple_stmt:
   679                     first_line = self.first_line_of_tree(tree[i])
   712                     first_line = self.first_line_of_tree(tree[i])
   680                     last_line = self.last_line_of_tree(tree[i])
   713                     last_line = self.last_line_of_tree(tree[i])
   681                     if first_line != last_line:
   714                     if first_line != last_line:
   682                         self.record_multiline(spots, first_line, last_line)
   715                         self.record_multiline(spots, first_line, last_line)
   697         # are multiline, and where suites begin and end.
   730         # are multiline, and where suites begin and end.
   698         import parser
   731         import parser
   699         tree = parser.suite(text+'\n\n').totuple(1)
   732         tree = parser.suite(text+'\n\n').totuple(1)
   700         self.get_suite_spots(tree, suite_spots)
   733         self.get_suite_spots(tree, suite_spots)
   701         #print "Suite spots:", suite_spots
   734         #print "Suite spots:", suite_spots
   702 
   735         
   703         # Use the compiler module to parse the text and find the executable
   736         # Use the compiler module to parse the text and find the executable
   704         # statements.  We add newlines to be impervious to final partial lines.
   737         # statements.  We add newlines to be impervious to final partial lines.
   705         statements = {}
   738         statements = {}
   706         ast = compiler.parse(text+'\n\n')
   739         ast = compiler.parse(text+'\n\n')
   707         visitor = StatementFindingAstVisitor(statements, excluded, suite_spots)
   740         visitor = StatementFindingAstVisitor(statements, excluded, suite_spots)
   753         return f, s, m, mf
   786         return f, s, m, mf
   754 
   787 
   755     def analysis2(self, morf):
   788     def analysis2(self, morf):
   756         filename, statements, excluded, line_map = self.analyze_morf(morf)
   789         filename, statements, excluded, line_map = self.analyze_morf(morf)
   757         self.canonicalize_filenames()
   790         self.canonicalize_filenames()
   758         if not filename in self.cexecuted:
   791         if not self.cexecuted.has_key(filename):
   759             self.cexecuted[filename] = {}
   792             self.cexecuted[filename] = {}
   760         missing = []
   793         missing = []
   761         for line in statements:
   794         for line in statements:
   762             lines = line_map.get(line, [line, line])
   795             lines = line_map.get(line, [line, line])
   763             for l in range(lines[0], lines[1]+1):
   796             for l in range(lines[0], lines[1]+1):
   764                 if l in self.cexecuted[filename]:
   797                 if self.cexecuted[filename].has_key(l):
   765                     break
   798                     break
   766             else:
   799             else:
   767                 missing.append(line)
   800                 missing.append(line)
   768         return (filename, statements, excluded, missing,
   801         return (filename, statements, excluded, missing,
   769                 self.format_lines(statements, missing))
   802                 self.format_lines(statements, missing))
   774         return filename.replace(self.relative_dir, "")
   807         return filename.replace(self.relative_dir, "")
   775 
   808 
   776     def morf_name(self, morf):
   809     def morf_name(self, morf):
   777         """ Return the name of morf as used in report.
   810         """ Return the name of morf as used in report.
   778         """
   811         """
   779         if isinstance(morf, types.ModuleType):
   812         if hasattr(morf, '__name__'):
   780             return morf.__name__
   813             return morf.__name__
   781         else:
   814         else:
   782             return self.relative_filename(os.path.splitext(morf)[0])
   815             return self.relative_filename(os.path.splitext(morf)[0])
   783 
   816 
   784     def filter_by_prefix(self, morfs, omit_prefixes):
   817     def filter_by_prefix(self, morfs, omit_prefixes):
   807             if isinstance(morf, strclass):
   840             if isinstance(morf, strclass):
   808                 globbed.extend(glob.glob(morf))
   841                 globbed.extend(glob.glob(morf))
   809             else:
   842             else:
   810                 globbed.append(morf)
   843                 globbed.append(morf)
   811         morfs = globbed
   844         morfs = globbed
   812 
   845         
   813         morfs = self.filter_by_prefix(morfs, omit_prefixes)
   846         morfs = self.filter_by_prefix(morfs, omit_prefixes)
   814         morfs.sort(self.morf_name_compare)
   847         morfs.sort(self.morf_name_compare)
   815 
   848 
   816         max_name = max([5,] + map(len, map(self.morf_name, morfs)))
   849         max_name = max([5,] + map(len, map(self.morf_name, morfs)))
   817         fmt_name = "%%- %ds  " % max_name
   850         fmt_name = "%%- %ds  " % max_name
   845                 total_executed = total_executed + m
   878                 total_executed = total_executed + m
   846             except KeyboardInterrupt:                       #pragma: no cover
   879             except KeyboardInterrupt:                       #pragma: no cover
   847                 raise
   880                 raise
   848             except:
   881             except:
   849                 if not ignore_errors:
   882                 if not ignore_errors:
   850                     typ, msg = sys.exc_info()[0:2]
   883                     typ, msg = sys.exc_info()[:2]
   851                     print >>file, fmt_err % (name, typ, msg)
   884                     print >>file, fmt_err % (name, typ, msg)
   852         if len(morfs) > 1:
   885         if len(morfs) > 1:
   853             print >>file, "-" * len(header)
   886             print >>file, "-" * len(header)
   854             if total_statements > 0:
   887             if total_statements > 0:
   855                 pc = 100.0 * total_executed / total_statements
   888                 pc = 100.0 * total_executed / total_statements
   874             except KeyboardInterrupt:
   907             except KeyboardInterrupt:
   875                 raise
   908                 raise
   876             except:
   909             except:
   877                 if not ignore_errors:
   910                 if not ignore_errors:
   878                     raise
   911                     raise
   879 
   912                 
   880     def annotate_file(self, filename, statements, excluded, missing, directory=None):
   913     def annotate_file(self, filename, statements, excluded, missing, directory=None):
   881         source = open(filename, 'r')
   914         source = open(filename, 'r')
   882         if directory:
   915         if directory:
   883             dest_file = os.path.join(directory,
   916             dest_file = os.path.join(directory,
   884                                      os.path.basename(filename)
   917                                      os.path.basename(filename)
   902             if i < len(statements) and statements[i] == lineno:
   935             if i < len(statements) and statements[i] == lineno:
   903                 covered = j >= len(missing) or missing[j] > lineno
   936                 covered = j >= len(missing) or missing[j] > lineno
   904             if self.blank_re.match(line):
   937             if self.blank_re.match(line):
   905                 dest.write('  ')
   938                 dest.write('  ')
   906             elif self.else_re.match(line):
   939             elif self.else_re.match(line):
   907                 # Special logic for lines containing only 'else:'.
   940                 # Special logic for lines containing only 'else:'.  
   908                 # See [GDR 2001-12-04b, 3.2].
   941                 # See [GDR 2001-12-04b, 3.2].
   909                 if i >= len(statements) and j >= len(missing):
   942                 if i >= len(statements) and j >= len(missing):
   910                     dest.write('! ')
   943                     dest.write('! ')
   911                 elif i >= len(statements) or j >= len(missing):
   944                 elif i >= len(statements) or j >= len(missing):
   912                     dest.write('> ')
   945                     dest.write('> ')
   926 
   959 
   927 # Singleton object.
   960 # Singleton object.
   928 the_coverage = coverage()
   961 the_coverage = coverage()
   929 
   962 
   930 # Module functions call methods in the singleton object.
   963 # Module functions call methods in the singleton object.
   931 def use_cache(*args, **kw):
   964 def use_cache(*args, **kw): 
   932     return the_coverage.use_cache(*args, **kw)
   965     return the_coverage.use_cache(*args, **kw)
   933 
   966 
   934 def start(*args, **kw):
   967 def start(*args, **kw): 
   935     return the_coverage.start(*args, **kw)
   968     return the_coverage.start(*args, **kw)
   936 
   969 
   937 def stop(*args, **kw):
   970 def stop(*args, **kw): 
   938     return the_coverage.stop(*args, **kw)
   971     return the_coverage.stop(*args, **kw)
   939 
   972 
   940 def erase(*args, **kw):
   973 def erase(*args, **kw): 
   941     return the_coverage.erase(*args, **kw)
   974     return the_coverage.erase(*args, **kw)
   942 
   975 
   943 def begin_recursive(*args, **kw):
   976 def begin_recursive(*args, **kw): 
   944     return the_coverage.begin_recursive(*args, **kw)
   977     return the_coverage.begin_recursive(*args, **kw)
   945 
   978 
   946 def end_recursive(*args, **kw):
   979 def end_recursive(*args, **kw): 
   947     return the_coverage.end_recursive(*args, **kw)
   980     return the_coverage.end_recursive(*args, **kw)
   948 
   981 
   949 def exclude(*args, **kw):
   982 def exclude(*args, **kw): 
   950     return the_coverage.exclude(*args, **kw)
   983     return the_coverage.exclude(*args, **kw)
   951 
   984 
   952 def analysis(*args, **kw):
   985 def analysis(*args, **kw): 
   953     return the_coverage.analysis(*args, **kw)
   986     return the_coverage.analysis(*args, **kw)
   954 
   987 
   955 def analysis2(*args, **kw):
   988 def analysis2(*args, **kw): 
   956     return the_coverage.analysis2(*args, **kw)
   989     return the_coverage.analysis2(*args, **kw)
   957 
   990 
   958 def report(*args, **kw):
   991 def report(*args, **kw): 
   959     return the_coverage.report(*args, **kw)
   992     return the_coverage.report(*args, **kw)
   960 
   993 
   961 def annotate(*args, **kw):
   994 def annotate(*args, **kw): 
   962     return the_coverage.annotate(*args, **kw)
   995     return the_coverage.annotate(*args, **kw)
   963 
   996 
   964 def annotate_file(*args, **kw):
   997 def annotate_file(*args, **kw): 
   965     return the_coverage.annotate_file(*args, **kw)
   998     return the_coverage.annotate_file(*args, **kw)
   966 
   999 
   967 # Save coverage data when Python exits.  (The atexit module wasn't
  1000 # Save coverage data when Python exits.  (The atexit module wasn't
   968 # introduced until Python 2.0, so use sys.exitfunc when it's not
  1001 # introduced until Python 2.0, so use sys.exitfunc when it's not
   969 # available.)
  1002 # available.)
   971     import atexit
  1004     import atexit
   972     atexit.register(the_coverage.save)
  1005     atexit.register(the_coverage.save)
   973 except ImportError:
  1006 except ImportError:
   974     sys.exitfunc = the_coverage.save
  1007     sys.exitfunc = the_coverage.save
   975 
  1008 
       
  1009 def main():
       
  1010     the_coverage.command_line(sys.argv[1:])
       
  1011     
   976 # Command-line interface.
  1012 # Command-line interface.
   977 if __name__ == '__main__':
  1013 if __name__ == '__main__':
   978     the_coverage.command_line(sys.argv[1:])
  1014     main()
   979 
  1015 
   980 
  1016 
   981 # A. REFERENCES
  1017 # A. REFERENCES
   982 #
  1018 #
   983 # [GDR 2001-12-04a] "Statement coverage for Python"; Gareth Rees;
  1019 # [GDR 2001-12-04a] "Statement coverage for Python"; Gareth Rees;
  1034 #
  1070 #
  1035 # 2004-12-31 NMB Allow for keyword arguments in the module global functions.
  1071 # 2004-12-31 NMB Allow for keyword arguments in the module global functions.
  1036 # Thanks, Allen.
  1072 # Thanks, Allen.
  1037 #
  1073 #
  1038 # 2005-12-02 NMB Call threading.settrace so that all threads are measured.
  1074 # 2005-12-02 NMB Call threading.settrace so that all threads are measured.
  1039 # Thanks Martin Fuzzey. Add a file argument to report so that reports can be
  1075 # Thanks Martin Fuzzey. Add a file argument to report so that reports can be 
  1040 # captured to a different destination.
  1076 # captured to a different destination.
  1041 #
  1077 #
  1042 # 2005-12-03 NMB coverage.py can now measure itself.
  1078 # 2005-12-03 NMB coverage.py can now measure itself.
  1043 #
  1079 #
  1044 # 2005-12-04 NMB Adapted Greg Rogers' patch for using relative filenames,
  1080 # 2005-12-04 NMB Adapted Greg Rogers' patch for using relative filenames,
  1077 #
  1113 #
  1078 # 2007-07-23 NMB Now Python 2.5 is *really* fully supported: the body of the
  1114 # 2007-07-23 NMB Now Python 2.5 is *really* fully supported: the body of the
  1079 # new with statement is counted as executable.
  1115 # new with statement is counted as executable.
  1080 #
  1116 #
  1081 # 2007-07-29 NMB Better packaging.
  1117 # 2007-07-29 NMB Better packaging.
  1082 
  1118 #
       
  1119 # 2007-09-30 NMB Don't try to predict whether a file is Python source based on
       
  1120 # the extension. Extensionless files are often Pythons scripts. Instead, simply
       
  1121 # parse the file and catch the syntax errors.  Hat tip to Ben Finney.
       
  1122 #
       
  1123 # 2008-05-25 NMB Open files in rU mode to avoid line ending craziness.
       
  1124 # Thanks, Edward Loper.
       
  1125 #
       
  1126 # 2008-09-14 NMB Add support for finding source files in eggs.
       
  1127 # Don't check for morf's being instances of ModuleType, instead use duck typing
       
  1128 # so that pseudo-modules can participate. Thanks, Imri Goldberg.
       
  1129 # Use os.realpath as part of the fixing of filenames so that symlinks won't
       
  1130 # confuse things.  Thanks, Patrick Mezard.
       
  1131 #
       
  1132 #
  1083 # C. COPYRIGHT AND LICENCE
  1133 # C. COPYRIGHT AND LICENCE
  1084 #
  1134 #
  1085 # Copyright 2001 Gareth Rees.  All rights reserved.
  1135 # Copyright 2001 Gareth Rees.  All rights reserved.
  1086 # Copyright 2004-2007 Ned Batchelder.  All rights reserved.
  1136 # Copyright 2004-2008 Ned Batchelder.  All rights reserved.
  1087 #
  1137 #
  1088 # Redistribution and use in source and binary forms, with or without
  1138 # Redistribution and use in source and binary forms, with or without
  1089 # modification, are permitted provided that the following conditions are
  1139 # modification, are permitted provided that the following conditions are
  1090 # met:
  1140 # met:
  1091 #
  1141 #
  1108 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  1158 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  1109 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  1159 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  1110 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  1160 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  1111 # DAMAGE.
  1161 # DAMAGE.
  1112 #
  1162 #
  1113 # $Id: coverage.py 74 2007-07-29 22:28:35Z nedbat $
  1163 # $Id: coverage.py 96 2008-09-14 18:34:13Z nedbat $