Skip to content

Commit f67bfba

Browse files
authored
Merge branch 'master' into issue/unique-delete-existing
2 parents 84f4364 + 46cadec commit f67bfba

18 files changed

+263
-91
lines changed

CHANGES.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
2121
From Mats Wichmann:
2222
- env.Dump() now considers the "key" positional argument to be a varargs
2323
type (zero, one or many). However called, it returns a serialized
24-
result that looks like a dict. Previously, only one "key" was
25-
accepted. and unlike the zero-args case, it was be serialized
26-
to a string containing the value without the key. For example, if
24+
result that looks like a dict. Previously, only a single "key" was
25+
accepted, and unlike the zero-args case, it was serialized to a
26+
string containing just the value (without the key). For example, if
2727
"print(repr(env.Dump('CC'))" previously returned "'gcc'", it will now
2828
return "{'CC': 'gcc'}".
2929
- Add a timeout to test/ninja/default_targets.py - it's gotten stuck on
@@ -42,6 +42,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
4242
DefaultEnvironment and Tool,.
4343
- Fix a problem with AppendUnique and PrependUnique where a value could
4444
be erroneously removed due to a substring match.
45+
- Fix handling of ListVariable when supplying a quoted choice containing
46+
a space character (issue #4585).
4547

4648

4749
RELEASE 4.8.0 - Sun, 07 Jul 2024 17:22:20 -0700

RELEASE.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ FIXES
4848
so this form of import should now work again.
4949
- Fix a problem with AppendUnique and PrependUnique where a value could
5050
be erroneously removed due to a substring match.
51+
- Fix handling of ListVariable when supplying a quoted choice containing
52+
a space character (issue #4585).
5153

5254
IMPROVEMENTS
5355
------------

SCons/Variables/BoolVariable.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
...
3333
"""
3434

35-
from typing import Tuple, Callable
35+
from typing import Callable, Tuple, Union
3636

3737
import SCons.Errors
3838

@@ -42,7 +42,7 @@
4242
FALSE_STRINGS = ('n', 'no', 'false', 'f', '0', 'off', 'none')
4343

4444

45-
def _text2bool(val: str) -> bool:
45+
def _text2bool(val: Union[str, bool]) -> bool:
4646
"""Convert boolean-like string to boolean.
4747
4848
If *val* looks like it expresses a bool-like value, based on
@@ -54,6 +54,9 @@ def _text2bool(val: str) -> bool:
5454
Raises:
5555
ValueError: if *val* cannot be converted to boolean.
5656
"""
57+
if isinstance(val, bool):
58+
# mainly for the subst=False case: default might be a bool
59+
return val
5760
lval = val.lower()
5861
if lval in TRUE_STRINGS:
5962
return True
@@ -63,7 +66,7 @@ def _text2bool(val: str) -> bool:
6366
raise ValueError(f"Invalid value for boolean variable: {val!r}")
6467

6568

66-
def _validator(key, val, env) -> None:
69+
def _validator(key: str, val, env) -> None:
6770
"""Validate that the value of *key* in *env* is a boolean.
6871
6972
Parameter *val* is not used in the check.

SCons/Variables/BoolVariableTests.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ def test_converter(self) -> None:
4343
"""Test the BoolVariable converter"""
4444
opts = SCons.Variables.Variables()
4545
opts.Add(SCons.Variables.BoolVariable('test', 'test option help', False))
46-
4746
o = opts.options[0]
4847

4948
true_values = [
@@ -76,6 +75,17 @@ def test_converter(self) -> None:
7675
with self.assertRaises(ValueError):
7776
o.converter('x')
7877

78+
# Synthesize the case where the variable is created with subst=False:
79+
# Variables code won't subst before calling the converter,
80+
# and we might have pulled a bool from the option default.
81+
with self.subTest():
82+
x = o.converter(True)
83+
assert x, f"converter returned False for {t!r}"
84+
with self.subTest():
85+
x = o.converter(False)
86+
assert not x, f"converter returned False for {t!r}"
87+
88+
7989
def test_validator(self) -> None:
8090
"""Test the BoolVariable validator"""
8191
opts = SCons.Variables.Variables()

SCons/Variables/EnumVariableTests.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def valid(o, v) -> None:
159159
def invalid(o, v) -> None:
160160
with self.assertRaises(
161161
SCons.Errors.UserError,
162-
msg=f"did not catch expected UserError for o = {o.key}, v = {v}",
162+
msg=f"did not catch expected UserError for o = {o.key!r}, v = {v!r}",
163163
):
164164
o.validator('X', v, {})
165165
table = {
@@ -186,6 +186,22 @@ def invalid(o, v) -> None:
186186
expected[1](opt1, v)
187187
expected[2](opt2, v)
188188

189+
# make sure there are no problems with space-containing entries
190+
opts = SCons.Variables.Variables()
191+
opts.Add(
192+
SCons.Variables.EnumVariable(
193+
'test0',
194+
help='test option help',
195+
default='nospace',
196+
allowed_values=['nospace', 'with space'],
197+
map={},
198+
ignorecase=0,
199+
)
200+
)
201+
opt = opts.options[0]
202+
valid(opt, 'nospace')
203+
valid(opt, 'with space')
204+
189205

190206
if __name__ == "__main__":
191207
unittest.main()

SCons/Variables/ListVariable.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ def __init__(
8181
if allowedElems is None:
8282
allowedElems = []
8383
super().__init__([_f for _f in initlist if _f])
84+
# TODO: why sorted? don't we want to display in the order user gave?
8485
self.allowedElems = sorted(allowedElems)
8586

8687
def __cmp__(self, other):
@@ -118,6 +119,9 @@ def _converter(val, allowedElems, mapdict) -> _ListVariable:
118119
The arguments *allowedElems* and *mapdict* are non-standard
119120
for a :class:`Variables` converter: the lambda in the
120121
:func:`ListVariable` function arranges for us to be called correctly.
122+
123+
Incoming values ``all`` and ``none`` are recognized and converted
124+
into their expanded form.
121125
"""
122126
if val == 'none':
123127
val = []
@@ -155,7 +159,7 @@ def _validator(key, val, env) -> None:
155159
allowedElems = env[key].allowedElems
156160
if isinstance(val, _ListVariable): # not substituted, use .data
157161
notAllowed = [v for v in val.data if v not in allowedElems]
158-
else: # val will be a string
162+
else: # presumably a string
159163
notAllowed = [v for v in val.split() if v not in allowedElems]
160164
if notAllowed:
161165
# Converter only synthesized 'all' and 'none', they are never

SCons/Variables/ListVariableTests.py

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
2222
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2323

24+
"""Test List Variables elements."""
25+
2426
import copy
2527
import unittest
2628

@@ -50,18 +52,22 @@ def test_ListVariable(self) -> None:
5052
assert o.default == 'one,three'
5153

5254
def test_converter(self) -> None:
53-
"""Test the ListVariable converter"""
55+
"""Test the ListVariable converter.
56+
57+
There is now a separate validator (for a long time validation was
58+
in the converter), but it depends on the _ListVariable instance the
59+
converter creates, so it's easier to test them in the same function.
60+
"""
5461
opts = SCons.Variables.Variables()
5562
opts.Add(
5663
SCons.Variables.ListVariable(
5764
'test',
5865
'test option help',
59-
'all',
60-
['one', 'two', 'three'],
61-
{'ONE': 'one', 'TWO': 'two'},
66+
default='all',
67+
names=['one', 'two', 'three'],
68+
map={'ONE': 'one', 'TWO': 'two'},
6269
)
6370
)
64-
6571
o = opts.options[0]
6672

6773
x = o.converter('all')
@@ -110,10 +116,48 @@ def test_converter(self) -> None:
110116
# invalid value should convert (no change) without error
111117
x = o.converter('no_match')
112118
assert str(x) == 'no_match', x
113-
# ... and fail to validate
119+
120+
# validator checks
121+
122+
# first, the one we just set up
114123
with self.assertRaises(SCons.Errors.UserError):
115124
o.validator('test', 'no_match', {"test": x})
116125

126+
# now a new option, this time with a name w/ space in it (issue #4585)
127+
opts.Add(
128+
SCons.Variables.ListVariable(
129+
'test2',
130+
help='test2 option help',
131+
default='two',
132+
names=['one', 'two', 'three', 'four space'],
133+
)
134+
)
135+
o = opts.options[1]
136+
137+
def test_valid(opt, seq):
138+
"""Validate a ListVariable value.
139+
140+
Call the converter manually, since we need the _ListVariable
141+
object to pass to the validator - this would normally be done
142+
by the Variables.Update method.
143+
"""
144+
x = opt.converter(seq)
145+
self.assertIsNone(opt.validator(opt.key, x, {opt.key: x}))
146+
147+
with self.subTest():
148+
test_valid(o, 'one')
149+
with self.subTest():
150+
test_valid(o, 'one,two,three')
151+
with self.subTest():
152+
test_valid(o, 'all')
153+
with self.subTest():
154+
test_valid(o, 'none')
155+
with self.subTest():
156+
test_valid(o, 'four space')
157+
with self.subTest():
158+
test_valid(o, 'one,four space')
159+
160+
117161
def test_copy(self) -> None:
118162
"""Test copying a ListVariable like an Environment would"""
119163
opts = SCons.Variables.Variables()

SCons/Variables/PackageVariable.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"""
5252

5353
import os
54-
from typing import Callable, Optional, Tuple
54+
from typing import Callable, Optional, Tuple, Union
5555

5656
import SCons.Errors
5757

@@ -60,13 +60,16 @@
6060
ENABLE_STRINGS = ('1', 'yes', 'true', 'on', 'enable', 'search')
6161
DISABLE_STRINGS = ('0', 'no', 'false', 'off', 'disable')
6262

63-
def _converter(val):
63+
def _converter(val: Union[str, bool]) -> Union[str, bool]:
6464
"""Convert package variables.
6565
6666
Returns True or False if one of the recognized truthy or falsy
6767
values is seen, else return the value unchanged (expected to
6868
be a path string).
6969
"""
70+
if isinstance(val, bool):
71+
# mainly for the subst=False case: default might be a bool
72+
return val
7073
lval = val.lower()
7174
if lval in ENABLE_STRINGS:
7275
return True
@@ -75,7 +78,7 @@ def _converter(val):
7578
return val
7679

7780

78-
def _validator(key, val, env, searchfunc) -> None:
81+
def _validator(key: str, val, env, searchfunc) -> None:
7982
"""Validate package variable for valid path.
8083
8184
Checks that if a path is given as the value, that pathname actually exists.

SCons/Variables/PackageVariableTests.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@ def test_converter(self) -> None:
8282
x = o.converter(str(False))
8383
assert not x, "converter returned a string when given str(False)"
8484

85+
# Synthesize the case where the variable is created with subst=False:
86+
# Variables code won't subst before calling the converter,
87+
# and we might have pulled a bool from the option default.
88+
with self.subTest():
89+
x = o.converter(True)
90+
assert x, f"converter returned False for {t!r}"
91+
with self.subTest():
92+
x = o.converter(False)
93+
assert not x, f"converter returned False for {t!r}"
94+
8595
def test_validator(self) -> None:
8696
"""Test the PackageVariable validator"""
8797
opts = SCons.Variables.Variables()

SCons/Variables/PathVariable.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,12 @@ class _PathVariableClass:
9393
"""
9494

9595
@staticmethod
96-
def PathAccept(key, val, env) -> None:
96+
def PathAccept(key: str, val, env) -> None:
9797
"""Validate path with no checking."""
9898
return
9999

100100
@staticmethod
101-
def PathIsDir(key, val, env) -> None:
101+
def PathIsDir(key: str, val, env) -> None:
102102
"""Validate path is a directory."""
103103
if os.path.isdir(val):
104104
return
@@ -109,7 +109,7 @@ def PathIsDir(key, val, env) -> None:
109109
raise SCons.Errors.UserError(msg)
110110

111111
@staticmethod
112-
def PathIsDirCreate(key, val, env) -> None:
112+
def PathIsDirCreate(key: str, val, env) -> None:
113113
"""Validate path is a directory, creating if needed."""
114114
if os.path.isdir(val):
115115
return
@@ -123,7 +123,7 @@ def PathIsDirCreate(key, val, env) -> None:
123123
raise SCons.Errors.UserError(msg) from exc
124124

125125
@staticmethod
126-
def PathIsFile(key, val, env) -> None:
126+
def PathIsFile(key: str, val, env) -> None:
127127
"""Validate path is a file."""
128128
if not os.path.isfile(val):
129129
if os.path.isdir(val):
@@ -133,7 +133,7 @@ def PathIsFile(key, val, env) -> None:
133133
raise SCons.Errors.UserError(msg)
134134

135135
@staticmethod
136-
def PathExists(key, val, env) -> None:
136+
def PathExists(key: str, val, env) -> None:
137137
"""Validate path exists."""
138138
if not os.path.exists(val):
139139
msg = f'Path for variable {key!r} does not exist: {val}'

0 commit comments

Comments
 (0)