Skip to content

Commit 10e00ec

Browse files
authored
Merge pull request SCons#4550 from mwichmann/variables-nosubst
Allow a Variable to not be substituted
2 parents a197ed4 + b78cfe9 commit 10e00ec

File tree

8 files changed

+110
-25
lines changed

8 files changed

+110
-25
lines changed

CHANGES.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
8585
Now matches the annotation and docstring (which were prematurely
8686
updated in 4.6). All SCons usage except unit test was already fully
8787
consistent with a bool.
88+
- When a variable is added to a Variables object, it can now be flagged
89+
as "don't perform substitution" by setting the argument subst.
90+
This allows variables to contain characters which would otherwise
91+
cause expansion. Fixes #4241.
8892
- The test runner now recognizes the unittest module's return code of 5,
8993
which means no tests were run. SCons/Script/MainTests.py currently
9094
has no tests, so this particular error code is expected - should not

RELEASE.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
5050
Now matches the annotation and docstring (which were prematurely
5151
updated in 4.6). All SCons usage except unit test was already fully
5252
consistent with a bool.
53+
- The Variables object Add method now accepts a subst keyword argument
54+
(defaults to True) which can be set to inhibit substitution prior to
55+
calling the variable's converter and validator.
5356

5457
FIXES
5558
-----

SCons/Tool/yacc.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ The value is used only if &cv-YACC_GRAPH_FILE_SUFFIX; is not set.
236236
The default value is <filename>.gv</filename>.
237237
</para>
238238
<para>
239-
<emphasis>Changed in version 4.X.Y</emphasis>: deprecated. The default value
239+
<emphasis>Changed in version 4.6.0</emphasis>: deprecated. The default value
240240
changed from <filename>.vcg</filename> (&bison; stopped generating
241241
<filename>.vcg</filename> output with version 2.4, in 2006).
242242
</para>
@@ -261,7 +261,7 @@ Various yacc tools have emitted various formats
261261
at different times.
262262
Set this to match what your parser generator produces.
263263
</para>
264-
<para><emphasis>New in version 4.X.Y</emphasis>. </para>
264+
<para><emphasis>New in version 4.6.0</emphasis>. </para>
265265
</summary>
266266
</cvar>
267267

SCons/Variables/PathVariable.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def PathExists(key, val, env) -> None:
141141

142142
# lint: W0622: Redefining built-in 'help' (redefined-builtin)
143143
def __call__(
144-
self, key, help: str, default, validator: Optional[Callable] = None
144+
self, key: str, help: str, default, validator: Optional[Callable] = None
145145
) -> Tuple[str, str, str, Callable, None]:
146146
"""Return a tuple describing a path list SCons Variable.
147147

SCons/Variables/VariablesTests.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,48 @@ def test_Update(self) -> None:
150150
opts.Update(env, {})
151151
assert env['ANSWER'] == 54
152152

153+
# Test that the value is not substituted if 'subst' is False
154+
# and that it is if 'subst' is True.
155+
def check_no_subst(key, value, env) -> None:
156+
"""Check that variable was not substituted before we get called."""
157+
assert value == "$ORIGIN", \
158+
f"Validator: '$ORIGIN' was substituted to {value!r}"
159+
160+
def conv_no_subst(value) -> None:
161+
"""Check that variable was not substituted before we get called."""
162+
assert value == "$ORIGIN", \
163+
f"Converter: '$ORIGIN' was substituted to {value!r}"
164+
return value
165+
166+
def check_subst(key, value, env) -> None:
167+
"""Check that variable was substituted before we get called."""
168+
assert value == "Value", \
169+
f"Validator: '$SUB' was not substituted {value!r} instead of 'Value'"
170+
171+
def conv_subst(value) -> None:
172+
"""Check that variable was not substituted before we get called."""
173+
assert value == "Value", \
174+
f"Converter: '$SUB' was substituted to {value!r} instead of 'Value'"
175+
return value
176+
177+
opts.Add('NOSUB',
178+
help='Variable whose value will not be substituted',
179+
default='$ORIGIN',
180+
validator=check_no_subst,
181+
converter=conv_no_subst,
182+
subst=False)
183+
opts.Add('SUB',
184+
help='Variable whose value will be substituted',
185+
default='$VAR',
186+
validator=check_subst,
187+
converter=conv_subst,
188+
subst=True)
189+
env = Environment()
190+
env['VAR'] = "Value"
191+
opts.Update(env)
192+
assert env['NOSUB'] == "$ORIGIN", env['NOSUB']
193+
assert env['SUB'] == env['VAR'], env['SUB']
194+
153195
# Test that a bad value from the file is used and
154196
# validation fails correctly.
155197
test = TestSCons.TestSCons()

SCons/Variables/__init__.py

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
class Variable:
5050
"""A Build Variable."""
5151

52-
__slots__ = ('key', 'aliases', 'help', 'default', 'validator', 'converter')
52+
__slots__ = ('key', 'aliases', 'help', 'default', 'validator', 'converter', 'do_subst')
5353

5454
def __lt__(self, other):
5555
"""Comparison fuction so Variable instances sort."""
@@ -87,9 +87,9 @@ def __init__(
8787
) -> None:
8888
self.options: List[Variable] = []
8989
self.args = args if args is not None else {}
90-
if not SCons.Util.is_List(files):
90+
if not SCons.Util.is_Sequence(files):
9191
files = [files] if files else []
92-
self.files = files
92+
self.files: Sequence[str] = files
9393
self.unknown: Dict[str, str] = {}
9494

9595
def __str__(self) -> str:
@@ -113,7 +113,11 @@ def _do_add(
113113
) -> None:
114114
"""Create a Variable and add it to the list.
115115
116-
Internal routine, not public API.
116+
This is the internal implementation for :meth:`Add` and
117+
:meth:`AddVariables`. Not part of the public API.
118+
119+
.. versionadded:: 4.8.0
120+
*subst* keyword argument is now recognized.
117121
"""
118122
option = Variable()
119123

@@ -132,6 +136,8 @@ def _do_add(
132136
option.default = default
133137
option.validator = validator
134138
option.converter = converter
139+
option.do_subst = kwargs.pop("subst", True)
140+
# TODO should any remaining kwargs be saved in the Variable?
135141

136142
self.options.append(option)
137143

@@ -152,27 +158,36 @@ def Add(
152158
"""Add a Build Variable.
153159
154160
Arguments:
155-
key: the name of the variable, or a 5-tuple (or list).
156-
If *key* is a tuple, and there are no additional positional
157-
arguments, it is unpacked into the variable name plus the four
158-
listed keyword arguments from below.
159-
If *key* is a tuple and there are additional positional arguments,
160-
the first word of the tuple is taken as the variable name,
161-
and the remainder as aliases.
162-
args: optional positional arguments, corresponding to the four
163-
listed keyword arguments.
161+
key: the name of the variable, or a 5-tuple (or other sequence).
162+
If *key* is a tuple, and there are no additional arguments
163+
except the *help*, *default*, *validator* and *converter*
164+
keyword arguments, *key* is unpacked into the variable name
165+
plus the *help*, *default*, *validator* and *converter*
166+
arguments; if there are additional arguments, the first
167+
elements of *key* is taken as the variable name, and the
168+
remainder as aliases.
169+
args: optional positional arguments, corresponding to the
170+
*help*, *default*, *validator* and *converter* keyword args.
164171
kwargs: arbitrary keyword arguments used by the variable itself.
165172
166173
Keyword Args:
167-
help: help text for the variable (default: ``""``)
174+
help: help text for the variable (default: empty string)
168175
default: default value for variable (default: ``None``)
169176
validator: function called to validate the value (default: ``None``)
170177
converter: function to be called to convert the variable's
171178
value before putting it in the environment. (default: ``None``)
179+
subst: perform substitution on the value before the converter
180+
and validator functions (if any) are called (default: ``True``)
181+
182+
.. versionadded:: 4.8.0
183+
The *subst* keyword argument is now specially recognized.
172184
"""
173185
if SCons.Util.is_Sequence(key):
174-
if not (len(args) or len(kwargs)):
175-
return self._do_add(*key)
186+
# If no other positional args (and no fundamental kwargs),
187+
# unpack key, and pass the kwargs on:
188+
known_kw = {'help', 'default', 'validator', 'converter'}
189+
if not args and not known_kw.intersection(kwargs.keys()):
190+
return self._do_add(*key, **kwargs)
176191

177192
return self._do_add(key, *args, **kwargs)
178193

@@ -247,7 +262,10 @@ def Update(self, env, args: Optional[dict] = None) -> None:
247262
# apply converters
248263
for option in self.options:
249264
if option.converter and option.key in values:
250-
value = env.subst(f'${option.key}')
265+
if option.do_subst:
266+
value = env.subst('${%s}' % option.key)
267+
else:
268+
value = env[option.key]
251269
try:
252270
try:
253271
env[option.key] = option.converter(value)
@@ -262,7 +280,11 @@ def Update(self, env, args: Optional[dict] = None) -> None:
262280
# apply validators
263281
for option in self.options:
264282
if option.validator and option.key in values:
265-
option.validator(option.key, env.subst(f'${option.key}'), env)
283+
if option.do_subst:
284+
value = env.subst('${%s}' % option.key)
285+
else:
286+
value = env[option.key]
287+
option.validator(option.key, value, env)
266288

267289
def UnknownVariables(self) -> dict:
268290
"""Return dict of unknown variables.
@@ -340,7 +362,6 @@ def GenerateHelpText(self, env, sort: Union[bool, Callable] = False) -> str:
340362
# removed so now we have to convert to a key.
341363
if callable(sort):
342364
options = sorted(self.options, key=cmp_to_key(lambda x, y: sort(x.key, y.key)))
343-
344365
elif sort is True:
345366
options = sorted(self.options)
346367
else:

doc/generated/variables.gen

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10668,7 +10668,7 @@ Various yacc tools have emitted various formats
1066810668
at different times.
1066910669
Set this to match what your parser generator produces.
1067010670
</para>
10671-
<para><emphasis>New in version 4.X.Y</emphasis>. </para>
10671+
<para><emphasis>New in version 4.6.0</emphasis>. </para>
1067210672
</listitem>
1067310673
</varlistentry>
1067410674
<varlistentry id="cv-YACC_HEADER_FILE">
@@ -10826,7 +10826,7 @@ The value is used only if &cv-YACC_GRAPH_FILE_SUFFIX; is not set.
1082610826
The default value is <filename>.gv</filename>.
1082710827
</para>
1082810828
<para>
10829-
<emphasis>Changed in version 4.X.Y</emphasis>: deprecated. The default value
10829+
<emphasis>Changed in version 4.6.0</emphasis>: deprecated. The default value
1083010830
changed from <filename>.vcg</filename> (&bison; stopped generating
1083110831
<filename>.vcg</filename> output with version 2.4, in 2006).
1083210832
</para>

doc/man/scons.xml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4835,7 +4835,7 @@ not to any stored-values files.
48354835

48364836
<variablelist>
48374837
<varlistentry id="v-Add">
4838-
<term><replaceable>vars</replaceable>.<function>Add</function>(<parameter>key, [help, default, validator, converter]</parameter>)</term>
4838+
<term><replaceable>vars</replaceable>.<function>Add</function>(<parameter>key, [help, default, validator, converter, subst]</parameter>)</term>
48394839
<listitem>
48404840
<para>Add a customizable &consvar; to the &Variables; object.
48414841
<parameter>key</parameter>
@@ -4887,6 +4887,16 @@ or there is no separate validator
48874887
it can raise a <exceptionname>ValueError</exceptionname>.
48884888
</para>
48894889

4890+
<para>
4891+
Substitution will be performed on the variable value
4892+
before the converter and validator are called,
4893+
unless the optional <parameter>subst</parameter> parameter
4894+
is false (default <literal>True</literal>).
4895+
Suppressing substitution may be useful if the variable value
4896+
looks like a &consvar; reference (e.g. <literal>$VAR</literal>)
4897+
and the validator and/or converter should see it unexpanded.
4898+
</para>
4899+
48904900
<para>
48914901
As a special case, if <parameter>key</parameter>
48924902
is a sequence and is the <emphasis>only</emphasis>
@@ -4919,6 +4929,11 @@ def valid_color(key, val, env):
49194929

49204930
vars.Add('COLOR', validator=valid_color)
49214931
</programlisting>
4932+
4933+
<para>
4934+
<emphasis>Changed in version 4.8.0:</emphasis>
4935+
added the <parameter>subst</parameter> parameter.
4936+
</para>
49224937
</listitem>
49234938
</varlistentry>
49244939

0 commit comments

Comments
 (0)