Statement Coverage : Programming « Utility « Python






Statement Coverage

from Tkinter import *

#!/usr/bin/env python

# $Id: trace.py,v 1.9 1999/08/20 20:44:24 skip Exp $
#
# Copyright 1995-1997, Automatrix, Inc., all rights reserved.
# Author: Skip Montanaro
#
# Copyright 1991-1995, Stichting Mathematisch Centrum, all rights reserved.
#
# Permission to use, copy, modify, and distribute this Python software and
# its associated documentation for any purpose without fee is hereby
# granted, provided that the above copyright notice appears in all copies,
# and that both that copyright notice and this permission notice appear in
# supporting documentation, and that the name of Automatrix not be used in
# advertising or publicity pertaining to distribution of the software
# without specific, written prior permission.
#
# Summary of recent changes:
#   Added run-time display of statements being executed
#   Incorporated portability and performance fixes from Greg Stein
#   Incorporated main program from Michael Scharf

# Sample use:
#    # create a StatementCoverage object, telling it where you want output
#    t = trace.StatementCoverage('/usr/local/Automatrix/concerts/coverage')
#    # run the application or function
#    t.run('main()')
#    # generate annotated listings, excluding several system modules
#    t.list(exclude_list=['regsub.py', 'string.py', 'copy.py',
#        'traceback.py'])

import sys, time
from string import split, rstrip

class StatementCoverage:
    # by default, any coverage files are written in the current
    # directory
    def __init__(self, dir = '.', verbose=0, dotimes=0):
  import marshal, os, stat
  self.tracedir = dir
  self.docounts = (not verbose)
  self.files = {'<string>': None}
  status = os.stat(self.tracedir)
  if not stat.S_ISDIR(status[stat.ST_MODE]):
      import tempfile
      d = tempfile.gettempdir()
      sys.stderr.write('%s: %s is not a directory - using %s instead...\n' %
           (__name__, self.tracedir, d))
      self.tracedir = d
      self.counts = {}

  self.counts_file = os.path.join(self.tracedir, 'counts')
  try:
      self.counts = marshal.load(open(self.counts_file, 'rb'))
  except IOError:
      self.counts = {}
  self.dotimes = dotimes
  self.time = time.clock()
  self.last_key = None

    # straight from profile.py
    def run(self, cmd):
  import __main__
  dict = __main__.__dict__
  self.runctx(cmd, dict, dict)

    # from profile.py, with obvious changes
    def runctx(self, cmd, globals=None, locals=None):
  if globals is None: globals = {}
  if locals is None: locals = {}
  sys.settrace(self.trace)
  try:
      exec cmd in globals, locals
  finally:
      sys.settrace(None)

    def runfunc(self, func, *args, **kw):
  result = None
  sys.settrace(self.trace)
  try:
      result = apply(func, args, kw)
  finally:
      sys.settrace(None)
  return result

    # thanks to Greg Stein for speeding this up somewhat...
    def trace(self, frame, why, arg):
  if why == 'line':
      if self.docounts:
    if self.dotimes and self.last_key is not None:
        # time increment is for the previous line
        newt = time.clock()
        count, t = self.counts[self.last_key]
        t = t + newt - self.time
        self.counts[self.last_key] = count, t
        self.time = time.clock()

    # counter is for this line
    key = self.last_key = \
          (frame.f_code.co_filename, frame.f_lineno)
    try:
        count, t = self.counts[key]
    except KeyError:
        count, t = (0, 0.0)
    count = count+1
    self.counts[key] = (count, t)

      else:
    # display the current line being executed...
    fname = frame.f_code.co_filename
    line = frame.f_lineno
    files = self.files
    if fname != '<string>' and not files.has_key(fname):
        try:
      files[fname] = map(rstrip, open(fname).readlines())
        except IOError:
      files[fname] = None
    if files[fname] != None:
        print '%s(%d): %s' % (split(fname, '/')[-1], line,
           files[fname][line-1])
    else:
        print '%s(%d): ??' % (split(fname, '/')[-1], line)
  return self.trace

    # create an annotated listing file for each Python module.
    # note that statements like
    #   for i in range(len(foo)): foo[i] = foo[i] + 1
    # count both the for statement and the assignment statement.
    # this is only a small nit for my purposes.   if you are
    # so inclined, feel free to correct things
    #
    # use exclude_list to exclude modules from being listed
    #
    def list(self, exclude_list=[]):
  from string import split, join, expandtabs
  import stat, sys, marshal, os, regex

  marshal.dump(self.counts, open(self.counts_file, 'wb'))

  per_file = { }
  for fname, lineno in self.counts.keys():
      try:
    lines_hit = per_file[fname]
      except KeyError:
    lines_hit = per_file[fname] = { }
      lines_hit[lineno] = self.counts[(fname, lineno)]

  blank = regex.compile('[ \t\r\n]*\(\|#.*\)$')
  for fname in per_file.keys():
      if fname == '<string>' or os.path.basename(fname) in exclude_list:
    continue

      try:
    lines = open(fname, 'r').readlines()
      except IOError:
    sys.stderr.write('%s: Could not open %s for reading - skipping\n' %
           (__name__, fname))
    continue

      lines_hit = per_file[fname]

      # build list file name by appending 'l' to filename component of
      # input and tacking it onto the trace directory
      file = os.path.join(self.tracedir, os.path.basename(fname) + 'l')
      try:
    out = open(file, 'w')
      except IOError:
    sys.stderr.write('%s: Could not open %s for writing - skipping\n' %
           (__name__, file))
    continue

      for i in range(len(lines)):
    line = lines[i]

    # do the blank/comment match to try to mark more lines
    # (help the reader find stuff that hasn't been covered)
    if lines_hit.has_key(i+1):
        count, t = lines_hit[i+1]
        # count precedes the lines that we captured
        if self.dotimes:
      out.write('%5d(%7.3fs): ' % (count, t))
        else:
      out.write('%5d: ' % count)
    elif blank.match(line) != -1:
        # blank lines and comments are preceded by dots
        out.write('    .          ')
    else:
        # lines preceded by no marks weren't hit
        out.write('#'*16)
    out.write(expandtabs(lines[i], 8))

      out.close()

def main():
    import os
    # split the args into two parts
    dir='.'
    verbose=0
    dotimes=0

    n=len(sys.argv)
    if n==1:
  sys.stderr.write("%s [-v] [-d directory] program {arg}\n"%sys.argv[0])
  sys.exit(-1)
    i=1
    while i<n:
  arg=sys.argv[i]
  if arg=='-d':
      i=i+1
      dir=arg=sys.argv[i]
  elif arg=='-v':
      verbose=1
  elif arg=='-t':
      dotimes=1
  else:
      break
  i=i+1

    prog_argv=sys.argv[i:]
    # set the sys.argv
    sys.argv=prog_argv
    # set the first entry of path to the path of the
    # main program
    progname=prog_argv[0]
    if eval(sys.version[:3])>1.3:
  sys.path[0]=os.path.split(progname)[0]

    tracer=StatementCoverage(dir,verbose,dotimes)
    tracer.run('execfile(' + 'progname' + ')')



def tester():
    root = Tk()
    root.title('Simple Plot - Version 3 - Smoothed')

    try:
        canvas = Canvas(root, width=450, height=300, bg = 'white')
        canvas.pack()
        Button(root, text='Quit', command=root.quit).pack()

        canvas.create_line(100,250,400,250, width=2)
        canvas.create_line(100,250,100,50,  width=2)

        for i in range(11):
            x = 100 + (i * 30)
            canvas.create_line(x,250,x,245, width=2)
            canvas.create_text(x,254, text='%d'% (10*i), anchor=N)

        for i in range(6):
            y = 250 - (i * 40)
            canvas.create_line(100,y,105,y, width=2)
            canvas.create_text(96,y, text='%5.1f'% (50.*i), anchor=E)

        scaled = []
        for x,y in [(12, 56), (20, 94), (33, 98), (45, 120), (61, 180),
                    (75, 160), (98, 223)]:
            s = 110 + 2*x, 250 - (1*y)/5
            print "x,y = %d,%d" % (s[0], s[1])
            scaled.append(s)

        canvas.create_line(scaled, fill='black', smooth=1)

        for x,y in scaled:
            canvas.create_oval(x-6,y-6,x+6,y+6, width=1,
                               outline='black', fill='SkyBlue2')
    except:
        print 'An error has occured!'

    root.mainloop()


StatementCoverage().run('tester()')


           
       








Related examples in the same category

1.Indent Python code, giving block-closing comments
2.Load a pickle generated by db2pickle.py to a database
3.Assorted Tk-related subroutines used in Grail
4.Generate binary message catalog from textual translation description
5.Minimally patched to make it even more xgettext compatible
6.Check that all '.pyc' files exist and are up-to-date
7.Search Python (.py) files for future statements, and remove the features from such statements
8.A helper for analyzing PYTHONDUMPREFS output
9.Dump a database file to a pickle
10.Create Emacs TAGS file for Python modules
11.Massive identifier substitution on C source files
12.Fix the copyright notice in source files
13.Fix Python script(s) to reference the interpreter via /usr/bin/env python.
14.Add some standard cpp magic to a header file
15.Create Python codecs from Unicode mapping files
16.Transform gprof(1) output into useful HTML.
17.Translate #define's into Python assignments
18.Validate Python LaTeX formatting (Raymond Hettinger)
19.Change .py files to use 4-space indents.
20.Find dependencies between a bunch of Python modules.