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