coverage 7.6.7__cp311-cp311-win_amd64.whl → 7.11.1__cp311-cp311-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. coverage/__init__.py +2 -0
  2. coverage/__main__.py +2 -0
  3. coverage/annotate.py +1 -2
  4. coverage/bytecode.py +177 -3
  5. coverage/cmdline.py +329 -154
  6. coverage/collector.py +31 -42
  7. coverage/config.py +166 -62
  8. coverage/context.py +4 -5
  9. coverage/control.py +164 -85
  10. coverage/core.py +70 -33
  11. coverage/data.py +3 -4
  12. coverage/debug.py +112 -56
  13. coverage/disposition.py +1 -0
  14. coverage/env.py +65 -55
  15. coverage/exceptions.py +35 -7
  16. coverage/execfile.py +18 -13
  17. coverage/files.py +23 -18
  18. coverage/html.py +134 -88
  19. coverage/htmlfiles/style.css +42 -2
  20. coverage/htmlfiles/style.scss +65 -1
  21. coverage/inorout.py +61 -44
  22. coverage/jsonreport.py +17 -8
  23. coverage/lcovreport.py +16 -20
  24. coverage/misc.py +50 -46
  25. coverage/multiproc.py +12 -7
  26. coverage/numbits.py +3 -4
  27. coverage/parser.py +193 -269
  28. coverage/patch.py +166 -0
  29. coverage/phystokens.py +24 -25
  30. coverage/plugin.py +13 -13
  31. coverage/plugin_support.py +36 -35
  32. coverage/python.py +9 -13
  33. coverage/pytracer.py +40 -33
  34. coverage/regions.py +2 -1
  35. coverage/report.py +59 -43
  36. coverage/report_core.py +6 -9
  37. coverage/results.py +118 -66
  38. coverage/sqldata.py +260 -210
  39. coverage/sqlitedb.py +33 -25
  40. coverage/sysmon.py +195 -157
  41. coverage/templite.py +6 -6
  42. coverage/tomlconfig.py +12 -12
  43. coverage/tracer.cp311-win_amd64.pyd +0 -0
  44. coverage/tracer.pyi +2 -0
  45. coverage/types.py +25 -22
  46. coverage/version.py +3 -18
  47. coverage/xmlreport.py +16 -13
  48. {coverage-7.6.7.dist-info → coverage-7.11.1.dist-info}/METADATA +40 -18
  49. coverage-7.11.1.dist-info/RECORD +59 -0
  50. {coverage-7.6.7.dist-info → coverage-7.11.1.dist-info}/WHEEL +1 -1
  51. coverage-7.6.7.dist-info/RECORD +0 -58
  52. {coverage-7.6.7.dist-info → coverage-7.11.1.dist-info}/entry_points.txt +0 -0
  53. {coverage-7.6.7.dist-info → coverage-7.11.1.dist-info/licenses}/LICENSE.txt +0 -0
  54. {coverage-7.6.7.dist-info → coverage-7.11.1.dist-info}/top_level.txt +0 -0
coverage/core.py CHANGED
@@ -10,27 +10,28 @@ import sys
10
10
  from typing import Any
11
11
 
12
12
  from coverage import env
13
+ from coverage.config import CoverageConfig
13
14
  from coverage.disposition import FileDisposition
14
15
  from coverage.exceptions import ConfigError
15
16
  from coverage.misc import isolate_module
16
17
  from coverage.pytracer import PyTracer
17
18
  from coverage.sysmon import SysMonitor
18
- from coverage.types import (
19
- TFileDisposition,
20
- Tracer,
21
- TWarnFn,
22
- )
23
-
19
+ from coverage.types import TDebugCtl, TFileDisposition, Tracer, TWarnFn
24
20
 
25
21
  os = isolate_module(os)
26
22
 
23
+ IMPORT_ERROR: str = ""
24
+
27
25
  try:
28
26
  # Use the C extension code when we can, for speed.
29
- from coverage.tracer import CTracer, CFileDisposition
30
- HAS_CTRACER = True
31
- except ImportError:
27
+ import coverage.tracer
28
+
29
+ CTRACER_FILE: str | None = getattr(coverage.tracer, "__file__", "unknown")
30
+ except ImportError as imp_err:
32
31
  # Couldn't import the C extension, maybe it isn't built.
33
- if os.getenv("COVERAGE_CORE") == "ctrace": # pragma: part covered
32
+ # We still need to check the environment variable directly here,
33
+ # as this code runs before configuration is loaded.
34
+ if os.getenv("COVERAGE_CORE") == "ctrace": # pragma: part covered
34
35
  # During testing, we use the COVERAGE_CORE environment variable
35
36
  # to indicate that we've fiddled with the environment to test this
36
37
  # fallback code. If we thought we had a C tracer, but couldn't import
@@ -39,7 +40,8 @@ except ImportError:
39
40
  # exception here causes all sorts of other noise in unittest.
40
41
  sys.stderr.write("*** COVERAGE_CORE is 'ctrace' but can't import CTracer!\n")
41
42
  sys.exit(1)
42
- HAS_CTRACER = False
43
+ IMPORT_ERROR = str(imp_err)
44
+ CTRACER_FILE = None
43
45
 
44
46
 
45
47
  class Core:
@@ -52,43 +54,78 @@ class Core:
52
54
  packed_arcs: bool
53
55
  systrace: bool
54
56
 
55
- def __init__(self,
57
+ def __init__(
58
+ self,
59
+ *,
56
60
  warn: TWarnFn,
57
- timid: bool,
61
+ debug: TDebugCtl | None,
62
+ config: CoverageConfig,
63
+ dynamic_contexts: bool,
58
64
  metacov: bool,
59
65
  ) -> None:
60
- # Defaults
61
- self.tracer_kwargs = {}
66
+ def _debug(msg: str) -> None:
67
+ if debug:
68
+ debug.write(msg)
69
+
70
+ _debug("in core.py")
71
+
72
+ # Check the conditions that preclude us from using sys.monitoring.
73
+ reason_no_sysmon = ""
74
+ if not env.PYBEHAVIOR.pep669:
75
+ reason_no_sysmon = "sys.monitoring isn't available in this version"
76
+ elif config.branch and not env.PYBEHAVIOR.branch_right_left:
77
+ reason_no_sysmon = "sys.monitoring can't measure branches in this version"
78
+ elif dynamic_contexts:
79
+ reason_no_sysmon = "it doesn't yet support dynamic contexts"
80
+ elif any((bad := c) in config.concurrency for c in ["greenlet", "eventlet", "gevent"]):
81
+ reason_no_sysmon = f"it doesn't support concurrency={bad}"
62
82
 
63
- core_name: str | None
64
- if timid:
83
+ core_name: str | None = None
84
+ if config.timid:
65
85
  core_name = "pytrace"
66
- else:
67
- core_name = os.getenv("COVERAGE_CORE")
86
+ _debug("core.py: Using pytrace because timid=True")
87
+ elif core_name is None:
88
+ # This could still leave core_name as None.
89
+ core_name = config.core
90
+ _debug(f"core.py: core from config is {core_name!r}")
91
+
92
+ if core_name == "sysmon" and reason_no_sysmon:
93
+ _debug(f"core.py: raising ConfigError because sysmon not usable: {reason_no_sysmon}")
94
+ raise ConfigError(
95
+ f"Can't use core=sysmon: {reason_no_sysmon}",
96
+ skip_tests=True,
97
+ slug="no-sysmon",
98
+ )
99
+
100
+ if core_name is None:
101
+ if env.SYSMON_DEFAULT and not reason_no_sysmon:
102
+ core_name = "sysmon"
103
+ _debug("core.py: Using sysmon because SYSMON_DEFAULT is set")
104
+ else:
105
+ core_name = "ctrace"
106
+ _debug("core.py: Defaulting to ctrace core")
68
107
 
69
- if core_name == "sysmon" and not env.PYBEHAVIOR.pep669:
70
- warn("sys.monitoring isn't available, using default core", slug="no-sysmon")
71
- core_name = None
108
+ if core_name == "ctrace":
109
+ if not CTRACER_FILE:
110
+ if IMPORT_ERROR and env.SHIPPING_WHEELS:
111
+ warn(f"Couldn't import C tracer: {IMPORT_ERROR}", slug="no-ctracer", once=True)
112
+ core_name = "pytrace"
113
+ _debug("core.py: Falling back to pytrace because C tracer not available")
72
114
 
73
- if not core_name:
74
- # Once we're comfortable with sysmon as a default:
75
- # if env.PYBEHAVIOR.pep669 and self.should_start_context is None:
76
- # core_name = "sysmon"
77
- if HAS_CTRACER:
78
- core_name = "ctrace"
79
- else:
80
- core_name = "pytrace"
115
+ _debug(f"core.py: Using core={core_name}")
116
+
117
+ self.tracer_kwargs = {}
81
118
 
82
119
  if core_name == "sysmon":
83
120
  self.tracer_class = SysMonitor
84
- self.tracer_kwargs = {"tool_id": 3 if metacov else 1}
121
+ self.tracer_kwargs["tool_id"] = 3 if metacov else 1
85
122
  self.file_disposition_class = FileDisposition
86
123
  self.supports_plugins = False
87
124
  self.packed_arcs = False
88
125
  self.systrace = False
89
126
  elif core_name == "ctrace":
90
- self.tracer_class = CTracer
91
- self.file_disposition_class = CFileDisposition
127
+ self.tracer_class = coverage.tracer.CTracer
128
+ self.file_disposition_class = coverage.tracer.CFileDisposition
92
129
  self.supports_plugins = True
93
130
  self.packed_arcs = True
94
131
  self.systrace = True
coverage/data.py CHANGED
@@ -16,14 +16,13 @@ import functools
16
16
  import glob
17
17
  import hashlib
18
18
  import os.path
19
-
20
- from typing import Callable
21
19
  from collections.abc import Iterable
20
+ from typing import Callable
22
21
 
23
22
  from coverage.exceptions import CoverageException, NoDataError
24
23
  from coverage.files import PathAliases
25
24
  from coverage.misc import Hasher, file_be_gone, human_sorted, plural
26
- from coverage.sqldata import CoverageData
25
+ from coverage.sqldata import CoverageData as CoverageData # pylint: disable=useless-import-alias
27
26
 
28
27
 
29
28
  def line_counts(data: CoverageData, fullpath: bool = False) -> dict[str, int]:
@@ -81,7 +80,7 @@ def combinable_files(data_file: str, data_paths: Iterable[str] | None = None) ->
81
80
  if os.path.isfile(p):
82
81
  files_to_combine.append(os.path.abspath(p))
83
82
  elif os.path.isdir(p):
84
- pattern = glob.escape(os.path.join(os.path.abspath(p), local)) +".*"
83
+ pattern = glob.escape(os.path.join(os.path.abspath(p), local)) + ".*"
85
84
  files_to_combine.extend(glob.glob(pattern))
86
85
  else:
87
86
  raise NoDataError(f"Couldn't combine from non-existent path '{p}'")
coverage/debug.py CHANGED
@@ -5,8 +5,10 @@
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
+ import _thread
8
9
  import atexit
9
10
  import contextlib
11
+ import datetime
10
12
  import functools
11
13
  import inspect
12
14
  import itertools
@@ -17,13 +19,8 @@ import reprlib
17
19
  import sys
18
20
  import traceback
19
21
  import types
20
- import _thread
21
-
22
- from typing import (
23
- overload,
24
- Any, Callable, IO,
25
- )
26
22
  from collections.abc import Iterable, Iterator, Mapping
23
+ from typing import IO, Any, Callable, Final, overload
27
24
 
28
25
  from coverage.misc import human_sorted_items, isolate_module
29
26
  from coverage.types import AnyCallable, TWritable
@@ -41,7 +38,7 @@ FORCED_DEBUG_FILE = None
41
38
  class DebugControl:
42
39
  """Control and output for debugging."""
43
40
 
44
- show_repr_attr = False # For auto_repr
41
+ show_repr_attr = False # For auto_repr
45
42
 
46
43
  def __init__(
47
44
  self,
@@ -76,7 +73,7 @@ class DebugControl:
76
73
  """Decide whether to output debug information in category `option`."""
77
74
  if option == "callers" and self.suppress_callers:
78
75
  return False
79
- return (option in self.options)
76
+ return option in self.options
80
77
 
81
78
  @contextlib.contextmanager
82
79
  def without_callers(self) -> Iterator[None]:
@@ -111,25 +108,38 @@ class DebugControl:
111
108
 
112
109
  class NoDebugging(DebugControl):
113
110
  """A replacement for DebugControl that will never try to do anything."""
111
+
114
112
  def __init__(self) -> None:
115
113
  # pylint: disable=super-init-not-called
116
- ...
114
+ pass
117
115
 
118
116
  def should(self, option: str) -> bool:
119
117
  """Should we write debug messages? Never."""
120
118
  return False
121
119
 
120
+ @contextlib.contextmanager
121
+ def without_callers(self) -> Iterator[None]:
122
+ """A dummy context manager to satisfy the api."""
123
+ yield # pragma: never called
124
+
122
125
  def write(self, msg: str, *, exc: BaseException | None = None) -> None:
123
126
  """This will never be called."""
124
127
  raise AssertionError("NoDebugging.write should never be called.")
125
128
 
126
129
 
130
+ class DevNullDebug(NoDebugging):
131
+ """A DebugControl that won't write anywhere."""
132
+
133
+ def write(self, msg: str, *, exc: BaseException | None = None) -> None:
134
+ pass
135
+
136
+
127
137
  def info_header(label: str) -> str:
128
138
  """Make a nice header string."""
129
- return "--{:-<60s}".format(" "+label+" ")
139
+ return "--{:-<60s}".format(" " + label + " ")
130
140
 
131
141
 
132
- def info_formatter(info: Iterable[tuple[str, Any]]) -> Iterator[str]:
142
+ def info_formatter(info: Iterable[tuple[str, Any]]) -> Iterable[str]:
133
143
  """Produce a sequence of formatted lines from info.
134
144
 
135
145
  `info` is a sequence of pairs (label, data). The produced lines are
@@ -139,21 +149,21 @@ def info_formatter(info: Iterable[tuple[str, Any]]) -> Iterator[str]:
139
149
  info = list(info)
140
150
  if not info:
141
151
  return
142
- label_len = 30
143
- assert all(len(l) < label_len for l, _ in info)
152
+ LABEL_LEN = 30
153
+ assert all(len(l) < LABEL_LEN for l, _ in info)
144
154
  for label, data in info:
145
155
  if data == []:
146
156
  data = "-none-"
147
- if isinstance(data, tuple) and len(repr(tuple(data))) < 30:
148
- # Convert to tuple to scrub namedtuples.
149
- yield "%*s: %r" % (label_len, label, tuple(data))
150
- elif isinstance(data, (list, set, tuple)):
151
- prefix = "%*s:" % (label_len, label)
152
- for e in data:
153
- yield "%*s %s" % (label_len+1, prefix, e)
154
- prefix = ""
155
- else:
156
- yield "%*s: %s" % (label_len, label, data)
157
+ prefix = f"{label:>{LABEL_LEN}}: "
158
+ match data:
159
+ case tuple() if len(str(data)) < 30:
160
+ yield f"{prefix}{data}"
161
+ case tuple() | list() | set():
162
+ for e in data:
163
+ yield f"{prefix}{e}"
164
+ prefix = " " * (LABEL_LEN + 2)
165
+ case _:
166
+ yield f"{prefix}{data}"
157
167
 
158
168
 
159
169
  def write_formatted_info(
@@ -185,20 +195,24 @@ _FILENAME_REGEXES: list[tuple[str, str]] = [
185
195
  ]
186
196
  _FILENAME_SUBS: list[tuple[str, str]] = []
187
197
 
198
+
188
199
  @overload
189
200
  def short_filename(filename: str) -> str:
190
201
  pass
191
202
 
203
+
192
204
  @overload
193
205
  def short_filename(filename: None) -> None:
194
206
  pass
195
207
 
208
+
196
209
  def short_filename(filename: str | None) -> str | None:
197
210
  """Shorten a file name. Directories are replaced by prefixes like 'syspath:'"""
198
211
  if not _FILENAME_SUBS:
199
212
  for pathdir in sys.path:
200
213
  _FILENAME_SUBS.append((pathdir, "syspath:"))
201
214
  import coverage
215
+
202
216
  _FILENAME_SUBS.append((os.path.dirname(coverage.__file__), "cov:"))
203
217
  _FILENAME_SUBS.sort(key=(lambda pair: len(pair[0])), reverse=True)
204
218
  if filename is not None:
@@ -209,6 +223,20 @@ def short_filename(filename: str | None) -> str | None:
209
223
  return filename
210
224
 
211
225
 
226
+ def file_summary(filename: str) -> str:
227
+ """A one-line summary of a file, for log messages."""
228
+ try:
229
+ s = os.stat(filename)
230
+ except FileNotFoundError:
231
+ summary = "does not exist"
232
+ except Exception as e:
233
+ summary = f"error: {e}"
234
+ else:
235
+ mod = datetime.datetime.fromtimestamp(s.st_mtime)
236
+ summary = f"{s.st_size} bytes, modified {mod}"
237
+ return summary
238
+
239
+
212
240
  def short_stack(
213
241
  skip: int = 0,
214
242
  full: bool = False,
@@ -237,11 +265,13 @@ def short_stack(
237
265
 
238
266
  """
239
267
  # Regexes in initial frames that we don't care about.
268
+ # fmt: off
240
269
  BORING_PRELUDE = [
241
270
  "<string>", # pytest-xdist has string execution.
242
271
  r"\bigor.py$", # Our test runner.
243
272
  r"\bsite-packages\b", # pytest etc getting to our tests.
244
273
  ]
274
+ # fmt: on
245
275
 
246
276
  stack: Iterable[inspect.FrameInfo] = inspect.stack()[:skip:-1]
247
277
  if not full:
@@ -265,7 +295,7 @@ def short_stack(
265
295
 
266
296
  def dump_stack_frames(out: TWritable, skip: int = 0) -> None:
267
297
  """Print a summary of the stack to `out`."""
268
- out.write(short_stack(skip=skip+1) + "\n")
298
+ out.write(short_stack(skip=skip + 1) + "\n")
269
299
 
270
300
 
271
301
  def clipped_repr(text: str, numchars: int = 50) -> str:
@@ -293,10 +323,12 @@ def add_pid_and_tid(text: str) -> str:
293
323
 
294
324
  AUTO_REPR_IGNORE = {"$coverage.object_id"}
295
325
 
326
+
296
327
  def auto_repr(self: Any) -> str:
297
328
  """A function implementing an automatic __repr__ for debugging."""
298
329
  show_attrs = (
299
- (k, v) for k, v in self.__dict__.items()
330
+ (k, v)
331
+ for k, v in self.__dict__.items()
300
332
  if getattr(v, "show_repr_attr", True)
301
333
  and not inspect.ismethod(v)
302
334
  and k not in AUTO_REPR_IGNORE
@@ -308,22 +340,26 @@ def auto_repr(self: Any) -> str:
308
340
  )
309
341
 
310
342
 
311
- def simplify(v: Any) -> Any: # pragma: debugging
343
+ def simplify(v: Any) -> Any: # pragma: debugging
312
344
  """Turn things which are nearly dict/list/etc into dict/list/etc."""
313
345
  if isinstance(v, dict):
314
- return {k:simplify(vv) for k, vv in v.items()}
346
+ return {k: simplify(vv) for k, vv in v.items()}
315
347
  elif isinstance(v, (list, tuple)):
316
348
  return type(v)(simplify(vv) for vv in v)
317
349
  elif hasattr(v, "__dict__"):
318
- return simplify({"."+k: v for k, v in v.__dict__.items()})
350
+ return simplify({"." + k: v for k, v in v.__dict__.items()})
319
351
  else:
320
352
  return v
321
353
 
322
354
 
323
- def pp(v: Any) -> None: # pragma: debugging
355
+ def ppformat(v: Any) -> str: # pragma: debugging
356
+ """Debug helper to pretty-print data, including SimpleNamespace objects."""
357
+ return pprint.pformat(simplify(v), indent=4, compact=True, sort_dicts=True, width=140)
358
+
359
+
360
+ def pp(v: Any) -> None: # pragma: debugging
324
361
  """Debug helper to pretty-print data, including SimpleNamespace objects."""
325
- # Might not be needed in 3.9+
326
- pprint.pprint(simplify(v))
362
+ print(ppformat(v))
327
363
 
328
364
 
329
365
  def filter_text(text: str, filters: Iterable[Callable[[str], str]]) -> str:
@@ -338,7 +374,7 @@ def filter_text(text: str, filters: Iterable[Callable[[str], str]]) -> str:
338
374
 
339
375
  """
340
376
  clean_text = text.rstrip()
341
- ending = text[len(clean_text):]
377
+ ending = text[len(clean_text) :]
342
378
  text = clean_text
343
379
  for filter_fn in filters:
344
380
  lines = []
@@ -350,6 +386,7 @@ def filter_text(text: str, filters: Iterable[Callable[[str], str]]) -> str:
350
386
 
351
387
  class CwdTracker:
352
388
  """A class to add cwd info to debug messages."""
389
+
353
390
  def __init__(self) -> None:
354
391
  self.cwd: str | None = None
355
392
 
@@ -357,13 +394,14 @@ class CwdTracker:
357
394
  """Add a cwd message for each new cwd."""
358
395
  cwd = os.getcwd()
359
396
  if cwd != self.cwd:
360
- text = f"cwd is now {cwd!r}\n" + text
397
+ text = f"cwd is now {cwd!r}\n{text}"
361
398
  self.cwd = cwd
362
399
  return text
363
400
 
364
401
 
365
402
  class ProcessTracker:
366
403
  """Track process creation for debug logging."""
404
+
367
405
  def __init__(self) -> None:
368
406
  self.pid: int = os.getpid()
369
407
  self.did_welcome = False
@@ -392,6 +430,7 @@ class ProcessTracker:
392
430
 
393
431
  class PytestTracker:
394
432
  """Track the current pytest test name to add to debug messages."""
433
+
395
434
  def __init__(self) -> None:
396
435
  self.test_name: str | None = None
397
436
 
@@ -399,13 +438,14 @@ class PytestTracker:
399
438
  """Add a message when the pytest test changes."""
400
439
  test_name = os.getenv("PYTEST_CURRENT_TEST")
401
440
  if test_name != self.test_name:
402
- text = f"Pytest context: {test_name}\n" + text
441
+ text = f"Pytest context: {test_name}\n{text}"
403
442
  self.test_name = test_name
404
443
  return text
405
444
 
406
445
 
407
446
  class DebugOutputFile:
408
447
  """A file-like object that includes pid and cwd information."""
448
+
409
449
  def __init__(
410
450
  self,
411
451
  outfile: IO[str] | None,
@@ -448,7 +488,7 @@ class DebugOutputFile:
448
488
  else:
449
489
  # $set_env.py: COVERAGE_DEBUG_FILE - Where to write debug output
450
490
  file_name = os.getenv("COVERAGE_DEBUG_FILE", FORCED_DEBUG_FILE)
451
- if file_name in ("stdout", "stderr"):
491
+ if file_name in ["stdout", "stderr"]:
452
492
  fileobj = getattr(sys, file_name)
453
493
  elif file_name:
454
494
  fileobj = open(file_name, "a", encoding="utf-8")
@@ -458,7 +498,7 @@ class DebugOutputFile:
458
498
  the_one = cls(fileobj, filters)
459
499
  cls._set_singleton_data(the_one, interim)
460
500
 
461
- if not(the_one.filters):
501
+ if not (the_one.filters):
462
502
  the_one.filters = list(filters)
463
503
  return the_one
464
504
 
@@ -467,8 +507,8 @@ class DebugOutputFile:
467
507
  # a process-wide singleton. So stash it in sys.modules instead of
468
508
  # on a class attribute. Yes, this is aggressively gross.
469
509
 
470
- SYS_MOD_NAME = "$coverage.debug.DebugOutputFile.the_one"
471
- SINGLETON_ATTR = "the_one_and_is_interim"
510
+ SYS_MOD_NAME: Final[str] = "$coverage.debug.DebugOutputFile.the_one"
511
+ SINGLETON_ATTR: Final[str] = "the_one_and_is_interim"
472
512
 
473
513
  @classmethod
474
514
  def _set_singleton_data(cls, the_one: DebugOutputFile, interim: bool) -> None:
@@ -492,19 +532,21 @@ class DebugOutputFile:
492
532
  def write(self, text: str) -> None:
493
533
  """Just like file.write, but filter through all our filters."""
494
534
  assert self.outfile is not None
495
- self.outfile.write(filter_text(text, self.filters))
496
- self.outfile.flush()
535
+ if not self.outfile.closed:
536
+ self.outfile.write(filter_text(text, self.filters))
537
+ self.outfile.flush()
497
538
 
498
539
  def flush(self) -> None:
499
540
  """Flush our file."""
500
541
  assert self.outfile is not None
501
- self.outfile.flush()
542
+ if not self.outfile.closed:
543
+ self.outfile.flush()
502
544
 
503
545
 
504
- def log(msg: str, stack: bool = False) -> None: # pragma: debugging
546
+ def log(msg: str, stack: bool = False) -> None: # pragma: debugging
505
547
  """Write a log message as forcefully as possible."""
506
548
  out = DebugOutputFile.get_one(interim=True)
507
- out.write(msg+"\n")
549
+ out.write(msg + "\n")
508
550
  if stack:
509
551
  dump_stack_frames(out=out, skip=1)
510
552
 
@@ -513,9 +555,10 @@ def decorate_methods(
513
555
  decorator: Callable[..., Any],
514
556
  butnot: Iterable[str] = (),
515
557
  private: bool = False,
516
- ) -> Callable[..., Any]: # pragma: debugging
558
+ ) -> Callable[..., Any]: # pragma: debugging
517
559
  """A class decorator to apply a decorator to methods."""
518
- def _decorator(cls): # type: ignore[no-untyped-def]
560
+
561
+ def _decorator(cls): # type: ignore[no-untyped-def]
519
562
  for name, meth in inspect.getmembers(cls, inspect.isroutine):
520
563
  if name not in cls.__dict__:
521
564
  continue
@@ -526,17 +569,21 @@ def decorate_methods(
526
569
  continue
527
570
  setattr(cls, name, decorator(meth))
528
571
  return cls
572
+
529
573
  return _decorator
530
574
 
531
575
 
532
576
  def break_in_pudb(func: AnyCallable) -> AnyCallable: # pragma: debugging
533
577
  """A function decorator to stop in the debugger for each call."""
578
+
534
579
  @functools.wraps(func)
535
580
  def _wrapper(*args: Any, **kwargs: Any) -> Any:
536
581
  import pudb
582
+
537
583
  sys.stdout = sys.__stdout__
538
584
  pudb.set_trace()
539
585
  return func(*args, **kwargs)
586
+
540
587
  return _wrapper
541
588
 
542
589
 
@@ -544,12 +591,14 @@ OBJ_IDS = itertools.count()
544
591
  CALLS = itertools.count()
545
592
  OBJ_ID_ATTR = "$coverage.object_id"
546
593
 
594
+
547
595
  def show_calls(
548
596
  show_args: bool = True,
549
597
  show_stack: bool = False,
550
598
  show_return: bool = False,
551
- ) -> Callable[..., Any]: # pragma: debugging
599
+ ) -> Callable[..., Any]: # pragma: debugging
552
600
  """A method decorator to debug-log each call to the function."""
601
+
553
602
  def _decorator(func: AnyCallable) -> AnyCallable:
554
603
  @functools.wraps(func)
555
604
  def _wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
@@ -578,7 +627,9 @@ def show_calls(
578
627
  msg = f"{oid} {callid:04d} {func.__name__} return {ret!r}\n"
579
628
  DebugOutputFile.get_one(interim=True).write(msg)
580
629
  return ret
630
+
581
631
  return _wrapper
632
+
582
633
  return _decorator
583
634
 
584
635
 
@@ -595,19 +646,24 @@ def relevant_environment_display(env: Mapping[str, str]) -> list[tuple[str, str]
595
646
  A list of pairs (name, value) to show.
596
647
 
597
648
  """
598
- slugs = {"COV", "PY"}
599
- include = {"HOME", "TEMP", "TMP"}
600
- cloak = {"API", "TOKEN", "KEY", "SECRET", "PASS", "SIGNATURE"}
649
+ SLUGS = {"COV", "PY"}
650
+ INCLUDE = {"HOME", "TEMP", "TMP"}
651
+ CLOAK = {"API", "TOKEN", "KEY", "SECRET", "PASS", "SIGNATURE"}
652
+ TRUNCATE = {"COVERAGE_PROCESS_CONFIG"}
653
+ TRUNCATE_LEN = 60
601
654
 
602
655
  to_show = []
603
656
  for name, val in env.items():
604
- keep = False
605
- if name in include:
606
- keep = True
607
- elif any(slug in name for slug in slugs):
608
- keep = True
609
- if keep:
610
- if any(slug in name for slug in cloak):
657
+ show = False
658
+ if name in INCLUDE:
659
+ show = True
660
+ elif any(slug in name for slug in SLUGS):
661
+ show = True
662
+ if show:
663
+ if any(slug in name for slug in CLOAK):
611
664
  val = re.sub(r"\w", "*", val)
665
+ if name in TRUNCATE:
666
+ if len(val) > TRUNCATE_LEN:
667
+ val = val[: TRUNCATE_LEN - 3] + "..."
612
668
  to_show.append((name, val))
613
669
  return human_sorted_items(to_show)
coverage/disposition.py CHANGED
@@ -32,6 +32,7 @@ class FileDisposition:
32
32
  # be implemented in either C or Python. Acting on them is done with these
33
33
  # functions.
34
34
 
35
+
35
36
  def disposition_init(cls: type[TFileDisposition], original_filename: str) -> TFileDisposition:
36
37
  """Construct and initialize a new FileDisposition object."""
37
38
  disp = cls()