Skip to content

Commit

Permalink
refactor: Analysis dataclass, slimmer and narrowable
Browse files Browse the repository at this point in the history
  • Loading branch information
nedbat committed Apr 22, 2024
1 parent 15f9c1c commit 4e50273
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 134 deletions.
4 changes: 2 additions & 2 deletions coverage/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from coverage.debug import info_header, short_stack, write_formatted_info
from coverage.exceptions import _BaseCoverageException, _ExceptionDuringRun, NoSource
from coverage.execfile import PyRunner
from coverage.results import Numbers, should_fail_under
from coverage.results import display_covered, should_fail_under
from coverage.version import __url__

# When adding to this file, alphabetization is important. Look for
Expand Down Expand Up @@ -760,7 +760,7 @@ def command_line(self, argv: list[str]) -> int:
precision = cast(int, self.coverage.get_option("report:precision"))
if should_fail_under(total, fail_under, precision):
msg = "total of {total} is less than fail-under={fail_under:.{p}f}".format(
total=Numbers(precision=precision).display_covered(total),
total=display_covered(total, precision),
fail_under=fail_under,
p=precision,
)
Expand Down
34 changes: 14 additions & 20 deletions coverage/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
from coverage.python import PythonFileReporter
from coverage.report import SummaryReporter
from coverage.report_core import render_report
from coverage.results import Analysis
from coverage.results import Analysis, analysis_from_file_reporter
from coverage.types import (
FilePath, TConfigurable, TConfigSectionIn, TConfigValueIn, TConfigValueOut,
TFileDisposition, TLineNo, TMorf,
Expand Down Expand Up @@ -922,7 +922,7 @@ def analysis2(
coverage data.
"""
analysis = self._analyze(morf)
analysis = self.analyze(morf)
return (
analysis.filename,
sorted(analysis.statements),
Expand All @@ -931,23 +931,15 @@ def analysis2(
analysis.missing_formatted(),
)

def _analyze(self, it: FileReporter | TMorf) -> Analysis:
"""Analyze a single morf or code unit.
Returns an `Analysis` object.
"""
# All reporting comes through here, so do reporting initialization.
def analyze(self, morf: TMorf) -> Analysis:
"""A public API for getting Analysis. TODO!!! """
self._init()
self._post_init()

data = self.get_data()
if isinstance(it, FileReporter):
fr = it
else:
fr = self._get_file_reporter(it)

return Analysis(data, self.config.precision, fr, self._file_mapper)
file_reporter = self._get_file_reporter(morf)
filename = self._file_mapper(file_reporter.filename)
return analysis_from_file_reporter(data, self.config.precision, file_reporter, filename)

@functools.lru_cache(maxsize=1)
def _get_file_reporter(self, morf: TMorf) -> FileReporter:
Expand Down Expand Up @@ -977,11 +969,14 @@ def _get_file_reporter(self, morf: TMorf) -> FileReporter:
assert isinstance(file_reporter, FileReporter)
return file_reporter

def _get_file_reporters(self, morfs: Iterable[TMorf] | None = None) -> list[FileReporter]:
"""Get a list of FileReporters for a list of modules or file names.
def _get_file_reporters(
self,
morfs: Iterable[TMorf] | None = None,
) -> list[tuple[FileReporter, TMorf]]:
"""Get FileReporters for a list of modules or file names.
For each module or file name in `morfs`, find a FileReporter. Return
the list of FileReporters.
a list pairing FileReporters with the morfs.
If `morfs` is a single module or file name, this returns a list of one
FileReporter. If `morfs` is empty or None, then the list of all files
Expand All @@ -996,8 +991,7 @@ def _get_file_reporters(self, morfs: Iterable[TMorf] | None = None) -> list[File
if not isinstance(morfs, (list, tuple, set)):
morfs = [morfs] # type: ignore[list-item]

file_reporters = [self._get_file_reporter(morf) for morf in morfs]
return file_reporters
return [(self._get_file_reporter(morf), morf) for morf in morfs]

def _prepare_data_for_reporting(self) -> None:
"""Re-map data before reporting, to get implicit "combine" behavior."""
Expand Down
12 changes: 6 additions & 6 deletions coverage/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,21 +112,21 @@ class HtmlDataGeneration:
def __init__(self, cov: Coverage) -> None:
self.coverage = cov
self.config = self.coverage.config
data = self.coverage.get_data()
self.has_arcs = data.has_arcs()
self.data = self.coverage.get_data()
self.has_arcs = self.data.has_arcs()
if self.config.show_contexts:
if data.measured_contexts() == {""}:
if self.data.measured_contexts() == {""}:
self.coverage._warn("No contexts were measured")
data.set_query_contexts(self.config.report_contexts)
self.data.set_query_contexts(self.config.report_contexts)

def data_for_file(self, fr: FileReporter, analysis: Analysis) -> FileData:
"""Produce the data needed for one file's report."""
if self.has_arcs:
missing_branch_arcs = analysis.missing_branch_arcs()
arcs_executed = analysis.arcs_executed()
arcs_executed = analysis.arcs_executed

if self.config.show_contexts:
contexts_by_lineno = analysis.data.contexts_by_lineno(analysis.filename)
contexts_by_lineno = self.data.contexts_by_lineno(analysis.filename)

lines = []

Expand Down
2 changes: 1 addition & 1 deletion coverage/jsonreport.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def report_one_file(self, coverage_data: CoverageData, analysis: Analysis) -> di
"excluded_lines": sorted(analysis.excluded),
}
if self.config.json_show_contexts:
reported_file["contexts"] = analysis.data.contexts_by_lineno(analysis.filename)
reported_file["contexts"] = coverage_data.contexts_by_lineno(analysis.filename)
if coverage_data.has_arcs():
summary.update({
"num_branches": nums.n_branches,
Expand Down
2 changes: 1 addition & 1 deletion coverage/lcovreport.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def get_lcov(self, fr: FileReporter, analysis: Analysis, outfile: IO[str]) -> No
outfile.write(f"BRDA:{line_number},{block_number},{branch_number},1\n")

# Summary of the branch coverage.
if analysis.has_arcs():
if analysis.has_arcs:
branch_stats = analysis.branch_stats()
brf = sum(t for t, k in branch_stats.values())
brh = brf - sum(t - k for t, k in branch_stats.values())
Expand Down
12 changes: 6 additions & 6 deletions coverage/report_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,23 +81,23 @@ def get_analysis_to_report(
`Analysis` for the morf.
"""
file_reporters = coverage._get_file_reporters(morfs)
fr_morfs = coverage._get_file_reporters(morfs)
config = coverage.config

if config.report_include:
matcher = GlobMatcher(prep_patterns(config.report_include), "report_include")
file_reporters = [fr for fr in file_reporters if matcher.match(fr.filename)]
fr_morfs = [(fr, morf) for (fr, morf) in fr_morfs if matcher.match(fr.filename)]

if config.report_omit:
matcher = GlobMatcher(prep_patterns(config.report_omit), "report_omit")
file_reporters = [fr for fr in file_reporters if not matcher.match(fr.filename)]
fr_morfs = [(fr, morf) for (fr, morf) in fr_morfs if not matcher.match(fr.filename)]

if not file_reporters:
if not fr_morfs:
raise NoDataError("No data to report.")

for fr in sorted(file_reporters):
for fr, morf in sorted(fr_morfs):
try:
analysis = coverage._analyze(fr)
analysis = coverage.analyze(morf)
except NotPython:
# Only report errors for .py files, and only if we didn't
# explicitly suppress those errors.
Expand Down
Loading

0 comments on commit 4e50273

Please sign in to comment.