Skip to content

Commit dde0a3e

Browse files
committed
Move variable substitution to be independent
1 parent f4a9985 commit dde0a3e

File tree

3 files changed

+56
-18
lines changed

3 files changed

+56
-18
lines changed

coverage/config.py

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from coverage import env
1111
from coverage.backward import configparser, iitems, string_class
1212
from coverage.misc import contract, CoverageException, isolate_module
13+
from coverage.misc import substitute_variables
1314

1415
os = isolate_module(os)
1516

@@ -85,23 +86,7 @@ def get(self, section, option, *args, **kwargs): # pylint: disable=argume
8586
raise configparser.NoOptionError
8687

8788
v = configparser.RawConfigParser.get(self, real_section, option, *args, **kwargs)
88-
def dollar_replace(m):
89-
"""Called for each $replacement."""
90-
# Only one of the groups will have matched, just get its text.
91-
word = next(w for w in m.groups() if w is not None) # pragma: part covered
92-
if word == "$":
93-
return "$"
94-
else:
95-
return os.environ.get(word, '')
96-
97-
dollar_pattern = r"""(?x) # Use extended regex syntax
98-
\$(?: # A dollar sign, then
99-
(?P<v1>\w+) | # a plain word,
100-
{(?P<v2>\w+)} | # or a {-wrapped word,
101-
(?P<char>[$]) # or a dollar sign.
102-
)
103-
"""
104-
v = re.sub(dollar_pattern, dollar_replace, v)
89+
v = substitute_variables(v)
10590
return v
10691

10792
def getlist(self, section, option):

coverage/misc.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import inspect
99
import locale
1010
import os
11+
import re
1112
import sys
1213
import types
1314

@@ -250,6 +251,42 @@ def _needs_to_implement(that, func_name):
250251
)
251252

252253

254+
def substitute_variables(text, variables=os.environ):
255+
"""Substitute ``${VAR}`` variables in `text` with their values.
256+
257+
Variables in the text can take a number of shell-inspired forms::
258+
259+
$VAR
260+
${VAR}
261+
262+
A dollar can be inserted with ``$$``.
263+
264+
`variables` is a dictionary of variable values, defaulting to the
265+
environment variables.
266+
267+
Returns the resulting text with values substituted.
268+
269+
"""
270+
def dollar_replace(m):
271+
"""Called for each $replacement."""
272+
# Only one of the groups will have matched, just get its text.
273+
word = next(w for w in m.groups() if w is not None) # pragma: part covered
274+
if word == "$":
275+
return "$"
276+
else:
277+
return variables.get(word, '')
278+
279+
dollar_pattern = r"""(?x) # Use extended regex syntax
280+
\$(?: # A dollar sign, then
281+
(?P<v1>\w+) | # a plain word,
282+
{(?P<v2>\w+)} | # or a {-wrapped word,
283+
(?P<char>[$]) # or a dollar sign.
284+
)
285+
"""
286+
text = re.sub(dollar_pattern, dollar_replace, text)
287+
return text
288+
289+
253290
class BaseCoverageException(Exception):
254291
"""The base of all Coverage exceptions."""
255292
pass

tests/test_misc.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import pytest
77

88
from coverage.misc import contract, dummy_decorator_with_args, file_be_gone
9-
from coverage.misc import format_lines, Hasher, one_of
9+
from coverage.misc import format_lines, Hasher, one_of, substitute_variables
1010

1111
from tests.coveragetest import CoverageTest
1212

@@ -135,3 +135,19 @@ def undecorated(a=None, b=None): # pylint: disable=missing-docstr
135135
])
136136
def test_format_lines(statements, lines, result):
137137
assert format_lines(statements, lines) == result
138+
139+
140+
@pytest.mark.parametrize("before, after", [
141+
("Nothing to do", "Nothing to do"),
142+
("Dollar: $$", "Dollar: $"),
143+
("Simple: $FOO is fooey", "Simple: fooey is fooey"),
144+
("Braced: X${FOO}X.", "Braced: XfooeyX."),
145+
("Missing: x$NOTHING is x", "Missing: x is x"),
146+
("Multiple: $$ $FOO $BAR ${FOO}", "Multiple: $ fooey xyzzy fooey"),
147+
])
148+
def test_substitute_variables(before, after):
149+
variables = {
150+
'FOO': 'fooey',
151+
'BAR': 'xyzzy',
152+
}
153+
assert substitute_variables(before, variables) == after

0 commit comments

Comments
 (0)