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