Skip to content

Commit e3c523c

Browse files
committed
mypy: add __init__.py, __main__.py, bytecode.py, context.py, exceptions.py, report.py, and version.py
1 parent 5a72a1e commit e3c523c

File tree

5 files changed

+66
-18
lines changed

5 files changed

+66
-18
lines changed

coverage/bytecode.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@
33

44
"""Bytecode manipulation for coverage.py"""
55

6-
import types
6+
from types import CodeType
7+
from typing import Generator
78

89

9-
def code_objects(code):
10+
def code_objects(code: CodeType) -> Generator[CodeType, None, None]:
1011
"""Iterate over all the code objects in `code`."""
1112
stack = [code]
1213
while stack:
1314
# We're going to return the code object on the stack, but first
1415
# push its children for later returning.
1516
code = stack.pop()
1617
for c in code.co_consts:
17-
if isinstance(c, types.CodeType):
18+
if isinstance(c, CodeType):
1819
stack.append(c)
1920
yield code

coverage/context.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@
33

44
"""Determine contexts for coverage.py"""
55

6+
from types import FrameType
7+
from typing import cast, Callable, Optional, Sequence
68

7-
def combine_context_switchers(context_switchers):
9+
10+
def combine_context_switchers(
11+
context_switchers: Sequence[Callable[[FrameType], Optional[str]]],
12+
) -> Optional[Callable[[FrameType], Optional[str]]]:
813
"""Create a single context switcher from multiple switchers.
914
1015
`context_switchers` is a list of functions that take a frame as an
@@ -23,7 +28,7 @@ def combine_context_switchers(context_switchers):
2328
if len(context_switchers) == 1:
2429
return context_switchers[0]
2530

26-
def should_start_context(frame):
31+
def should_start_context(frame: FrameType) -> Optional[str]:
2732
"""The combiner for multiple context switchers."""
2833
for switcher in context_switchers:
2934
new_context = switcher(frame)
@@ -34,15 +39,15 @@ def should_start_context(frame):
3439
return should_start_context
3540

3641

37-
def should_start_context_test_function(frame):
42+
def should_start_context_test_function(frame: FrameType) -> Optional[str]:
3843
"""Is this frame calling a test_* function?"""
3944
co_name = frame.f_code.co_name
4045
if co_name.startswith("test") or co_name == "runTest":
4146
return qualname_from_frame(frame)
4247
return None
4348

4449

45-
def qualname_from_frame(frame):
50+
def qualname_from_frame(frame: FrameType) -> Optional[str]:
4651
"""Get a qualified name for the code running in `frame`."""
4752
co = frame.f_code
4853
fname = co.co_name
@@ -55,11 +60,11 @@ def qualname_from_frame(frame):
5560
func = frame.f_globals.get(fname)
5661
if func is None:
5762
return None
58-
return func.__module__ + "." + fname
63+
return cast(str, func.__module__ + "." + fname)
5964

6065
func = getattr(method, "__func__", None)
6166
if func is None:
6267
cls = self.__class__
63-
return cls.__module__ + "." + cls.__name__ + "." + fname
68+
return cast(str, cls.__module__ + "." + cls.__name__ + "." + fname)
6469

65-
return func.__module__ + "." + func.__qualname__
70+
return cast(str, func.__module__ + "." + func.__qualname__)

coverage/report.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,38 @@
33

44
"""Reporter foundation for coverage.py."""
55

6+
from __future__ import annotations
7+
68
import sys
79

10+
from typing import Callable, Iterable, Iterator, IO, Optional, Tuple, TYPE_CHECKING
11+
812
from coverage.exceptions import CoverageException, NoDataError, NotPython
913
from coverage.files import prep_patterns, GlobMatcher
1014
from coverage.misc import ensure_dir_for_file, file_be_gone
15+
from coverage.plugin import FileReporter
16+
from coverage.results import Analysis
17+
from coverage.types import Protocol, TMorf
18+
19+
if TYPE_CHECKING:
20+
from coverage import Coverage
21+
22+
23+
class Reporter(Protocol):
24+
"""What we expect of reporters."""
25+
26+
report_type: str
27+
28+
def report(self, morfs: Optional[Iterable[TMorf]], outfile: IO[str]) -> float:
29+
"""Generate a report of `morfs`, written to `outfile`."""
1130

1231

13-
def render_report(output_path, reporter, morfs, msgfn) -> float:
32+
def render_report(
33+
output_path: str,
34+
reporter: Reporter,
35+
morfs: Optional[Iterable[TMorf]],
36+
msgfn: Callable[[str], None],
37+
) -> float:
1438
"""Run a one-file report generator, managing the output file.
1539
1640
This function ensures the output file is ready to be written to. Then writes
@@ -45,7 +69,10 @@ def render_report(output_path, reporter, morfs, msgfn) -> float:
4569
msgfn(f"Wrote {reporter.report_type} to {output_path}")
4670

4771

48-
def get_analysis_to_report(coverage, morfs):
72+
def get_analysis_to_report(
73+
coverage: Coverage,
74+
morfs: Iterable[TMorf]
75+
) -> Iterator[Tuple[FileReporter, Analysis]]:
4976
"""Get the files to report on.
5077
5178
For each morf in `morfs`, if it should be reported on (based on the omit
@@ -75,7 +102,7 @@ def get_analysis_to_report(coverage, morfs):
75102
# explicitly suppress those errors.
76103
# NotPython is only raised by PythonFileReporter, which has a
77104
# should_be_python() method.
78-
if fr.should_be_python():
105+
if fr.should_be_python(): # type: ignore[attr-defined]
79106
if config.ignore_errors:
80107
msg = f"Couldn't parse Python file '{fr.filename}'"
81108
coverage._warn(msg, slug="couldnt-parse")

coverage/version.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,14 @@
1010
_dev = 1
1111

1212

13-
def _make_version(major, minor, micro, releaselevel="final", serial=0, dev=0):
13+
def _make_version(
14+
major: int,
15+
minor: int,
16+
micro: int,
17+
releaselevel: str="final",
18+
serial: int=0,
19+
dev: int=0,
20+
) -> str:
1421
"""Create a readable version string from version_info tuple components."""
1522
assert releaselevel in ['alpha', 'beta', 'candidate', 'final']
1623
version = "%d.%d.%d" % (major, minor, micro)
@@ -22,7 +29,14 @@ def _make_version(major, minor, micro, releaselevel="final", serial=0, dev=0):
2229
return version
2330

2431

25-
def _make_url(major, minor, micro, releaselevel, serial=0, dev=0):
32+
def _make_url(
33+
major: int,
34+
minor: int,
35+
micro: int,
36+
releaselevel: str,
37+
serial: int=0,
38+
dev: int=0,
39+
) -> str:
2640
"""Make the URL people should start at for this version of coverage.py."""
2741
url = "https://coverage.readthedocs.io"
2842
if releaselevel != "final" or dev != 0:

tox.ini

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,13 @@ deps =
9595

9696
setenv =
9797
{[testenv]setenv}
98-
C_AE=coverage/config.py coverage/control.py coverage/data.py coverage/disposition.py
98+
C__B=coverage/__init__.py coverage/__main__.py coverage/bytecode.py
99+
C_CE=coverage/config.py coverage/context.py coverage/control.py coverage/data.py coverage/disposition.py coverage/exceptions.py
99100
C_FN=coverage/files.py coverage/inorout.py coverage/multiproc.py coverage/numbits.py
100101
C_OP=coverage/parser.py coverage/phystokens.py coverage/plugin.py coverage/python.py
101-
C_QZ=coverage/results.py coverage/sqldata.py coverage/tomlconfig.py coverage/types.py
102+
C_QZ=coverage/report.py coverage/results.py coverage/sqldata.py coverage/tomlconfig.py coverage/types.py coverage/version.py
102103
T_AN=tests/test_api.py tests/goldtest.py tests/helpers.py tests/test_html.py
103-
TYPEABLE={env:C_AE} {env:C_FN} {env:C_OP} {env:C_QZ} {env:T_AN}
104+
TYPEABLE={env:C__B} {env:C_CE} {env:C_FN} {env:C_OP} {env:C_QZ} {env:T_AN}
104105

105106
commands =
106107
# PYVERSIONS

0 commit comments

Comments
 (0)