summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt5
-rw-r--r--coverage/config.py3
-rw-r--r--coverage/control.py19
-rw-r--r--coverage/results.py34
-rw-r--r--coverage/summary.py5
-rw-r--r--doc/config.rst5
-rw-r--r--test/test_config.py11
-rw-r--r--test/test_results.py14
8 files changed, 83 insertions, 13 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 1af4d9ea..88a28b04 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -10,8 +10,13 @@ Version 3.4b2
percentage is only reported as 0% or 100% if they are truly 0 or 100, and
are rounded otherwise. Fixes `issue 41` and issue 70`.
+- The precision of reported coverage percentages can be set with the
+ ``[report] precision`` config file setting. Default is still 0. Completes
+ ``issue 16``.
+
.. _issue 70: http://bitbucket.org/ned/coveragepy/issue/70/text-report-and-html-report-disagree-on-coverage
.. _issue 41: http://bitbucket.org/ned/coveragepy/issue/41/report-says-100-when-it-isnt-quite-there
+.. _issue 16: http://bitbucket.org/ned/coveragepy/issue/16/allow-configuration-of-accuracy-of-percentage-totals
Version 3.4b1 --- 21 August 2010
diff --git a/coverage/config.py b/coverage/config.py
index 7c22f64b..1f6a879f 100644
--- a/coverage/config.py
+++ b/coverage/config.py
@@ -27,6 +27,7 @@ class CoverageConfig(object):
self.ignore_errors = False
self.omit = None
self.include = None
+ self.precision = 0
# Defaults for [html]
self.html_dir = "htmlcov"
@@ -87,6 +88,8 @@ class CoverageConfig(object):
self.omit = self.get_list(cp, 'report', 'omit')
if cp.has_option('report', 'include'):
self.include = self.get_list(cp, 'report', 'include')
+ if cp.has_option('report', 'precision'):
+ self.precision = cp.getint('report', 'precision')
# [html]
if cp.has_option('html', 'directory'):
diff --git a/coverage/control.py b/coverage/control.py
index 59d237ea..7df608db 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -10,8 +10,8 @@ from coverage.config import CoverageConfig
from coverage.data import CoverageData
from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher
from coverage.html import HtmlReporter
-from coverage.misc import bool_or_none
-from coverage.results import Analysis
+from coverage.misc import CoverageException, bool_or_none
+from coverage.results import Analysis, Numbers
from coverage.summary import SummaryReporter
from coverage.xmlreport import XmlReporter
@@ -76,7 +76,13 @@ class coverage(object):
if config_file:
if config_file is True:
config_file = ".coveragerc"
- self.config.from_file(config_file)
+ try:
+ self.config.from_file(config_file)
+ except ValueError:
+ _, err, _ = sys.exc_info()
+ raise CoverageException(
+ "Couldn't read config file %s: %s" % (config_file, err)
+ )
# 3: from environment variables:
self.config.from_environment('COVERAGE_OPTIONS')
@@ -163,8 +169,11 @@ class coverage(object):
# Only _harvest_data once per measurement cycle.
self._harvested = False
- # When tearing down the coverage object, modules can become None.
- # Saving the modules as object attributes avoids problems, but it is
+ # Set the reporting precision.
+ Numbers.set_precision(self.config.precision)
+
+ # When tearing down the coverage object, modules can become None.
+ # Saving the modules as object attributes avoids problems, but it is
# quite ad-hoc which modules need to be saved and which references
# need to use the object attributes.
self.socket = socket
diff --git a/coverage/results.py b/coverage/results.py
index a3bdec15..e6e39429 100644
--- a/coverage/results.py
+++ b/coverage/results.py
@@ -127,6 +127,12 @@ class Numbers(object):
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_missing_branches=0
):
@@ -137,6 +143,14 @@ class Numbers(object):
self.n_branches = n_branches
self.n_missing_branches = n_missing_branches
+ 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
+ set_precision = classmethod(set_precision)
+
def _get_n_executed(self):
"""Returns the number of executed statements."""
return self.n_statements - self.n_missing
@@ -165,15 +179,23 @@ class Numbers(object):
"""
pc = self.pc_covered
- if 0 < pc < 1:
- pc = 1.0
- elif 99 < pc < 100:
- pc = 99.0
+ if 0 < pc < self._near0:
+ pc = self._near0
+ elif self._near100 < pc < 100:
+ pc = self._near100
else:
- pc = round(pc)
- return "%.0f" % pc
+ pc = round(pc, self._precision)
+ return "%.*f" % (self._precision, pc)
pc_covered_str = property(_get_pc_covered_str)
+ def pc_str_width(cls):
+ """How many characters wide can pc_covered_str be?"""
+ width = 3 # "100"
+ if cls._precision > 0:
+ width += 1 + cls._precision
+ return width
+ pc_str_width = classmethod(pc_str_width)
+
def __add__(self, other):
nums = Numbers()
nums.n_files = self.n_files + other.n_files
diff --git a/coverage/summary.py b/coverage/summary.py
index 53c844d1..a1206af5 100644
--- a/coverage/summary.py
+++ b/coverage/summary.py
@@ -31,8 +31,9 @@ class SummaryReporter(Reporter):
if self.branches:
header += " Branch BrPart"
fmt_coverage += " %6d %6d"
- header += " Cover"
- fmt_coverage += " %5s%%"
+ width100 = Numbers.pc_str_width()
+ header += "%*s" % (width100+4, "Cover")
+ fmt_coverage += "%%%ds%%%%" % (width100+3,)
if self.show_missing:
header += " Missing"
fmt_coverage += " %s"
diff --git a/doc/config.rst b/doc/config.rst
index b0e5bf84..bcb24412 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -6,6 +6,7 @@ Configuration files
:history: 20100223T201600, new for 3.3
:history: 20100725T211700, updated for 3.4.
+:history: 20100824T092900, added ``precision``.
Coverage.py options can be specified in a configuration file. This makes it
@@ -116,6 +117,10 @@ in reporting. See :ref:`source` for details.
``omit`` (multi-string): a list of filename patterns, the files to leave out
of reporting. See :ref:`source` for details.
+``precision`` (integer): the number of digits after the decimal point to
+display for reported coverage percentages. The default is 0, displaying
+for example "87%". A value of 2 will display percentages like "87.32%".
+
[html]
------
diff --git a/test/test_config.py b/test/test_config.py
index 96b87650..d5290584 100644
--- a/test/test_config.py
+++ b/test/test_config.py
@@ -2,6 +2,7 @@
import os, sys
import coverage
+from coverage.misc import CoverageException
sys.path.insert(0, os.path.split(__file__)[0]) # Force relative import for Py3k
from coveragetest import CoverageTest
@@ -88,6 +89,14 @@ class ConfigTest(CoverageTest):
cov = coverage.coverage(data_file="fromarg.dat")
self.assertEqual(cov.config.data_file, "fromarg.dat")
+ def test_parse_errors(self):
+ # Im-parseable values raise CoverageException
+ self.make_file(".coveragerc", """\
+ [run]
+ timid = maybe?
+ """)
+ self.assertRaises(CoverageException, coverage.coverage)
+
class ConfigFileTest(CoverageTest):
"""Tests of the config file settings in particular."""
@@ -116,6 +125,7 @@ class ConfigFileTest(CoverageTest):
omit =
one, another, some_more,
yet_more
+ precision = 3
[html]
@@ -141,6 +151,7 @@ class ConfigFileTest(CoverageTest):
self.assertEqual(cov.config.omit,
["one", "another", "some_more", "yet_more"]
)
+ self.assertEqual(cov.config.precision, 3)
self.assertEqual(cov.config.html_dir, r"c:\tricky\dir.somewhere")
diff --git a/test/test_results.py b/test/test_results.py
index 79beb4e9..d6919fa2 100644
--- a/test/test_results.py
+++ b/test/test_results.py
@@ -49,3 +49,17 @@ class NumbersTest(CoverageTest):
self.assertEqual(n1.pc_covered_str, "99")
self.assertEqual(n999.pc_covered_str, "1")
self.assertEqual(n1000.pc_covered_str, "0")
+
+ def test_pc_covered_str_precision(self):
+ assert Numbers._precision == 0
+ 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)
+ self.assertEqual(n0.pc_covered_str, "100.0")
+ self.assertEqual(n1.pc_covered_str, "99.9")
+ self.assertEqual(n9999.pc_covered_str, "0.1")
+ self.assertEqual(n10000.pc_covered_str, "0.0")
+ Numbers.set_precision(0)
+