1313# The dataclasses in this module are accessible in the template, which is overridable by the user.
1414# As a coutesy, we should do our best to keep the existing fields for backward compatibility,
1515# and if we really can't and can't add properties, at least bump the major version.
16- @dataclasses .dataclass
16+ @dataclasses .dataclass ( kw_only = True )
1717class CoverageMetadata :
1818 version : str
1919 timestamp : datetime .datetime
2020 branch_coverage : bool
2121 show_contexts : bool
2222
2323
24- @dataclasses .dataclass
24+ @dataclasses .dataclass ( kw_only = True )
2525class CoverageInfo :
2626 covered_lines : int
2727 num_statements : int
2828 percent_covered : decimal .Decimal
2929 missing_lines : int
3030 excluded_lines : int
31- num_branches : int | None
32- num_partial_branches : int | None
33- covered_branches : int | None
34- missing_branches : int | None
31+ num_branches : int = 0
32+ num_partial_branches : int = 0
33+ covered_branches : int = 0
34+ missing_branches : int = 0
3535
3636
37- @dataclasses .dataclass
37+ @dataclasses .dataclass ( kw_only = True )
3838class FileCoverage :
3939 path : pathlib .Path
4040 executed_lines : list [int ]
4141 missing_lines : list [int ]
4242 excluded_lines : list [int ]
4343 info : CoverageInfo
44+ executed_branches : list [list [int ]] | None = None
45+ missing_branches : list [list [int ]] | None = None
4446
4547
4648@dataclasses .dataclass
@@ -56,7 +58,7 @@ class Coverage:
5658# Maybe in v4, we can change it to a simpler format.
5759
5860
59- @dataclasses .dataclass
61+ @dataclasses .dataclass ( kw_only = True )
6062class FileDiffCoverage :
6163 path : pathlib .Path
6264 percent_covered : decimal .Decimal
@@ -73,7 +75,7 @@ def violation_lines(self) -> list[int]:
7375 return self .missing_statements
7476
7577
76- @dataclasses .dataclass
78+ @dataclasses .dataclass ( kw_only = True )
7779class DiffCoverage :
7880 total_num_lines : int
7981 total_num_violations : int
@@ -82,10 +84,18 @@ class DiffCoverage:
8284 files : dict [pathlib .Path , FileDiffCoverage ]
8385
8486
85- def compute_coverage (num_covered : int , num_total : int ) -> decimal .Decimal :
86- if num_total == 0 :
87+ def compute_coverage (
88+ num_covered : int ,
89+ num_total : int ,
90+ num_branches_covered : int = 0 ,
91+ num_branches_total : int = 0 ,
92+ ) -> decimal .Decimal :
93+ """Compute the coverage percentage, with or without branch coverage."""
94+ numerator = decimal .Decimal (num_covered + num_branches_covered )
95+ denominator = decimal .Decimal (num_total + num_branches_total )
96+ if denominator == 0 :
8797 return decimal .Decimal ("1" )
88- return decimal . Decimal ( num_covered ) / decimal . Decimal ( num_total )
98+ return numerator / denominator
8999
90100
91101def get_coverage_info (
@@ -138,6 +148,26 @@ def generate_coverage_markdown(coverage_path: pathlib.Path) -> str:
138148 )
139149
140150
151+ def _make_coverage_info (data : dict ) -> CoverageInfo :
152+ """Build a CoverageInfo object from a "summary" or "totals" key."""
153+ return CoverageInfo (
154+ covered_lines = data ["covered_lines" ],
155+ num_statements = data ["num_statements" ],
156+ percent_covered = compute_coverage (
157+ num_covered = data ["covered_lines" ],
158+ num_total = data ["num_statements" ],
159+ num_branches_covered = data .get ("covered_branches" , 0 ),
160+ num_branches_total = data .get ("num_branches" , 0 ),
161+ ),
162+ missing_lines = data ["missing_lines" ],
163+ excluded_lines = data ["excluded_lines" ],
164+ num_branches = data .get ("num_branches" , 0 ),
165+ num_partial_branches = data .get ("num_partial_branches" , 0 ),
166+ covered_branches = data .get ("covered_branches" , 0 ),
167+ missing_branches = data .get ("missing_branches" , 0 ),
168+ )
169+
170+
141171def extract_info (data : dict , coverage_path : pathlib .Path ) -> Coverage :
142172 """
143173 {
@@ -191,39 +221,13 @@ def extract_info(data: dict, coverage_path: pathlib.Path) -> Coverage:
191221 excluded_lines = file_data ["excluded_lines" ],
192222 executed_lines = file_data ["executed_lines" ],
193223 missing_lines = file_data ["missing_lines" ],
194- info = CoverageInfo (
195- covered_lines = file_data ["summary" ]["covered_lines" ],
196- num_statements = file_data ["summary" ]["num_statements" ],
197- percent_covered = compute_coverage (
198- file_data ["summary" ]["covered_lines" ],
199- file_data ["summary" ]["num_statements" ],
200- ),
201- missing_lines = file_data ["summary" ]["missing_lines" ],
202- excluded_lines = file_data ["summary" ]["excluded_lines" ],
203- num_branches = file_data ["summary" ].get ("num_branches" ),
204- num_partial_branches = file_data ["summary" ].get (
205- "num_partial_branches"
206- ),
207- covered_branches = file_data ["summary" ].get ("covered_branches" ),
208- missing_branches = file_data ["summary" ].get ("missing_branches" ),
209- ),
224+ executed_branches = file_data .get ("executed_branches" ),
225+ missing_branches = file_data .get ("missing_branches" ),
226+ info = _make_coverage_info (file_data ["summary" ]),
210227 )
211228 for path , file_data in data ["files" ].items ()
212229 },
213- info = CoverageInfo (
214- covered_lines = data ["totals" ]["covered_lines" ],
215- num_statements = data ["totals" ]["num_statements" ],
216- percent_covered = compute_coverage (
217- data ["totals" ]["covered_lines" ],
218- data ["totals" ]["num_statements" ],
219- ),
220- missing_lines = data ["totals" ]["missing_lines" ],
221- excluded_lines = data ["totals" ]["excluded_lines" ],
222- num_branches = data ["totals" ].get ("num_branches" ),
223- num_partial_branches = data ["totals" ].get ("num_partial_branches" ),
224- covered_branches = data ["totals" ].get ("covered_branches" ),
225- missing_branches = data ["totals" ].get ("missing_branches" ),
226- ),
230+ info = _make_coverage_info (data ["totals" ]),
227231 )
228232
229233
@@ -256,7 +260,8 @@ def get_diff_coverage_info(
256260 total_num_violations += count_missing
257261
258262 percent_covered = compute_coverage (
259- num_covered = count_executed , num_total = count_total
263+ num_covered = count_executed ,
264+ num_total = count_total ,
260265 )
261266
262267 files [path ] = FileDiffCoverage (
0 commit comments