summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.rst6
-rw-r--r--coverage/control.py27
-rw-r--r--coverage/misc.py14
-rw-r--r--doc/whatsnew5x.rst5
-rw-r--r--tests/test_api.py27
5 files changed, 72 insertions, 7 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index e58ca9e6..3a27c69f 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -29,6 +29,11 @@ Unreleased
one (or many) environments, and then report in another. It has not had much
real-world testing, so it may change in incompatible ways in the future.
+- When constructing a :class:`coverage.Coverage` object, `data_file` can be
+ specified as None to prevent writing any data file at all. In previous
+ versions, an explicit `data_file=None` argument would use the default of
+ ".coverage". Fixes `issue 871`_.
+
- Python files run with ``-m`` now have ``__spec__`` defined properly. This
fixes `issue 745`_ (about not being able to run unittest tests that spawn
subprocesses), and `issue 838`_, which described the problem directly.
@@ -46,6 +51,7 @@ Unreleased
.. _issue 649: https://github.com/nedbat/coveragepy/issues/649
.. _issue 745: https://github.com/nedbat/coveragepy/issues/745
.. _issue 838: https://github.com/nedbat/coveragepy/issues/838
+.. _issue 871: https://github.com/nedbat/coveragepy/issues/871
.. _changes_50b1:
diff --git a/coverage/control.py b/coverage/control.py
index 3a19695a..53942242 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -25,7 +25,7 @@ from coverage.html import HtmlReporter
from coverage.inorout import InOrOut
from coverage.jsonreport import JsonReporter
from coverage.misc import CoverageException, bool_or_none, join_regex
-from coverage.misc import ensure_dir_for_file, isolate_module
+from coverage.misc import DefaultValue, ensure_dir_for_file, isolate_module
from coverage.plugin import FileReporter
from coverage.plugin_support import Plugins
from coverage.python import PythonFileReporter
@@ -58,6 +58,8 @@ def override_config(cov, **kwargs):
cov.config = original_config
+_DEFAULT_DATAFILE = DefaultValue("MISSING")
+
class Coverage(object):
"""Programmatic access to coverage.py.
@@ -91,16 +93,21 @@ class Coverage(object):
return None
def __init__(
- self, data_file=None, data_suffix=None, cover_pylib=None,
+ self, data_file=_DEFAULT_DATAFILE, data_suffix=None, cover_pylib=None,
auto_data=False, timid=None, branch=None, config_file=True,
source=None, omit=None, include=None, debug=None,
concurrency=None, check_preimported=False, context=None,
):
"""
- `data_file` is the base name of the data file to use, defaulting to
- ".coverage". `data_suffix` is appended (with a dot) to `data_file` to
- create the final file name. If `data_suffix` is simply True, then a
- suffix is created with the machine and process identity included.
+ Many of these arguments duplicate and override values that can be
+ provided in a configuration file. Parameters that are missing here
+ will use values from the config file.
+
+ `data_file` is the base name of the data file to use. The config value
+ defaults to ".coverage". None can be provied to prevent writing a data
+ file. `data_suffix` is appended (with a dot) to `data_file` to create
+ the final file name. If `data_suffix` is simply True, then a suffix is
+ created with the machine and process identity included.
`cover_pylib` is a boolean determining whether Python code installed
with the Python interpreter is measured. This includes the Python
@@ -166,6 +173,12 @@ class Coverage(object):
The `check_preimported` and `context` parameters.
"""
+ # data_file=None means no disk file at all. data_file missing means
+ # use the value from the config file.
+ self._no_disk = data_file is None
+ if data_file is _DEFAULT_DATAFILE:
+ data_file = None
+
# Build our configuration from a number of sources.
self.config = read_coverage_config(
config_file=config_file,
@@ -460,6 +473,7 @@ class Coverage(object):
suffix=suffix,
warn=self._warn,
debug=self._debug,
+ no_disk=self._no_disk,
)
def start(self):
@@ -526,6 +540,7 @@ class Coverage(object):
self._init_data(suffix=None)
self._data.erase(parallel=self.config.parallel)
self._data = None
+ self._inited_for_start = False
def switch_context(self, new_context):
"""Switch to a new dynamic context.
diff --git a/coverage/misc.py b/coverage/misc.py
index 2f3bcac6..6f8c368a 100644
--- a/coverage/misc.py
+++ b/coverage/misc.py
@@ -254,6 +254,20 @@ def _needs_to_implement(that, func_name):
)
+class DefaultValue(object):
+ """A sentinel object to use for unusual default-value needs.
+
+ Construct with a string that will be used as the repr, for display in help
+ and Sphinx output.
+
+ """
+ def __init__(self, display_as):
+ self.display_as = display_as
+
+ def __repr__(self):
+ return self.display_as
+
+
def substitute_variables(text, variables):
"""Substitute ``${VAR}`` variables in `text` with their values.
diff --git a/doc/whatsnew5x.rst b/doc/whatsnew5x.rst
index c805b8d7..4724dee7 100644
--- a/doc/whatsnew5x.rst
+++ b/doc/whatsnew5x.rst
@@ -33,6 +33,11 @@ Backward Incompatibilities
circumstances, you may need to use ``parallel=true`` to avoid multiple
processes overwriting each others' data.
+- When constructing a :class:`coverage.Coverage` object, `data_file` can be
+ specified as None to prevent writing any data file at all. In previous
+ versions, an explicit `data_file=None` argument would use the default of
+ ".coverage". Fixes :github:`871`.
+
- The ``[run] note`` setting has been deprecated. Using it will result in a
warning, and the note will not be written to the data file. The
corresponding :class:`.CoverageData` methods have been removed.
diff --git a/tests/test_api.py b/tests/test_api.py
index 228f0147..93b6dc9a 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -16,7 +16,7 @@ from unittest_mixins import change_dir
import coverage
from coverage import env
-from coverage.backward import StringIO, import_local_file
+from coverage.backward import code_object, import_local_file, StringIO
from coverage.data import line_counts
from coverage.files import abs_file
from coverage.misc import CoverageException
@@ -263,6 +263,31 @@ class ApiTest(CoverageTest):
self.assertFiles(["datatest5.py", "deep"])
self.assert_exists("deep/sub/cov.data")
+ def test_datafile_none(self):
+ cov = coverage.Coverage(data_file=None)
+
+ def f1():
+ a = 1 # pylint: disable=unused-variable
+
+ one_line_number = code_object(f1).co_firstlineno + 1
+ lines = []
+
+ def run_one_function(f):
+ cov.erase()
+ cov.start()
+ f()
+ cov.stop()
+
+ fs = cov.get_data().measured_files()
+ lines.append(cov.get_data().lines(list(fs)[0]))
+
+ run_one_function(f1)
+ run_one_function(f1)
+ run_one_function(f1)
+ assert lines == [[one_line_number]] * 3
+ self.assert_doesnt_exist(".coverage")
+ assert os.listdir(".") == []
+
def test_empty_reporting(self):
# empty summary reports raise exception, just like the xml report
cov = coverage.Coverage()