diff options
-rw-r--r-- | coverage/config.py | 2 | ||||
-rw-r--r-- | coverage/misc.py | 50 | ||||
-rw-r--r-- | tests/test_misc.py | 1 |
3 files changed, 27 insertions, 26 deletions
diff --git a/coverage/config.py b/coverage/config.py index 85493df1..7d691145 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -87,7 +87,7 @@ class HandyConfigParser(configparser.RawConfigParser): raise configparser.NoOptionError v = configparser.RawConfigParser.get(self, real_section, option, *args, **kwargs) - v = substitute_variables(v) + v = substitute_variables(v, os.environ) return v def getlist(self, section, option): diff --git a/coverage/misc.py b/coverage/misc.py index 3423170f..4ac24ff0 100644 --- a/coverage/misc.py +++ b/coverage/misc.py @@ -246,7 +246,7 @@ def _needs_to_implement(that, func_name): ) -def substitute_variables(text, variables=os.environ): +def substitute_variables(text, variables): """Substitute ``${VAR}`` variables in `text` with their values. Variables in the text can take a number of shell-inspired forms:: @@ -255,42 +255,42 @@ def substitute_variables(text, variables=os.environ): ${VAR} ${VAR?} strict: an error if VAR isn't defined. ${VAR-missing} defaulted: "missing" if VAR isn't defined. + $$ just a dollar sign. - A dollar can be inserted with ``$$``. - - `variables` is a dictionary of variable values, defaulting to the - environment variables. + `variables` is a dictionary of variable values. Returns the resulting text with values substituted. """ + dollar_pattern = r"""(?x) # Use extended regex syntax + \$ # A dollar sign, + (?: # then + (?P<w1>\w+) | # a plain word, + (?P<dollar>\$) | # or a dollar sign. + { # or a {-wrapped + (?P<w2>\w+) # word, + (?: + (?P<strict>\?) | # with a strict marker + -(?P<defval>[^}]*) # or a default value + )? # maybe. + } + ) + """ + def dollar_replace(m): """Called for each $replacement.""" # Only one of the groups will have matched, just get its text. - word = next(v for v in m.group('v1', 'v2', 'char') if v) + word = next(w for w in m.group('w1', 'w2', 'dollar') if w) if word == "$": return "$" + elif word in variables: + return variables[word] + elif m.group('strict'): + msg = "Variable {} is undefined: {!r}".format(word, text) + raise CoverageException(msg) else: - strict = bool(m.group('strict')) - if strict: - if word not in variables: - msg = "Variable {} is undefined: {!r}".format(word, text) - raise CoverageException(msg) - return variables.get(word, m.group('defval') or '') + return m.group('defval') - dollar_pattern = r"""(?x) # Use extended regex syntax - \$(?: # A dollar sign, then - (?P<v1>\w+) | # a plain word, - (?P<char>\$) | # or a dollar sign. - { # or a {-wrapped word, - (?P<v2>\w+) - (?: - (?P<strict>\?) | # with a strict marker - -(?P<defval>[^}]*) # or a default value - )? - } - ) - """ text = re.sub(dollar_pattern, dollar_replace, text) return text diff --git a/tests/test_misc.py b/tests/test_misc.py index d2777281..896dbf47 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -145,6 +145,7 @@ VARS = { ("Ill-formed: ${%5} ${{HI}} ${", "Ill-formed: ${%5} ${{HI}} ${"), ("Strict: ${FOO?} is there", "Strict: fooey is there"), ("Defaulted: ${WUT-missing}!", "Defaulted: missing!"), + ("Defaulted empty: ${WUT-}!", "Defaulted empty: !"), ]) def test_substitute_variables(before, after): assert substitute_variables(before, VARS) == after |