2727
2828import os .path
2929import sys
30+ from contextlib import suppress
3031from functools import cmp_to_key
3132from typing import Callable , Sequence
3233
@@ -58,22 +59,30 @@ class Variable:
5859 __slots__ = ('key' , 'aliases' , 'help' , 'default' , 'validator' , 'converter' , 'do_subst' )
5960
6061 def __lt__ (self , other ):
61- """Comparison fuction so Variable instances sort."""
62+ """Comparison fuction so :class:` Variable` instances sort."""
6263 return self .key < other .key
6364
6465 def __str__ (self ) -> str :
65- """Provide a way to "print" a Variable object."""
66+ """Provide a way to "print" a :class:` Variable` object."""
6667 return (
67- f"({ self .key !r} , { self .aliases } , { self .help !r} , { self .default !r} , "
68+ f"({ self .key !r} , { self .aliases } , "
69+ f"help={ self .help !r} , default={ self .default !r} , "
6870 f"validator={ self .validator } , converter={ self .converter } )"
6971 )
7072
7173
7274class Variables :
73- """A container for multiple Build Variables.
75+ """A container for Build Variables.
7476
75- Includes methods to updates the environment with the variables,
76- and to render the help text.
77+ Includes a method to populate the variables with values into a
78+ construction envirionment, and methods to render the help text.
79+
80+ Note that the pubic API for creating a ``Variables`` object is
81+ :func:`SCons.Script.Variables`, a kind of factory function, which
82+ defaults to supplying the contents of :attr:`~SCons.Script.ARGUMENTS`
83+ as the *args* parameter if it was not otherwise given. That is the
84+ behavior documented in the manpage for ``Variables`` - and different
85+ from the default if you instantiate this directly.
7786
7887 Arguments:
7988 files: string or list of strings naming variable config scripts
@@ -83,11 +92,15 @@ class Variables:
8392 instead of a fresh instance. Currently inoperable (default ``False``)
8493
8594 .. versionchanged:: 4.8.0
86- The default for *is_global* changed to ``False`` (previously
87- ``True`` but it had no effect due to an implementation error).
95+ The default for *is_global* changed to ``False`` (the previous
96+ default ``True`` had no effect due to an implementation error).
8897
8998 .. deprecated:: 4.8.0
9099 *is_global* is deprecated.
100+
101+ .. versionadded:: NEXT_RELEASE
102+ The :attr:`defaulted` attribute now lists those variables which
103+ were filled in from default values.
91104 """
92105
93106 def __init__ (
@@ -102,15 +115,18 @@ def __init__(
102115 files = [files ] if files else []
103116 self .files : Sequence [str ] = files
104117 self .unknown : dict [str , str ] = {}
118+ self .defaulted : list [str ] = []
105119
106120 def __str__ (self ) -> str :
107- """Provide a way to "print" a Variables object."""
108- s = "Variables(\n options=[\n "
109- for option in self .options :
110- s += f" { str (option )} ,\n "
111- s += " ],\n "
112- s += f" args={ self .args } ,\n files={ self .files } ,\n unknown={ self .unknown } ,\n )"
113- return s
121+ """Provide a way to "print" a :class:`Variables` object."""
122+ opts = ',\n ' .join ((f" { option !s} " for option in self .options ))
123+ return (
124+ f"Variables(\n options=[\n { opts } \n ],\n "
125+ f" args={ self .args } ,\n "
126+ f" files={ self .files } ,\n "
127+ f" unknown={ self .unknown } ,\n "
128+ f" defaulted={ self .defaulted } ,\n )"
129+ )
114130
115131 # lint: W0622: Redefining built-in 'help'
116132 def _do_add (
@@ -122,7 +138,7 @@ def _do_add(
122138 converter : Callable | None = None ,
123139 ** kwargs ,
124140 ) -> None :
125- """Create a Variable and add it to the list.
141+ """Create a :class:` Variable` and add it to the list.
126142
127143 This is the internal implementation for :meth:`Add` and
128144 :meth:`AddVariables`. Not part of the public API.
@@ -203,9 +219,9 @@ def Add(
203219 return self ._do_add (key , * args , ** kwargs )
204220
205221 def AddVariables (self , * optlist ) -> None :
206- """Add a list of Build Variables.
222+ """Add Build Variables.
207223
208- Each list element is a tuple/list of arguments to be passed on
224+ Each *optlist* element is a sequence of arguments to be passed on
209225 to the underlying method for adding variables.
210226
211227 Example::
@@ -223,13 +239,22 @@ def AddVariables(self, *optlist) -> None:
223239 def Update (self , env , args : dict | None = None ) -> None :
224240 """Update an environment with the Build Variables.
225241
242+ Collects variables from the input sources which do not match
243+ a variable description in this object. These are ignored for
244+ purposes of adding to *env*, but can be retrieved using the
245+ :meth:`UnknownVariables` method. Also collects variables which
246+ are set in *env* from the default in a variable description and
247+ not from the input sources. These are available in the
248+ :attr:`defaulted` attribute.
249+
226250 Args:
227251 env: the environment to update.
228252 args: a dictionary of keys and values to update in *env*.
229253 If omitted, uses the saved :attr:`args`
230254 """
231- # first pull in the defaults
255+ # first pull in the defaults, except any which are None.
232256 values = {opt .key : opt .default for opt in self .options if opt .default is not None }
257+ self .defaulted = list (values )
233258
234259 # next set the values specified in any options script(s)
235260 for filename in self .files :
@@ -256,6 +281,8 @@ def Update(self, env, args: dict | None = None) -> None:
256281 for option in self .options :
257282 if arg in option .aliases + [option .key ,]:
258283 values [option .key ] = value
284+ with suppress (ValueError ):
285+ self .defaulted .remove (option .key )
259286 added = True
260287 if not added :
261288 self .unknown [arg ] = value
@@ -269,6 +296,8 @@ def Update(self, env, args: dict | None = None) -> None:
269296 for option in self .options :
270297 if arg in option .aliases + [option .key ,]:
271298 values [option .key ] = value
299+ with suppress (ValueError ):
300+ self .defaulted .remove (option .key )
272301 added = True
273302 if not added :
274303 self .unknown [arg ] = value
0 commit comments