summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2021-06-01 06:10:39 -0400
committerNed Batchelder <ned@nedbatchelder.com>2021-06-01 06:10:39 -0400
commit5ed7c17b6f8e3a4b9b9826d2aa4459695b9e5a86 (patch)
tree197e95d7fce3c70df04d12e947302ed7dbade701
parent1157999dc5c6c3aa3129a7cd4bf249fe73598501 (diff)
downloadpython-coveragepy-git-5ed7c17b6f8e3a4b9b9826d2aa4459695b9e5a86.tar.gz
refactor: remove globals from Numbers
-rw-r--r--coverage/cmdline.py4
-rw-r--r--coverage/control.py5
-rw-r--r--coverage/html.py2
-rw-r--r--coverage/jsonreport.py2
-rw-r--r--coverage/results.py55
-rw-r--r--coverage/summary.py4
-rw-r--r--tests/test_results.py41
7 files changed, 46 insertions, 67 deletions
diff --git a/coverage/cmdline.py b/coverage/cmdline.py
index 697d2960..3d8dcf85 100644
--- a/coverage/cmdline.py
+++ b/coverage/cmdline.py
@@ -656,7 +656,9 @@ class CoverageScript:
precision = 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.display_covered(total), fail_under=fail_under, p=precision,
+ total=Numbers(precision=precision).display_covered(total),
+ fail_under=fail_under,
+ p=precision,
)
print("Coverage failure:", msg)
return FAIL_UNDER
diff --git a/coverage/control.py b/coverage/control.py
index b13acf45..d609c8c7 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -32,7 +32,7 @@ from coverage.plugin import FileReporter
from coverage.plugin_support import Plugins
from coverage.python import PythonFileReporter
from coverage.report import render_report
-from coverage.results import Analysis, Numbers
+from coverage.results import Analysis
from coverage.summary import SummaryReporter
from coverage.xmlreport import XmlReporter
@@ -799,14 +799,13 @@ class Coverage:
"""
# All reporting comes through here, so do reporting initialization.
self._init()
- Numbers.set_precision(self.config.precision)
self._post_init()
data = self.get_data()
if not isinstance(it, FileReporter):
it = self._get_file_reporter(it)
- return Analysis(data, it, self._file_mapper)
+ return Analysis(data, self.config.precision, it, self._file_mapper)
def _get_file_reporter(self, morf):
"""Get a FileReporter for a module or file name."""
diff --git a/coverage/html.py b/coverage/html.py
index 7626f54e..15f35a66 100644
--- a/coverage/html.py
+++ b/coverage/html.py
@@ -195,7 +195,7 @@ class HtmlReporter:
self.all_files_nums = []
self.incr = IncrementalChecker(self.directory)
self.datagen = HtmlDataGeneration(self.coverage)
- self.totals = Numbers()
+ self.totals = Numbers(precision=self.config.precision)
self.template_globals = {
# Functions available in the templates.
diff --git a/coverage/jsonreport.py b/coverage/jsonreport.py
index 70ceb71f..b22ab10b 100644
--- a/coverage/jsonreport.py
+++ b/coverage/jsonreport.py
@@ -17,7 +17,7 @@ class JsonReporter:
def __init__(self, coverage):
self.coverage = coverage
self.config = self.coverage.config
- self.total = Numbers()
+ self.total = Numbers(self.config.precision)
self.report_data = {}
def report(self, morfs, outfile=None):
diff --git a/coverage/results.py b/coverage/results.py
index f7331b41..60ba4384 100644
--- a/coverage/results.py
+++ b/coverage/results.py
@@ -13,7 +13,7 @@ from coverage.misc import contract, nice_pair
class Analysis:
"""The results of analyzing a FileReporter."""
- def __init__(self, data, file_reporter, file_mapper):
+ def __init__(self, data, precision, file_reporter, file_mapper):
self.data = data
self.file_reporter = file_reporter
self.filename = file_mapper(self.file_reporter.filename)
@@ -41,6 +41,7 @@ class Analysis:
n_branches = n_partial_branches = n_missing_branches = 0
self.numbers = Numbers(
+ precision=precision,
n_files=1,
n_statements=len(self.statements),
n_excluded=len(self.excluded),
@@ -158,15 +159,16 @@ class Numbers(SimpleReprMixin):
up statistics across files.
"""
- # A global to determine the precision on coverage percentages, the number
- # of decimal places.
- _precision = 0
- _near0 = 1.0 # These will change when _precision is changed.
- _near100 = 99.0
-
- def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0,
- n_branches=0, n_partial_branches=0, n_missing_branches=0
- ):
+
+ def __init__(self,
+ precision=0,
+ n_files=0, n_statements=0, n_excluded=0, n_missing=0,
+ n_branches=0, n_partial_branches=0, n_missing_branches=0
+ ):
+ assert 0 <= precision < 10
+ self._precision = precision
+ self._near0 = 1.0 / 10**precision
+ self._near100 = 100.0 - self._near0
self.n_files = n_files
self.n_statements = n_statements
self.n_excluded = n_excluded
@@ -178,18 +180,11 @@ class Numbers(SimpleReprMixin):
def init_args(self):
"""Return a list for __init__(*args) to recreate this object."""
return [
+ self._precision,
self.n_files, self.n_statements, self.n_excluded, self.n_missing,
self.n_branches, self.n_partial_branches, self.n_missing_branches,
]
- @classmethod
- def set_precision(cls, precision):
- """Set the number of decimal places used to report percentages."""
- assert 0 <= precision < 10
- cls._precision = precision
- cls._near0 = 1.0 / 10**precision
- cls._near100 = 100.0 - cls._near0
-
@property
def n_executed(self):
"""Returns the number of executed statements."""
@@ -221,8 +216,7 @@ class Numbers(SimpleReprMixin):
"""
return self.display_covered(self.pc_covered)
- @classmethod
- def display_covered(cls, pc):
+ def display_covered(self, pc):
"""Return a displayable total percentage, as a string.
Note that "0" is only returned when the value is truly zero, and "100"
@@ -230,20 +224,19 @@ class Numbers(SimpleReprMixin):
result in either "0" or "100".
"""
- if 0 < pc < cls._near0:
- pc = cls._near0
- elif cls._near100 < pc < 100:
- pc = cls._near100
+ if 0 < pc < self._near0:
+ pc = self._near0
+ elif self._near100 < pc < 100:
+ pc = self._near100
else:
- pc = round(pc, cls._precision)
- return "%.*f" % (cls._precision, pc)
+ pc = round(pc, self._precision)
+ return "%.*f" % (self._precision, pc)
- @classmethod
- def pc_str_width(cls):
+ def pc_str_width(self):
"""How many characters wide can pc_covered_str be?"""
width = 3 # "100"
- if cls._precision > 0:
- width += 1 + cls._precision
+ if self._precision > 0:
+ width += 1 + self._precision
return width
@property
@@ -254,7 +247,7 @@ class Numbers(SimpleReprMixin):
return numerator, denominator
def __add__(self, other):
- nums = Numbers()
+ nums = Numbers(precision=self._precision)
nums.n_files = self.n_files + other.n_files
nums.n_statements = self.n_statements + other.n_statements
nums.n_excluded = self.n_excluded + other.n_excluded
diff --git a/coverage/summary.py b/coverage/summary.py
index 0597a2aa..b7b172f8 100644
--- a/coverage/summary.py
+++ b/coverage/summary.py
@@ -21,7 +21,7 @@ class SummaryReporter:
self.fr_analysis = []
self.skipped_count = 0
self.empty_count = 0
- self.total = Numbers()
+ self.total = Numbers(precision=self.config.precision)
self.fmt_err = "%s %s: %s"
def writeout(self, line):
@@ -53,7 +53,7 @@ class SummaryReporter:
if self.branches:
header += " Branch BrPart"
fmt_coverage += " %6d %6d"
- width100 = Numbers.pc_str_width()
+ width100 = Numbers(precision=self.config.precision).pc_str_width()
header += "%*s" % (width100+4, "Cover")
fmt_coverage += "%%%ds%%%%" % (width100+3,)
if self.config.show_missing:
diff --git a/tests/test_results.py b/tests/test_results.py
index fa239e92..02b1f963 100644
--- a/tests/test_results.py
+++ b/tests/test_results.py
@@ -43,30 +43,18 @@ class NumbersTest(CoverageTest):
assert n3.n_missing == 28
assert round(abs(n3.pc_covered-86.666666666), 7) == 0
- def test_pc_covered_str(self):
- # Numbers._precision is a global, which is bad.
- Numbers.set_precision(0)
- n0 = Numbers(n_files=1, n_statements=1000, n_missing=0)
- n1 = Numbers(n_files=1, n_statements=1000, n_missing=1)
- n999 = Numbers(n_files=1, n_statements=1000, n_missing=999)
- n1000 = Numbers(n_files=1, n_statements=1000, n_missing=1000)
- assert n0.pc_covered_str == "100"
- assert n1.pc_covered_str == "99"
- assert n999.pc_covered_str == "1"
- assert n1000.pc_covered_str == "0"
-
- def test_pc_covered_str_precision(self):
- # Numbers._precision is a global, which is bad.
- Numbers.set_precision(1)
- n0 = Numbers(n_files=1, n_statements=10000, n_missing=0)
- n1 = Numbers(n_files=1, n_statements=10000, n_missing=1)
- n9999 = Numbers(n_files=1, n_statements=10000, n_missing=9999)
- n10000 = Numbers(n_files=1, n_statements=10000, n_missing=10000)
- assert n0.pc_covered_str == "100.0"
- assert n1.pc_covered_str == "99.9"
- assert n9999.pc_covered_str == "0.1"
- assert n10000.pc_covered_str == "0.0"
- Numbers.set_precision(0)
+ @pytest.mark.parametrize("kwargs, res", [
+ (dict(n_files=1, n_statements=1000, n_missing=0), "100"),
+ (dict(n_files=1, n_statements=1000, n_missing=1), "99"),
+ (dict(n_files=1, n_statements=1000, n_missing=999), "1"),
+ (dict(n_files=1, n_statements=1000, n_missing=1000), "0"),
+ (dict(precision=1, n_files=1, n_statements=10000, n_missing=0), "100.0"),
+ (dict(precision=1, n_files=1, n_statements=10000, n_missing=1), "99.9"),
+ (dict(precision=1, n_files=1, n_statements=10000, n_missing=9999), "0.1"),
+ (dict(precision=1, n_files=1, n_statements=10000, n_missing=10000), "0.0"),
+ ])
+ def test_pc_covered_str(self, kwargs, res):
+ assert Numbers(**kwargs).pc_covered_str == res
@pytest.mark.parametrize("prec, pc, res", [
(0, 47.87, "48"),
@@ -75,10 +63,7 @@ class NumbersTest(CoverageTest):
(2, 99.99995, "99.99"),
])
def test_display_covered(self, prec, pc, res):
- # Numbers._precision is a global, which is bad.
- Numbers.set_precision(prec)
- assert Numbers.display_covered(pc) == res
- Numbers.set_precision(0)
+ assert Numbers(precision=prec).display_covered(pc) == res
def test_covered_ratio(self):
n = Numbers(n_files=1, n_statements=200, n_missing=47)