Skip to content

Commit 9ee6390

Browse files
committed
debug: more details in debug messages
1 parent 6af8a5d commit 9ee6390

File tree

5 files changed

+45
-38
lines changed

5 files changed

+45
-38
lines changed

coverage/debug.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import _thread
99
import atexit
1010
import contextlib
11+
import datetime
1112
import functools
1213
import inspect
1314
import itertools
@@ -19,13 +20,7 @@
1920
import traceback
2021
import types
2122
from collections.abc import Iterable, Iterator, Mapping
22-
from typing import (
23-
IO,
24-
Any,
25-
Callable,
26-
Final,
27-
overload,
28-
)
23+
from typing import IO, Any, Callable, Final, overload
2924

3025
from coverage.misc import human_sorted_items, isolate_module
3126
from coverage.types import AnyCallable, TWritable
@@ -222,6 +217,20 @@ def short_filename(filename: str | None) -> str | None:
222217
return filename
223218

224219

220+
def file_summary(filename: str) -> str:
221+
"""A one-line summary of a file, for log messages."""
222+
try:
223+
s = os.stat(filename)
224+
except FileNotFoundError:
225+
summary = "does not exist"
226+
except Exception as e:
227+
summary = f"error: {e}"
228+
else:
229+
mod = datetime.datetime.fromtimestamp(s.st_mtime)
230+
summary = f"{s.st_size} bytes, modified {mod}"
231+
return summary
232+
233+
225234
def short_stack(
226235
skip: int = 0,
227236
full: bool = False,

coverage/patch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def _patch__exit(cov: Coverage, debug: TDebugCtl) -> None:
5656

5757
def coverage_os_exit_patch(status: int) -> NoReturn:
5858
with contextlib.suppress(Exception):
59-
debug.write("Using _exit patch")
59+
debug.write(f"Using _exit patch with {cov = }")
6060
with contextlib.suppress(Exception):
6161
cov.save()
6262
old_exit(status)
@@ -74,7 +74,7 @@ def _patch_execv(cov: Coverage, config: CoverageConfig, debug: TDebugCtl) -> Non
7474
def make_execv_patch(fname: str, old_execv: Any) -> Any:
7575
def coverage_execv_patch(*args: Any, **kwargs: Any) -> Any:
7676
with contextlib.suppress(Exception):
77-
debug.write(f"Using execv patch for {fname}")
77+
debug.write(f"Using execv patch for {fname} with {cov = }")
7878
with contextlib.suppress(Exception):
7979
cov.save()
8080

coverage/sqldata.py

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,9 @@
2020
import threading
2121
import zlib
2222
from collections.abc import Collection, Mapping, Sequence
23-
from typing import (
24-
Any,
25-
Callable,
26-
cast,
27-
)
23+
from typing import Any, Callable, cast
2824

29-
from coverage.debug import NoDebugging, auto_repr
25+
from coverage.debug import NoDebugging, auto_repr, file_summary
3026
from coverage.exceptions import CoverageException, DataError
3127
from coverage.misc import file_be_gone, isolate_module
3228
from coverage.numbits import numbits_to_nums, numbits_union, nums_to_numbits
@@ -120,7 +116,7 @@ def _wrapped(self: CoverageData, *args: Any, **kwargs: Any) -> Any:
120116
self._debug.write(f"Locking {self._lock!r} for {method.__name__}")
121117
with self._lock:
122118
if self._debug.should("lock"):
123-
self._debug.write(f"Locked {self._lock!r} for {method.__name__}")
119+
self._debug.write(f"Locked {self._lock!r} for {method.__name__}")
124120
return method(self, *args, **kwargs)
125121
return _wrapped
126122

@@ -259,6 +255,11 @@ def __init__(
259255

260256
__repr__ = auto_repr
261257

258+
def _debug_dataio(self, msg: str, filename: str) -> None:
259+
"""A helper for debug messages which are all similar."""
260+
if self._debug.should("dataio"):
261+
self._debug.write(f"{msg} {filename!r} ({file_summary(filename)})")
262+
262263
def _choose_filename(self) -> None:
263264
"""Set self._filename based on inited attributes."""
264265
if self._no_disk:
@@ -287,8 +288,7 @@ def close(self, force: bool = False) -> None:
287288

288289
def _open_db(self) -> None:
289290
"""Open an existing db file, and read its metadata."""
290-
if self._debug.should("dataio"):
291-
self._debug.write(f"Opening data file {self._filename!r}")
291+
self._debug_dataio("Opening data file", self._filename)
292292
self._dbs[threading.get_ident()] = SqliteDb(self._filename, self._debug)
293293
self._read_db()
294294

@@ -327,8 +327,7 @@ def _read_db(self) -> None:
327327

328328
def _init_db(self, db: SqliteDb) -> None:
329329
"""Write the initial contents of the database."""
330-
if self._debug.should("dataio"):
331-
self._debug.write(f"Initing data file {self._filename!r}")
330+
self._debug_dataio("Initing data file", self._filename)
332331
db.executescript(SCHEMA)
333332
db.execute_void("insert into coverage_schema (version) values (?)", (SCHEMA_VERSION,))
334333

@@ -377,8 +376,7 @@ def dumps(self) -> bytes:
377376
.. versionadded:: 5.0
378377
379378
"""
380-
if self._debug.should("dataio"):
381-
self._debug.write(f"Dumping data from data file {self._filename!r}")
379+
self._debug_dataio("Dumping data from data file", self._filename)
382380
with self._connect() as con:
383381
script = con.dump()
384382
return b"z" + zlib.compress(script.encode("utf-8"))
@@ -398,8 +396,7 @@ def loads(self, data: bytes) -> None:
398396
.. versionadded:: 5.0
399397
400398
"""
401-
if self._debug.should("dataio"):
402-
self._debug.write(f"Loading data into data file {self._filename!r}")
399+
self._debug_dataio("Loading data into data file", self._filename)
403400
if data[:1] != b"z":
404401
raise DataError(
405402
f"Unrecognized serialization: {data[:40]!r} (head of {len(data)} bytes)",
@@ -834,16 +831,14 @@ def erase(self, parallel: bool = False) -> None:
834831
self._reset()
835832
if self._no_disk:
836833
return
837-
if self._debug.should("dataio"):
838-
self._debug.write(f"Erasing data file {self._filename!r}")
834+
self._debug_dataio("Erasing data file", self._filename)
839835
file_be_gone(self._filename)
840836
if parallel:
841837
data_dir, local = os.path.split(self._filename)
842838
local_abs_path = os.path.join(os.path.abspath(data_dir), local)
843839
pattern = glob.escape(local_abs_path) + ".*"
844840
for filename in glob.glob(pattern):
845-
if self._debug.should("dataio"):
846-
self._debug.write(f"Erasing parallel data file {filename!r}")
841+
self._debug_dataio("Erasing parallel data file", filename)
847842
file_be_gone(filename)
848843

849844
def read(self) -> None:
@@ -854,7 +849,7 @@ def read(self) -> None:
854849

855850
def write(self) -> None:
856851
"""Ensure the data is written to the data file."""
857-
pass
852+
self._debug_dataio("Writing (no-op) data file", self._filename)
858853

859854
def _start_using(self) -> None:
860855
"""Call this before using the database at all."""

tests/test_data.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -727,11 +727,14 @@ def test_debug_output_with_debug_option(self) -> None:
727727
print(debug.get_output())
728728

729729
assert re.search(
730-
r"^Closing dbs, force=False: {}\n" +
731-
r"Erasing data file '.*\.coverage'\n" +
732-
r"Opening data file '.*\.coverage'\n" +
733-
r"Initing data file '.*\.coverage'\n" +
734-
r"Opening data file '.*\.coverage'\n$",
730+
r"^" +
731+
r"Closing dbs, force=False: {}\n" +
732+
r"Erasing data file '.*\.coverage' \(does not exist\)\n" +
733+
r"Opening data file '.*\.coverage' \(does not exist\)\n" +
734+
r"Initing data file '.*\.coverage' \(0 bytes, modified [-:. 0-9]+\)\n" +
735+
r"Writing \(no-op\) data file '.*\.coverage' \(\d+ bytes, modified [-:. 0-9]+\)\n" +
736+
r"Opening data file '.*\.coverage' \(\d+ bytes, modified [-:. 0-9]+\)\n" +
737+
r"$",
735738
debug.get_output(),
736739
)
737740

tests/test_debug.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ def test_debug_trace_pid(self) -> None:
162162
def test_debug_callers(self) -> None:
163163
out_text = self.f1_debug_output(["pid", "dataop", "dataio", "callers", "lock"])
164164
# For every real message, there should be a stack trace with a line like
165-
# "f1_debug_output : /Users/ned/coverage/tests/test_debug.py @71"
166-
real_messages = re_lines(r":\d+", out_text, match=False)
165+
# "f1_debug_output : /Users/ned/coverage/tests/test_debug.py:71"
166+
real_messages = re_lines(r"\.py:\d+$", out_text, match=False)
167167
frame_pattern = r"\s+f1_debug_output : .*tests[/\\]test_debug.py:\d+$"
168168
frames = re_lines(frame_pattern, out_text)
169169
assert len(real_messages) == len(frames)
@@ -173,8 +173,8 @@ def test_debug_callers(self) -> None:
173173
# The details of what to expect on the stack are empirical, and can change
174174
# as the code changes. This test is here to ensure that the debug code
175175
# continues working. It's ok to adjust these details over time.
176-
assert re_lines(r"^\s*\d+\.\w{4}: Adding file tracers: 0 files", real_messages[-1])
177-
assert re_lines(r"\s+add_file_tracers : .*coverage[/\\]sqldata.py:\d+$", last_line)
176+
assert re_lines(r"^\s*\d+\.\w{4}: Writing \(no-op\) data file", real_messages[-1])
177+
assert re_lines(r"\s+_debug_dataio : .*coverage[/\\]sqldata.py:\d+$", last_line)
178178

179179
def test_debug_config(self) -> None:
180180
out_text = self.f1_debug_output(["config"])

0 commit comments

Comments
 (0)