Skip to content

Commit 36e09bd

Browse files
authored
Merge pull request #4874 from mwichmann/maint/more-subst-2
Subst cleanups
2 parents 5b4d28d + 1c42e32 commit 36e09bd

3 files changed

Lines changed: 81 additions & 49 deletions

File tree

RELEASE.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ IMPROVEMENTS
8181
* Fix for_signature logic bug in ListSubber.expand() for correct signature
8282
generation
8383
* Modernize string formatting to f-strings
84-
Also, separately, some code-style cleanups in Subst.py.
84+
Also, separately, some code-style cleanups and docstring work in Subst.py.
8585

8686
- Test runner reworked to use Python logging; the portion of the test suite
8787
which tests the runner was adjusted to match.

SCons/ActionTests.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,17 @@ def __init__(self, **kw) -> None:
154154
self.d[k] = v
155155

156156
# Just use the underlying scons_subst*() utility methods.
157-
def subst(self, strSubst, raw: int=0, target=[], source=[], conv=None, overrides: bool=False):
157+
def subst(self, strSubst, raw: int=0, target=[], source=[], conv=None, overrides: dict | None = None):
158+
if overrides is None:
159+
overrides = {}
158160
return SCons.Subst.scons_subst(strSubst, self, raw,
159161
target, source, self.d, conv=conv, overrides=overrides)
160162

161163
subst_target_source = subst
162164

163-
def subst_list(self, strSubst, raw: int=0, target=[], source=[], conv=None, overrides: bool=False):
165+
def subst_list(self, strSubst, raw: int=0, target=[], source=[], conv=None, overrides: dict | None = None):
166+
if overrides is None:
167+
overrides = {}
164168
return SCons.Subst.scons_subst_list(strSubst, self, raw,
165169
target, source, self.d, conv=conv, overrides=overrides)
166170

SCons/Subst.py

Lines changed: 74 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@
2828
import re
2929
from collections import UserList, UserString
3030
from functools import lru_cache
31-
from inspect import signature, Parameter
31+
from inspect import Parameter, signature
32+
from typing import Callable
3233

3334
import SCons.Errors
3435
import SCons.Util
35-
from SCons.Util import is_String, is_Sequence
36+
from SCons.Util import is_Sequence, is_String
3637

3738
# Indexed by the SUBST_* constants below.
3839
_strconv = [
@@ -58,11 +59,11 @@ def raise_exception(exception, target, s):
5859

5960

6061
class Literal:
61-
"""A string wrapper for a string to prevent expansion.
62+
"""A wrapper for non-subsitutable strings.
6263
63-
If you use this object wrapped around a string, then it will
64-
be interpreted as literal. When passed to the command interpreter,
65-
all special characters will be escaped.
64+
The substitution logic will not change the wrapped string.
65+
When passed to the command interpreter, all special
66+
characters will be escaped and/or the string quoted.
6667
"""
6768

6869
def __init__(self, lstr) -> None:
@@ -71,7 +72,7 @@ def __init__(self, lstr) -> None:
7172
def __str__(self) -> str:
7273
return self.lstr
7374

74-
def escape(self, escape_func):
75+
def escape(self, escape_func: Callable) -> str:
7576
return escape_func(self.lstr)
7677

7778
def for_signature(self):
@@ -97,18 +98,20 @@ def __contains__(self, key) -> bool:
9798
return key in self.lstr
9899

99100
class SpecialAttrWrapper:
100-
"""This is a wrapper for what we call a 'Node special attribute.'
101-
This is any of the attributes of a Node that we can reference from
102-
Environment variable substitution, such as $TARGET.abspath or
103-
$SOURCES[1].filebase. We implement the same methods as Literal
104-
so we can handle special characters, plus a for_signature method,
105-
such that we can return some canonical string during signature
106-
calculation to avoid unnecessary rebuilds."""
107-
108-
def __init__(self, lstr, for_signature=None) -> None:
109-
"""The for_signature parameter, if supplied, will be the
110-
canonical string we return from for_signature(). Else
111-
we will simply return lstr."""
101+
"""A wrapper for Node special attributes.
102+
103+
"Special" are any attributes of a Node that we can reference from
104+
Environment variable substitution, such as ``$TARGET.abspath`` or
105+
``$SOURCES[1].filebase``. Implements the same methods as :class:`Literal
106+
so we can handle special characters, plus a :meth:`for_signature` method,
107+
so we can return some canonical string during signature
108+
calculation to avoid unnecessary rebuilds.
109+
110+
If the *for_signature* parameter is supplied at creation time,
111+
it is the "signature" :meth:`for_signature` will return.
112+
"""
113+
114+
def __init__(self, lstr: str, for_signature: str | None = None) -> None:
112115
self.lstr = lstr
113116
if for_signature:
114117
self.forsig = for_signature
@@ -118,22 +121,21 @@ def __init__(self, lstr, for_signature=None) -> None:
118121
def __str__(self) -> str:
119122
return self.lstr
120123

121-
def escape(self, escape_func):
124+
def escape(self, escape_func: Callable) -> str:
122125
return escape_func(self.lstr)
123126

124-
def for_signature(self):
127+
def for_signature(self) -> str:
125128
return self.forsig
126129

127130
def is_literal(self) -> bool:
128131
return True
129132

130-
def quote_spaces(arg):
133+
def quote_spaces(arg: str) -> str:
131134
"""Generic function for putting double quotes around any string that
132135
has white space in it."""
133136
if ' ' in arg or '\t' in arg:
134137
return f'"{arg}"'
135-
else:
136-
return str(arg)
138+
return str(arg)
137139

138140
class CmdStringHolder(UserString):
139141
"""Holder for substituted strings intended for the command line.
@@ -152,14 +154,19 @@ def __init__(self, cmd, literal: bool = False) -> None:
152154
def is_literal(self) -> bool:
153155
return self.literal
154156

155-
def escape(self, escape_func, quote_func=quote_spaces) -> str:
156-
"""Escape the string with the supplied function. The
157-
function is expected to take an arbitrary string, then
158-
return it with all special characters escaped and ready
159-
for passing to the command interpreter.
157+
def escape(self, escape_func: Callable, quote_func: Callable = quote_spaces) -> str:
158+
"""Escape characters in a saved string.
160159
161-
After calling this function, the next call to str() will
162-
return the escaped string.
160+
Like ``escape`` methods in other subst-related classes, this is
161+
indirect - you call ``object.escape``, but have to pass a function
162+
which actually performs the escaping - this is because the approach
163+
needed is platform-specific.
164+
165+
Args:
166+
escape_func: function to escape special characters.
167+
quote_func: function for quoting the string. Used only if
168+
the string is not marked as "literal". Defaults to
169+
:func:`quote_spaces`.
163170
"""
164171
data = self.data
165172
if self.literal:
@@ -168,11 +175,11 @@ def escape(self, escape_func, quote_func=quote_spaces) -> str:
168175
return quote_func(data)
169176
return data
170177

171-
def escape_list(mylist, escape_func) -> list[str]:
178+
def escape_list(mylist: list, escape_func: Callable) -> list[str]:
172179
"""Escape a list of arguments by running the specified escape_func
173180
on every object in the list that has an escape() method."""
174181

175-
def escape(obj, escape_func=escape_func):
182+
def escape(obj, escape_func: Callable = escape_func):
176183
try:
177184
e = obj.escape
178185
except AttributeError:
@@ -403,7 +410,7 @@ class StringSubber:
403410
the expansion.
404411
"""
405412

406-
def __init__(self, env, mode: int, conv, gvars: dict) -> None:
413+
def __init__(self, env, mode: int, conv: Callable, gvars: dict) -> None:
407414
self.env = env
408415
self.mode = mode
409416
self.conv = conv
@@ -559,7 +566,7 @@ class ListSubber(UserList):
559566
internally.
560567
"""
561568

562-
def __init__(self, env, mode: int, conv, gvars: dict) -> None:
569+
def __init__(self, env, mode: int, conv: Callable, gvars: dict) -> None:
563570
super().__init__([])
564571
self.env = env
565572
self.mode = mode
@@ -595,8 +602,8 @@ def expanded(self, s) -> bool:
595602
return False
596603
return _unexpandable.search(s) is None
597604

598-
def expand(self, s, lvars, within_list):
599-
"""Expand a single "token" as necessary, appending the
605+
def expand(self, s, lvars, within_list) -> None:
606+
"""Expand a single token *s* as necessary, appending the
600607
expansion to the current result.
601608
602609
This handles expanding different types of things (strings,
@@ -696,7 +703,7 @@ def expand(self, s, lvars, within_list):
696703
else:
697704
self.append(s)
698705

699-
def substitute(self, args, lvars, within_list) -> None:
706+
def substitute(self, args, lvars, within_list: bool) -> None:
700707
"""Substitute expansions in an argument or list of arguments.
701708
702709
This serves as a wrapper for splitting up a string into
@@ -884,9 +891,18 @@ def _remove_list(list):
884891
_space_sep = re.compile(r'[\t ]+(?![^{]*})')
885892

886893

887-
def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars=None, lvars=None, conv=None, overrides: dict | None = None):
888-
"""Expand a string or list containing construction variable
889-
substitutions.
894+
def scons_subst(
895+
strSubst,
896+
env,
897+
mode=SUBST_RAW,
898+
target=None,
899+
source=None,
900+
gvars: dict | None = None,
901+
lvars: dict | None = None,
902+
conv: Callable | None = None,
903+
overrides: dict | None = None,
904+
):
905+
"""Expand a string or list containing construction variable substitutions.
890906
891907
This is the work-horse function for substitutions in file names
892908
and the like. The companion scons_subst_list() function (below)
@@ -902,9 +918,10 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars=N
902918
gvars = {}
903919
if lvars is None:
904920
lvars = {}
905-
906921
if conv is None:
907922
conv = _strconv[mode]
923+
if overrides is None:
924+
overrides = {}
908925

909926
# Doing this every time is a bit of a waste, since the Executor
910927
# has typically already populated the OverrideEnvironment with
@@ -973,7 +990,17 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars=N
973990

974991
return result
975992

976-
def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars=None, lvars=None, conv=None, overrides: dict | None = None):
993+
def scons_subst_list(
994+
strSubst,
995+
env,
996+
mode=SUBST_RAW,
997+
target=None,
998+
source=None,
999+
gvars: dict | None = None,
1000+
lvars: dict | None = None,
1001+
conv: Callable | None = None,
1002+
overrides: dict | None = None,
1003+
):
9771004
"""Substitute construction variables in a string (or list or other
9781005
object) and separate the arguments into a command list.
9791006
@@ -985,9 +1012,10 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gv
9851012
gvars = {}
9861013
if lvars is None:
9871014
lvars = {}
988-
9891015
if conv is None:
9901016
conv = _strconv[mode]
1017+
if overrides is None:
1018+
overrides = {}
9911019

9921020
# Doing this every time is a bit of a waste, since the Executor
9931021
# has typically already populated the OverrideEnvironment with
@@ -1070,5 +1098,5 @@ def sub_match(match, val=val, matchlist=matchlist):
10701098

10711099
if is_String(strSubst):
10721100
return _dollar_exps.sub(sub_match, strSubst)
1073-
else:
1074-
return strSubst
1101+
1102+
return strSubst

0 commit comments

Comments
 (0)