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