diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2019-09-01 21:34:20 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2019-09-01 21:34:20 -0400 |
commit | dd98bb1ebb687e4ff22e255ee97ec8e8cca5e1f6 (patch) | |
tree | 4b25e042b0827aac5c68e62d413a5d6e8e1a5fdd | |
parent | 4042ce713e373f19ca81948eefcfab83d8dd0bbc (diff) | |
download | python-coveragepy-git-dd98bb1ebb687e4ff22e255ee97ec8e8cca5e1f6.tar.gz |
Reporting methods shouldn't permanently change the configuration
-rw-r--r-- | CHANGES.rst | 4 | ||||
-rw-r--r-- | coverage/config.py | 7 | ||||
-rw-r--r-- | coverage/control.py | 53 | ||||
-rw-r--r-- | tests/test_api.py | 11 |
4 files changed, 56 insertions, 19 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 5d0ff126..ec79353f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -28,6 +28,10 @@ Unreleased "numbits." The :mod:`coverage.numbits` module provides functions for working with them. +- The reporting methods used to permanently apply their arguments to the + configuration of the Coverage object. Now they no longer do. The arguments + affect the operation of the method, but do not persist. + - A class named "test_something" no longer confuses the ``test_function`` dynamic context setting. Fixes `issue 829`_. diff --git a/coverage/config.py b/coverage/config.py index 516fe1b9..85493df1 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -4,6 +4,7 @@ """Config file for coverage.py""" import collections +import copy import os import re @@ -215,7 +216,7 @@ class CoverageConfig(object): self.xml_output = "coverage.xml" self.xml_package_depth = 99 - # Defaults for [JSON] + # Defaults for [json] self.json_output = "coverage.json" self.json_pretty_print = False self.json_show_contexts = False @@ -318,6 +319,10 @@ class CoverageConfig(object): return used + def copy(self): + """Return a copy of the configuration.""" + return copy.deepcopy(self) + CONFIG_FILE_OPTIONS = [ # These are *args for _set_attr_from_config_option: # (attr, where, type_="") diff --git a/coverage/control.py b/coverage/control.py index d5988456..c342eca4 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -4,6 +4,7 @@ """Core control stuff for coverage.py.""" import atexit +import contextlib import os import os.path import platform @@ -41,6 +42,21 @@ except ImportError: # pragma: only jytho os = isolate_module(os) +@contextlib.contextmanager +def override_config(cov, **kwargs): + """Temporarily tweak the configuration of `cov`. + + The arguments are applied to `cov.config` with the `from_args` method. + At the end of the with-statement, the old configuration is restored. + """ + original_config = cov.config + cov.config = cov.config.copy() + try: + cov.config.from_args(**kwargs) + yield + finally: + cov.config = original_config + class Coverage(object): """Programmatic access to coverage.py. @@ -781,13 +797,14 @@ class Coverage(object): Returns a float, the total percentage covered. """ - self.config.from_args( + 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, - ) - reporter = SummaryReporter(self) - return reporter.report(morfs, outfile=file) + ): + reporter = SummaryReporter(self) + return reporter.report(morfs, outfile=file) def annotate( self, morfs=None, directory=None, ignore_errors=None, @@ -803,12 +820,12 @@ class Coverage(object): See :meth:`report` for other arguments. """ - self.config.from_args( + with override_config(self, ignore_errors=ignore_errors, report_omit=omit, report_include=include, report_contexts=contexts, - ) - reporter = AnnotateReporter(self) - reporter.report(morfs, directory=directory) + ): + 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, @@ -836,13 +853,13 @@ class Coverage(object): changing the files in the report folder. """ - self.config.from_args( + with override_config(self, 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, - ) - reporter = HtmlReporter(self) - return reporter.report(morfs) + ): + reporter = HtmlReporter(self) + return reporter.report(morfs) def xml_report( self, morfs=None, outfile=None, ignore_errors=None, @@ -860,11 +877,11 @@ class Coverage(object): Returns a float, the total percentage covered. """ - self.config.from_args( + with override_config(self, ignore_errors=ignore_errors, report_omit=omit, report_include=include, xml_output=outfile, report_contexts=contexts, - ) - return render_report(self.config.xml_output, XmlReporter(self), morfs) + ): + return render_report(self.config.xml_output, XmlReporter(self), morfs) def json_report( self, morfs=None, outfile=None, ignore_errors=None, @@ -881,12 +898,12 @@ class Coverage(object): Returns a float, the total percentage covered. """ - self.config.from_args( + with override_config(self, ignore_errors=ignore_errors, report_omit=omit, report_include=include, json_output=outfile, report_contexts=contexts, json_pretty_print=pretty_print, json_show_contexts=show_contexts - ) - return render_report(self.config.json_output, JsonReporter(self), morfs) + ): + return render_report(self.config.json_output, JsonReporter(self), morfs) def sys_info(self): """Return a list of (key, value) pairs showing internal information.""" diff --git a/tests/test_api.py b/tests/test_api.py index 301257dc..4f248441 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -918,3 +918,14 @@ class TestRunnerPluginTest(CoverageTest): def test_pytestcov_parallel_append(self): self.pretend_to_be_pytestcov(append=True) + + +class ImmutableConfigTest(CoverageTest): + """Check that reporting methods don't permanently change the configuration.""" + def test_config_doesnt_change(self): + self.make_file("simple.py", "a = 1") + cov = coverage.Coverage() + self.start_import_stop(cov, "simple") + self.assertEqual(cov.get_option("report:show_missing"), False) + cov.report(show_missing=True) + self.assertEqual(cov.get_option("report:show_missing"), False) |