1ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#!/usr/bin/env python
2ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
3ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# portions copyright 2001, Autonomous Zones Industries, Inc., all rights...
4ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# err...  reserved and offered to the public under the terms of the
5ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Python 2.2 license.
6ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Author: Zooko O'Whielacronx
7ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# http://zooko.com/
8ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# mailto:zooko@zooko.com
9ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
10ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Copyright 2000, Mojam Media, Inc., all rights reserved.
11ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Author: Skip Montanaro
12ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
13ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Copyright 1999, Bioreason, Inc., all rights reserved.
14ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Author: Andrew Dalke
15ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
16ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Copyright 1995-1997, Automatrix, Inc., all rights reserved.
17ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Author: Skip Montanaro
18ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
19ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Copyright 1991-1995, Stichting Mathematisch Centrum, all rights reserved.
20ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
21ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
22ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Permission to use, copy, modify, and distribute this Python software and
23ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# its associated documentation for any purpose without fee is hereby
24ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# granted, provided that the above copyright notice appears in all copies,
25ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# and that both that copyright notice and this permission notice appear in
26ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# supporting documentation, and that the name of neither Automatrix,
27ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Bioreason or Mojam Media be used in advertising or publicity pertaining to
28ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# distribution of the software without specific, written prior permission.
29ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
30ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh"""program/module to trace Python program or function execution
31ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
32ffab958fd8d42ed7227d83007350e61555a1fa36Andrew HsiehSample use, command line:
33ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh  trace.py -c -f counts --ignore-dir '$prefix' spam.py eggs
34ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh  trace.py -t --ignore-dir '$prefix' spam.py eggs
35ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh  trace.py --trackcalls spam.py eggs
36ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
37ffab958fd8d42ed7227d83007350e61555a1fa36Andrew HsiehSample use, programmatically
38ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh  import sys
39ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
40ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh  # create a Trace object, telling it what to ignore, and whether to
41ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh  # do tracing or line-counting or both.
42ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh  tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,], trace=0,
43ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    count=1)
44ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh  # run the new command using the given tracer
45ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh  tracer.run('main()')
46ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh  # make a report, placing output in /tmp
47ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh  r = tracer.results()
48ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh  r.write_results(show_missing=True, coverdir="/tmp")
49ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh"""
50ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
51ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport linecache
52ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport os
53ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport re
54ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport sys
55ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport time
56ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport token
57ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport tokenize
58ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport inspect
59ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport gc
60ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport dis
61ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehtry:
62ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    import cPickle
63ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    pickle = cPickle
64ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehexcept ImportError:
65ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    import pickle
66ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
67ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehtry:
68ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    import threading
69ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehexcept ImportError:
70ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    _settrace = sys.settrace
71ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
72ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _unsettrace():
73ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.settrace(None)
74ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehelse:
75ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _settrace(func):
76ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        threading.settrace(func)
77ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.settrace(func)
78ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
79ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _unsettrace():
80ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.settrace(None)
81ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        threading.settrace(None)
82ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
83ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef usage(outfile):
84ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    outfile.write("""Usage: %s [OPTIONS] <file> [ARGS]
85ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
86ffab958fd8d42ed7227d83007350e61555a1fa36Andrew HsiehMeta-options:
87ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh--help                Display this help then exit.
88ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh--version             Output version information then exit.
89ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
90ffab958fd8d42ed7227d83007350e61555a1fa36Andrew HsiehOtherwise, exactly one of the following three options must be given:
91ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh-t, --trace           Print each line to sys.stdout before it is executed.
92ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh-c, --count           Count the number of times each line is executed
93ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      and write the counts to <module>.cover for each
94ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      module executed, in the module's directory.
95ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      See also `--coverdir', `--file', `--no-report' below.
96ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh-l, --listfuncs       Keep track of which functions are executed at least
97ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      once and write the results to sys.stdout after the
98ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      program exits.
99ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh-T, --trackcalls      Keep track of caller/called pairs and write the
100ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      results to sys.stdout after the program exits.
101ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh-r, --report          Generate a report from a counts file; do not execute
102ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      any code.  `--file' must specify the results file to
103ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      read, which must have been created in a previous run
104ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      with `--count --file=FILE'.
105ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
106ffab958fd8d42ed7227d83007350e61555a1fa36Andrew HsiehModifiers:
107ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh-f, --file=<file>     File to accumulate counts over several runs.
108ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh-R, --no-report       Do not generate the coverage report files.
109ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      Useful if you want to accumulate over several runs.
110ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh-C, --coverdir=<dir>  Directory where the report files.  The coverage
111ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      report for <package>.<module> is written to file
112ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      <dir>/<package>/<module>.cover.
113ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh-m, --missing         Annotate executable lines that were not executed
114ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      with '>>>>>> '.
115ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh-s, --summary         Write a brief summary on stdout for each file.
116ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      (Can only be used with --count or --report.)
117ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh-g, --timing          Prefix each line with the time since the program started.
118ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      Only used while tracing.
119ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
120ffab958fd8d42ed7227d83007350e61555a1fa36Andrew HsiehFilters, may be repeated multiple times:
121ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh--ignore-module=<mod> Ignore the given module(s) and its submodules
122ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      (if it is a package).  Accepts comma separated
123ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      list of module names
124ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh--ignore-dir=<dir>    Ignore files in the given directory (multiple
125ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      directories can be joined by os.pathsep).
126ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh""" % sys.argv[0])
127ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
128ffab958fd8d42ed7227d83007350e61555a1fa36Andrew HsiehPRAGMA_NOCOVER = "#pragma NO COVER"
129ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
130ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Simple rx to find lines with no code.
131ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehrx_blank = re.compile(r'^\s*(#.*)?$')
132ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
133ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehclass Ignore:
134ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def __init__(self, modules = None, dirs = None):
135ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self._mods = modules or []
136ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self._dirs = dirs or []
137ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
138ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self._dirs = map(os.path.normpath, self._dirs)
139ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self._ignore = { '<string>': 1 }
140ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
141ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def names(self, filename, modulename):
142ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if modulename in self._ignore:
143ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return self._ignore[modulename]
144ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
145ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # haven't seen this one before, so see if the module name is
146ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # on the ignore list.  Need to take some care since ignoring
147ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # "cmp" musn't mean ignoring "cmpcache" but ignoring
148ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # "Spam" must also mean ignoring "Spam.Eggs".
149ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        for mod in self._mods:
150ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if mod == modulename:  # Identical names, so ignore
151ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self._ignore[modulename] = 1
152ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                return 1
153ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # check if the module is a proper submodule of something on
154ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # the ignore list
155ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            n = len(mod)
156ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # (will not overflow since if the first n characters are the
157ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # same and the name has not already occurred, then the size
158ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # of "name" is greater than that of "mod")
159ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if mod == modulename[:n] and modulename[n] == '.':
160ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self._ignore[modulename] = 1
161ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                return 1
162ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
163ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Now check that __file__ isn't in one of the directories
164ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if filename is None:
165ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # must be a built-in, so we must ignore
166ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self._ignore[modulename] = 1
167ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return 1
168ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
169ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Ignore a file when it contains one of the ignorable paths
170ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        for d in self._dirs:
171ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # The '+ os.sep' is to ensure that d is a parent directory,
172ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # as compared to cases like:
173ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            #  d = "/usr/local"
174ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            #  filename = "/usr/local.py"
175ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # or
176ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            #  d = "/usr/local.py"
177ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            #  filename = "/usr/local.py"
178ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if filename.startswith(d + os.sep):
179ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self._ignore[modulename] = 1
180ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                return 1
181ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
182ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Tried the different ways, so we don't ignore this module
183ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self._ignore[modulename] = 0
184ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return 0
185ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
186ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef modname(path):
187ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    """Return a plausible module name for the patch."""
188ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
189ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    base = os.path.basename(path)
190ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    filename, ext = os.path.splitext(base)
191ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    return filename
192ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
193ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef fullmodname(path):
194ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    """Return a plausible module name for the path."""
195ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
196ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # If the file 'path' is part of a package, then the filename isn't
197ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # enough to uniquely identify it.  Try to do the right thing by
198ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # looking in sys.path for the longest matching prefix.  We'll
199ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # assume that the rest is the package name.
200ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
201ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    comparepath = os.path.normcase(path)
202ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    longest = ""
203ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    for dir in sys.path:
204ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        dir = os.path.normcase(dir)
205ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if comparepath.startswith(dir) and comparepath[len(dir)] == os.sep:
206ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if len(dir) > len(longest):
207ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                longest = dir
208ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
209ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if longest:
210ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        base = path[len(longest) + 1:]
211ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    else:
212ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        base = path
213ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # the drive letter is never part of the module name
214ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    drive, base = os.path.splitdrive(base)
215ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    base = base.replace(os.sep, ".")
216ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if os.altsep:
217ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        base = base.replace(os.altsep, ".")
218ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    filename, ext = os.path.splitext(base)
219ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    return filename.lstrip(".")
220ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
221ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehclass CoverageResults:
222ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def __init__(self, counts=None, calledfuncs=None, infile=None,
223ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                 callers=None, outfile=None):
224ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.counts = counts
225ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.counts is None:
226ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.counts = {}
227ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.counter = self.counts.copy() # map (filename, lineno) to count
228ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.calledfuncs = calledfuncs
229ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.calledfuncs is None:
230ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.calledfuncs = {}
231ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.calledfuncs = self.calledfuncs.copy()
232ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.callers = callers
233ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.callers is None:
234ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.callers = {}
235ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.callers = self.callers.copy()
236ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.infile = infile
237ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.outfile = outfile
238ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.infile:
239ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # Try to merge existing counts file.
240ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            try:
241ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                counts, calledfuncs, callers = \
242ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                        pickle.load(open(self.infile, 'rb'))
243ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.update(self.__class__(counts, calledfuncs, callers))
244ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            except (IOError, EOFError, ValueError), err:
245ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                print >> sys.stderr, ("Skipping counts file %r: %s"
246ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                      % (self.infile, err))
247ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
248ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def update(self, other):
249ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        """Merge in the data from another CoverageResults"""
250ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        counts = self.counts
251ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        calledfuncs = self.calledfuncs
252ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        callers = self.callers
253ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        other_counts = other.counts
254ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        other_calledfuncs = other.calledfuncs
255ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        other_callers = other.callers
256ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
257ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        for key in other_counts.keys():
258ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            counts[key] = counts.get(key, 0) + other_counts[key]
259ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
260ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        for key in other_calledfuncs.keys():
261ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            calledfuncs[key] = 1
262ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
263ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        for key in other_callers.keys():
264ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            callers[key] = 1
265ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
266ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def write_results(self, show_missing=True, summary=False, coverdir=None):
267ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        """
268ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        @param coverdir
269ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        """
270ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.calledfuncs:
271ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            print
272ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            print "functions called:"
273ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            calls = self.calledfuncs.keys()
274ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            calls.sort()
275ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            for filename, modulename, funcname in calls:
276ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                print ("filename: %s, modulename: %s, funcname: %s"
277ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                       % (filename, modulename, funcname))
278ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
279ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.callers:
280ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            print
281ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            print "calling relationships:"
282ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            calls = self.callers.keys()
283ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            calls.sort()
284ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            lastfile = lastcfile = ""
285ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            for ((pfile, pmod, pfunc), (cfile, cmod, cfunc)) in calls:
286ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                if pfile != lastfile:
287ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    print
288ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    print "***", pfile, "***"
289ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    lastfile = pfile
290ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    lastcfile = ""
291ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                if cfile != pfile and lastcfile != cfile:
292ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    print "  -->", cfile
293ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    lastcfile = cfile
294ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                print "    %s.%s -> %s.%s" % (pmod, pfunc, cmod, cfunc)
295ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
296ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # turn the counts data ("(filename, lineno) = count") into something
297ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # accessible on a per-file basis
298ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        per_file = {}
299ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        for filename, lineno in self.counts.keys():
300ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            lines_hit = per_file[filename] = per_file.get(filename, {})
301ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            lines_hit[lineno] = self.counts[(filename, lineno)]
302ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
303ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # accumulate summary info, if needed
304ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sums = {}
305ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
306ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        for filename, count in per_file.iteritems():
307ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # skip some "files" we don't care about...
308ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if filename == "<string>":
309ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                continue
310ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if filename.startswith("<doctest "):
311ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                continue
312ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
313ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if filename.endswith((".pyc", ".pyo")):
314ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                filename = filename[:-1]
315ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
316ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if coverdir is None:
317ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                dir = os.path.dirname(os.path.abspath(filename))
318ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                modulename = modname(filename)
319ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            else:
320ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                dir = coverdir
321ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                if not os.path.exists(dir):
322ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    os.makedirs(dir)
323ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                modulename = fullmodname(filename)
324ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
325ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # If desired, get a list of the line numbers which represent
326ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # executable content (returned as a dict for better lookup speed)
327ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if show_missing:
328ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                lnotab = find_executable_linenos(filename)
329ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            else:
330ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                lnotab = {}
331ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
332ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            source = linecache.getlines(filename)
333ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            coverpath = os.path.join(dir, modulename + ".cover")
334ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            n_hits, n_lines = self.write_results_file(coverpath, source,
335ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                                      lnotab, count)
336ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
337ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if summary and n_lines:
338ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                percent = 100 * n_hits // n_lines
339ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                sums[modulename] = n_lines, percent, modulename, filename
340ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
341ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if summary and sums:
342ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            mods = sums.keys()
343ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            mods.sort()
344ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            print "lines   cov%   module   (path)"
345ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            for m in mods:
346ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                n_lines, percent, modulename, filename = sums[m]
347ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                print "%5d   %3d%%   %s   (%s)" % sums[m]
348ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
349ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.outfile:
350ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # try and store counts and module info into self.outfile
351ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            try:
352ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                pickle.dump((self.counts, self.calledfuncs, self.callers),
353ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                            open(self.outfile, 'wb'), 1)
354ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            except IOError, err:
355ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                print >> sys.stderr, "Can't save counts files because %s" % err
356ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
357ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def write_results_file(self, path, lines, lnotab, lines_hit):
358ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        """Return a coverage results file in path."""
359ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
360ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        try:
361ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            outfile = open(path, "w")
362ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        except IOError, err:
363ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            print >> sys.stderr, ("trace: Could not open %r for writing: %s"
364ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                  "- skipping" % (path, err))
365ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return 0, 0
366ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
367ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        n_lines = 0
368ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        n_hits = 0
369ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        for i, line in enumerate(lines):
370ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            lineno = i + 1
371ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # do the blank/comment match to try to mark more lines
372ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # (help the reader find stuff that hasn't been covered)
373ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if lineno in lines_hit:
374ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                outfile.write("%5d: " % lines_hit[lineno])
375ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                n_hits += 1
376ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                n_lines += 1
377ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            elif rx_blank.match(line):
378ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                outfile.write("       ")
379ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            else:
380ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # lines preceded by no marks weren't hit
381ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # Highlight them if so indicated, unless the line contains
382ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # #pragma: NO COVER
383ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                if lineno in lnotab and not PRAGMA_NOCOVER in lines[i]:
384ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    outfile.write(">>>>>> ")
385ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    n_lines += 1
386ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                else:
387ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    outfile.write("       ")
388ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            outfile.write(lines[i].expandtabs(8))
389ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        outfile.close()
390ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
391ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return n_hits, n_lines
392ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
393ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef find_lines_from_code(code, strs):
394ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    """Return dict where keys are lines in the line number table."""
395ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    linenos = {}
396ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
397ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    for _, lineno in dis.findlinestarts(code):
398ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if lineno not in strs:
399ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            linenos[lineno] = 1
400ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
401ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    return linenos
402ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
403ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef find_lines(code, strs):
404ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    """Return lineno dict for all code objects reachable from code."""
405ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # get all of the lineno information from the code of this scope level
406ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    linenos = find_lines_from_code(code, strs)
407ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
408ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # and check the constants for references to other code objects
409ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    for c in code.co_consts:
410ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if inspect.iscode(c):
411ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # find another code object, so recurse into it
412ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            linenos.update(find_lines(c, strs))
413ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    return linenos
414ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
415ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef find_strings(filename):
416ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    """Return a dict of possible docstring positions.
417ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
418ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    The dict maps line numbers to strings.  There is an entry for
419ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    line that contains only a string or a part of a triple-quoted
420ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    string.
421ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    """
422ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    d = {}
423ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # If the first token is a string, then it's the module docstring.
424ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # Add this special case so that the test in the loop passes.
425ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    prev_ttype = token.INDENT
426ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    f = open(filename)
427ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    for ttype, tstr, start, end, line in tokenize.generate_tokens(f.readline):
428ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if ttype == token.STRING:
429ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if prev_ttype == token.INDENT:
430ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                sline, scol = start
431ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                eline, ecol = end
432ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                for i in range(sline, eline + 1):
433ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    d[i] = 1
434ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        prev_ttype = ttype
435ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    f.close()
436ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    return d
437ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
438ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef find_executable_linenos(filename):
439ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    """Return dict where keys are line numbers in the line number table."""
440ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    try:
441ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        prog = open(filename, "rU").read()
442ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    except IOError, err:
443ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        print >> sys.stderr, ("Not printing coverage data for %r: %s"
444ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                              % (filename, err))
445ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return {}
446ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    code = compile(prog, filename, "exec")
447ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    strs = find_strings(filename)
448ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    return find_lines(code, strs)
449ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
450ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehclass Trace:
451ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def __init__(self, count=1, trace=1, countfuncs=0, countcallers=0,
452ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                 ignoremods=(), ignoredirs=(), infile=None, outfile=None,
453ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                 timing=False):
454ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        """
455ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        @param count true iff it should count number of times each
456ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     line is executed
457ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        @param trace true iff it should print out each line that is
458ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     being counted
459ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        @param countfuncs true iff it should just output a list of
460ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     (filename, modulename, funcname,) for functions
461ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     that were called at least once;  This overrides
462ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     `count' and `trace'
463ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        @param ignoremods a list of the names of modules to ignore
464ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        @param ignoredirs a list of the names of directories to ignore
465ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     all of the (recursive) contents of
466ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        @param infile file from which to read stored counts to be
467ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     added into the results
468ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        @param outfile file in which to write the results
469ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        @param timing true iff timing information be displayed
470ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        """
471ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.infile = infile
472ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.outfile = outfile
473ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.ignore = Ignore(ignoremods, ignoredirs)
474ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.counts = {}   # keys are (filename, linenumber)
475ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.blabbed = {} # for debugging
476ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.pathtobasename = {} # for memoizing os.path.basename
477ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.donothing = 0
478ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.trace = trace
479ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self._calledfuncs = {}
480ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self._callers = {}
481ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self._caller_cache = {}
482ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.start_time = None
483ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if timing:
484ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.start_time = time.time()
485ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if countcallers:
486ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.globaltrace = self.globaltrace_trackcallers
487ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        elif countfuncs:
488ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.globaltrace = self.globaltrace_countfuncs
489ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        elif trace and count:
490ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.globaltrace = self.globaltrace_lt
491ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.localtrace = self.localtrace_trace_and_count
492ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        elif trace:
493ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.globaltrace = self.globaltrace_lt
494ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.localtrace = self.localtrace_trace
495ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        elif count:
496ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.globaltrace = self.globaltrace_lt
497ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.localtrace = self.localtrace_count
498ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
499ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # Ahem -- do nothing?  Okay.
500ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.donothing = 1
501ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
502ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def run(self, cmd):
503ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        import __main__
504ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        dict = __main__.__dict__
505ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.runctx(cmd, dict, dict)
506ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
507ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def runctx(self, cmd, globals=None, locals=None):
508ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if globals is None: globals = {}
509ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if locals is None: locals = {}
510ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not self.donothing:
511ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            _settrace(self.globaltrace)
512ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        try:
513ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            exec cmd in globals, locals
514ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        finally:
515ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if not self.donothing:
516ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                _unsettrace()
517ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
518ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def runfunc(self, func, *args, **kw):
519ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        result = None
520ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not self.donothing:
521ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            sys.settrace(self.globaltrace)
522ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        try:
523ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            result = func(*args, **kw)
524ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        finally:
525ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if not self.donothing:
526ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                sys.settrace(None)
527ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return result
528ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
529ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def file_module_function_of(self, frame):
530ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        code = frame.f_code
531ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        filename = code.co_filename
532ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if filename:
533ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            modulename = modname(filename)
534ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
535ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            modulename = None
536ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
537ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        funcname = code.co_name
538ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        clsname = None
539ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if code in self._caller_cache:
540ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if self._caller_cache[code] is not None:
541ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                clsname = self._caller_cache[code]
542ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
543ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self._caller_cache[code] = None
544ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            ## use of gc.get_referrers() was suggested by Michael Hudson
545ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # all functions which refer to this code object
546ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            funcs = [f for f in gc.get_referrers(code)
547ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                         if inspect.isfunction(f)]
548ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # require len(func) == 1 to avoid ambiguity caused by calls to
549ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # new.function(): "In the face of ambiguity, refuse the
550ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # temptation to guess."
551ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if len(funcs) == 1:
552ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                dicts = [d for d in gc.get_referrers(funcs[0])
553ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                             if isinstance(d, dict)]
554ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                if len(dicts) == 1:
555ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    classes = [c for c in gc.get_referrers(dicts[0])
556ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                   if hasattr(c, "__bases__")]
557ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    if len(classes) == 1:
558ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                        # ditto for new.classobj()
559ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                        clsname = classes[0].__name__
560ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                        # cache the result - assumption is that new.* is
561ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                        # not called later to disturb this relationship
562ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                        # _caller_cache could be flushed if functions in
563ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                        # the new module get called.
564ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                        self._caller_cache[code] = clsname
565ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if clsname is not None:
566ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            funcname = "%s.%s" % (clsname, funcname)
567ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
568ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return filename, modulename, funcname
569ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
570ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def globaltrace_trackcallers(self, frame, why, arg):
571ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        """Handler for call events.
572ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
573ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        Adds information about who called who to the self._callers dict.
574ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        """
575ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if why == 'call':
576ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # XXX Should do a better job of identifying methods
577ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            this_func = self.file_module_function_of(frame)
578ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            parent_func = self.file_module_function_of(frame.f_back)
579ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self._callers[(parent_func, this_func)] = 1
580ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
581ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def globaltrace_countfuncs(self, frame, why, arg):
582ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        """Handler for call events.
583ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
584ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        Adds (filename, modulename, funcname) to the self._calledfuncs dict.
585ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        """
586ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if why == 'call':
587ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            this_func = self.file_module_function_of(frame)
588ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self._calledfuncs[this_func] = 1
589ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
590ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def globaltrace_lt(self, frame, why, arg):
591ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        """Handler for call events.
592ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
593ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        If the code block being entered is to be ignored, returns `None',
594ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else returns self.localtrace.
595ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        """
596ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if why == 'call':
597ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            code = frame.f_code
598ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            filename = frame.f_globals.get('__file__', None)
599ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if filename:
600ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # XXX modname() doesn't work right for packages, so
601ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # the ignore support won't work right for packages
602ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                modulename = modname(filename)
603ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                if modulename is not None:
604ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    ignore_it = self.ignore.names(filename, modulename)
605ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    if not ignore_it:
606ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                        if self.trace:
607ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                            print (" --- modulename: %s, funcname: %s"
608ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                   % (modulename, code.co_name))
609ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                        return self.localtrace
610ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            else:
611ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                return None
612ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
613ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def localtrace_trace_and_count(self, frame, why, arg):
614ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if why == "line":
615ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # record the file name and line number of every trace
616ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            filename = frame.f_code.co_filename
617ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            lineno = frame.f_lineno
618ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            key = filename, lineno
619ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.counts[key] = self.counts.get(key, 0) + 1
620ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
621ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if self.start_time:
622ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                print '%.2f' % (time.time() - self.start_time),
623ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            bname = os.path.basename(filename)
624ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            print "%s(%d): %s" % (bname, lineno,
625ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                  linecache.getline(filename, lineno)),
626ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self.localtrace
627ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
628ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def localtrace_trace(self, frame, why, arg):
629ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if why == "line":
630ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # record the file name and line number of every trace
631ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            filename = frame.f_code.co_filename
632ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            lineno = frame.f_lineno
633ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
634ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if self.start_time:
635ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                print '%.2f' % (time.time() - self.start_time),
636ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            bname = os.path.basename(filename)
637ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            print "%s(%d): %s" % (bname, lineno,
638ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                  linecache.getline(filename, lineno)),
639ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self.localtrace
640ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
641ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def localtrace_count(self, frame, why, arg):
642ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if why == "line":
643ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            filename = frame.f_code.co_filename
644ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            lineno = frame.f_lineno
645ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            key = filename, lineno
646ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.counts[key] = self.counts.get(key, 0) + 1
647ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self.localtrace
648ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
649ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def results(self):
650ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return CoverageResults(self.counts, infile=self.infile,
651ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                               outfile=self.outfile,
652ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                               calledfuncs=self._calledfuncs,
653ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                               callers=self._callers)
654ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
655ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef _err_exit(msg):
656ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
657ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    sys.exit(1)
658ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
659ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef main(argv=None):
660ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    import getopt
661ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
662ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if argv is None:
663ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        argv = sys.argv
664ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    try:
665ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        opts, prog_argv = getopt.getopt(argv[1:], "tcrRf:d:msC:lTg",
666ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                        ["help", "version", "trace", "count",
667ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                         "report", "no-report", "summary",
668ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                         "file=", "missing",
669ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                         "ignore-module=", "ignore-dir=",
670ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                         "coverdir=", "listfuncs",
671ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                         "trackcalls", "timing"])
672ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
673ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    except getopt.error, msg:
674ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
675ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.stderr.write("Try `%s --help' for more information\n"
676ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                         % sys.argv[0])
677ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.exit(1)
678ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
679ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    trace = 0
680ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    count = 0
681ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    report = 0
682ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    no_report = 0
683ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    counts_file = None
684ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    missing = 0
685ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    ignore_modules = []
686ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    ignore_dirs = []
687ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    coverdir = None
688ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    summary = 0
689ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    listfuncs = False
690ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    countcallers = False
691ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    timing = False
692ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
693ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    for opt, val in opts:
694ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "--help":
695ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            usage(sys.stdout)
696ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            sys.exit(0)
697ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
698ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "--version":
699ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            sys.stdout.write("trace 2.0\n")
700ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            sys.exit(0)
701ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
702ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "-T" or opt == "--trackcalls":
703ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            countcallers = True
704ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            continue
705ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
706ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "-l" or opt == "--listfuncs":
707ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            listfuncs = True
708ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            continue
709ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
710ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "-g" or opt == "--timing":
711ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            timing = True
712ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            continue
713ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
714ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "-t" or opt == "--trace":
715ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            trace = 1
716ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            continue
717ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
718ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "-c" or opt == "--count":
719ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            count = 1
720ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            continue
721ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
722ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "-r" or opt == "--report":
723ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            report = 1
724ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            continue
725ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
726ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "-R" or opt == "--no-report":
727ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            no_report = 1
728ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            continue
729ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
730ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "-f" or opt == "--file":
731ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            counts_file = val
732ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            continue
733ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
734ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "-m" or opt == "--missing":
735ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            missing = 1
736ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            continue
737ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
738ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "-C" or opt == "--coverdir":
739ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            coverdir = val
740ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            continue
741ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
742ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "-s" or opt == "--summary":
743ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            summary = 1
744ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            continue
745ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
746ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "--ignore-module":
747ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            for mod in val.split(","):
748ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                ignore_modules.append(mod.strip())
749ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            continue
750ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
751ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if opt == "--ignore-dir":
752ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            for s in val.split(os.pathsep):
753ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                s = os.path.expandvars(s)
754ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # should I also call expanduser? (after all, could use $HOME)
755ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
756ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                s = s.replace("$prefix",
757ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                              os.path.join(sys.prefix, "lib",
758ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                           "python" + sys.version[:3]))
759ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                s = s.replace("$exec_prefix",
760ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                              os.path.join(sys.exec_prefix, "lib",
761ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                           "python" + sys.version[:3]))
762ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                s = os.path.normpath(s)
763ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                ignore_dirs.append(s)
764ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            continue
765ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
766ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        assert 0, "Should never get here"
767ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
768ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if listfuncs and (count or trace):
769ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        _err_exit("cannot specify both --listfuncs and (--trace or --count)")
770ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
771ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if not (count or trace or report or listfuncs or countcallers):
772ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        _err_exit("must specify one of --trace, --count, --report, "
773ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                  "--listfuncs, or --trackcalls")
774ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
775ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if report and no_report:
776ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        _err_exit("cannot specify both --report and --no-report")
777ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
778ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if report and not counts_file:
779ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        _err_exit("--report requires a --file")
780ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
781ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if no_report and len(prog_argv) == 0:
782ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        _err_exit("missing name of file to run")
783ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
784ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # everything is ready
785ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if report:
786ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        results = CoverageResults(infile=counts_file, outfile=counts_file)
787ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        results.write_results(missing, summary=summary, coverdir=coverdir)
788ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    else:
789ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.argv = prog_argv
790ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        progname = prog_argv[0]
791ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.path[0] = os.path.split(progname)[0]
792ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
793ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        t = Trace(count, trace, countfuncs=listfuncs,
794ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                  countcallers=countcallers, ignoremods=ignore_modules,
795ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                  ignoredirs=ignore_dirs, infile=counts_file,
796ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                  outfile=counts_file, timing=timing)
797ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        try:
798ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            with open(progname) as fp:
799ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                code = compile(fp.read(), progname, 'exec')
800ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # try to emulate __main__ namespace as much as possible
801ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            globs = {
802ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                '__file__': progname,
803ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                '__name__': '__main__',
804ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                '__package__': None,
805ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                '__cached__': None,
806ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            }
807ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            t.runctx(code, globs, globs)
808ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        except IOError, err:
809ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            _err_exit("Cannot run file %r because: %s" % (sys.argv[0], err))
810ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        except SystemExit:
811ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            pass
812ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
813ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        results = t.results()
814ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
815ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not no_report:
816ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            results.write_results(missing, summary=summary, coverdir=coverdir)
817ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
818ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehif __name__=='__main__':
819ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    main()
820