summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.rst4
-rw-r--r--coverage/misc.py13
-rw-r--r--doc/config.rst6
-rw-r--r--tests/test_misc.py25
4 files changed, 37 insertions, 11 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 6ed73e59..23aa0921 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -17,7 +17,9 @@ Change history for Coverage.py
Unreleased
----------
-- nothing yet.
+- Environment variable substitution in configuration files can now be strict:
+ using a question mark suffix like ``${VARNAME?}`` will raise an error if
+ ``VARNAME`` is not defined as an environment variable.
.. _changes_50a2:
diff --git a/coverage/misc.py b/coverage/misc.py
index e2031852..c6a7c8cf 100644
--- a/coverage/misc.py
+++ b/coverage/misc.py
@@ -258,6 +258,7 @@ def substitute_variables(text, variables=os.environ):
$VAR
${VAR}
+ ${VAR?} strict: an error if VAR isn't defined.
A dollar can be inserted with ``$$``.
@@ -270,16 +271,24 @@ def substitute_variables(text, variables=os.environ):
def dollar_replace(m):
"""Called for each $replacement."""
# Only one of the groups will have matched, just get its text.
- word = next(w for w in m.groups() if w is not None) # pragma: part covered
+ word = m.expand(r"\g<v1>\g<v2>\g<char>")
if word == "$":
return "$"
else:
+ strict = bool(m.group('strict'))
+ if strict:
+ if word not in variables:
+ msg = "Variable {} is undefined: {}".format(word, text)
+ raise CoverageException(msg)
return variables.get(word, '')
dollar_pattern = r"""(?x) # Use extended regex syntax
\$(?: # A dollar sign, then
(?P<v1>\w+) | # a plain word,
- {(?P<v2>\w+)} | # or a {-wrapped word,
+ { # or a {-wrapped word,
+ (?P<v2>\w+)
+ (?P<strict>\??) # with maybe a strict marker
+ } |
(?P<char>[$]) # or a dollar sign.
)
"""
diff --git a/doc/config.rst b/doc/config.rst
index 3e76e3d0..ab874619 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -59,8 +59,10 @@ or ``0`` and are case-insensitive.
Environment variables can be substituted in by using dollar signs: ``$WORD``
or ``${WORD}`` will be replaced with the value of ``WORD`` in the environment.
-A dollar sign can be inserted with ``$$``. Missing environment variables
-will result in empty strings with no error.
+A dollar sign can be inserted with ``$$``. If you want to raise an error if
+an environment variable is undefined, use a question mark suffix: ``${WORD?}``.
+Otherwise, missing environment variables will result in empty strings with no
+error.
Many sections and values correspond roughly to commands and options in
the :ref:`command-line interface <cmd>`.
diff --git a/tests/test_misc.py b/tests/test_misc.py
index 77200b3c..65476928 100644
--- a/tests/test_misc.py
+++ b/tests/test_misc.py
@@ -7,6 +7,7 @@ import pytest
from coverage.misc import contract, dummy_decorator_with_args, file_be_gone
from coverage.misc import format_lines, Hasher, one_of, substitute_variables
+from coverage.misc import CoverageException
from tests.coveragetest import CoverageTest
@@ -137,17 +138,29 @@ def test_format_lines(statements, lines, result):
assert format_lines(statements, lines) == result
+VARS = {
+ 'FOO': 'fooey',
+ 'BAR': 'xyzzy',
+}
+
@pytest.mark.parametrize("before, after", [
("Nothing to do", "Nothing to do"),
("Dollar: $$", "Dollar: $"),
("Simple: $FOO is fooey", "Simple: fooey is fooey"),
("Braced: X${FOO}X.", "Braced: XfooeyX."),
- ("Missing: x$NOTHING is x", "Missing: x is x"),
+ ("Missing: x${NOTHING}y is xy", "Missing: xy is xy"),
("Multiple: $$ $FOO $BAR ${FOO}", "Multiple: $ fooey xyzzy fooey"),
+ ("Ill-formed: ${%5} ${{HI}} ${", "Ill-formed: ${%5} ${{HI}} ${"),
+ ("Strict: ${FOO?} is there", "Strict: fooey is there"),
])
def test_substitute_variables(before, after):
- variables = {
- 'FOO': 'fooey',
- 'BAR': 'xyzzy',
- }
- assert substitute_variables(before, variables) == after
+ assert substitute_variables(before, VARS) == after
+
+@pytest.mark.parametrize("text", [
+ "Strict: ${NOTHING?} is an error",
+])
+def test_substitute_variables_errors(text):
+ with pytest.raises(CoverageException) as exc_info:
+ substitute_variables(text, VARS)
+ assert text in str(exc_info.value)
+ assert "Variable NOTHING is undefined" in str(exc_info.value)