tests/coverage.py
branchstable
changeset 10648 58128004cca1
parent 10646 86dc21148bdb
child 10649 e13797685ee6
child 10656 f6ee02933af9
equal deleted inserted replaced
10646:86dc21148bdb 10648:58128004cca1
     1 #!/usr/bin/env python
       
     2 #
       
     3 #             Perforce Defect Tracking Integration Project
       
     4 #              <http://www.ravenbrook.com/project/p4dti/>
       
     5 #
       
     6 #                   COVERAGE.PY -- COVERAGE TESTING
       
     7 #
       
     8 #             Gareth Rees, Ravenbrook Limited, 2001-12-04
       
     9 #                     Ned Batchelder, 2004-12-12
       
    10 #         http://nedbatchelder.com/code/modules/coverage.html
       
    11 #
       
    12 #
       
    13 # 1. INTRODUCTION
       
    14 #
       
    15 # This module provides coverage testing for Python code.
       
    16 #
       
    17 # The intended readership is all Python developers.
       
    18 #
       
    19 # This document is not confidential.
       
    20 #
       
    21 # See [GDR 2001-12-04a] for the command-line interface, programmatic
       
    22 # interface and limitations.  See [GDR 2001-12-04b] for requirements and
       
    23 # design.
       
    24 
       
    25 r"""Usage:
       
    26 
       
    27 coverage.py -x [-p] MODULE.py [ARG1 ARG2 ...]
       
    28     Execute module, passing the given command-line arguments, collecting
       
    29     coverage data. With the -p option, write to a temporary file containing
       
    30     the machine name and process ID.
       
    31 
       
    32 coverage.py -e
       
    33     Erase collected coverage data.
       
    34 
       
    35 coverage.py -c
       
    36     Collect data from multiple coverage files (as created by -p option above)
       
    37     and store it into a single file representing the union of the coverage.
       
    38 
       
    39 coverage.py -r [-m] [-o dir1,dir2,...] FILE1 FILE2 ...
       
    40     Report on the statement coverage for the given files.  With the -m
       
    41     option, show line numbers of the statements that weren't executed.
       
    42 
       
    43 coverage.py -a [-d dir] [-o dir1,dir2,...] FILE1 FILE2 ...
       
    44     Make annotated copies of the given files, marking statements that
       
    45     are executed with > and statements that are missed with !.  With
       
    46     the -d option, make the copies in that directory.  Without the -d
       
    47     option, make each copy in the same directory as the original.
       
    48 
       
    49 -o dir,dir2,...
       
    50   Omit reporting or annotating files when their filename path starts with
       
    51   a directory listed in the omit list.
       
    52   e.g. python coverage.py -i -r -o c:\python23,lib\enthought\traits
       
    53 
       
    54 Coverage data is saved in the file .coverage by default.  Set the
       
    55 COVERAGE_FILE environment variable to save it somewhere else."""
       
    56 
       
    57 __version__ = "2.85.20080914"    # see detailed history at the end of this file.
       
    58 
       
    59 import compiler
       
    60 import compiler.visitor
       
    61 import glob
       
    62 import os
       
    63 import re
       
    64 import string
       
    65 import symbol
       
    66 import sys
       
    67 import threading
       
    68 import token
       
    69 import types
       
    70 import zipimport
       
    71 from socket import gethostname
       
    72 
       
    73 # Python version compatibility
       
    74 try:
       
    75     strclass = basestring   # new to 2.3
       
    76 except:
       
    77     strclass = str
       
    78 
       
    79 # 2. IMPLEMENTATION
       
    80 #
       
    81 # This uses the "singleton" pattern.
       
    82 #
       
    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
       
    85 # filename.
       
    86 #
       
    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.
       
    92 #
       
    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.
       
    98 
       
    99 class StatementFindingAstVisitor(compiler.visitor.ASTVisitor):
       
   100     """ A visitor for a parsed Abstract Syntax Tree which finds executable
       
   101         statements.
       
   102     """
       
   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
       
   109 
       
   110     def doRecursive(self, node):
       
   111         for n in node.getChildNodes():
       
   112             self.dispatch(n)
       
   113 
       
   114     visitStmt = visitModule = doRecursive
       
   115 
       
   116     def doCode(self, node):
       
   117         if hasattr(node, 'decorators') and node.decorators:
       
   118             self.dispatch(node.decorators)
       
   119             self.recordAndDispatch(node.code)
       
   120         else:
       
   121             self.doSuite(node, node.code)
       
   122 
       
   123     visitFunction = visitClass = doCode
       
   124 
       
   125     def getFirstLine(self, node):
       
   126         # Find the first line in the tree node.
       
   127         lineno = node.lineno
       
   128         for n in node.getChildNodes():
       
   129             f = self.getFirstLine(n)
       
   130             if lineno and f:
       
   131                 lineno = min(lineno, f)
       
   132             else:
       
   133                 lineno = lineno or f
       
   134         return lineno
       
   135 
       
   136     def getLastLine(self, node):
       
   137         # Find the first line in the tree node.
       
   138         lineno = node.lineno
       
   139         for n in node.getChildNodes():
       
   140             lineno = max(lineno, self.getLastLine(n))
       
   141         return lineno
       
   142 
       
   143     def doStatement(self, node):
       
   144         self.recordLine(self.getFirstLine(node))
       
   145 
       
   146     visitAssert = visitAssign = visitAssTuple = visitPrint = \
       
   147         visitPrintnl = visitRaise = visitSubscript = visitDecorators = \
       
   148         doStatement
       
   149 
       
   150     def visitPass(self, node):
       
   151         # Pass statements have weird interactions with docstrings. If
       
   152         # this pass statement is part of one of those pairs, claim
       
   153         # that the statement is on the later of the two lines.
       
   154         l = node.lineno
       
   155         if l:
       
   156             lines = self.suite_spots.get(l, [l, l])
       
   157             self.statements[lines[1]] = 1
       
   158 
       
   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)
       
   166 
       
   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
       
   170         # like "global a").
       
   171         if node.__class__.__name__ != 'Stmt':
       
   172             return self.recordLine(self.getFirstLine(node))
       
   173         else:
       
   174             return 0
       
   175 
       
   176     def recordLine(self, lineno):
       
   177         # Returns a bool, whether the line is included or excluded.
       
   178         if lineno:
       
   179             # Multi-line tests introducing suites have to get charged to their
       
   180             # keyword.
       
   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
       
   184             # excluded.
       
   185             if self.excluding_suite:
       
   186                 self.excluded[lineno] = 1
       
   187                 return 0
       
   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]):
       
   193                 return 0
       
   194             # Otherwise, this is an executable line.
       
   195             else:
       
   196                 self.statements[lineno] = 1
       
   197                 return 1
       
   198         return 0
       
   199 
       
   200     default = recordNodeLine
       
   201 
       
   202     def recordAndDispatch(self, node):
       
   203         self.recordNodeLine(node)
       
   204         self.dispatch(node)
       
   205 
       
   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
       
   212 
       
   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))
       
   223                 break
       
   224         else:
       
   225             self.doSuite(None, suite)
       
   226 
       
   227     def doElse(self, prevsuite, node):
       
   228         if node.else_:
       
   229             self.doPlainWordSuite(prevsuite, node.else_)
       
   230 
       
   231     def visitFor(self, node):
       
   232         self.doSuite(node, node.body)
       
   233         self.doElse(node.body, node)
       
   234 
       
   235     visitWhile = visitFor
       
   236 
       
   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:]:
       
   243             self.doSuite(t, n)
       
   244         self.doElse(node.tests[-1][1], node)
       
   245 
       
   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]
       
   250             if not a:
       
   251                 # It's a plain "except:".  Find the previous suite.
       
   252                 if i > 0:
       
   253                     prev = node.handlers[i - 1][2]
       
   254                 else:
       
   255                     prev = node.body
       
   256                 self.doPlainWordSuite(prev, h)
       
   257             else:
       
   258                 self.doSuite(a, h)
       
   259         self.doElse(node.handlers[-1][2], node)
       
   260 
       
   261     def visitTryFinally(self, node):
       
   262         self.doSuite(node, node.body)
       
   263         self.doPlainWordSuite(node.body, node.final)
       
   264 
       
   265     def visitWith(self, node):
       
   266         self.doSuite(node, node.body)
       
   267 
       
   268     def visitGlobal(self, node):
       
   269         # "global" statements don't execute like others (they don't call the
       
   270         # trace function), so don't record their line numbers.
       
   271         pass
       
   272 
       
   273 the_coverage = None
       
   274 
       
   275 class CoverageException(Exception):
       
   276     pass
       
   277 
       
   278 class coverage:
       
   279     # Name of the cache file (unless environment variable is set).
       
   280     cache_default = ".coverage"
       
   281 
       
   282     # Environment variable naming the cache file.
       
   283     cache_env = "COVERAGE_FILE"
       
   284 
       
   285     # A dictionary with an entry for (Python source file name, line number
       
   286     # in that file) if that line has been executed.
       
   287     c = {}
       
   288 
       
   289     # A map from canonical Python source file name to a dictionary in
       
   290     # which there's an entry for each line number that has been
       
   291     # executed.
       
   292     cexecuted = {}
       
   293 
       
   294     # Cache of results of calling the analysis2() method, so that you can
       
   295     # specify both -r and -a without doing double work.
       
   296     analysis_cache = {}
       
   297 
       
   298     # Cache of results of calling the canonical_filename() method, to
       
   299     # avoid duplicating work.
       
   300     canonical_filename_cache = {}
       
   301 
       
   302     def __init__(self):
       
   303         global the_coverage
       
   304         if the_coverage:
       
   305             raise CoverageException("Only one coverage object allowed.")
       
   306         self.usecache = 1
       
   307         self.cache = None
       
   308         self.parallel_mode = False
       
   309         self.exclude_re = ''
       
   310         self.nesting = 0
       
   311         self.cstack = []
       
   312         self.xstack = []
       
   313         self.relative_dir = self.abs_file(os.curdir)+os.sep
       
   314         self.exclude('# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]')
       
   315 
       
   316     # t(f, x, y).  This method is passed to sys.settrace as a trace function.
       
   317     # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and
       
   318     # the arguments and return value of the trace function.
       
   319     # See [van Rossum 2001-07-20a, 3.2] for a description of frame and code
       
   320     # objects.
       
   321     def t(self, f, w, unused): #pragma: no cover
       
   322         if w == 'line':
       
   323             self.c[(f.f_code.co_filename, f.f_lineno)] = 1
       
   324             #-for c in self.cstack:
       
   325             #-    c[(f.f_code.co_filename, f.f_lineno)] = 1
       
   326         return self.t
       
   327 
       
   328     def help(self, error=None):     #pragma: no cover
       
   329         if error:
       
   330             print error
       
   331             print
       
   332         print __doc__
       
   333         sys.exit(1)
       
   334 
       
   335     def command_line(self, argv, help_fn=None):
       
   336         import getopt
       
   337         help_fn = help_fn or self.help
       
   338         settings = {}
       
   339         optmap = {
       
   340             '-a': 'annotate',
       
   341             '-c': 'collect',
       
   342             '-d:': 'directory=',
       
   343             '-e': 'erase',
       
   344             '-h': 'help',
       
   345             '-i': 'ignore-errors',
       
   346             '-m': 'show-missing',
       
   347             '-p': 'parallel-mode',
       
   348             '-r': 'report',
       
   349             '-x': 'execute',
       
   350             '-o:': 'omit=',
       
   351             }
       
   352         short_opts = string.join(map(lambda o: o[1:], optmap.keys()), '')
       
   353         long_opts = optmap.values()
       
   354         options, args = getopt.getopt(argv, short_opts, long_opts)
       
   355         for o, a in options:
       
   356             if optmap.has_key(o):
       
   357                 settings[optmap[o]] = 1
       
   358             elif optmap.has_key(o + ':'):
       
   359                 settings[optmap[o + ':']] = a
       
   360             elif o[2:] in long_opts:
       
   361                 settings[o[2:]] = 1
       
   362             elif o[2:] + '=' in long_opts:
       
   363                 settings[o[2:]+'='] = a
       
   364             else:       #pragma: no cover
       
   365                 # Can't get here, because getopt won't return anything unknown.
       
   366                 pass
       
   367 
       
   368         if settings.get('help'):
       
   369             help_fn()
       
   370 
       
   371         for i in ['erase', 'execute']:
       
   372             for j in ['annotate', 'report', 'collect']:
       
   373                 if settings.get(i) and settings.get(j):
       
   374                     help_fn("You can't specify the '%s' and '%s' "
       
   375                               "options at the same time." % (i, j))
       
   376 
       
   377         args_needed = (settings.get('execute')
       
   378                        or settings.get('annotate')
       
   379                        or settings.get('report'))
       
   380         action = (settings.get('erase')
       
   381                   or settings.get('collect')
       
   382                   or args_needed)
       
   383         if not action:
       
   384             help_fn("You must specify at least one of -e, -x, -c, -r, or -a.")
       
   385         if not args_needed and args:
       
   386             help_fn("Unexpected arguments: %s" % " ".join(args))
       
   387 
       
   388         self.parallel_mode = settings.get('parallel-mode')
       
   389         self.get_ready()
       
   390 
       
   391         if settings.get('erase'):
       
   392             self.erase()
       
   393         if settings.get('execute'):
       
   394             if not args:
       
   395                 help_fn("Nothing to do.")
       
   396             sys.argv = args
       
   397             self.start()
       
   398             import __main__
       
   399             sys.path[0] = os.path.dirname(sys.argv[0])
       
   400             execfile(sys.argv[0], __main__.__dict__)
       
   401         if settings.get('collect'):
       
   402             self.collect()
       
   403         if not args:
       
   404             args = self.cexecuted.keys()
       
   405 
       
   406         ignore_errors = settings.get('ignore-errors')
       
   407         show_missing = settings.get('show-missing')
       
   408         directory = settings.get('directory=')
       
   409 
       
   410         omit = settings.get('omit=')
       
   411         if omit is not None:
       
   412             omit = [self.abs_file(p) for p in omit.split(',')]
       
   413         else:
       
   414             omit = []
       
   415 
       
   416         if settings.get('report'):
       
   417             self.report(args, show_missing, ignore_errors, omit_prefixes=omit)
       
   418         if settings.get('annotate'):
       
   419             self.annotate(args, directory, ignore_errors, omit_prefixes=omit)
       
   420 
       
   421     def use_cache(self, usecache, cache_file=None):
       
   422         self.usecache = usecache
       
   423         if cache_file and not self.cache:
       
   424             self.cache_default = cache_file
       
   425 
       
   426     def get_ready(self, parallel_mode=False):
       
   427         if self.usecache and not self.cache:
       
   428             self.cache = os.environ.get(self.cache_env, self.cache_default)
       
   429             if self.parallel_mode:
       
   430                 self.cache += "." + gethostname() + "." + str(os.getpid())
       
   431             self.restore()
       
   432         self.analysis_cache = {}
       
   433 
       
   434     def start(self, parallel_mode=False):
       
   435         self.get_ready()
       
   436         if self.nesting == 0:                               #pragma: no cover
       
   437             sys.settrace(self.t)
       
   438             if hasattr(threading, 'settrace'):
       
   439                 threading.settrace(self.t)
       
   440         self.nesting += 1
       
   441 
       
   442     def stop(self):
       
   443         self.nesting -= 1
       
   444         if self.nesting == 0:                               #pragma: no cover
       
   445             sys.settrace(None)
       
   446             if hasattr(threading, 'settrace'):
       
   447                 threading.settrace(None)
       
   448 
       
   449     def erase(self):
       
   450         self.get_ready()
       
   451         self.c = {}
       
   452         self.analysis_cache = {}
       
   453         self.cexecuted = {}
       
   454         if self.cache and os.path.exists(self.cache):
       
   455             os.remove(self.cache)
       
   456 
       
   457     def exclude(self, re):
       
   458         if self.exclude_re:
       
   459             self.exclude_re += "|"
       
   460         self.exclude_re += "(" + re + ")"
       
   461 
       
   462     def begin_recursive(self):
       
   463         self.cstack.append(self.c)
       
   464         self.xstack.append(self.exclude_re)
       
   465 
       
   466     def end_recursive(self):
       
   467         self.c = self.cstack.pop()
       
   468         self.exclude_re = self.xstack.pop()
       
   469 
       
   470     # save().  Save coverage data to the coverage cache.
       
   471 
       
   472     def save(self):
       
   473         if self.usecache and self.cache:
       
   474             self.canonicalize_filenames()
       
   475             cache = open(self.cache, 'wb')
       
   476             import marshal
       
   477             marshal.dump(self.cexecuted, cache)
       
   478             cache.close()
       
   479 
       
   480     # restore().  Restore coverage data from the coverage cache (if it exists).
       
   481 
       
   482     def restore(self):
       
   483         self.c = {}
       
   484         self.cexecuted = {}
       
   485         assert self.usecache
       
   486         if os.path.exists(self.cache):
       
   487             self.cexecuted = self.restore_file(self.cache)
       
   488 
       
   489     def restore_file(self, file_name):
       
   490         try:
       
   491             cache = open(file_name, 'rb')
       
   492             import marshal
       
   493             cexecuted = marshal.load(cache)
       
   494             cache.close()
       
   495             if isinstance(cexecuted, types.DictType):
       
   496                 return cexecuted
       
   497             else:
       
   498                 return {}
       
   499         except:
       
   500             return {}
       
   501 
       
   502     # collect(). Collect data in multiple files produced by parallel mode
       
   503 
       
   504     def collect(self):
       
   505         cache_dir, local = os.path.split(self.cache)
       
   506         for f in os.listdir(cache_dir or '.'):
       
   507             if not f.startswith(local):
       
   508                 continue
       
   509 
       
   510             full_path = os.path.join(cache_dir, f)
       
   511             cexecuted = self.restore_file(full_path)
       
   512             self.merge_data(cexecuted)
       
   513 
       
   514     def merge_data(self, new_data):
       
   515         for file_name, file_data in new_data.items():
       
   516             if self.cexecuted.has_key(file_name):
       
   517                 self.merge_file_data(self.cexecuted[file_name], file_data)
       
   518             else:
       
   519                 self.cexecuted[file_name] = file_data
       
   520 
       
   521     def merge_file_data(self, cache_data, new_data):
       
   522         for line_number in new_data.keys():
       
   523             if not cache_data.has_key(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
       
   550 
       
   551     # canonical_filename(filename).  Return a canonical filename for the
       
   552     # file (that is, an absolute path with no redundant components and
       
   553     # normalized case).  See [GDR 2001-12-04b, 3.3].
       
   554 
       
   555     def canonical_filename(self, filename):
       
   556         if not self.canonical_filename_cache.has_key(filename):
       
   557             f = filename
       
   558             if os.path.isabs(f) and not os.path.exists(f):
       
   559                 if not self.get_zip_data(f):
       
   560                     f = os.path.basename(f)
       
   561             if not os.path.isabs(f):
       
   562                 for path in [os.curdir] + sys.path:
       
   563                     g = os.path.join(path, f)
       
   564                     if os.path.exists(g):
       
   565                         f = g
       
   566                         break
       
   567             cf = self.abs_file(f)
       
   568             self.canonical_filename_cache[filename] = cf
       
   569         return self.canonical_filename_cache[filename]
       
   570 
       
   571     # canonicalize_filenames().  Copy results from "c" to "cexecuted",
       
   572     # canonicalizing filenames on the way.  Clear the "c" map.
       
   573 
       
   574     def canonicalize_filenames(self):
       
   575         for filename, lineno in self.c.keys():
       
   576             if filename == '<string>':
       
   577                 # Can't do anything useful with exec'd strings, so skip them.
       
   578                 continue
       
   579             f = self.canonical_filename(filename)
       
   580             if not self.cexecuted.has_key(f):
       
   581                 self.cexecuted[f] = {}
       
   582             self.cexecuted[f][lineno] = 1
       
   583         self.c = {}
       
   584 
       
   585     # morf_filename(morf).  Return the filename for a module or file.
       
   586 
       
   587     def morf_filename(self, morf):
       
   588         if hasattr(morf, '__file__'):
       
   589             f = morf.__file__
       
   590         else:
       
   591             f = morf
       
   592         return self.canonical_filename(f)
       
   593 
       
   594     # analyze_morf(morf).  Analyze the module or filename passed as
       
   595     # the argument.  If the source code can't be found, raise an error.
       
   596     # Otherwise, return a tuple of (1) the canonical filename of the
       
   597     # source code for the module, (2) a list of lines of statements
       
   598     # in the source code, (3) a list of lines of excluded statements,
       
   599     # and (4), a map of line numbers to multi-line line number ranges, for
       
   600     # statements that cross lines.
       
   601     def analyze_morf(self, morf):
       
   602         if self.analysis_cache.has_key(morf):
       
   603             return self.analysis_cache[morf]
       
   604         filename = self.morf_filename(morf)
       
   605         ext = os.path.splitext(filename)[1]
       
   606         source, sourcef = None, None
       
   607         if ext == '.pyc':
       
   608             if not os.path.exists(filename[:-1]):
       
   609                 source = self.get_zip_data(filename[:-1])
       
   610                 if not source:
       
   611                     raise CoverageException(
       
   612                         "No source for compiled code '%s'." % filename
       
   613                         )
       
   614             filename = filename[:-1]
       
   615         if not source:
       
   616             sourcef = open(filename, 'rU')
       
   617             source = sourcef.read()
       
   618         try:
       
   619             lines, excluded_lines, line_map = self.find_executable_statements(
       
   620                 source, exclude=self.exclude_re
       
   621                 )
       
   622         except SyntaxError, synerr:
       
   623             raise CoverageException(
       
   624                 "Couldn't parse '%s' as Python source: '%s' at line %d" %
       
   625                     (filename, synerr.msg, synerr.lineno)
       
   626                 )
       
   627         if sourcef:
       
   628             sourcef.close()
       
   629         result = filename, lines, excluded_lines, line_map
       
   630         self.analysis_cache[morf] = result
       
   631         return result
       
   632 
       
   633     def first_line_of_tree(self, tree):
       
   634         while True:
       
   635             if len(tree) == 3 and type(tree[2]) == type(1):
       
   636                 return tree[2]
       
   637             tree = tree[1]
       
   638 
       
   639     def last_line_of_tree(self, tree):
       
   640         while True:
       
   641             if len(tree) == 3 and type(tree[2]) == type(1):
       
   642                 return tree[2]
       
   643             tree = tree[-1]
       
   644 
       
   645     def find_docstring_pass_pair(self, tree, spots):
       
   646         for i in range(1, len(tree)):
       
   647             if (self.is_string_constant(tree[i]) and
       
   648                 self.is_pass_stmt(tree[i + 1])):
       
   649                 first_line = self.first_line_of_tree(tree[i])
       
   650                 last_line = self.last_line_of_tree(tree[i + 1])
       
   651                 self.record_multiline(spots, first_line, last_line)
       
   652 
       
   653     def is_string_constant(self, tree):
       
   654         try:
       
   655             return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.expr_stmt
       
   656         except:
       
   657             return False
       
   658 
       
   659     def is_pass_stmt(self, tree):
       
   660         try:
       
   661             return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.pass_stmt
       
   662         except:
       
   663             return False
       
   664 
       
   665     def record_multiline(self, spots, i, j):
       
   666         for l in range(i, j + 1):
       
   667             spots[l] = (i, j)
       
   668 
       
   669     def get_suite_spots(self, tree, spots):
       
   670         """ Analyze a parse tree to find suite introducers which span a number
       
   671             of lines.
       
   672         """
       
   673         for i in range(1, len(tree)):
       
   674             if type(tree[i]) == type(()):
       
   675                 if tree[i][0] == symbol.suite:
       
   676                     # Found a suite, look back for the colon and keyword.
       
   677                     lineno_colon = lineno_word = None
       
   678                     for j in range(i - 1, 0, -1):
       
   679                         if tree[j][0] == token.COLON:
       
   680                             # Colons are never executed themselves: we want the
       
   681                             # line number of the last token before the colon.
       
   682                             lineno_colon = self.last_line_of_tree(tree[j - 1])
       
   683                         elif tree[j][0] == token.NAME:
       
   684                             if tree[j][1] == 'elif':
       
   685                                 # Find the line number of the first
       
   686                                 # non-terminal after the keyword.
       
   687                                 t = tree[j + 1]
       
   688                                 while t and token.ISNONTERMINAL(t[0]):
       
   689                                     t = t[1]
       
   690                                 if t:
       
   691                                     lineno_word = t[2]
       
   692                             else:
       
   693                                 lineno_word = tree[j][2]
       
   694                             break
       
   695                         elif tree[j][0] == symbol.except_clause:
       
   696                             # "except" clauses look like:
       
   697                             # ('except_clause', ('NAME', 'except', lineno),...)
       
   698                             if tree[j][1][0] == token.NAME:
       
   699                                 lineno_word = tree[j][1][2]
       
   700                                 break
       
   701                     if lineno_colon and lineno_word:
       
   702                         # Found colon and keyword, mark all the lines
       
   703                         # between the two with the two line numbers.
       
   704                         self.record_multiline(spots, lineno_word, lineno_colon)
       
   705 
       
   706                     # "pass" statements are tricky: different versions
       
   707                     # of Python treat them differently, especially in
       
   708                     # the common case of a function with a doc string
       
   709                     # and a single pass statement.
       
   710                     self.find_docstring_pass_pair(tree[i], spots)
       
   711                 elif tree[i][0] == symbol.simple_stmt:
       
   712                     first_line = self.first_line_of_tree(tree[i])
       
   713                     last_line = self.last_line_of_tree(tree[i])
       
   714                     if first_line != last_line:
       
   715                         self.record_multiline(spots, first_line, last_line)
       
   716                 self.get_suite_spots(tree[i], spots)
       
   717 
       
   718     def find_executable_statements(self, text, exclude=None):
       
   719         # Find lines which match an exclusion pattern.
       
   720         excluded = {}
       
   721         suite_spots = {}
       
   722         if exclude:
       
   723             reExclude = re.compile(exclude)
       
   724             lines = text.split('\n')
       
   725             for i in range(len(lines)):
       
   726                 if reExclude.search(lines[i]):
       
   727                     excluded[i + 1] = 1
       
   728 
       
   729         # Parse the code and analyze the parse tree to find out which statements
       
   730         # are multiline, and where suites begin and end.
       
   731         import parser
       
   732         tree = parser.suite(text+'\n\n').totuple(1)
       
   733         self.get_suite_spots(tree, suite_spots)
       
   734         #print "Suite spots:", suite_spots
       
   735 
       
   736         # Use the compiler module to parse the text and find the executable
       
   737         # statements.  We add newlines to be impervious to final partial lines.
       
   738         statements = {}
       
   739         ast = compiler.parse(text+'\n\n')
       
   740         visitor = StatementFindingAstVisitor(statements, excluded, suite_spots)
       
   741         compiler.walk(ast, visitor, walker=visitor)
       
   742 
       
   743         lines = statements.keys()
       
   744         lines.sort()
       
   745         excluded_lines = excluded.keys()
       
   746         excluded_lines.sort()
       
   747         return lines, excluded_lines, suite_spots
       
   748 
       
   749     # format_lines(statements, lines).  Format a list of line numbers
       
   750     # for printing by coalescing groups of lines as long as the lines
       
   751     # represent consecutive statements.  This will coalesce even if
       
   752     # there are gaps between statements, so if statements =
       
   753     # [1,2,3,4,5,10,11,12,13,14] and lines = [1,2,5,10,11,13,14] then
       
   754     # format_lines will return "1-2, 5-11, 13-14".
       
   755 
       
   756     def format_lines(self, statements, lines):
       
   757         pairs = []
       
   758         i = 0
       
   759         j = 0
       
   760         start = None
       
   761         pairs = []
       
   762         while i < len(statements) and j < len(lines):
       
   763             if statements[i] == lines[j]:
       
   764                 if start == None:
       
   765                     start = lines[j]
       
   766                 end = lines[j]
       
   767                 j = j + 1
       
   768             elif start:
       
   769                 pairs.append((start, end))
       
   770                 start = None
       
   771             i = i + 1
       
   772         if start:
       
   773             pairs.append((start, end))
       
   774         def stringify(pair):
       
   775             start, end = pair
       
   776             if start == end:
       
   777                 return "%d" % start
       
   778             else:
       
   779                 return "%d-%d" % (start, end)
       
   780         ret = string.join(map(stringify, pairs), ", ")
       
   781         return ret
       
   782 
       
   783     # Backward compatibility with version 1.
       
   784     def analysis(self, morf):
       
   785         f, s, _, m, mf = self.analysis2(morf)
       
   786         return f, s, m, mf
       
   787 
       
   788     def analysis2(self, morf):
       
   789         filename, statements, excluded, line_map = self.analyze_morf(morf)
       
   790         self.canonicalize_filenames()
       
   791         if not self.cexecuted.has_key(filename):
       
   792             self.cexecuted[filename] = {}
       
   793         missing = []
       
   794         for line in statements:
       
   795             lines = line_map.get(line, [line, line])
       
   796             for l in range(lines[0], lines[1]+1):
       
   797                 if self.cexecuted[filename].has_key(l):
       
   798                     break
       
   799             else:
       
   800                 missing.append(line)
       
   801         return (filename, statements, excluded, missing,
       
   802                 self.format_lines(statements, missing))
       
   803 
       
   804     def relative_filename(self, filename):
       
   805         """ Convert filename to relative filename from self.relative_dir.
       
   806         """
       
   807         return filename.replace(self.relative_dir, "")
       
   808 
       
   809     def morf_name(self, morf):
       
   810         """ Return the name of morf as used in report.
       
   811         """
       
   812         if hasattr(morf, '__name__'):
       
   813             return morf.__name__
       
   814         else:
       
   815             return self.relative_filename(os.path.splitext(morf)[0])
       
   816 
       
   817     def filter_by_prefix(self, morfs, omit_prefixes):
       
   818         """ Return list of morfs where the morf name does not begin
       
   819             with any one of the omit_prefixes.
       
   820         """
       
   821         filtered_morfs = []
       
   822         for morf in morfs:
       
   823             for prefix in omit_prefixes:
       
   824                 if self.morf_name(morf).startswith(prefix):
       
   825                     break
       
   826             else:
       
   827                 filtered_morfs.append(morf)
       
   828 
       
   829         return filtered_morfs
       
   830 
       
   831     def morf_name_compare(self, x, y):
       
   832         return cmp(self.morf_name(x), self.morf_name(y))
       
   833 
       
   834     def report(self, morfs, show_missing=1, ignore_errors=0, file=None,
       
   835                omit_prefixes=[]):
       
   836         if not isinstance(morfs, types.ListType):
       
   837             morfs = [morfs]
       
   838         # On windows, the shell doesn't expand wildcards.  Do it here.
       
   839         globbed = []
       
   840         for morf in morfs:
       
   841             if isinstance(morf, strclass):
       
   842                 globbed.extend(glob.glob(morf))
       
   843             else:
       
   844                 globbed.append(morf)
       
   845         morfs = globbed
       
   846 
       
   847         morfs = self.filter_by_prefix(morfs, omit_prefixes)
       
   848         morfs.sort(self.morf_name_compare)
       
   849 
       
   850         max_name = max([5] + map(len, map(self.morf_name, morfs)))
       
   851         fmt_name = "%%- %ds  " % max_name
       
   852         fmt_err = fmt_name + "%s: %s"
       
   853         header = fmt_name % "Name" + " Stmts   Exec  Cover"
       
   854         fmt_coverage = fmt_name + "% 6d % 6d % 5d%%"
       
   855         if show_missing:
       
   856             header = header + "   Missing"
       
   857             fmt_coverage = fmt_coverage + "   %s"
       
   858         if not file:
       
   859             file = sys.stdout
       
   860         print >> file, header
       
   861         print >> file, "-" * len(header)
       
   862         total_statements = 0
       
   863         total_executed = 0
       
   864         for morf in morfs:
       
   865             name = self.morf_name(morf)
       
   866             try:
       
   867                 _, statements, _, missing, readable  = self.analysis2(morf)
       
   868                 n = len(statements)
       
   869                 m = n - len(missing)
       
   870                 if n > 0:
       
   871                     pc = 100.0 * m / n
       
   872                 else:
       
   873                     pc = 100.0
       
   874                 args = (name, n, m, pc)
       
   875                 if show_missing:
       
   876                     args = args + (readable,)
       
   877                 print >>file, fmt_coverage % args
       
   878                 total_statements = total_statements + n
       
   879                 total_executed = total_executed + m
       
   880             except KeyboardInterrupt:                       #pragma: no cover
       
   881                 raise
       
   882             except:
       
   883                 if not ignore_errors:
       
   884                     typ, msg = sys.exc_info()[:2]
       
   885                     print >>file, fmt_err % (name, typ, msg)
       
   886         if len(morfs) > 1:
       
   887             print >>file, "-" * len(header)
       
   888             if total_statements > 0:
       
   889                 pc = 100.0 * total_executed / total_statements
       
   890             else:
       
   891                 pc = 100.0
       
   892             args = ("TOTAL", total_statements, total_executed, pc)
       
   893             if show_missing:
       
   894                 args = args + ("",)
       
   895             print >>file, fmt_coverage % args
       
   896 
       
   897     # annotate(morfs, ignore_errors).
       
   898 
       
   899     blank_re = re.compile(r"\s*(#|$)")
       
   900     else_re = re.compile(r"\s*else\s*:\s*(#|$)")
       
   901 
       
   902     def annotate(self, morfs, directory=None, ignore_errors=0, omit_prefixes=[]):
       
   903         morfs = self.filter_by_prefix(morfs, omit_prefixes)
       
   904         for morf in morfs:
       
   905             try:
       
   906                 filename, statements, excluded, missing, _ = self.analysis2(morf)
       
   907                 self.annotate_file(filename, statements, excluded, missing,
       
   908                                    directory)
       
   909             except KeyboardInterrupt:
       
   910                 raise
       
   911             except:
       
   912                 if not ignore_errors:
       
   913                     raise
       
   914 
       
   915     def annotate_file(self, filename, statements, excluded, missing,
       
   916                       directory=None):
       
   917         source = open(filename, 'r')
       
   918         if directory:
       
   919             dest_file = os.path.join(directory,
       
   920                                      os.path.basename(filename)
       
   921                                      + ',cover')
       
   922         else:
       
   923             dest_file = filename + ',cover'
       
   924         dest = open(dest_file, 'w')
       
   925         lineno = 0
       
   926         i = 0
       
   927         j = 0
       
   928         covered = 1
       
   929         while 1:
       
   930             line = source.readline()
       
   931             if line == '':
       
   932                 break
       
   933             lineno = lineno + 1
       
   934             while i < len(statements) and statements[i] < lineno:
       
   935                 i = i + 1
       
   936             while j < len(missing) and missing[j] < lineno:
       
   937                 j = j + 1
       
   938             if i < len(statements) and statements[i] == lineno:
       
   939                 covered = j >= len(missing) or missing[j] > lineno
       
   940             if self.blank_re.match(line):
       
   941                 dest.write('  ')
       
   942             elif self.else_re.match(line):
       
   943                 # Special logic for lines containing only 'else:'.
       
   944                 # See [GDR 2001-12-04b, 3.2].
       
   945                 if i >= len(statements) and j >= len(missing):
       
   946                     dest.write('! ')
       
   947                 elif i >= len(statements) or j >= len(missing):
       
   948                     dest.write('> ')
       
   949                 elif statements[i] == missing[j]:
       
   950                     dest.write('! ')
       
   951                 else:
       
   952                     dest.write('> ')
       
   953             elif lineno in excluded:
       
   954                 dest.write('- ')
       
   955             elif covered:
       
   956                 dest.write('> ')
       
   957             else:
       
   958                 dest.write('! ')
       
   959             dest.write(line)
       
   960         source.close()
       
   961         dest.close()
       
   962 
       
   963 # Singleton object.
       
   964 the_coverage = coverage()
       
   965 
       
   966 # Module functions call methods in the singleton object.
       
   967 def use_cache(*args, **kw):
       
   968     return the_coverage.use_cache(*args, **kw)
       
   969 
       
   970 def start(*args, **kw):
       
   971     return the_coverage.start(*args, **kw)
       
   972 
       
   973 def stop(*args, **kw):
       
   974     return the_coverage.stop(*args, **kw)
       
   975 
       
   976 def erase(*args, **kw):
       
   977     return the_coverage.erase(*args, **kw)
       
   978 
       
   979 def begin_recursive(*args, **kw):
       
   980     return the_coverage.begin_recursive(*args, **kw)
       
   981 
       
   982 def end_recursive(*args, **kw):
       
   983     return the_coverage.end_recursive(*args, **kw)
       
   984 
       
   985 def exclude(*args, **kw):
       
   986     return the_coverage.exclude(*args, **kw)
       
   987 
       
   988 def analysis(*args, **kw):
       
   989     return the_coverage.analysis(*args, **kw)
       
   990 
       
   991 def analysis2(*args, **kw):
       
   992     return the_coverage.analysis2(*args, **kw)
       
   993 
       
   994 def report(*args, **kw):
       
   995     return the_coverage.report(*args, **kw)
       
   996 
       
   997 def annotate(*args, **kw):
       
   998     return the_coverage.annotate(*args, **kw)
       
   999 
       
  1000 def annotate_file(*args, **kw):
       
  1001     return the_coverage.annotate_file(*args, **kw)
       
  1002 
       
  1003 # Save coverage data when Python exits.  (The atexit module wasn't
       
  1004 # introduced until Python 2.0, so use sys.exitfunc when it's not
       
  1005 # available.)
       
  1006 try:
       
  1007     import atexit
       
  1008     atexit.register(the_coverage.save)
       
  1009 except ImportError:
       
  1010     sys.exitfunc = the_coverage.save
       
  1011 
       
  1012 def main():
       
  1013     the_coverage.command_line(sys.argv[1:])
       
  1014 
       
  1015 # Command-line interface.
       
  1016 if __name__ == '__main__':
       
  1017     main()
       
  1018 
       
  1019 
       
  1020 # A. REFERENCES
       
  1021 #
       
  1022 # [GDR 2001-12-04a] "Statement coverage for Python"; Gareth Rees;
       
  1023 # Ravenbrook Limited; 2001-12-04;
       
  1024 # <http://www.nedbatchelder.com/code/modules/rees-coverage.html>.
       
  1025 #
       
  1026 # [GDR 2001-12-04b] "Statement coverage for Python: design and
       
  1027 # analysis"; Gareth Rees; Ravenbrook Limited; 2001-12-04;
       
  1028 # <http://www.nedbatchelder.com/code/modules/rees-design.html>.
       
  1029 #
       
  1030 # [van Rossum 2001-07-20a] "Python Reference Manual (releae 2.1.1)";
       
  1031 # Guide van Rossum; 2001-07-20;
       
  1032 # <http://www.python.org/doc/2.1.1/ref/ref.html>.
       
  1033 #
       
  1034 # [van Rossum 2001-07-20b] "Python Library Reference"; Guido van Rossum;
       
  1035 # 2001-07-20; <http://www.python.org/doc/2.1.1/lib/lib.html>.
       
  1036 #
       
  1037 #
       
  1038 # B. DOCUMENT HISTORY
       
  1039 #
       
  1040 # 2001-12-04 GDR Created.
       
  1041 #
       
  1042 # 2001-12-06 GDR Added command-line interface and source code
       
  1043 # annotation.
       
  1044 #
       
  1045 # 2001-12-09 GDR Moved design and interface to separate documents.
       
  1046 #
       
  1047 # 2001-12-10 GDR Open cache file as binary on Windows.  Allow
       
  1048 # simultaneous -e and -x, or -a and -r.
       
  1049 #
       
  1050 # 2001-12-12 GDR Added command-line help.  Cache analysis so that it
       
  1051 # only needs to be done once when you specify -a and -r.
       
  1052 #
       
  1053 # 2001-12-13 GDR Improved speed while recording.  Portable between
       
  1054 # Python 1.5.2 and 2.1.1.
       
  1055 #
       
  1056 # 2002-01-03 GDR Module-level functions work correctly.
       
  1057 #
       
  1058 # 2002-01-07 GDR Update sys.path when running a file with the -x option,
       
  1059 # so that it matches the value the program would get if it were run on
       
  1060 # its own.
       
  1061 #
       
  1062 # 2004-12-12 NMB Significant code changes.
       
  1063 # - Finding executable statements has been rewritten so that docstrings and
       
  1064 #   other quirks of Python execution aren't mistakenly identified as missing
       
  1065 #   lines.
       
  1066 # - Lines can be excluded from consideration, even entire suites of lines.
       
  1067 # - The filesystem cache of covered lines can be disabled programmatically.
       
  1068 # - Modernized the code.
       
  1069 #
       
  1070 # 2004-12-14 NMB Minor tweaks.  Return 'analysis' to its original behavior
       
  1071 # and add 'analysis2'.  Add a global for 'annotate', and factor it, adding
       
  1072 # 'annotate_file'.
       
  1073 #
       
  1074 # 2004-12-31 NMB Allow for keyword arguments in the module global functions.
       
  1075 # Thanks, Allen.
       
  1076 #
       
  1077 # 2005-12-02 NMB Call threading.settrace so that all threads are measured.
       
  1078 # Thanks Martin Fuzzey. Add a file argument to report so that reports can be
       
  1079 # captured to a different destination.
       
  1080 #
       
  1081 # 2005-12-03 NMB coverage.py can now measure itself.
       
  1082 #
       
  1083 # 2005-12-04 NMB Adapted Greg Rogers' patch for using relative filenames,
       
  1084 # and sorting and omitting files to report on.
       
  1085 #
       
  1086 # 2006-07-23 NMB Applied Joseph Tate's patch for function decorators.
       
  1087 #
       
  1088 # 2006-08-21 NMB Applied Sigve Tjora and Mark van der Wal's fixes for argument
       
  1089 # handling.
       
  1090 #
       
  1091 # 2006-08-22 NMB Applied Geoff Bache's parallel mode patch.
       
  1092 #
       
  1093 # 2006-08-23 NMB Refactorings to improve testability.  Fixes to command-line
       
  1094 # logic for parallel mode and collect.
       
  1095 #
       
  1096 # 2006-08-25 NMB "#pragma: nocover" is excluded by default.
       
  1097 #
       
  1098 # 2006-09-10 NMB Properly ignore docstrings and other constant expressions that
       
  1099 # appear in the middle of a function, a problem reported by Tim Leslie.
       
  1100 # Minor changes to avoid lint warnings.
       
  1101 #
       
  1102 # 2006-09-17 NMB coverage.erase() shouldn't clobber the exclude regex.
       
  1103 # Change how parallel mode is invoked, and fix erase() so that it erases the
       
  1104 # cache when called programmatically.
       
  1105 #
       
  1106 # 2007-07-21 NMB In reports, ignore code executed from strings, since we can't
       
  1107 # do anything useful with it anyway.
       
  1108 # Better file handling on Linux, thanks Guillaume Chazarain.
       
  1109 # Better shell support on Windows, thanks Noel O'Boyle.
       
  1110 # Python 2.2 support maintained, thanks Catherine Proulx.
       
  1111 #
       
  1112 # 2007-07-22 NMB Python 2.5 now fully supported. The method of dealing with
       
  1113 # multi-line statements is now less sensitive to the exact line that Python
       
  1114 # reports during execution. Pass statements are handled specially so that their
       
  1115 # disappearance during execution won't throw off the measurement.
       
  1116 #
       
  1117 # 2007-07-23 NMB Now Python 2.5 is *really* fully supported: the body of the
       
  1118 # new with statement is counted as executable.
       
  1119 #
       
  1120 # 2007-07-29 NMB Better packaging.
       
  1121 #
       
  1122 # 2007-09-30 NMB Don't try to predict whether a file is Python source based on
       
  1123 # the extension. Extensionless files are often Pythons scripts. Instead, simply
       
  1124 # parse the file and catch the syntax errors.  Hat tip to Ben Finney.
       
  1125 #
       
  1126 # 2008-05-25 NMB Open files in rU mode to avoid line ending craziness.
       
  1127 # Thanks, Edward Loper.
       
  1128 #
       
  1129 # 2008-09-14 NMB Add support for finding source files in eggs.
       
  1130 # Don't check for morf's being instances of ModuleType, instead use duck typing
       
  1131 # so that pseudo-modules can participate. Thanks, Imri Goldberg.
       
  1132 # Use os.realpath as part of the fixing of filenames so that symlinks won't
       
  1133 # confuse things.  Thanks, Patrick Mezard.
       
  1134 #
       
  1135 #
       
  1136 # C. COPYRIGHT AND LICENCE
       
  1137 #
       
  1138 # Copyright 2001 Gareth Rees.  All rights reserved.
       
  1139 # Copyright 2004-2008 Ned Batchelder.  All rights reserved.
       
  1140 #
       
  1141 # Redistribution and use in source and binary forms, with or without
       
  1142 # modification, are permitted provided that the following conditions are
       
  1143 # met:
       
  1144 #
       
  1145 # 1. Redistributions of source code must retain the above copyright
       
  1146 #    notice, this list of conditions and the following disclaimer.
       
  1147 #
       
  1148 # 2. Redistributions in binary form must reproduce the above copyright
       
  1149 #    notice, this list of conditions and the following disclaimer in the
       
  1150 #    documentation and/or other materials provided with the
       
  1151 #    distribution.
       
  1152 #
       
  1153 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
  1154 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
  1155 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
  1156 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
       
  1157 # HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
       
  1158 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
       
  1159 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
       
  1160 # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
       
  1161 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
       
  1162 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
       
  1163 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
       
  1164 # DAMAGE.
       
  1165 #
       
  1166 # $Id: coverage.py 96 2008-09-14 18:34:13Z nedbat $