1# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 2# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt 3 4"""Control of and utilities for debugging.""" 5 6import inspect 7import os 8import sys 9 10from coverage.misc import isolate_module 11 12os = isolate_module(os) 13 14 15# When debugging, it can be helpful to force some options, especially when 16# debugging the configuration mechanisms you usually use to control debugging! 17# This is a list of forced debugging options. 18FORCED_DEBUG = [] 19 20# A hack for debugging testing in sub-processes. 21_TEST_NAME_FILE = "" # "/tmp/covtest.txt" 22 23 24class DebugControl(object): 25 """Control and output for debugging.""" 26 27 def __init__(self, options, output): 28 """Configure the options and output file for debugging.""" 29 self.options = options 30 self.output = output 31 32 def __repr__(self): 33 return "<DebugControl options=%r output=%r>" % (self.options, self.output) 34 35 def should(self, option): 36 """Decide whether to output debug information in category `option`.""" 37 return (option in self.options or option in FORCED_DEBUG) 38 39 def write(self, msg): 40 """Write a line of debug output.""" 41 if self.should('pid'): 42 msg = "pid %5d: %s" % (os.getpid(), msg) 43 self.output.write(msg+"\n") 44 if self.should('callers'): 45 dump_stack_frames(self.output) 46 self.output.flush() 47 48 def write_formatted_info(self, header, info): 49 """Write a sequence of (label,data) pairs nicely.""" 50 self.write(info_header(header)) 51 for line in info_formatter(info): 52 self.write(" %s" % line) 53 54 55def info_header(label): 56 """Make a nice header string.""" 57 return "--{0:-<60s}".format(" "+label+" ") 58 59 60def info_formatter(info): 61 """Produce a sequence of formatted lines from info. 62 63 `info` is a sequence of pairs (label, data). The produced lines are 64 nicely formatted, ready to print. 65 66 """ 67 info = list(info) 68 if not info: 69 return 70 label_len = max(len(l) for l, _d in info) 71 for label, data in info: 72 if data == []: 73 data = "-none-" 74 if isinstance(data, (list, set, tuple)): 75 prefix = "%*s:" % (label_len, label) 76 for e in data: 77 yield "%*s %s" % (label_len+1, prefix, e) 78 prefix = "" 79 else: 80 yield "%*s: %s" % (label_len, label, data) 81 82 83def short_stack(): # pragma: debugging 84 """Return a string summarizing the call stack. 85 86 The string is multi-line, with one line per stack frame. Each line shows 87 the function name, the file name, and the line number: 88 89 ... 90 start_import_stop : /Users/ned/coverage/trunk/tests/coveragetest.py @95 91 import_local_file : /Users/ned/coverage/trunk/tests/coveragetest.py @81 92 import_local_file : /Users/ned/coverage/trunk/coverage/backward.py @159 93 ... 94 95 """ 96 stack = inspect.stack()[:0:-1] 97 return "\n".join("%30s : %s @%d" % (t[3], t[1], t[2]) for t in stack) 98 99 100def dump_stack_frames(out=None): # pragma: debugging 101 """Print a summary of the stack to stdout, or some place else.""" 102 out = out or sys.stdout 103 out.write(short_stack()) 104 out.write("\n") 105