debug.py 3.6 KB

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