Skip to content

Commit 7882b8c

Browse files
committed
refactor: clean lint and mypy for sysmon et al
1 parent 5dad1a1 commit 7882b8c

File tree

6 files changed

+68
-42
lines changed

6 files changed

+68
-42
lines changed

coverage/collector.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from coverage.pytracer import PyTracer
2626
from coverage.sysmon import SysMonitor
2727
from coverage.types import (
28-
TArc, TFileDisposition, TTraceData, TTraceFn, TTracer, TWarnFn,
28+
TArc, TFileDisposition, TTraceData, TTraceFn, TracerCore, TWarnFn,
2929
)
3030

3131
os = isolate_module(os)
@@ -140,7 +140,7 @@ def __init__(
140140

141141
self.concur_id_func = None
142142

143-
self._trace_class: Type[TTracer]
143+
self._trace_class: Type[TracerCore]
144144
self.file_disposition_class: Type[TFileDisposition]
145145

146146
core: Optional[str]
@@ -291,13 +291,12 @@ def reset(self) -> None:
291291
self.should_trace_cache = {}
292292

293293
# Our active Tracers.
294-
self.tracers: List[TTracer] = []
294+
self.tracers: List[TracerCore] = []
295295

296296
self._clear_data()
297297

298-
def _start_tracer(self) -> TTraceFn:
298+
def _start_tracer(self) -> TTraceFn | None:
299299
"""Start a new Tracer object, and store it in self.tracers."""
300-
# TODO: for pep669, this doesn't return a TTraceFn
301300
tracer = self._trace_class()
302301
tracer.data = self.data
303302
tracer.trace_arcs = self.branch

coverage/pytracer.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from coverage import env
1717
from coverage.types import (
1818
TArc, TFileDisposition, TLineNo, TTraceData, TTraceFileData, TTraceFn,
19-
TTracer, TWarnFn,
19+
TracerCore, TWarnFn,
2020
)
2121

2222
# We need the YIELD_VALUE opcode below, in a comparison-friendly form.
@@ -32,7 +32,7 @@
3232

3333
THIS_FILE = __file__.rstrip("co")
3434

35-
class PyTracer(TTracer):
35+
class PyTracer(TracerCore):
3636
"""Python implementation of the raw data tracer."""
3737

3838
# Because of poor implementations of trace-function-manipulating tools,

coverage/sysmon.py

+54-28
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,16 @@
1717
import traceback
1818

1919
from types import CodeType, FrameType
20-
from typing import Any, Callable, Dict, List, Optional, Set, cast
20+
from typing import (
21+
Any,
22+
Callable,
23+
Dict,
24+
List,
25+
Optional,
26+
Set,
27+
TYPE_CHECKING,
28+
cast,
29+
)
2130

2231
from coverage.debug import short_filename, short_stack
2332
from coverage.types import (
@@ -27,21 +36,26 @@
2736
TLineNo,
2837
TTraceData,
2938
TTraceFileData,
30-
TTraceFn,
31-
TTracer,
39+
TracerCore,
3240
TWarnFn,
3341
)
3442

3543
# pylint: disable=unused-argument
36-
# As of mypy 1.7.1, sys.monitoring isn't in typeshed stubs.
37-
# mypy: ignore-errors
3844

3945
LOG = False
4046

4147
# This module will be imported in all versions of Python, but only used in 3.12+
48+
# It will be type-checked for 3.12, but not for earlier versions.
4249
sys_monitoring = getattr(sys, "monitoring", None)
4350

44-
if LOG: # pragma: debugging
51+
if TYPE_CHECKING:
52+
assert sys_monitoring is not None
53+
# I want to say this but it's not allowed:
54+
# MonitorReturn = Literal[sys.monitoring.DISABLE] | None
55+
MonitorReturn = Any
56+
57+
58+
if LOG: # pragma: debugging
4559

4660
class LoggingWrapper:
4761
"""Wrap a namespace to log all its functions."""
@@ -58,6 +72,7 @@ def _wrapped(*args: Any, **kwargs: Any) -> Any:
5872
return _wrapped
5973

6074
sys_monitoring = LoggingWrapper(sys_monitoring, "sys.monitoring")
75+
assert sys_monitoring is not None
6176

6277
short_stack = functools.partial(
6378
short_stack, full=True, short_filenames=True, frame_ids=True
@@ -114,8 +129,9 @@ def _wrapped(self: Any, *args: Any) -> Any:
114129
return ret
115130
except Exception as exc:
116131
log(f"!!{exc.__class__.__name__}: {exc}")
117-
log("".join(traceback.format_exception(exc))) # pylint: disable=no-value-for-parameter
132+
log("".join(traceback.format_exception(exc))) # pylint: disable=[no-value-for-parameter]
118133
try:
134+
assert sys_monitoring is not None
119135
sys_monitoring.set_events(sys.monitoring.COVERAGE_ID, 0)
120136
except ValueError:
121137
# We might have already shut off monitoring.
@@ -146,13 +162,14 @@ class CodeInfo:
146162

147163
tracing: bool
148164
file_data: Optional[TTraceFileData]
149-
byte_to_line: Dict[int, int]
165+
# TODO: what is byte_to_line for?
166+
byte_to_line: Dict[int, int] | None
150167

151168

152169
def bytes_to_lines(code: CodeType) -> Dict[int, int]:
153170
"""Make a dict mapping byte code offsets to line numbers."""
154171
b2l = {}
155-
cur_line = None
172+
cur_line = 0
156173
for inst in dis.get_instructions(code):
157174
if inst.starts_line is not None:
158175
cur_line = inst.starts_line
@@ -161,7 +178,7 @@ def bytes_to_lines(code: CodeType) -> Dict[int, int]:
161178
return b2l
162179

163180

164-
class SysMonitor(TTracer):
181+
class SysMonitor(TracerCore):
165182
"""Python implementation of the raw data tracer for PEP669 implementations."""
166183

167184
# One of these will be used across threads. Be careful.
@@ -185,7 +202,7 @@ def __init__(self) -> None:
185202
self.code_infos: Dict[int, CodeInfo] = {}
186203
# A list of code_objects, just to keep them alive so that id's are
187204
# useful as identity.
188-
self.code_objects: List[CodeInfo] = []
205+
self.code_objects: List[CodeType] = []
189206
self.last_lines: Dict[FrameType, int] = {}
190207
# Map id(code_object) -> code_object
191208
self.local_event_codes: Dict[int, CodeType] = {}
@@ -205,15 +222,14 @@ def __init__(self) -> None:
205222
def __repr__(self) -> str:
206223
points = sum(len(v) for v in self.data.values())
207224
files = len(self.data)
208-
return (
209-
f"<SysMonitor at {id(self):#x}: {points} data points in {files} files>"
210-
)
225+
return f"<SysMonitor at {id(self):#x}: {points} data points in {files} files>"
211226

212227
@panopticon()
213-
def start(self) -> TTraceFn:
228+
def start(self) -> None:
214229
"""Start this Tracer."""
215230
self.stopped = False
216231

232+
assert sys_monitoring is not None
217233
sys_monitoring.use_tool_id(self.myid, "coverage.py")
218234
register = functools.partial(sys_monitoring.register_callback, self.myid)
219235
events = sys.monitoring.events
@@ -237,6 +253,7 @@ def start(self) -> TTraceFn:
237253
@panopticon()
238254
def stop(self) -> None:
239255
"""Stop this Tracer."""
256+
assert sys_monitoring is not None
240257
sys_monitoring.set_events(self.myid, 0)
241258
for code in self.local_event_codes.values():
242259
sys_monitoring.set_local_events(self.myid, code, 0)
@@ -266,36 +283,39 @@ def get_stats(self) -> Optional[Dict[str, int]]:
266283

267284
def callers_frame(self) -> FrameType:
268285
"""Get the frame of the Python code we're monitoring."""
269-
return inspect.currentframe().f_back.f_back.f_back
286+
return (
287+
inspect.currentframe().f_back.f_back.f_back # type: ignore[union-attr,return-value]
288+
)
289+
270290
else:
271291

272292
def callers_frame(self) -> FrameType:
273293
"""Get the frame of the Python code we're monitoring."""
274-
return inspect.currentframe().f_back.f_back
294+
return inspect.currentframe().f_back.f_back # type: ignore[union-attr,return-value]
275295

276296
@panopticon("code", "@")
277-
def sysmon_py_start(self, code: CodeType, instruction_offset: int):
297+
def sysmon_py_start(self, code: CodeType, instruction_offset: int) -> MonitorReturn:
278298
"""Handle sys.monitoring.events.PY_START events."""
279299
# Entering a new frame. Decide if we should trace in this file.
280300
self._activity = True
281301
self.stats["starts"] += 1
282302

283303
code_info = self.code_infos.get(id(code))
304+
tracing_code: bool | None = None
305+
file_data: TTraceFileData | None = None
284306
if code_info is not None:
285307
tracing_code = code_info.tracing
286308
file_data = code_info.file_data
287-
else:
288-
tracing_code = file_data = None
289309

290310
if tracing_code is None:
291311
filename = code.co_filename
292312
disp = self.should_trace_cache.get(filename)
293313
if disp is None:
294-
frame = inspect.currentframe().f_back
314+
frame = inspect.currentframe().f_back # type: ignore[union-attr]
295315
if LOG:
296316
# @panopticon adds a frame.
297-
frame = frame.f_back
298-
disp = self.should_trace(filename, frame)
317+
frame = frame.f_back # type: ignore[union-attr]
318+
disp = self.should_trace(filename, frame) # type: ignore[arg-type]
299319
self.should_trace_cache[filename] = disp
300320

301321
tracing_code = disp.trace
@@ -320,10 +340,12 @@ def sysmon_py_start(self, code: CodeType, instruction_offset: int):
320340
if tracing_code:
321341
events = sys.monitoring.events
322342
if self.sysmon_on:
343+
assert sys_monitoring is not None
323344
sys_monitoring.set_local_events(
324345
self.myid,
325346
code,
326347
events.PY_RETURN
348+
#
327349
| events.PY_RESUME
328350
# | events.PY_YIELD
329351
| events.LINE,
@@ -340,15 +362,17 @@ def sysmon_py_start(self, code: CodeType, instruction_offset: int):
340362
return sys.monitoring.DISABLE
341363

342364
@panopticon("code", "@")
343-
def sysmon_py_resume_arcs(self, code: CodeType, instruction_offset: int):
365+
def sysmon_py_resume_arcs(
366+
self, code: CodeType, instruction_offset: int
367+
) -> MonitorReturn:
344368
"""Handle sys.monitoring.events.PY_RESUME events for branch coverage."""
345369
frame = self.callers_frame()
346370
self.last_lines[frame] = frame.f_lineno
347371

348372
@panopticon("code", "@", None)
349373
def sysmon_py_return_arcs(
350374
self, code: CodeType, instruction_offset: int, retval: object
351-
):
375+
) -> MonitorReturn:
352376
"""Handle sys.monitoring.events.PY_RETURN events for branch coverage."""
353377
frame = self.callers_frame()
354378
code_info = self.code_infos.get(id(code))
@@ -360,7 +384,9 @@ def sysmon_py_return_arcs(
360384
self.last_lines.pop(frame, None)
361385

362386
@panopticon("code", "@", None)
363-
def sysmon_py_unwind_arcs(self, code: CodeType, instruction_offset: int, exception):
387+
def sysmon_py_unwind_arcs(
388+
self, code: CodeType, instruction_offset: int, exception: BaseException
389+
) -> MonitorReturn:
364390
"""Handle sys.monitoring.events.PY_UNWIND events for branch coverage."""
365391
frame = self.callers_frame()
366392
code_info = self.code_infos.get(id(code))
@@ -372,7 +398,7 @@ def sysmon_py_unwind_arcs(self, code: CodeType, instruction_offset: int, excepti
372398
self.last_lines.pop(frame, None)
373399

374400
@panopticon("code", "line")
375-
def sysmon_line_lines(self, code: CodeType, line_number: int):
401+
def sysmon_line_lines(self, code: CodeType, line_number: int) -> MonitorReturn:
376402
"""Handle sys.monitoring.events.LINE events for line coverage."""
377403
code_info = self.code_infos[id(code)]
378404
if code_info.file_data is not None:
@@ -381,7 +407,7 @@ def sysmon_line_lines(self, code: CodeType, line_number: int):
381407
return sys.monitoring.DISABLE
382408

383409
@panopticon("code", "line")
384-
def sysmon_line_arcs(self, code: CodeType, line_number: int):
410+
def sysmon_line_arcs(self, code: CodeType, line_number: int) -> MonitorReturn:
385411
"""Handle sys.monitoring.events.LINE events for branch coverage."""
386412
code_info = self.code_infos[id(code)]
387413
ret = None

coverage/tracer.pyi

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from typing import Any, Dict
55

6-
from coverage.types import TFileDisposition, TTraceData, TTraceFn, TTracer
6+
from coverage.types import TFileDisposition, TTraceData, TTraceFn, TracerCore
77

88
class CFileDisposition(TFileDisposition):
99
canonical_filename: Any
@@ -15,7 +15,7 @@ class CFileDisposition(TFileDisposition):
1515
trace: Any
1616
def __init__(self) -> None: ...
1717

18-
class CTracer(TTracer):
18+
class CTracer(TracerCore):
1919
check_include: Any
2020
concur_id_func: Any
2121
data: TTraceData

coverage/types.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ class TFileDisposition(Protocol):
7878

7979
TTraceData = Dict[str, TTraceFileData]
8080

81-
class TTracer(Protocol):
82-
"""TODO: Either CTracer or PyTracer."""
81+
class TracerCore(Protocol):
82+
"""Anything that can report on Python execution."""
8383

8484
data: TTraceData
8585
trace_arcs: bool
@@ -92,8 +92,8 @@ class TTracer(Protocol):
9292
def __init__(self) -> None:
9393
...
9494

95-
def start(self) -> TTraceFn:
96-
"""Start this tracer, returning a trace function."""
95+
def start(self) -> TTraceFn | None:
96+
"""Start this tracer, return a trace function if based on sys.settrace."""
9797

9898
def stop(self) -> None:
9999
"""Stop this tracer."""
@@ -107,6 +107,7 @@ def reset_activity(self) -> None:
107107
def get_stats(self) -> Optional[Dict[str, int]]:
108108
"""Return a dictionary of statistics, or None."""
109109

110+
110111
## Coverage
111112

112113
# Many places use kwargs as Coverage kwargs.

tox.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ setenv =
112112

113113
commands =
114114
# PYVERSIONS
115-
mypy --python-version=3.8 {env:TYPEABLE}
115+
mypy --python-version=3.8 --exclude=sysmon {env:TYPEABLE}
116116
mypy --python-version=3.12 {env:TYPEABLE}
117117

118118
[gh]

0 commit comments

Comments
 (0)