diff options
author | William Deegan <bill@baddogconsulting.com> | 2024-12-16 05:07:39 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-16 05:07:39 (GMT) |
commit | f906d016df29c879f7eb5d8a28d5f12a4d0a4d0f (patch) | |
tree | fa495355cea8182c5155f40e2979bb5e0671c34d | |
parent | 3f6be17927839c2cf0a94216929911a4136a833b (diff) | |
parent | 393792cb0b334bec81f9b32e70bf2c356ab35d37 (diff) | |
download | SCons-f906d016df29c879f7eb5d8a28d5f12a4d0a4d0f.zip SCons-f906d016df29c879f7eb5d8a28d5f12a4d0a4d0f.tar.gz SCons-f906d016df29c879f7eb5d8a28d5f12a4d0a4d0f.tar.bz2 |
Merge branch 'master' into maint/st_mode
-rw-r--r-- | CHANGES.txt | 3 | ||||
-rw-r--r-- | RELEASE.txt | 2 | ||||
-rw-r--r-- | SCons/Tool/install.xml | 2 | ||||
-rw-r--r-- | SCons/Variables/EnumVariable.py | 11 | ||||
-rw-r--r-- | SCons/Variables/ListVariable.py | 23 | ||||
-rw-r--r-- | SCons/Variables/__init__.py | 37 | ||||
-rw-r--r-- | doc/man/scons.xml | 333 | ||||
-rw-r--r-- | test/update-release-info/update-release-info.py | 17 |
8 files changed, 261 insertions, 167 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index b86d8f5..5383cb7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -174,6 +174,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - A build Variable is now a dataclass, with initialization moving to the automatically provided method; the Variables class no longer writes directly to a Variable (makes static checkers happier). + - Improved Variables documentation. - The (optional) C Conditional Scanner now does limited macro replacement on the contents of CPPDEFINES, to improve finding deps that are conditionally included. Previously replacement was only @@ -183,6 +184,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER and does not error out if that limit is reached (issue #4523). - Minor modernization: make use of stat object's st_mode, st_mtime and other attributes rather than indexing into stat return. + - The update-release-info test is adapted to accept changed help output + introduced in Python 3.12.8/3.13.1. RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700 diff --git a/RELEASE.txt b/RELEASE.txt index 1251be6..d386cb3 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -209,6 +209,8 @@ DOCUMENTATION - Update Clean and NoClean documentation. +- Improved Variables documentation. + DEVELOPMENT ----------- diff --git a/SCons/Tool/install.xml b/SCons/Tool/install.xml index cdb044b..3cbb0f4 100644 --- a/SCons/Tool/install.xml +++ b/SCons/Tool/install.xml @@ -77,7 +77,7 @@ a "live" location in the system. </para> <para> -See also &FindInstalledFiles;. +See also &f-link-FindInstalledFiles;. For more thoughts on installation, see the User Guide (particularly the section on Command-Line Targets and the chapters on Installing Files and on Alias Targets). diff --git a/SCons/Variables/EnumVariable.py b/SCons/Variables/EnumVariable.py index f154a13..a576af5 100644 --- a/SCons/Variables/EnumVariable.py +++ b/SCons/Variables/EnumVariable.py @@ -77,19 +77,20 @@ def EnumVariable( ) -> tuple[str, str, str, Callable, Callable]: """Return a tuple describing an enumaration SCons Variable. - The input parameters describe a variable with only predefined values - allowed. The value of *ignorecase* defines the behavior of the + An Enum Variable is an abstraction that allows choosing one + value from a provided list of possibilities (*allowed_values*). + The value of *ignorecase* defines the behavior of the validator and converter: if ``0``, the validator/converter are case-sensitive; if ``1``, the validator/converter are case-insensitive; if ``2``, the validator/converter are case-insensitive and the converted value will always be lower-case. Arguments: - key: variable name, passed directly through to the return tuple. - default: default values, passed directly through to the return tuple. + key: the name of the variable. + default: default value, passed directly through to the return tuple. help: descriptive part of the help text, will have the allowed values automatically appended. - allowed_values: list of the allowed values for this variable. + allowed_values: the values for the choice. map: optional dictionary which may be used for converting the input value into canonical values (e.g. for aliases). ignorecase: defines the behavior of the validator and converter. diff --git a/SCons/Variables/ListVariable.py b/SCons/Variables/ListVariable.py index 4ea7dc3..880496f 100644 --- a/SCons/Variables/ListVariable.py +++ b/SCons/Variables/ListVariable.py @@ -185,23 +185,24 @@ def ListVariable( names: list[str], map: dict | None = None, validator: Callable | None = None, -) -> tuple[str, str, str, None, Callable]: +) -> tuple[str, str, str, Callable, Callable]: """Return a tuple describing a list variable. - The input parameters describe a list variable, where the values - can be one or more from *names* plus the special values ``all`` - and ``none``. + A List Variable is an abstraction that allows choosing one or more + values from a provided list of possibilities (*names). The special terms + ``all`` and ``none`` are also provided to help make the selection. Arguments: key: the name of the list variable. help: the basic help message. Will have text appended indicating - the allowable values (not including any extra names from *map*). - default: the default value(s) for the list variable. Can be - given as string (possibly comma-separated), or as a list of strings. - ``all`` or ``none`` are allowed as *default*. You can also simulate - a must-specify ListVariable by giving a *default* that is not part - of *names*, it will fail validation if not supplied. - names: the allowable values. Must be a list of strings. + the allowed values (not including any extra names from *map*). + default: the default value(s) for the list variable. Can be given + as string (use commas to -separated multiple values), or as a list + of strings. ``all`` or ``none`` are allowed as *default*. + A must-specify ListVariable can be simulated by giving a value + that is not part of *names*, which will cause validation to fail + if the variable is not given in the input sources. + names: the values to choose from. Must be a list of strings. map: optional dictionary to map alternative names to the ones in *names*, providing a form of alias. The converter will make the replacement, names from *map* are not stored and will diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 5213804..2ac95d0 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -225,13 +225,17 @@ class Variables: def Update(self, env, args: dict | None = None) -> None: """Update an environment with the Build Variables. - Collects variables from the input sources which do not match - a variable description in this object. These are ignored for - purposes of adding to *env*, but can be retrieved using the - :meth:`UnknownVariables` method. Also collects variables which - are set in *env* from the default in a variable description and - not from the input sources. These are available in the - :attr:`defaulted` attribute. + This is where the work of adding variables to the environment + happens, The input sources saved at init time are scanned for + variables to add, though if *args* is passed, then it is used + instead of the saved one. If any variable description set up + a callback for a validator and/or converter, those are called. + Variables from the input sources which do not match a variable + description in this object are ignored for purposes of adding + to *env*, but are saved in the :attr:`unknown` dict attribute. + Variables which are set in *env* from the default in a variable + description and not from the input sources are saved in the + :attr:`defaulted` list attribute. Args: env: the environment to update. @@ -242,7 +246,7 @@ class Variables: values = {opt.key: opt.default for opt in self.options if opt.default is not None} self.defaulted = list(values) - # next set the values specified in any options script(s) + # next set the values specified in any saved-variables script(s) for filename in self.files: # TODO: issue #816 use Node to access saved-variables file? if os.path.exists(filename): @@ -288,8 +292,16 @@ class Variables: if not added: self.unknown[arg] = value - # put the variables in the environment: + # put the variables in the environment # (don't copy over variables that are not declared as options) + # + # Nitpicking: in OO terms, this method increases coupling as its + # main work is to update a different object (env), rather than + # the object it's bound to (although it does update self, too). + # It's tricky to decouple because the algorithm counts on directly + # setting a var in *env* first so it can call env.subst() on it + # to transform it. + for option in self.options: try: env[option.key] = values[option.key] @@ -400,9 +412,10 @@ class Variables: (must take two arguments and return ``-1``, ``0`` or ``1``) or a boolean to indicate if it should be sorted. """ - # TODO the 'sort' argument matched the old way Python's sorted() - # worked, taking a comparison function argument. That has been - # removed so now we have to convert to a key. + # TODO this interface was designed when Python's sorted() took an + # optional comparison function (pre-3.0). Since it no longer does, + # we use functools.cmp_to_key() since can't really change the + # documented meaning of the "sort" argument. Maybe someday? if callable(sort): options = sorted(self.options, key=cmp_to_key(lambda x, y: sort(x.key, y.key))) elif sort is True: diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 1b7e886..2d52414 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -4832,20 +4832,22 @@ added by calling the <link linkend='v-Add'><function>Add</function></link> or <link linkend='v-AddVariables'><function>AddVariables</function></link> methods. -Each variable description consists of a name (which will -be used as the &consvar; name), aliases for the name, +A variable description consists of a name, +a list of aliases for the name, a help message, a default value, and functions to validate and convert values. -Processing of input sources -is deferred until the +Processing of input sources is deferred until the <link linkend='v-Update'><function>Update</function></link> method is called, at which time the variables are added to the -specified &consenv;. +specified &consenv;, +using the name as the &consvar; name; +any aliases are not added. Variables from the input sources which do not match any names or aliases from the variable descriptions in this object are skipped, -except that a dictionary of their names and values are made available -in the <varname>.unknown</varname> attribute of the &Variables; object. +except that a dictionary of their names and values are made available in the +<link linkend='v-unknown'><varname>unknown</varname></link> +attribute of the &Variables; object. This list can also be obtained via the <link linkend='v-UnknownVariables'><function>UnknownVariables</function></link> method. @@ -4854,19 +4856,15 @@ other than <literal>None</literal> and does not appear in the input sources, it is added to the &consenv; with its default value. A list of variables set from their defaults and -not supplied a value in the input sources is -available as the <varname>.defaulted</varname> attribute -of the &Variables; object. +not from the input sources is available as the +<link linkend='v-defaulted'><varname>defaulted</varname></link> +attribute of the &Variables; object. The unknown variables and defaulted information is not available until the &Update; method has run. </para> -<para><emphasis>New in NEXT_RELEASE</emphasis>: -the <parameter>defaulted</parameter> attribute. -</para> - <para> -Note that since the variables are eventually added as &consvars;, +Since the variables are eventually added as &consvars;, you should choose variable names which do not unintentionally change pre-defined &consvars; that your project will make use of (see <xref linkend="construction_variables"/> for a reference), @@ -4875,16 +4873,16 @@ to the respective &consvars;. </para> <para> -Also note there is currently no way to use the &Variables; -mechanism to define a variable which the user is -<emphasis>required</emphasis> to supply; -if necessary this can be implemented by accessing -<link linkend="v-ARGUMENTS">&ARGUMENTS;</link> directly, -although that only applies to the command line, -not to any stored-values files. +The &Variables; subsystem does not directly support a way +to define a variable the user <emphasis>must</emphasis> supply, +but this can be simulated by using a validator function, +and specifying a default value which the validator will reject, +resulting in an invalid value error message +(the convenience methods &EnumVariable; and +&ListVariable; make this relatively straightforward). </para> -<para>A Variables object has the following methods:</para> +<para>A &Variables; object has the following methods:</para> <variablelist> <varlistentry id="v-Add"> @@ -4897,17 +4895,24 @@ or a sequence of strings, in which case the first item in the sequence is taken as the variable name, and any remaining values are considered aliases for the variable. <parameter>key</parameter> is mandatory, -there is no default. +the other fields are optional. <parameter>help</parameter> is the help text for the variable (defaults to an empty string). <parameter>default</parameter> is the default value of the variable (defaults to <constant>None</constant>). +The variable will be set to the value of +<parameter>default</parameter> if it does +not appear in the input sources, +except if <parameter>default</parameter> +is <literal>None</literal>, +in which case it is not added to the &consenv; +unless it has been set in the input sources. </para> <para> -If the optional <parameter>validator</parameter> argument is supplied, +If the <parameter>validator</parameter> argument is supplied, it is a callback function to validate the value of the variable when the variables are processed (that is, when the <link linkend='v-Update'>&Update;</link> @@ -4922,7 +4927,7 @@ No return value is expected from the validator. </para> <para> -If the optional <parameter>converter</parameter> argument is supplied, +If the <parameter>converter</parameter> argument is supplied, it is a callback function to convert the value into one suitable for adding to the &consenv;. A converter function must accept the @@ -4944,7 +4949,7 @@ it can raise a <exceptionname>ValueError</exceptionname>. Substitution will be performed on the variable value before the converter and validator are called, unless the optional <parameter>subst</parameter> parameter -is false (default <literal>True</literal>). +is false (the default is <literal>True</literal>). Suppressing substitution may be useful if the variable value looks like a &consvar; reference (e.g. <literal>$VAR</literal>) and the validator and/or converter should see it unexpanded. @@ -5018,60 +5023,97 @@ opt.AddVariables( </listitem> </varlistentry> - <varlistentry id="v-Update"> - <term><replaceable>vars</replaceable>.<function>Update</function>(<parameter>env, [args]</parameter>)</term> + <varlistentry id="v-FormatVariableHelpText"> + <term><replaceable>vars</replaceable>.<function>FormatVariableHelpText</function>(<parameter>env, opt, help, default, actual, aliases</parameter>)</term> <listitem> -<para>Process the input sources recorded -when the &Variables; object was initialized -and update +<para>Returns a formatted string +containing the printable help text +for the single variable <parameter>opt</parameter>. +All of the arguments must be supplied +except <parameter>aliases</parameter>, which is optional. <parameter>env</parameter> -with the customized &consvars;. -The names of any variables in the input sources that are not -configured in the &Variables; object -are recorded and may be retrieved using the -<link linkend='v-UnknownVariables'>&UnknownVariables;</link> -method.</para> +is the &consenv; containing the variable values, +(<parameter>env</parameter> is not used by the standard +implementation of <function>FormatVariableHelpText</function>); +<parameter>var</parameter> +is the name of the variable; +<parameter>help</parameter> +is the text of the initial help message when the variable was +added to the &Variables; object; +<parameter>default</parameter> +is the default value assigned when the variable was added +to the &Variables; object; +<parameter>actual</parameter> +is the value as assigned in <parameter>env</parameter> +(which may be the same as <parameter>default</parameter>, +if none of the input sources assign to the variable); +and <parameter>aliases</parameter> +are any alias names for the variable, +if omitted defaults to an empty list. +</para> <para> -If the optional -<parameter>args</parameter> -argument is provided, it is a dictionary of variables -to use in place of the one saved when -<link linkend='v-Variables'>&Variables;</link> -was called. +<function>FormatVariableHelpText</function> +is normally not called directly, but by +&GenerateHelpText;, which does the work of +obtaining the necessary values. +You can patch in your own +function that takes the same function signature +in order to customize the appearance of variable help messages. +Example: </para> -<para>Normally, &Update; is not called directly, -but rather invoked indirectly by passing the &Variables; object to -the &f-link-Environment; function:</para> - <programlisting language="python"> -env = Environment(..., variables=vars) +def my_format(env, var, help, default, actual): + fmt = "\n%s: default=%s actual=%s (%s)\n" + return fmt % (var, default, actual, help) + +vars.FormatVariableHelpText = my_format </programlisting> +<para> +Note that &GenerateHelpText; +will not put any blank lines or extra +characters between the entries, +so you must add those characters to the returned +string if you want the entries separated. +</para> </listitem> </varlistentry> - <varlistentry id="v-UnknownVariables"> - <term><replaceable>vars</replaceable>.<function>UnknownVariables</function>()</term> + <varlistentry id="v-GenerateHelpText"> + <term><replaceable>vars</replaceable>.<function>GenerateHelpText</function>(<parameter>env, [sort]</parameter>)</term> <listitem> -<para>Returns a dictionary containing any -variables that were specified in the -<parameter>files</parameter> and/or -<parameter>args</parameter> parameters -when <link linkend='v-Variables'>&Variables;</link> -was called, but which were not configured in the object. -The same dictionary is also available as the -<varname>unknown</varname> attribute of the object. -This information is not available until the -<link linkend='v-Update'><function>Update</function></link> -method has run. +<para> +Return a formatted string with the help text collected +from all the variables configured in this &Variables; object. +This string is suitable for passing in to the &f-link-Help; function. +The generated string include an indication of the +actual value in the environment given by <parameter>env</parameter>. +</para> + +<para> +If the optional +<parameter>sort</parameter> parameter is set to +a callable value, it is used as a comparison function to +determine how to sort the added variables. +This function must accept two arguments, compare them, +and return a negative integer if the first is +less-than the second, zero if equal, or a positive integer +if greater-than. +If <parameter>sort</parameter> is not callable, +but evaluates true, +an alphabetical sort is performed. +The default is <constant>False</constant> (unsorted). </para> <programlisting language="python"> -env = Environment(variables=vars) -for key, value in vars.UnknownVariables(): - print("unknown variable: %s=%s" % (key, value)) +Help(vars.GenerateHelpText(env)) + +def cmp(a, b): + return (a > b) - (a < b) + +Help(vars.GenerateHelpText(env, sort=cmp)) </programlisting> </listitem> @@ -5101,73 +5143,99 @@ vars.Save('variables.cache', env) </listitem> </varlistentry> - <varlistentry id="v-GenerateHelpText"> - <term><replaceable>vars</replaceable>.<function>GenerateHelpText</function>(<parameter>env, [sort]</parameter>)</term> + <varlistentry id="v-UnknownVariables"> + <term><replaceable>vars</replaceable>.<function>UnknownVariables</function>()</term> <listitem> -<para> -Return a formatted string with the help text collected -from all the variables configured in this &Variables; object. -This string is suitable for passing in to the &f-link-Help; function. -The generated string include an indication of the -actual value in the environment given by <parameter>env</parameter>. +<para>Returns a dictionary containing any +variables that were specified in the +<parameter>files</parameter> and/or +<parameter>args</parameter> parameters +when <link linkend='v-Variables'>&Variables;</link> +was called, but the object was not actually configured for. +This information is not available until the +<link linkend='v-Update'><function>Update</function></link> +method has run. </para> +<programlisting language="python"> +env = Environment(variables=vars) +for key, value in vars.UnknownVariables(): + print("unknown variable: %s=%s" % (key, value)) +</programlisting> + + </listitem> + </varlistentry> + + <varlistentry id="v-Update"> + <term><replaceable>vars</replaceable>.<function>Update</function>(<parameter>env, [args]</parameter>)</term> + <listitem> +<para>Process the input sources recorded +when the &Variables; object was initialized +and update +<parameter>env</parameter> +with the customized &consvars;. +The names of any variables in the input sources that are not +configured in the &Variables; object +are recorded and may be retrieved using the +<link linkend='v-UnknownVariables'>&UnknownVariables;</link> +method.</para> + <para> If the optional -<parameter>sort</parameter> parameter is set to -a callable value, it is used as a comparison function to -determine how to sort the added variables. -This function must accept two arguments, compare them, -and return a negative integer if the first is -less-than the second, zero if equal, or a positive integer -if greater-than. -If <parameter>sort</parameter> is not callable, -but is set to <constant>True</constant>, -an alphabetical sort is performed. -The default is <constant>False</constant> (unsorted). +<parameter>args</parameter> +argument is provided, it must be a dictionary of variables, +which will be used in place of the one saved when the +<link linkend='v-Variables'>&Variables;</link> object +was created. </para> -<programlisting language="python"> -Help(vars.GenerateHelpText(env)) - -def cmp(a, b): - return (a > b) - (a < b) +<para>Normally, &Update; is not called directly, +but rather invoked indirectly by passing the &Variables; object to +the &f-link-Environment; function:</para> -Help(vars.GenerateHelpText(env, sort=cmp)) +<programlisting language="python"> +env = Environment(..., variables=vars) </programlisting> </listitem> </varlistentry> +</variablelist> - <varlistentry id="v-FormatVariableHelpText"> - <term><replaceable>vars</replaceable>.<function>FormatVariableHelpText</function>(<parameter>env, opt, help, default, actual</parameter>)</term> - <listitem> -<para>Returns a formatted string -containing the printable help text -for the single option <parameter>opt</parameter>. -It is normally not called directly, -but is called by the &GenerateHelpText; -method to create the returned help text. -It may be overridden with your own -function that takes the arguments specified above -and returns a string of help text formatted to your liking. -Note that &GenerateHelpText; -will not put any blank lines or extra -characters in between the entries, -so you must add those characters to the returned -string if you want the entries separated.</para> +<para> +A &Variables; object also makes available two data attributes +that can be read for further information. These only have +values if <link linkend='v-Update'><function>Update</function></link> +has previously run. +</para> -<programlisting language="python"> -def my_format(env, opt, help, default, actual): - fmt = "\n%s: default=%s actual=%s (%s)\n" - return fmt % (opt, default, actual, help) +<variablelist> + <varlistentry id="v-defaulted"> + <term><replaceable>vars</replaceable>.<parameter>defaulted</parameter></term> + <listitem> +<para> +A list of variable names that were set in the &consenv; +from the default values in the variable descriptions - +that is, variables that have a default value and were +not defined in the input sources. +</para> + </listitem> + </varlistentry> -vars.FormatVariableHelpText = my_format -</programlisting> + <varlistentry id="v-unknown"> + <term><replaceable>vars</replaceable>.<parameter>unknown</parameter></term> + <listitem> +<para> +A dictionary of variables that were specified in the input sources, +but do not have matching variable definitions. +This is the same information that is returned by the +<link linkend='v-UnknownVariables'>&UnknownVariables;</link> method. +</para> </listitem> </varlistentry> </variablelist> - +<para><emphasis>Added in NEXT_RELEASE</emphasis>: +the <parameter>defaulted</parameter> attribute. +</para> <para> &SCons; provides five pre-defined variable types, @@ -5216,7 +5284,7 @@ as false.</para> <listitem> <para> Set up a variable named <parameter>key</parameter> -whose value will be a choice from +whose value may only be chosen from a specified list ("enumeration") of values. The variable will have a default value of <parameter>default</parameter> @@ -5231,23 +5299,14 @@ argument is a dictionary that can be used to map additional names into a particular name in the <parameter>allowed_values</parameter> list. -If the value of optional -<parameter>ignore_case</parameter> -is -<literal>0</literal> -(the default), -then the values are case-sensitive. -If the value of -<parameter>ignore_case</parameter> -is -<literal>1</literal>, -then values will be matched +If the optional +<parameter>ignorecase</parameter> is <literal>0</literal> (the default), +the values are considered case-sensitive. +If <parameter>ignorecase</parameter> is <literal>1</literal>, +values will be matched case-insensitively. -If the value of -<parameter>ignore_case</parameter> -is -<literal>2</literal>, -then values will be matched +If <parameter>ignorecase</parameter> is <literal>2</literal>, +values will be matched case-insensitively, and all input values will be converted to lower case.</para> @@ -5259,8 +5318,8 @@ converted to lower case.</para> <listitem> <para> Set up a variable named <parameter>key</parameter> -whose value will be one or more -choices from a specified list of values. +whose value may be one or more choices +from a specified list of values. The variable will have a default value of <parameter>default</parameter>, and <parameter>help</parameter> @@ -5293,8 +5352,9 @@ can be used to specify a custom validator callback function, as described for <link linkend='v-Add'><function>Add</function></link>. The default is to use an internal validator routine. </para> -<para><emphasis>New in 4.8.0: <parameter>validator</parameter>. -</emphasis></para> +<para><emphasis>Added in 4.8.0</emphasis>: +the <parameter>validator</parameter> parameter. +</para> </listitem> </varlistentry> @@ -5466,7 +5526,8 @@ vars.AddVariables( PathVariable( "qtdir", help="where the root of Qt is installed", - default=qtdir), + default=qtdir + ), PathVariable( "foopath", help="where the foo library is installed", diff --git a/test/update-release-info/update-release-info.py b/test/update-release-info/update-release-info.py index 2de4713..bebd8a9 100644 --- a/test/update-release-info/update-release-info.py +++ b/test/update-release-info/update-release-info.py @@ -53,11 +53,24 @@ test = TestRuntest.TestRuntest( if not os.path.exists(test.program): test.skip_test("update-release-info.py is not distributed in this package\n") -expected_stderr = """usage: update-release-info.py [-h] [--verbose] [--timestamp TIMESTAMP] +expected_stderr = """\ +usage: update-release-info.py [-h] [--verbose] [--timestamp TIMESTAMP] [{develop,release,post}] update-release-info.py: error: argument mode: invalid choice: 'bad' (choose from 'develop', 'release', 'post') """ -test.run(arguments='bad', stderr=expected_stderr, status=2) +# The way the choices are rendered in help by argparse changed with +# Python 3.12.8, # 3.13.1, 3.14.0a2. Change the test to accept either. +expected_stderr_new = """\ +usage: update-release-info.py [-h] [--verbose] [--timestamp TIMESTAMP] + [{develop,release,post}] +update-release-info.py: error: argument mode: invalid choice: 'bad' (choose from develop, release, post) +""" +test.run(arguments='bad', stderr=None, status=2) +fail_strings = [ + expected_stderr, + expected_stderr_new, +] +test.must_contain_any_line(test.stderr(), fail_strings) # Strings to go in ReleaseConfig combo_strings = [ |