eric7/DebugClients/Python/coverage/debug.py

branch
eric7
changeset 8775
0802ae193343
parent 8312
800c432b34c8
child 8991
2fc945191992
equal deleted inserted replaced
8774:d728227e8ebb 8775:0802ae193343
4 """Control of and utilities for debugging.""" 4 """Control of and utilities for debugging."""
5 5
6 import contextlib 6 import contextlib
7 import functools 7 import functools
8 import inspect 8 import inspect
9 import io
9 import itertools 10 import itertools
10 import os 11 import os
11 import pprint 12 import pprint
13 import reprlib
12 import sys 14 import sys
13 try: 15 import _thread
14 import _thread 16
15 except ImportError:
16 import thread as _thread
17
18 from coverage.backward import reprlib, StringIO
19 from coverage.misc import isolate_module 17 from coverage.misc import isolate_module
20 18
21 os = isolate_module(os) 19 os = isolate_module(os)
22 20
23 21
26 # This is a list of forced debugging options. 24 # This is a list of forced debugging options.
27 FORCED_DEBUG = [] 25 FORCED_DEBUG = []
28 FORCED_DEBUG_FILE = None 26 FORCED_DEBUG_FILE = None
29 27
30 28
31 class DebugControl(object): 29 class DebugControl:
32 """Control and output for debugging.""" 30 """Control and output for debugging."""
33 31
34 show_repr_attr = False # For SimpleReprMixin 32 show_repr_attr = False # For SimpleReprMixin
35 33
36 def __init__(self, options, output): 34 def __init__(self, options, output):
47 filters=filters, 45 filters=filters,
48 ) 46 )
49 self.raw_output = self.output.outfile 47 self.raw_output = self.output.outfile
50 48
51 def __repr__(self): 49 def __repr__(self):
52 return "<DebugControl options=%r raw_output=%r>" % (self.options, self.raw_output) 50 return f"<DebugControl options={self.options!r} raw_output={self.raw_output!r}>"
53 51
54 def should(self, option): 52 def should(self, option):
55 """Decide whether to output debug information in category `option`.""" 53 """Decide whether to output debug information in category `option`."""
56 if option == "callers" and self.suppress_callers: 54 if option == "callers" and self.suppress_callers:
57 return False 55 return False
75 """ 73 """
76 self.output.write(msg+"\n") 74 self.output.write(msg+"\n")
77 if self.should('self'): 75 if self.should('self'):
78 caller_self = inspect.stack()[1][0].f_locals.get('self') 76 caller_self = inspect.stack()[1][0].f_locals.get('self')
79 if caller_self is not None: 77 if caller_self is not None:
80 self.output.write("self: {!r}\n".format(caller_self)) 78 self.output.write(f"self: {caller_self!r}\n")
81 if self.should('callers'): 79 if self.should('callers'):
82 dump_stack_frames(out=self.output, skip=1) 80 dump_stack_frames(out=self.output, skip=1)
83 self.output.flush() 81 self.output.flush()
84 82
85 83
86 class DebugControlString(DebugControl): 84 class DebugControlString(DebugControl):
87 """A `DebugControl` that writes to a StringIO, for testing.""" 85 """A `DebugControl` that writes to a StringIO, for testing."""
88 def __init__(self, options): 86 def __init__(self, options):
89 super(DebugControlString, self).__init__(options, StringIO()) 87 super().__init__(options, io.StringIO())
90 88
91 def get_output(self): 89 def get_output(self):
92 """Get the output text from the `DebugControl`.""" 90 """Get the output text from the `DebugControl`."""
93 return self.raw_output.getvalue() 91 return self.raw_output.getvalue()
94 92
95 93
96 class NoDebugging(object): 94 class NoDebugging:
97 """A replacement for DebugControl that will never try to do anything.""" 95 """A replacement for DebugControl that will never try to do anything."""
98 def should(self, option): # pylint: disable=unused-argument 96 def should(self, option): # pylint: disable=unused-argument
99 """Should we write debug messages? Never.""" 97 """Should we write debug messages? Never."""
100 return False 98 return False
101 99
181 179
182 180
183 def add_pid_and_tid(text): 181 def add_pid_and_tid(text):
184 """A filter to add pid and tid to debug messages.""" 182 """A filter to add pid and tid to debug messages."""
185 # Thread ids are useful, but too long. Make a shorter one. 183 # Thread ids are useful, but too long. Make a shorter one.
186 tid = "{:04x}".format(short_id(_thread.get_ident())) 184 tid = f"{short_id(_thread.get_ident()):04x}"
187 text = "{:5d}.{}: {}".format(os.getpid(), tid, text) 185 text = f"{os.getpid():5d}.{tid}: {text}"
188 return text 186 return text
189 187
190 188
191 class SimpleReprMixin(object): 189 class SimpleReprMixin:
192 """A mixin implementing a simple __repr__.""" 190 """A mixin implementing a simple __repr__."""
193 simple_repr_ignore = ['simple_repr_ignore', '$coverage.object_id'] 191 simple_repr_ignore = ['simple_repr_ignore', '$coverage.object_id']
194 192
195 def __repr__(self): 193 def __repr__(self):
196 show_attrs = ( 194 show_attrs = (
200 and k not in self.simple_repr_ignore 198 and k not in self.simple_repr_ignore
201 ) 199 )
202 return "<{klass} @0x{id:x} {attrs}>".format( 200 return "<{klass} @0x{id:x} {attrs}>".format(
203 klass=self.__class__.__name__, 201 klass=self.__class__.__name__,
204 id=id(self), 202 id=id(self),
205 attrs=" ".join("{}={!r}".format(k, v) for k, v in show_attrs), 203 attrs=" ".join(f"{k}={v!r}" for k, v in show_attrs),
206 ) 204 )
207 205
208 206
209 def simplify(v): # pragma: debugging 207 def simplify(v): # pragma: debugging
210 """Turn things which are nearly dict/list/etc into dict/list/etc.""" 208 """Turn things which are nearly dict/list/etc into dict/list/etc."""
243 lines.extend(fn(line).splitlines()) 241 lines.extend(fn(line).splitlines())
244 text = "\n".join(lines) 242 text = "\n".join(lines)
245 return text + ending 243 return text + ending
246 244
247 245
248 class CwdTracker(object): # pragma: debugging 246 class CwdTracker: # pragma: debugging
249 """A class to add cwd info to debug messages.""" 247 """A class to add cwd info to debug messages."""
250 def __init__(self): 248 def __init__(self):
251 self.cwd = None 249 self.cwd = None
252 250
253 def filter(self, text): 251 def filter(self, text):
254 """Add a cwd message for each new cwd.""" 252 """Add a cwd message for each new cwd."""
255 cwd = os.getcwd() 253 cwd = os.getcwd()
256 if cwd != self.cwd: 254 if cwd != self.cwd:
257 text = "cwd is now {!r}\n".format(cwd) + text 255 text = f"cwd is now {cwd!r}\n" + text
258 self.cwd = cwd 256 self.cwd = cwd
259 return text 257 return text
260 258
261 259
262 class DebugOutputFile(object): # pragma: debugging 260 class DebugOutputFile: # pragma: debugging
263 """A file-like object that includes pid and cwd information.""" 261 """A file-like object that includes pid and cwd information."""
264 def __init__(self, outfile, show_process, filters): 262 def __init__(self, outfile, show_process, filters):
265 self.outfile = outfile 263 self.outfile = outfile
266 self.show_process = show_process 264 self.show_process = show_process
267 self.filters = list(filters) 265 self.filters = list(filters)
268 266
269 if self.show_process: 267 if self.show_process:
270 self.filters.insert(0, CwdTracker().filter) 268 self.filters.insert(0, CwdTracker().filter)
271 self.write("New process: executable: %r\n" % (sys.executable,)) 269 self.write(f"New process: executable: {sys.executable!r}\n")
272 self.write("New process: cmd: %r\n" % (getattr(sys, 'argv', None),)) 270 self.write("New process: cmd: {!r}\n".format(getattr(sys, 'argv', None)))
273 if hasattr(os, 'getppid'): 271 if hasattr(os, 'getppid'):
274 self.write("New process: pid: %r, parent pid: %r\n" % (os.getpid(), os.getppid())) 272 self.write(f"New process: pid: {os.getpid()!r}, parent pid: {os.getppid()!r}\n")
275 273
276 SYS_MOD_NAME = '$coverage.debug.DebugOutputFile.the_one' 274 SYS_MOD_NAME = '$coverage.debug.DebugOutputFile.the_one'
277 275
278 @classmethod 276 @classmethod
279 def get_one(cls, fileobj=None, show_process=True, filters=(), interim=False): 277 def get_one(cls, fileobj=None, show_process=True, filters=(), interim=False):
304 # on a class attribute. Yes, this is aggressively gross. 302 # on a class attribute. Yes, this is aggressively gross.
305 the_one, is_interim = sys.modules.get(cls.SYS_MOD_NAME, (None, True)) 303 the_one, is_interim = sys.modules.get(cls.SYS_MOD_NAME, (None, True))
306 if the_one is None or is_interim: 304 if the_one is None or is_interim:
307 if fileobj is None: 305 if fileobj is None:
308 debug_file_name = os.environ.get("COVERAGE_DEBUG_FILE", FORCED_DEBUG_FILE) 306 debug_file_name = os.environ.get("COVERAGE_DEBUG_FILE", FORCED_DEBUG_FILE)
309 if debug_file_name: 307 if debug_file_name in ("stdout", "stderr"):
308 fileobj = getattr(sys, debug_file_name)
309 elif debug_file_name:
310 fileobj = open(debug_file_name, "a") 310 fileobj = open(debug_file_name, "a")
311 else: 311 else:
312 fileobj = sys.stderr 312 fileobj = sys.stderr
313 the_one = cls(fileobj, show_process, filters) 313 the_one = cls(fileobj, show_process, filters)
314 sys.modules[cls.SYS_MOD_NAME] = (the_one, interim) 314 sys.modules[cls.SYS_MOD_NAME] = (the_one, interim)
368 def _decorator(func): 368 def _decorator(func):
369 @functools.wraps(func) 369 @functools.wraps(func)
370 def _wrapper(self, *args, **kwargs): 370 def _wrapper(self, *args, **kwargs):
371 oid = getattr(self, OBJ_ID_ATTR, None) 371 oid = getattr(self, OBJ_ID_ATTR, None)
372 if oid is None: 372 if oid is None:
373 oid = "{:08d} {:04d}".format(os.getpid(), next(OBJ_IDS)) 373 oid = f"{os.getpid():08d} {next(OBJ_IDS):04d}"
374 setattr(self, OBJ_ID_ATTR, oid) 374 setattr(self, OBJ_ID_ATTR, oid)
375 extra = "" 375 extra = ""
376 if show_args: 376 if show_args:
377 eargs = ", ".join(map(repr, args)) 377 eargs = ", ".join(map(repr, args))
378 ekwargs = ", ".join("{}={!r}".format(*item) for item in kwargs.items()) 378 ekwargs = ", ".join("{}={!r}".format(*item) for item in kwargs.items())
384 extra += ")" 384 extra += ")"
385 if show_stack: 385 if show_stack:
386 extra += " @ " 386 extra += " @ "
387 extra += "; ".join(_clean_stack_line(l) for l in short_stack().splitlines()) 387 extra += "; ".join(_clean_stack_line(l) for l in short_stack().splitlines())
388 callid = next(CALLS) 388 callid = next(CALLS)
389 msg = "{} {:04d} {}{}\n".format(oid, callid, func.__name__, extra) 389 msg = f"{oid} {callid:04d} {func.__name__}{extra}\n"
390 DebugOutputFile.get_one(interim=True).write(msg) 390 DebugOutputFile.get_one(interim=True).write(msg)
391 ret = func(self, *args, **kwargs) 391 ret = func(self, *args, **kwargs)
392 if show_return: 392 if show_return:
393 msg = "{} {:04d} {} return {!r}\n".format(oid, callid, func.__name__, ret) 393 msg = f"{oid} {callid:04d} {func.__name__} return {ret!r}\n"
394 DebugOutputFile.get_one(interim=True).write(msg) 394 DebugOutputFile.get_one(interim=True).write(msg)
395 return ret 395 return ret
396 return _wrapper 396 return _wrapper
397 return _decorator 397 return _decorator
398 398

eric ide

mercurial