DebugClients/Python/coverage/debug.py

changeset 4491
0d8612e24fef
parent 4489
d0d6e4ad31bd
child 5051
3586ebd9fac8
diff -r 4ba7a8ab24f2 -r 0d8612e24fef DebugClients/Python/coverage/debug.py
--- a/DebugClients/Python/coverage/debug.py	Sat Oct 10 12:06:10 2015 +0200
+++ b/DebugClients/Python/coverage/debug.py	Sat Oct 10 12:44:52 2015 +0200
@@ -1,6 +1,11 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
 """Control of and utilities for debugging."""
 
+import inspect
 import os
+import sys
 
 
 # When debugging, it can be helpful to force some options, especially when
@@ -8,6 +13,9 @@
 # This is a list of forced debugging options.
 FORCED_DEBUG = []
 
+# A hack for debugging testing in sub-processes.
+_TEST_NAME_FILE = ""    # "/tmp/covtest.txt"
+
 
 class DebugControl(object):
     """Control and output for debugging."""
@@ -17,6 +25,9 @@
         self.options = options
         self.output = output
 
+    def __repr__(self):
+        return "<DebugControl options=%r output=%r>" % (self.options, self.output)
+
     def should(self, option):
         """Decide whether to output debug information in category `option`."""
         return (option in self.options or option in FORCED_DEBUG)
@@ -26,14 +37,22 @@
         if self.should('pid'):
             msg = "pid %5d: %s" % (os.getpid(), msg)
         self.output.write(msg+"\n")
+        if self.should('callers'):
+            dump_stack_frames(self.output)
         self.output.flush()
 
-    def write_formatted_info(self, info):
+    def write_formatted_info(self, header, info):
         """Write a sequence of (label,data) pairs nicely."""
+        self.write(info_header(header))
         for line in info_formatter(info):
             self.write(" %s" % line)
 
 
+def info_header(label):
+    """Make a nice header string."""
+    return "--{0:-<60s}".format(" "+label+" ")
+
+
 def info_formatter(info):
     """Produce a sequence of formatted lines from info.
 
@@ -41,11 +60,14 @@
     nicely formatted, ready to print.
 
     """
-    label_len = max([len(l) for l, _d in info])
+    info = list(info)
+    if not info:
+        return
+    label_len = max(len(l) for l, _d in info)
     for label, data in info:
         if data == []:
             data = "-none-"
-        if isinstance(data, (list, tuple)):
+        if isinstance(data, (list, set, tuple)):
             prefix = "%*s:" % (label_len, label)
             for e in data:
                 yield "%*s %s" % (label_len+1, prefix, e)
@@ -53,5 +75,29 @@
         else:
             yield "%*s: %s" % (label_len, label, data)
 
+
+def short_stack():                                          # pragma: debugging
+    """Return a string summarizing the call stack.
+
+    The string is multi-line, with one line per stack frame. Each line shows
+    the function name, the file name, and the line number:
+
+        ...
+        start_import_stop : /Users/ned/coverage/trunk/tests/coveragetest.py @95
+        import_local_file : /Users/ned/coverage/trunk/tests/coveragetest.py @81
+        import_local_file : /Users/ned/coverage/trunk/coverage/backward.py @159
+        ...
+
+    """
+    stack = inspect.stack()[:0:-1]
+    return "\n".join("%30s : %s @%d" % (t[3], t[1], t[2]) for t in stack)
+
+
+def dump_stack_frames(out=None):                            # pragma: debugging
+    """Print a summary of the stack to stdout, or some place else."""
+    out = out or sys.stdout
+    out.write(short_stack())
+    out.write("\n")
+
 #
 # eflag: FileType = Python2

eric ide

mercurial