summaryrefslogtreecommitdiff
path: root/coverage
diff options
context:
space:
mode:
Diffstat (limited to 'coverage')
-rw-r--r--coverage/backunittest.py2
-rw-r--r--coverage/cmdline.py16
-rw-r--r--coverage/config.py12
-rw-r--r--coverage/control.py31
-rw-r--r--coverage/env.py2
-rw-r--r--coverage/htmlfiles/coverage_html.js9
-rw-r--r--coverage/jsonreport.py4
-rw-r--r--coverage/multiproc.py2
-rw-r--r--coverage/parser.py1
-rw-r--r--coverage/plugin.py1
-rw-r--r--coverage/version.py2
-rw-r--r--coverage/xmlreport.py8
12 files changed, 69 insertions, 21 deletions
diff --git a/coverage/backunittest.py b/coverage/backunittest.py
index 078f48cc..123bb2a1 100644
--- a/coverage/backunittest.py
+++ b/coverage/backunittest.py
@@ -18,7 +18,7 @@ class TestCase(unittest.TestCase):
`unittest` doesn't have them.
"""
- # pylint: disable=arguments-differ, deprecated-method
+ # pylint: disable=signature-differs, deprecated-method
if not unittest_has('assertCountEqual'):
def assertCountEqual(self, *args, **kwargs):
diff --git a/coverage/cmdline.py b/coverage/cmdline.py
index 9fddb6bb..8b7fd3aa 100644
--- a/coverage/cmdline.py
+++ b/coverage/cmdline.py
@@ -146,6 +146,13 @@ class Opts(object):
"to be run as 'python -m' would run it."
),
)
+ precision = optparse.make_option(
+ '', '--precision', action='store', metavar='N', type=int,
+ help=(
+ "Number of digits after the decimal point to display for "
+ "reported coverage percentages."
+ ),
+ )
rcfile = optparse.make_option(
'', '--rcfile', action='store',
help=(
@@ -203,6 +210,7 @@ class CoverageOptionParser(optparse.OptionParser, object):
omit=None,
contexts=None,
parallel_mode=None,
+ precision=None,
pylib=None,
rcfile=True,
show_missing=None,
@@ -358,6 +366,7 @@ CMDS = {
Opts.ignore_errors,
Opts.include,
Opts.omit,
+ Opts.precision,
Opts.show_contexts,
Opts.skip_covered,
Opts.skip_empty,
@@ -395,6 +404,7 @@ CMDS = {
Opts.ignore_errors,
Opts.include,
Opts.omit,
+ Opts.precision,
Opts.show_missing,
Opts.skip_covered,
Opts.skip_empty,
@@ -583,6 +593,7 @@ class CoverageScript(object):
show_missing=options.show_missing,
skip_covered=options.skip_covered,
skip_empty=options.skip_empty,
+ precision=options.precision,
**report_args
)
elif options.action == "annotate":
@@ -594,6 +605,7 @@ class CoverageScript(object):
skip_covered=options.skip_covered,
skip_empty=options.skip_empty,
show_contexts=options.show_contexts,
+ precision=options.precision,
**report_args
)
elif options.action == "xml":
@@ -806,7 +818,7 @@ HELP_TOPICS = {
""",
'minimum_help': """\
- Code coverage for Python. Use '{program_name} help' for help.
+ Code coverage for Python, version {__version__} {extension_modifier}. Use '{program_name} help' for help.
""",
'version': """\
@@ -857,8 +869,8 @@ if _profile: # pragma: debugging
def main(argv=None): # pylint: disable=function-redefined
"""A wrapper around main that profiles."""
+ profiler = SimpleLauncher.launch()
try:
- profiler = SimpleLauncher.launch()
return original_main(argv)
finally:
data, _ = profiler.query(re_filter='coverage', max_records=100)
diff --git a/coverage/config.py b/coverage/config.py
index 78a3e86a..6d336d1f 100644
--- a/coverage/config.py
+++ b/coverage/config.py
@@ -72,7 +72,7 @@ class HandyConfigParser(configparser.RawConfigParser):
d[opt] = self.get(section, opt)
return d
- def get(self, section, option, *args, **kwargs): # pylint: disable=arguments-differ
+ def get(self, section, option, *args, **kwargs):
"""Get a value, replacing environment variables also.
The arguments are the same as `RawConfigParser.get`, but in the found
@@ -325,7 +325,7 @@ class CoverageConfig(object):
if used:
self.config_file = os.path.abspath(filename)
- with open(filename) as f:
+ with open(filename, "rb") as f:
self._config_contents = f.read()
return used
@@ -421,6 +421,10 @@ class CoverageConfig(object):
`value` is the new value for the option.
"""
+ # Special-cased options.
+ if option_name == "paths":
+ self.paths = value
+ return
# Check all the hard-coded options.
for option_spec in self.CONFIG_FILE_OPTIONS:
@@ -448,6 +452,10 @@ class CoverageConfig(object):
Returns the value of the option.
"""
+ # Special-cased options.
+ if option_name == "paths":
+ return self.paths
+
# Check all the hard-coded options.
for option_spec in self.CONFIG_FILE_OPTIONS:
attr, where = option_spec[:2]
diff --git a/coverage/control.py b/coverage/control.py
index f7db26e9..c2f40e70 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -367,7 +367,11 @@ class Coverage(object):
option name. For example, the ``branch`` option in the ``[run]``
section of the config file would be indicated with `"run:branch"`.
- Returns the value of the option.
+ Returns the value of the option. The type depends on the option
+ selected.
+
+ As a special case, an `option_name` of ``"paths"`` will return an
+ OrderedDict with the entire ``[paths]`` section value.
.. versionadded:: 4.0
@@ -394,6 +398,9 @@ class Coverage(object):
[run]
branch = True
+ As a special case, an `option_name` of ``"paths"`` will replace the
+ entire ``[paths]`` section. The value should be an OrderedDict.
+
.. versionadded:: 4.0
"""
@@ -822,7 +829,7 @@ class Coverage(object):
def report(
self, morfs=None, show_missing=None, ignore_errors=None,
file=None, omit=None, include=None, skip_covered=None,
- contexts=None, skip_empty=None,
+ contexts=None, skip_empty=None, precision=None,
):
"""Write a textual summary report to `file`.
@@ -850,6 +857,9 @@ class Coverage(object):
expressions (using :func:`re.search <python:re.search>`) will be
included in the report.
+ `precision` is the number of digits to display after the decimal
+ point for percentages.
+
All of the arguments default to the settings read from the
:ref:`configuration file <config>`.
@@ -861,12 +871,15 @@ class Coverage(object):
.. versionadded:: 5.0
The `contexts` and `skip_empty` parameters.
+ .. versionadded:: 5.2
+ The `precision` parameter.
+
"""
with override_config(
self,
ignore_errors=ignore_errors, report_omit=omit, report_include=include,
show_missing=show_missing, skip_covered=skip_covered,
- report_contexts=contexts, skip_empty=skip_empty,
+ report_contexts=contexts, skip_empty=skip_empty, precision=precision,
):
reporter = SummaryReporter(self)
return reporter.report(morfs, outfile=file)
@@ -892,10 +905,12 @@ class Coverage(object):
reporter = AnnotateReporter(self)
reporter.report(morfs, directory=directory)
- def html_report(self, morfs=None, directory=None, ignore_errors=None,
- omit=None, include=None, extra_css=None, title=None,
- skip_covered=None, show_contexts=None, contexts=None,
- skip_empty=None):
+ def html_report(
+ self, morfs=None, directory=None, ignore_errors=None,
+ omit=None, include=None, extra_css=None, title=None,
+ skip_covered=None, show_contexts=None, contexts=None,
+ skip_empty=None, precision=None,
+ ):
"""Generate an HTML report.
The HTML is written to `directory`. The file "index.html" is the
@@ -923,7 +938,7 @@ class Coverage(object):
ignore_errors=ignore_errors, report_omit=omit, report_include=include,
html_dir=directory, extra_css=extra_css, html_title=title,
skip_covered=skip_covered, show_contexts=show_contexts, report_contexts=contexts,
- skip_empty=skip_empty,
+ skip_empty=skip_empty, precision=precision,
):
reporter = HtmlReporter(self)
return reporter.report(morfs)
diff --git a/coverage/env.py b/coverage/env.py
index d194f34a..b5da3b47 100644
--- a/coverage/env.py
+++ b/coverage/env.py
@@ -57,7 +57,7 @@ class PYBEHAVIOR(object):
unpackings_pep448 = (PYVERSION >= (3, 5))
# Can co_lnotab have negative deltas?
- negative_lnotab = (PYVERSION >= (3, 6))
+ negative_lnotab = (PYVERSION >= (3, 6)) and not (PYPY and PYPYVERSION < (7, 2))
# Do .pyc files conform to PEP 552? Hash-based pyc's.
hashed_pyc_pep552 = (PYVERSION >= (3, 7, 0, 'alpha', 4))
diff --git a/coverage/htmlfiles/coverage_html.js b/coverage/htmlfiles/coverage_html.js
index 22152333..3bf04bf9 100644
--- a/coverage/htmlfiles/coverage_html.js
+++ b/coverage/htmlfiles/coverage_html.js
@@ -172,7 +172,10 @@ coverage.index_ready = function ($) {
// Look for a localStorage item containing previous sort settings:
var sort_list = [];
var storage_name = "COVERAGE_INDEX_SORT";
- var stored_list = localStorage.getItem(storage_name);
+ var stored_list = undefined;
+ try {
+ stored_list = localStorage.getItem(storage_name);
+ } catch(err) {}
if (stored_list) {
sort_list = JSON.parse('[[' + stored_list + ']]');
@@ -222,7 +225,9 @@ coverage.index_ready = function ($) {
// Watch for page unload events so we can save the final sort settings:
$(window).unload(function () {
- localStorage.setItem(storage_name, sort_list.toString())
+ try {
+ localStorage.setItem(storage_name, sort_list.toString())
+ } catch(err) {}
});
};
diff --git a/coverage/jsonreport.py b/coverage/jsonreport.py
index 0c3f313d..4287bc79 100644
--- a/coverage/jsonreport.py
+++ b/coverage/jsonreport.py
@@ -60,6 +60,8 @@ class JsonReporter(object):
self.report_data["totals"].update({
'num_branches': self.total.n_branches,
'num_partial_branches': self.total.n_partial_branches,
+ 'covered_branches': self.total.n_executed_branches,
+ 'missing_branches': self.total.n_missing_branches,
})
json.dump(
@@ -95,5 +97,7 @@ class JsonReporter(object):
reported_file['summary'].update({
'num_branches': nums.n_branches,
'num_partial_branches': nums.n_partial_branches,
+ 'covered_branches': nums.n_executed_branches,
+ 'missing_branches': nums.n_missing_branches,
})
return reported_file
diff --git a/coverage/multiproc.py b/coverage/multiproc.py
index 2931b3be..0afcb0c9 100644
--- a/coverage/multiproc.py
+++ b/coverage/multiproc.py
@@ -28,7 +28,7 @@ original_bootstrap = OriginalProcess._bootstrap
class ProcessWithCoverage(OriginalProcess): # pylint: disable=abstract-method
"""A replacement for multiprocess.Process that starts coverage."""
- def _bootstrap(self, *args, **kwargs): # pylint: disable=arguments-differ
+ def _bootstrap(self, *args, **kwargs): # pylint: disable=signature-differs
"""Wrapper around _bootstrap to start coverage."""
try:
from coverage import Coverage # avoid circular import
diff --git a/coverage/parser.py b/coverage/parser.py
index f0d378c6..e3e43149 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -610,6 +610,7 @@ class AstArcAnalyzer(object):
return node.lineno
_line__FunctionDef = _line_decorated
+ _line__AsyncFunctionDef = _line_decorated
def _line__List(self, node):
if node.elts:
diff --git a/coverage/plugin.py b/coverage/plugin.py
index b9cb7238..6997b489 100644
--- a/coverage/plugin.py
+++ b/coverage/plugin.py
@@ -496,6 +496,7 @@ class FileReporter(object):
* ``'num'``: a number
* ``'op'``: an operator
* ``'str'``: a string literal
+ * ``'ws'``: some white space
* ``'txt'``: some other kind of text
If you concatenate all the token texts, and then join them with
diff --git a/coverage/version.py b/coverage/version.py
index bc86fbaa..5167b71d 100644
--- a/coverage/version.py
+++ b/coverage/version.py
@@ -5,7 +5,7 @@
# This file is exec'ed in setup.py, don't import anything!
# Same semantics as sys.version_info.
-version_info = (5, 0, 4, 'alpha', 0)
+version_info = (5, 1, 1, 'alpha', 0)
def _make_version(major, minor, micro, releaselevel, serial):
diff --git a/coverage/xmlreport.py b/coverage/xmlreport.py
index 265bf02c..ad44775f 100644
--- a/coverage/xmlreport.py
+++ b/coverage/xmlreport.py
@@ -41,7 +41,9 @@ class XmlReporter(object):
if self.config.source:
for src in self.config.source:
if os.path.exists(src):
- self.source_paths.add(files.canonical_filename(src))
+ if not self.config.relative_files:
+ src = files.canonical_filename(src)
+ self.source_paths.add(src)
self.packages = {}
self.xml_out = None
@@ -144,18 +146,18 @@ class XmlReporter(object):
# are populated later. Note that a package == a directory.
filename = fr.filename.replace("\\", "/")
for source_path in self.source_paths:
+ source_path = files.canonical_filename(source_path)
if filename.startswith(source_path.replace("\\", "/") + "/"):
rel_name = filename[len(source_path)+1:]
break
else:
rel_name = fr.relative_filename()
+ self.source_paths.add(fr.filename[:-len(rel_name)].rstrip(r"\/"))
dirname = os.path.dirname(rel_name) or u"."
dirname = "/".join(dirname.split("/")[:self.config.xml_package_depth])
package_name = dirname.replace("/", ".")
- if rel_name != fr.filename:
- self.source_paths.add(fr.filename[:-len(rel_name)].rstrip(r"\/"))
package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0])
xclass = self.xml_out.createElement("class")