summaryrefslogtreecommitdiff
path: root/coverage/xmlreport.py
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2023-01-01 20:32:43 -0500
committerNed Batchelder <ned@nedbatchelder.com>2023-01-01 20:32:43 -0500
commit5580cf8a97841f0a385ebd048aa389f942d316f5 (patch)
treeae4b135d9f29892229b035acf62240aabd7dac2f /coverage/xmlreport.py
parent0c9b5e0e9da9c2cffd50db7b28142d22d0f66cee (diff)
downloadpython-coveragepy-git-5580cf8a97841f0a385ebd048aa389f942d316f5.tar.gz
mypy: xmlreport.py
Diffstat (limited to 'coverage/xmlreport.py')
-rw-r--r--coverage/xmlreport.py73
1 files changed, 47 insertions, 26 deletions
diff --git a/coverage/xmlreport.py b/coverage/xmlreport.py
index 5eb940bf..19a8dba5 100644
--- a/coverage/xmlreport.py
+++ b/coverage/xmlreport.py
@@ -3,15 +3,26 @@
"""XML reporting for coverage.py"""
+from __future__ import annotations
+
import os
import os.path
import sys
import time
import xml.dom.minidom
+from dataclasses import dataclass
+from typing import Dict, IO, Iterable, Optional, TYPE_CHECKING, cast
+
from coverage import __url__, __version__, files
from coverage.misc import isolate_module, human_sorted, human_sorted_items
+from coverage.plugin import FileReporter
from coverage.report import get_analysis_to_report
+from coverage.results import Analysis
+from coverage.types import TMorf
+
+if TYPE_CHECKING:
+ from coverage import Coverage
os = isolate_module(os)
@@ -19,12 +30,22 @@ os = isolate_module(os)
DTD_URL = 'https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd'
-def rate(hit, num):
+def rate(hit: int, num: int) -> str:
"""Return the fraction of `hit`/`num`, as a string."""
if num == 0:
return "1"
else:
- return "%.4g" % (float(hit) / num)
+ return "%.4g" % (hit / num)
+
+
+@dataclass
+class PackageData:
+ """Data we keep about each "package" (in Java terms)."""
+ elements: Dict[str, xml.dom.minidom.Element]
+ hits: int
+ lines: int
+ br_hits: int
+ branches: int
class XmlReporter:
@@ -32,7 +53,7 @@ class XmlReporter:
report_type = "XML report"
- def __init__(self, coverage):
+ def __init__(self, coverage: Coverage) -> None:
self.coverage = coverage
self.config = self.coverage.config
@@ -43,10 +64,10 @@ class XmlReporter:
if not self.config.relative_files:
src = files.canonical_filename(src)
self.source_paths.add(src)
- self.packages = {}
- self.xml_out = None
+ self.packages: Dict[str, PackageData] = {}
+ self.xml_out: xml.dom.minidom.Document
- def report(self, morfs, outfile=None):
+ def report(self, morfs: Optional[Iterable[TMorf]], outfile: Optional[IO[str]]=None) -> float:
"""Generate a Cobertura-compatible XML report for `morfs`.
`morfs` is a list of modules or file names.
@@ -60,6 +81,7 @@ class XmlReporter:
# Create the DOM that will store the data.
impl = xml.dom.minidom.getDOMImplementation()
+ assert impl is not None
self.xml_out = impl.createDocument(None, "coverage", None)
# Write header stuff.
@@ -93,26 +115,25 @@ class XmlReporter:
# Populate the XML DOM with the package info.
for pkg_name, pkg_data in human_sorted_items(self.packages.items()):
- class_elts, lhits, lnum, bhits, bnum = pkg_data
xpackage = self.xml_out.createElement("package")
xpackages.appendChild(xpackage)
xclasses = self.xml_out.createElement("classes")
xpackage.appendChild(xclasses)
- for _, class_elt in human_sorted_items(class_elts.items()):
+ for _, class_elt in human_sorted_items(pkg_data.elements.items()):
xclasses.appendChild(class_elt)
xpackage.setAttribute("name", pkg_name.replace(os.sep, '.'))
- xpackage.setAttribute("line-rate", rate(lhits, lnum))
+ xpackage.setAttribute("line-rate", rate(pkg_data.hits, pkg_data.lines))
if has_arcs:
- branch_rate = rate(bhits, bnum)
+ branch_rate = rate(pkg_data.br_hits, pkg_data.branches)
else:
branch_rate = "0"
xpackage.setAttribute("branch-rate", branch_rate)
xpackage.setAttribute("complexity", "0")
- lnum_tot += lnum
- lhits_tot += lhits
- bnum_tot += bnum
- bhits_tot += bhits
+ lhits_tot += pkg_data.hits
+ lnum_tot += pkg_data.lines
+ bhits_tot += pkg_data.br_hits
+ bnum_tot += pkg_data.branches
xcoverage.setAttribute("lines-valid", str(lnum_tot))
xcoverage.setAttribute("lines-covered", str(lhits_tot))
@@ -138,7 +159,7 @@ class XmlReporter:
pct = 100.0 * (lhits_tot + bhits_tot) / denom
return pct
- def xml_file(self, fr, analysis, has_arcs):
+ def xml_file(self, fr: FileReporter, analysis: Analysis, has_arcs: bool) -> None:
"""Add to the XML report for a single file."""
if self.config.skip_empty:
@@ -162,9 +183,9 @@ class XmlReporter:
dirname = "/".join(dirname.split("/")[:self.config.xml_package_depth])
package_name = dirname.replace("/", ".")
- package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0])
+ package = self.packages.setdefault(package_name, PackageData({}, 0, 0, 0, 0))
- xclass = self.xml_out.createElement("class")
+ xclass: xml.dom.minidom.Element = self.xml_out.createElement("class")
xclass.appendChild(self.xml_out.createElement("methods"))
@@ -208,8 +229,8 @@ class XmlReporter:
missing_branches = sum(t - k for t, k in branch_stats.values())
class_br_hits = class_branches - missing_branches
else:
- class_branches = 0.0
- class_br_hits = 0.0
+ class_branches = 0
+ class_br_hits = 0
# Finalize the statistics that are collected in the XML DOM.
xclass.setAttribute("line-rate", rate(class_hits, class_lines))
@@ -219,13 +240,13 @@ class XmlReporter:
branch_rate = "0"
xclass.setAttribute("branch-rate", branch_rate)
- package[0][rel_name] = xclass
- package[1] += class_hits
- package[2] += class_lines
- package[3] += class_br_hits
- package[4] += class_branches
+ package.elements[rel_name] = xclass
+ package.hits += class_hits
+ package.lines += class_lines
+ package.br_hits += class_br_hits
+ package.branches += class_branches
-def serialize_xml(dom):
+def serialize_xml(dom: xml.dom.minidom.Document) -> str:
"""Serialize a minidom node to XML."""
- return dom.toprettyxml()
+ return cast(str, dom.toprettyxml())