diff options
-rw-r--r-- | CHANGES.rst | 4 | ||||
-rw-r--r-- | coverage/misc.py | 13 | ||||
-rw-r--r-- | doc/config.rst | 6 | ||||
-rw-r--r-- | tests/test_misc.py | 25 |
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) |