diff options
| author | Ned Batchelder <ned@nedbatchelder.com> | 2022-12-29 10:19:14 -0500 |
|---|---|---|
| committer | Ned Batchelder <ned@nedbatchelder.com> | 2022-12-29 11:28:00 -0500 |
| commit | d4c2b18bdd0102ff873514e53ec560c3083c3413 (patch) | |
| tree | 4e91bacd91f5a0fbcf4976376bbc87e88d46b551 /coverage | |
| parent | 0accb68cd9ac353bd5464750987e02012bdb8e0c (diff) | |
| download | python-coveragepy-git-d4c2b18bdd0102ff873514e53ec560c3083c3413.tar.gz | |
mypy: mypy checks plugin.py
Diffstat (limited to 'coverage')
| -rw-r--r-- | coverage/config.py | 10 | ||||
| -rw-r--r-- | coverage/parser.py | 11 | ||||
| -rw-r--r-- | coverage/plugin.py | 84 | ||||
| -rw-r--r-- | coverage/types.py | 47 |
4 files changed, 105 insertions, 47 deletions
diff --git a/coverage/config.py b/coverage/config.py index 3e4a8dd0..1846aee4 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -17,18 +17,12 @@ from typing import ( from coverage.exceptions import ConfigError from coverage.misc import isolate_module, human_sorted_items, substitute_variables - from coverage.tomlconfig import TomlConfigParser, TomlDecodeError +from coverage.types import TConfigurable, TConfigSection, TConfigValue os = isolate_module(os) -# One value read from a config file. -TConfigValue = Union[str, List[str]] -# An entire config section, mapping option names to values. -TConfigSection = Dict[str, TConfigValue] - - class HandyConfigParser(configparser.ConfigParser): """Our specialization of ConfigParser.""" @@ -169,7 +163,7 @@ DEFAULT_PARTIAL_ALWAYS = [ ] -class CoverageConfig: +class CoverageConfig(TConfigurable): """Coverage.py configuration. The attributes of this class are the various settings that control the diff --git a/coverage/parser.py b/coverage/parser.py index 1e2011e2..09b2f094 100644 --- a/coverage/parser.py +++ b/coverage/parser.py @@ -15,8 +15,7 @@ import tokenize from types import CodeType from typing import ( - cast, TYPE_CHECKING, - Any, Callable, Dict, Iterable, List, Optional, Sequence, Set, Tuple, + cast, Any, Callable, Dict, Iterable, List, Optional, Sequence, Set, Tuple, ) from coverage import env @@ -25,13 +24,8 @@ from coverage.debug import short_stack from coverage.exceptions import NoSource, NotPython, _StopEverything from coverage.misc import join_regex, nice_pair from coverage.phystokens import generate_tokens +from coverage.types import Protocol, TArc -if TYPE_CHECKING: - # Protocol is new in 3.8. PYVERSIONS - from typing import Protocol -else: - class Protocol: # pylint: disable=missing-class-docstring - pass class PythonParser: """Parse code to find executable lines, excluded lines, etc. @@ -489,7 +483,6 @@ class TAddArcFn(Protocol): ) -> None: ... -TArc = Tuple[int, int] TArcFragments = Dict[TArc, List[Tuple[Optional[str], Optional[str]]]] class Block: diff --git a/coverage/plugin.py b/coverage/plugin.py index bf30b1b7..b6df72e4 100644 --- a/coverage/plugin.py +++ b/coverage/plugin.py @@ -112,16 +112,22 @@ register your dynamic context switcher. """ +from __future__ import annotations + import functools +from types import FrameType +from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Union + from coverage import files -from coverage.misc import contract, _needs_to_implement +from coverage.misc import _needs_to_implement +from coverage.types import TArc, TConfigurable class CoveragePlugin: """Base class for coverage.py plug-ins.""" - def file_tracer(self, filename): # pylint: disable=unused-argument + def file_tracer(self, filename: str) -> Optional[FileTracer]: # pylint: disable=unused-argument """Get a :class:`FileTracer` object for a file. Plug-in type: file tracer. @@ -161,7 +167,10 @@ class CoveragePlugin: """ return None - def file_reporter(self, filename): # pylint: disable=unused-argument + def file_reporter( # type: ignore[return] + self, + filename: str, # pylint: disable=unused-argument + ) -> Union[FileReporter, str]: # str should be Literal["python"] """Get the :class:`FileReporter` class to use for a file. Plug-in type: file tracer. @@ -175,7 +184,10 @@ class CoveragePlugin: """ _needs_to_implement(self, "file_reporter") - def dynamic_context(self, frame): # pylint: disable=unused-argument + def dynamic_context( + self, + frame: FrameType, # pylint: disable=unused-argument + ) -> Optional[str]: """Get the dynamically computed context label for `frame`. Plug-in type: dynamic context. @@ -191,7 +203,10 @@ class CoveragePlugin: """ return None - def find_executable_files(self, src_dir): # pylint: disable=unused-argument + def find_executable_files( + self, + src_dir: str, # pylint: disable=unused-argument + ) -> Iterable[str]: """Yield all of the executable files in `src_dir`, recursively. Plug-in type: file tracer. @@ -206,7 +221,7 @@ class CoveragePlugin: """ return [] - def configure(self, config): + def configure(self, config: TConfigurable) -> None: """Modify the configuration of coverage.py. Plug-in type: configurer. @@ -220,7 +235,7 @@ class CoveragePlugin: """ pass - def sys_info(self): + def sys_info(self) -> List[Tuple[str, str]]: """Get a list of information useful for debugging. Plug-in type: any. @@ -251,7 +266,7 @@ class FileTracer: """ - def source_filename(self): + def source_filename(self) -> str: # type: ignore[return] """The source file name for this file. This may be any file name you like. A key responsibility of a plug-in @@ -266,7 +281,7 @@ class FileTracer: """ _needs_to_implement(self, "source_filename") - def has_dynamic_source_filename(self): + def has_dynamic_source_filename(self) -> bool: """Does this FileTracer have dynamic source file names? FileTracers can provide dynamically determined file names by @@ -284,7 +299,11 @@ class FileTracer: """ return False - def dynamic_source_filename(self, filename, frame): # pylint: disable=unused-argument + def dynamic_source_filename( + self, + filename: str, # pylint: disable=unused-argument + frame: FrameType, # pylint: disable=unused-argument + ) -> Optional[str]: """Get a dynamically computed source file name. Some plug-ins need to compute the source file name dynamically for each @@ -299,7 +318,7 @@ class FileTracer: """ return None - def line_number_range(self, frame): + def line_number_range(self, frame: FrameType) -> Tuple[int, int]: """Get the range of source line numbers for a given a call frame. The call frame is examined, and the source line number in the original @@ -331,7 +350,7 @@ class FileReporter: """ - def __init__(self, filename): + def __init__(self, filename: str) -> None: """Simple initialization of a `FileReporter`. The `filename` argument is the path to the file being reported. This @@ -341,10 +360,10 @@ class FileReporter: """ self.filename = filename - def __repr__(self): + def __repr__(self) -> str: return "<{0.__class__.__name__} filename={0.filename!r}>".format(self) - def relative_filename(self): + def relative_filename(self) -> str: """Get the relative file name for this file. This file path will be displayed in reports. The default @@ -355,8 +374,7 @@ class FileReporter: """ return files.relative_filename(self.filename) - @contract(returns='unicode') - def source(self): + def source(self) -> str: """Get the source for the file. Returns a Unicode string. @@ -366,10 +384,10 @@ class FileReporter: as a text file, or if you need other encoding support. """ - with open(self.filename, "rb") as f: - return f.read().decode("utf-8") + with open(self.filename, encoding="utf-8") as f: + return f.read() - def lines(self): + def lines(self) -> Set[int]: # type: ignore[return] """Get the executable lines in this file. Your plug-in must determine which lines in the file were possibly @@ -380,7 +398,7 @@ class FileReporter: """ _needs_to_implement(self, "lines") - def excluded_lines(self): + def excluded_lines(self) -> Set[int]: """Get the excluded executable lines in this file. Your plug-in can use any method it likes to allow the user to exclude @@ -393,7 +411,7 @@ class FileReporter: """ return set() - def translate_lines(self, lines): + def translate_lines(self, lines: Iterable[int]) -> Set[int]: """Translate recorded lines into reported lines. Some file formats will want to report lines slightly differently than @@ -413,7 +431,7 @@ class FileReporter: """ return set(lines) - def arcs(self): + def arcs(self) -> Set[TArc]: """Get the executable arcs in this file. To support branch coverage, your plug-in needs to be able to indicate @@ -427,7 +445,7 @@ class FileReporter: """ return set() - def no_branch_lines(self): + def no_branch_lines(self) -> Set[int]: """Get the lines excused from branch coverage in this file. Your plug-in can use any method it likes to allow the user to exclude @@ -440,7 +458,7 @@ class FileReporter: """ return set() - def translate_arcs(self, arcs): + def translate_arcs(self, arcs: Set[TArc]) -> Set[TArc]: """Translate recorded arcs into reported arcs. Similar to :meth:`translate_lines`, but for arcs. `arcs` is a set of @@ -453,7 +471,7 @@ class FileReporter: """ return arcs - def exit_counts(self): + def exit_counts(self) -> Dict[int, int]: """Get a count of exits from that each line. To determine which lines are branches, coverage.py looks for lines that @@ -466,7 +484,12 @@ class FileReporter: """ return {} - def missing_arc_description(self, start, end, executed_arcs=None): # pylint: disable=unused-argument + def missing_arc_description( + self, + start: int, + end: int, + executed_arcs: Optional[Set[TArc]]=None, # pylint: disable=unused-argument + ) -> str: """Provide an English sentence describing a missing arc. The `start` and `end` arguments are the line numbers of the missing @@ -481,7 +504,7 @@ class FileReporter: """ return f"Line {start} didn't jump to line {end}" - def source_token_lines(self): + def source_token_lines(self) -> Iterable[List[Tuple[str, str]]]: """Generate a series of tokenized lines, one for each line in `source`. These tokens are used for syntax-colored reports. @@ -512,10 +535,11 @@ class FileReporter: for line in self.source().splitlines(): yield [('txt', line)] - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: return isinstance(other, FileReporter) and self.filename == other.filename - def __lt__(self, other): + def __lt__(self, other: Any) -> bool: return isinstance(other, FileReporter) and self.filename < other.filename - __hash__ = None # This object doesn't need to be hashed. + # This object doesn't need to be hashed. + __hash__ = None # type: ignore[assignment] diff --git a/coverage/types.py b/coverage/types.py new file mode 100644 index 00000000..23c7ef8b --- /dev/null +++ b/coverage/types.py @@ -0,0 +1,47 @@ +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +""" +Types for use throughout coverage.py. +""" + +from typing import Dict, List, Optional, Tuple, Union, TYPE_CHECKING + +if TYPE_CHECKING: + # Protocol is new in 3.8. PYVERSIONS + from typing import Protocol +else: + class Protocol: # pylint: disable=missing-class-docstring + pass + +# One value read from a config file. +TConfigValue = Union[str, List[str]] +# An entire config section, mapping option names to values. +TConfigSection = Dict[str, TConfigValue] + +class TConfigurable(Protocol): + """Something that can proxy to the coverage configuration settings.""" + + def get_option(self, option_name: str) -> Optional[TConfigValue]: + """Get an option from the configuration. + + `option_name` is a colon-separated string indicating the section and + option name. For example, the ``branch`` option in the ``[run]`` + section of the config file would be indicated with `"run:branch"`. + + Returns the value of the option. + + """ + + def set_option(self, option_name: str, value: Union[TConfigValue, TConfigSection]) -> None: + """Set an option in the configuration. + + `option_name` is a colon-separated string indicating the section and + option name. For example, the ``branch`` option in the ``[run]`` + section of the config file would be indicated with `"run:branch"`. + + `value` is the new value for the option. + + """ + +TArc = Tuple[int, int] |
