summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMats Wichmann <mats@linux.com>2024-09-11 18:12:14 (GMT)
committerMats Wichmann <mats@linux.com>2024-09-11 19:09:07 (GMT)
commit50bee2e7edcfd46dc21f28b131c6f1065ac44dcc (patch)
tree63bdb02d079d98dfa28634ef1197e3c72c18c4f2
parentaad42dd6eeb9b1fe8756895f2567b25ef8e7d82f (diff)
downloadSCons-50bee2e7edcfd46dc21f28b131c6f1065ac44dcc.zip
SCons-50bee2e7edcfd46dc21f28b131c6f1065ac44dcc.tar.gz
SCons-50bee2e7edcfd46dc21f28b131c6f1065ac44dcc.tar.bz2
Fix some AddOption issues
The optparse add_option method supports an additional calling style that is not directly described in SCons docs, but is included by reference ("see the optparse documentation for details"): it takes a single arg consisting of a premade option object. Because the optparse code detects that case based on seeing zero kwargs, and we always add at least one (default=) that would fail for AddOption. Fix for consistency, but don't advertise it further: not addewd to manpage synoposis/description. Signed-off-by: Mats Wichmann <mats@linux.com>
-rw-r--r--CHANGES.txt8
-rw-r--r--RELEASE.txt5
-rw-r--r--SCons/Script/Main.py36
-rw-r--r--SCons/Script/SConsOptions.py20
-rw-r--r--test/AddOption/basic.py9
5 files changed, 59 insertions, 19 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 1ac7043..4ecb32c 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -23,6 +23,14 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
the test fails. The rest is cleanup and type annotations. Be more
careful that the returns from stderr() and stdout(), which *can*
return None, are not used without checking.
+ - The optparse add_option method supports an additional calling style
+ that is not directly described in SCons docs, but is included
+ by reference ("see the optparse documentation for details"):
+ a single arg consisting of a premade option object. Because optparse
+ detects that case based on seeing zero kwargs and we always
+ added at least one (default=) that would fail for AddOption. Fix
+ for consistency, but don't advertise it further - not addewd to
+ manpage synoposis/description.
RELEASE 4.8.1 - Tue, 03 Sep 2024 17:22:20 -0700
diff --git a/RELEASE.txt b/RELEASE.txt
index 196103c..05bdf7b 100644
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -44,6 +44,11 @@ IMPROVEMENTS
documentation: performance improvements (describe the circumstances
under which they would be observed), or major code cleanups
+- For consistency with the optparse "add_option" method, AddOption accepts
+ an SConsOption object as a single argument (this failed previouly).
+ Calling AddOption with the full set of arguments (option names and
+ attributes) to set up the option is still the recommended approach.
+
PACKAGING
---------
diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py
index 77d80cb..04b420a 100644
--- a/SCons/Script/Main.py
+++ b/SCons/Script/Main.py
@@ -34,6 +34,7 @@ it goes here.
import SCons.compat
import importlib.util
+import optparse
import os
import re
import sys
@@ -59,8 +60,7 @@ import SCons.Taskmaster
import SCons.Util
import SCons.Warnings
import SCons.Script.Interactive
-if TYPE_CHECKING:
- from SCons.Script import SConsOption
+from .SConsOptions import SConsOption
from SCons.Util.stats import count_stats, memory_stats, time_stats, ENABLE_JSON, write_scons_stats_file, JSON_OUTPUT_FILE
from SCons import __version__ as SConsVersion
@@ -508,29 +508,41 @@ class FakeOptionParser:
# TODO: to quiet checkers, FakeOptionParser should also define
# raise_exception_on_error, preserve_unknown_options, largs and parse_args
- def add_local_option(self, *args, **kw) -> "SConsOption":
+ def add_local_option(self, *args, **kw) -> SConsOption:
pass
OptionsParser = FakeOptionParser()
-def AddOption(*args, settable: bool = False, **kw) -> "SConsOption":
+def AddOption(*args, **kw) -> SConsOption:
"""Add a local option to the option parser - Public API.
- If the *settable* parameter is true, the option will be included in the
- list of settable options; all other keyword arguments are passed on to
- :meth:`~SCons.Script.SConsOptions.SConsOptionParser.add_local_option`.
+ If the SCons-specific *settable* kwarg is true (default ``False``),
+ the option will allow calling :func:``SetOption`.
.. versionchanged:: 4.8.0
The *settable* parameter added to allow including the new option
- to the table of options eligible to use :func:`SetOption`.
-
+ in the table of options eligible to use :func:`SetOption`.
"""
+ settable = kw.get('settable', False)
+ if len(args) == 1 and isinstance(args[0], SConsOption):
+ # If they passed an SConsOption object, ignore kw - the underlying
+ # add_option method relies on seeing zero kwargs to recognize this.
+ # Since we don't support an omitted default, overrwrite optparse's
+ # marker to get the same effect as setting it in kw otherwise.
+ optobj = args[0]
+ if optobj.default is optparse.NO_DEFAULT:
+ optobj.default = None
+ # make sure settable attribute exists; positive setting wins
+ attr_settable = getattr(optobj, "settable")
+ if attr_settable is None or settable > attr_settable:
+ optobj.settable = settable
+ return OptionsParser.add_local_option(*args)
+
if 'default' not in kw:
kw['default'] = None
- kw['settable'] = settable
- result = OptionsParser.add_local_option(*args, **kw)
- return result
+ kw['settable'] = settable # just to make sure it gets set
+ return OptionsParser.add_local_option(*args, **kw)
def GetOption(name: str):
"""Get the value from an option - Public API."""
diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py
index 202640a..c9c7aa0 100644
--- a/SCons/Script/SConsOptions.py
+++ b/SCons/Script/SConsOptions.py
@@ -240,6 +240,11 @@ class SConsOption(optparse.Option):
New function :meth:`_check_nargs_optional` implements the ``nargs=?``
syntax from :mod:`argparse`, and is added to the ``CHECK_METHODS`` list.
Overridden :meth:`convert_value` supports this usage.
+
+ .. versionchanged:: NEXT_RELEASE
+ The *settable* attribute is added to ``ATTRS``, allowing it to be
+ set in the option. A parameter to mark the option settable was added
+ in 4.8.0, but was not initially made part of the option object itself.
"""
# can uncomment to have a place to trap SConsOption creation for debugging:
# def __init__(self, *args, **kwargs):
@@ -274,10 +279,11 @@ class SConsOption(optparse.Option):
fmt = "option %s: nargs='?' is incompatible with short options"
raise SCons.Errors.UserError(fmt % self._short_opts[0])
+ ATTRS = optparse.Option.ATTRS + ['settable'] # added for SCons
CHECK_METHODS = optparse.Option.CHECK_METHODS
if CHECK_METHODS is None:
CHECK_METHODS = []
- CHECK_METHODS = CHECK_METHODS + [_check_nargs_optional] # added for SCons
+ CHECK_METHODS += [_check_nargs_optional] # added for SCons
CONST_ACTIONS = optparse.Option.CONST_ACTIONS + optparse.Option.TYPED_ACTIONS
@@ -481,19 +487,19 @@ class SConsOptionParser(optparse.OptionParser):
by default "local" (project-added) options are not eligible for
:func:`~SCons.Script.Main.SetOption` calls.
- .. versionchanged:: 4.8.0
- Added special handling of *settable*.
-
+ .. versionchanged:: NEXT_VERSION
+ If the option's *settable* attribute is true, it is added to
+ the :attr:`SConsValues.settable` list. *settable* handling was
+ added in 4.8.0, but was not made an option attribute at the time.
"""
group: SConsOptionGroup
try:
group = self.local_option_group
except AttributeError:
group = SConsOptionGroup(self, 'Local Options')
- group = self.add_option_group(group)
+ self.add_option_group(group)
self.local_option_group = group
- settable = kw.pop('settable')
# this gives us an SConsOption due to the setting of self.option_class
result = group.add_option(*args, **kw)
if result:
@@ -508,7 +514,7 @@ class SConsOptionParser(optparse.OptionParser):
# TODO: what if dest is None?
setattr(self.values.__defaults__, result.dest, result.default)
self.reparse_local_options()
- if settable:
+ if result.settable:
SConsValues.settable.append(result.dest)
return result
diff --git a/test/AddOption/basic.py b/test/AddOption/basic.py
index 9fc3c4d..83297a8 100644
--- a/test/AddOption/basic.py
+++ b/test/AddOption/basic.py
@@ -33,6 +33,8 @@ import TestSCons
test = TestSCons.TestSCons()
test.write('SConstruct', """\
+from SCons.Script.SConsOptions import SConsOption
+
DefaultEnvironment(tools=[])
env = Environment(tools=[])
AddOption(
@@ -55,6 +57,9 @@ AddOption(
action="store_true",
help="try SetOption of 'prefix' to '/opt/share'"
)
+z_opt = SConsOption("--zcount", type="int", nargs=1, settable=True)
+AddOption(z_opt)
+
f = GetOption('force')
if f:
f = "True"
@@ -63,6 +68,8 @@ print(GetOption('prefix'))
if GetOption('set'):
SetOption('prefix', '/opt/share')
print(GetOption('prefix'))
+if GetOption('zcount'):
+ print(GetOption('zcount'))
""")
test.run('-Q -q .', stdout="None\nNone\n")
@@ -73,6 +80,8 @@ test.run('-Q -q . -- --prefix=/home/foo --force', status=1, stdout="None\nNone\n
test.run('-Q -q . --set', stdout="None\nNone\n/opt/share\n")
# but the "command line wins" rule is not violated
test.run('-Q -q . --set --prefix=/home/foo', stdout="None\n/home/foo\n/home/foo\n")
+# also try in case we pass a premade option object to AddOption
+test.run('-Q -q . --zcount=22', stdout="None\nNone\n22\n")
test.pass_test()