2828import re
2929from collections import UserList , UserString
3030from functools import lru_cache
31- from inspect import signature , Parameter
31+ from inspect import Parameter , signature
32+ from typing import Callable
3233
3334import SCons .Errors
3435import 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
6061class 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
99100class 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
138140class 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