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