Fix several bugs and add two features.

Assertion error message had typos in arguments to string format.

.cover files for modules in packages are now put in the right place.

The code that generate .cover files seemed to prepend a "./" to many
absolute paths, causing them to fail.  The code now checks explicitly
for absolute paths and leaves them alone.

In trace/coverage code, recover from case where module has no __name__
attribute, when e.g. it is executed by PyRun_String().  In this case,
assign modulename to None and hope for the best.  There isn't anywhere
to write out coverage data for this code anyway.

Also, replace several sys.stderr.writes with print >> sys.stderr.

New features:

-C/--coverdir dir: Generate .cover files in specified directory
instead of in the directory where the .py file is.

-s: Print a short summary of files coverred (# lines, % coverage,
name)
diff --git a/Tools/scripts/trace.py b/Tools/scripts/trace.py
index 78715e0..35dc71f 100644
--- a/Tools/scripts/trace.py
+++ b/Tools/scripts/trace.py
@@ -74,6 +74,8 @@
    -r,--report         Generate a report from a results file; do not
                          execute any code.
         (One of `-t', `-c' or `-r' must be specified)
+   -s,--summary        Generate a brief summary for each file.  (Can only
+                         be used with -c or -r.)
 
 I/O:
    -f,--file=          File name for accumulating results over several runs.
@@ -85,6 +87,7 @@
                          with a '>>>>>> '.
    -R,--no-report      Do not generate the annotated reports.  Useful if
                          you want to accumulate several over tests.
+   -C,--coverdir=      Generate .cover files in this directory
 
 Selection:                 Do not trace or log lines from ...
   --ignore-module=[string]   modules with the given __name__, and submodules
@@ -197,8 +200,9 @@
             if modules.has_key(key):
                 # make sure they point to the same file
                 assert modules[key] == other_modules[key], \
-                      "Strange! filename %s has two different module names" % \
-                      (key, modules[key], other_module[key])
+                      "Strange! filename %s has two different module " \
+                      "names: %s and %s" % \
+                      (key, modules[key], other_modules[key])
             else:
                 modules[key] = other_modules[key]
 
@@ -254,6 +258,8 @@
     """
     import parser
 
+    assert filename.endswith('.py')
+
     prog = open(filename).read()
     ast = parser.suite(prog)
     code = parser.compileast(ast, filename)
@@ -281,7 +287,7 @@
     return os.sep.join(prefix)
 
 def create_results_log(results, dirname = ".", show_missing = 1,
-                       save_counts = 0):
+                       save_counts = 0, summary = 0, coverdir = None):
     import re
     # turn the counts data ("(filename, lineno) = count") into something
     # accessible on a per-file basis
@@ -302,6 +308,9 @@
     # line embedded in a multiline string.
     blank = re.compile(r'^\s*(#.*)?$')
 
+    # accumulate summary info, if needed
+    sums = {}
+
     # generate file paths for the coverage files we are going to write...
     fnlist = []
     tfdir = tempfile.gettempdir()
@@ -315,36 +324,38 @@
         if filename.startswith(tfdir):
             continue
 
-        # XXX this is almost certainly not portable!!!
-        fndir = os.path.dirname(filename)
-        if filename[:1] == os.sep:
-            coverpath = os.path.join(dirname, "."+fndir)
-        else:
-            coverpath = os.path.join(dirname, fndir)
+        modulename = os.path.split(results.modules[key])[1]
 
         if filename.endswith(".pyc") or filename.endswith(".pyo"):
             filename = filename[:-1]
 
+        if coverdir:
+            listfilename = os.path.join(coverdir, modulename + ".cover")
+        else:
+            # XXX this is almost certainly not portable!!!
+            fndir = os.path.dirname(filename)
+            if os.path.isabs(filename):
+                coverpath = fndir
+            else:
+                coverpath = os.path.join(dirname, fndir)
+
+            # build list file name by appending a ".cover" to the module name
+            # and sticking it into the specified directory
+            if "." in modulename:
+                # A module in a package
+                finalname = modulename.split(".")[-1]
+                listfilename = os.path.join(coverpath, finalname + ".cover")
+            else:
+                listfilename = os.path.join(coverpath, modulename + ".cover")
+
         # Get the original lines from the .py file
         try:
             lines = open(filename, 'r').readlines()
         except IOError, err:
-            sys.stderr.write("%s: Could not open %s for reading " \
-                             "because: %s - skipping\n" % \
-                             ("trace", `filename`, err.strerror))
+            print >> sys.stderr, "trace: Could not open %s for reading " \
+                  "because: %s - skipping" % (`filename`, err.strerror)
             continue
 
-        modulename = os.path.split(results.modules[key])[1]
-
-        # build list file name by appending a ".cover" to the module name
-        # and sticking it into the specified directory
-        listfilename = os.path.join(coverpath, modulename + ".cover")
-        #sys.stderr.write("modulename: %(modulename)s\n"
-        #                 "filename: %(filename)s\n"
-        #                 "coverpath: %(coverpath)s\n"
-        #                 "listfilename: %(listfilename)s\n"
-        #                 "dirname: %(dirname)s\n"
-        #                 % locals())
         try:
             outfile = open(listfilename, 'w')
         except IOError, err:
@@ -360,6 +371,8 @@
         else:
             executable_linenos = {}
 
+        n_lines = 0
+        n_hits = 0
         lines_hit = per_file[key]
         for i in range(len(lines)):
             line = lines[i]
@@ -369,6 +382,8 @@
             if lines_hit.has_key(i+1):
                 # count precedes the lines that we captured
                 outfile.write('%5d: ' % lines_hit[i+1])
+                n_hits = n_hits + 1
+                n_lines = n_lines + 1
             elif blank.match(line):
                 # blank lines and comments are preceded by dots
                 outfile.write('    . ')
@@ -383,10 +398,15 @@
                     outfile.write('>>>>>> ')
                 else:
                     outfile.write(' '*7)
+                n_lines = n_lines + 1
             outfile.write(string.expandtabs(lines[i], 8))
 
         outfile.close()
 
+        if summary and n_lines:
+            percent = int(100 * n_hits / n_lines)
+            sums[modulename] = n_lines, percent, modulename, filename
+
         if save_counts:
             # try and store counts and module info into dirname
             try:
@@ -398,6 +418,14 @@
                 sys.stderr.write("cannot save counts/modules " \
                                  "files because %s" % err.strerror)
 
+    if summary and sums:
+        mods = sums.keys()
+        mods.sort()
+        print "lines   cov%   module   (path)"
+        for m in mods:
+            n_lines, percent, modulename, filename = sums[m]
+            print "%5d   %3d%%   %s   (%s)" % sums[m]
+
 # There is a lot of code shared between these two classes even though
 # it is straightforward to make a super class to share code.  However,
 # for performance reasons (remember, this is called at every step) I
@@ -419,7 +447,12 @@
             filename = frame.f_globals.get("__file__", None)
             if filename is None:
                 filename = frame.f_code.co_filename
-            modulename = frame.f_globals["__name__"]
+            try:
+                modulename = frame.f_globals["__name__"]
+            except KeyError:
+                # PyRun_String() for example
+                # XXX what to do?
+                modulename = None
 
             # We do this next block to keep from having to make methods
             # calls, which also requires resetting the trace
@@ -449,12 +482,18 @@
         self.ignore = ignore
         self.ignore_names = ignore._ignore # access ignore's cache (speed hack)
 
-        self.files = {'<string>': None}  # stores lines from the .py file, or None
+        self.files = {'<string>': None}  # stores lines from the .py file,
+                                         # or None
 
     def trace(self, frame, why, arg):
         if why == 'line':
             filename = frame.f_code.co_filename
-            modulename = frame.f_globals["__name__"]
+            try:
+                modulename = frame.f_globals["__name__"]
+            except KeyError:
+                # PyRun_String() for example
+                # XXX what to do?
+                modulename = None
 
             # We do this next block to keep from having to make methods
             # calls, which also requires resetting the trace
@@ -474,7 +513,8 @@
 
                 # If you want to see filenames (the original behaviour), try:
                 #   modulename = filename
-                # or, prettier but confusing when several files have the same name
+                # or, prettier but confusing when several files have the
+                # same name
                 #   modulename = os.path.basename(filename)
 
                 if files[filename] != None:
@@ -487,7 +527,7 @@
 
 
 def _err_exit(msg):
-    sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
+    print >> sys.stderr, "%s: %s" % (sys.argv[0], msg)
     sys.exit(1)
 
 def main(argv = None):
@@ -496,15 +536,17 @@
     if argv is None:
         argv = sys.argv
     try:
-        opts, prog_argv = getopt.getopt(argv[1:], "tcrRf:d:m",
+        opts, prog_argv = getopt.getopt(argv[1:], "tcrRf:d:msC:",
                                         ["help", "version", "trace", "count",
                                          "report", "no-report",
                                          "file=", "logdir=", "missing",
-                                         "ignore-module=", "ignore-dir="])
+                                         "ignore-module=", "ignore-dir=",
+                                         "coverdir="])
 
     except getopt.error, msg:
-        sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
-        sys.stderr.write("Try `%s --help' for more information\n" % sys.argv[0])
+        print >> sys.stderr, "%s: %s" % (sys.argv[0], msg)
+        print >> sys.stderr, "Try `%s --help' for more information" \
+              % sys.argv[0]
         sys.exit(1)
 
     trace = 0
@@ -516,6 +558,8 @@
     missing = 0
     ignore_modules = []
     ignore_dirs = []
+    coverdir = None
+    summary = 0
 
     for opt, val in opts:
         if opt == "--help":
@@ -554,6 +598,14 @@
             missing = 1
             continue
 
+        if opt == "-C" or opt == "--coverdir":
+            coverdir = val
+            continue
+
+        if opt == "-s" or opt == "--summary":
+            summary = 1
+            continue
+
         if opt == "--ignore-module":
             ignore_modules.append(val)
             continue
@@ -638,7 +690,8 @@
                 results.update(CoverageResults(old_counts, old_modules))
 
         if not no_report:
-            create_results_log(results, logdir, missing)
+            create_results_log(results, logdir, missing,
+                               summary=summary, coverdir=coverdir)
 
         if counts_file:
             try:
@@ -651,7 +704,8 @@
     elif report:
         old_counts, old_modules = marshal.load(open(counts_file, 'rb'))
         results = CoverageResults(old_counts, old_modules)
-        create_results_log(results, logdir, missing)
+        create_results_log(results, logdir, missing,
+                           summary=summary, coverdir=coverdir)
 
     else:
         assert 0, "Should never get here"