summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt5
-rw-r--r--RELEASE.txt6
-rw-r--r--SCons/Script/SConsOptions.py6
-rw-r--r--SCons/Warnings.py230
-rw-r--r--SCons/WarningsTests.py116
-rw-r--r--doc/man/scons.xml27
-rw-r--r--doc/sphinx/SCons.rst2
7 files changed, 243 insertions, 149 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index a07fcbe..f1e9117 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -99,6 +99,11 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
describe the Microsoft C++ compiler. Update the version table slightly.
Amplified the usage of MSVC_VERSION.
- Improve SharedLibrary docs a bit.
+ - Update warnings module: adds docstrings, drop three unused warnings
+ (DeprecatedSourceCodeWarning, TaskmasterNeedsExecuteWarning,
+ DeprecatedMissingSConscriptWarning) add two warnings to manpage
+ (cache-cleanup-error, future-reserved-variable), improve unittest, tweak
+ Sphinx build.
RELEASE 4.6.0 - Sun, 19 Nov 2023 17:22:20 -0700
diff --git a/RELEASE.txt b/RELEASE.txt
index 0ca6aa1..b339c44 100644
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -34,6 +34,10 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
prototype when including a header file. Fixes GH Issue #4320
- Now supports pre-release Python 3.13
- Support for Python versions without support for the `threading` package has been removed
+- Dropped three unused warning classes: DeprecatedSourceCodeWarning,
+ TaskmasterNeedsExecuteWarning, DeprecatedMissingSConscriptWarning.
+* Two warning classes that are actually used were added to manpage section on
+ enabling warnings (cache-cleanup-error, future-reserved-variable).
FIXES
-----
@@ -90,6 +94,8 @@ DOCUMENTATION
the Scanner Objects section of the manpage.
- The manpage entry for Pseudo was clarified.
- The manpage entry for SharedLibrary was clarified.
+- Update API docs for Warnings framework; add two warns to manpage
+ enable/disable control.
DEVELOPMENT
-----------
diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py
index 18fe0e4..e8e5cbf 100644
--- a/SCons/Script/SConsOptions.py
+++ b/SCons/Script/SConsOptions.py
@@ -875,9 +875,9 @@ def Parser(version):
def warn_md5_chunksize_deprecated(option, opt, value, parser) -> None:
if opt == '--md5-chunksize':
- SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
- "Parameter %s is deprecated. Use "
- "--hash-chunksize instead." % opt)
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning,
+ f"Option {opt} is deprecated. "
+ "Use --hash-chunksize instead.")
setattr(parser.values, option.dest, value)
diff --git a/SCons/Warnings.py b/SCons/Warnings.py
index f6809fb..d604659 100644
--- a/SCons/Warnings.py
+++ b/SCons/Warnings.py
@@ -21,159 +21,211 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-"""The SCons warnings framework."""
+"""The SCons Warnings framework.
+
+Enables issuing warnings in situations where it is useful to alert
+the user of a condition that does not warrant raising an exception
+that could terminate the program.
+
+A new warning class should inherit (perhaps indirectly) from one of
+two base classes: :exc:`SConsWarning` or :exc:`WarningOnByDefault`,
+which are the same except warnings derived from the latter will start
+out in an enabled state. Enabled warnings cause a message to be
+printed when called, disabled warnings are silent.
+
+There is also a hierarchy for indicating deprecations and future
+changes: for these, derive from :exc:`DeprecatedWarning`,
+:exc:`MandatoryDeprecatedWarning`, :exc:`FutureDeprecatedWarning`
+or :exc:`FutureReservedVariableWarning`.
+
+Whether or not to display warnings, beyond those that are on by
+default, is controlled through the command line (``--warn``) or
+through ``SetOption('warn')``. The names used there use a different
+naming style than the warning class names. :func:`process_warn_strings`
+converts the names before enabling/disabling.
+
+The behavior of issuing only a message (for "enabled" warnings) can
+be toggled to raising an exception instead by calling the
+:func:`warningAsException` function.
+
+For new/removed warnings, the manpage needs to be kept in sync.
+Any warning class defined here is accepted, but we don't want to make
+people have to dig around to find the names. Warnings do not have to
+be defined in this file, though it is preferred: those defined elsewhere
+cannot use the enable/disable functionality unless they monkeypatch the
+warning into this module's namespace.
+
+You issue a warning, either in SCons code or in a build project's
+SConscripts, by calling the :func:`warn` function defined in this module.
+Raising directly with an instance of a warning class bypasses the
+framework and it will behave like an ordinary exception.
+"""
import sys
+from typing import Callable, Sequence, Optional
import SCons.Errors
+# _enabled is a list of 2-tuples with a warning class object and a
+# boolean (True if that warning is enabled). Initialized in SCons/Main.py.
+_enabled = []
+
+# If False, just emit the msg for an enabled warning; else raise exception
+_warningAsException: bool = False
+
+# Function to emit the warning. Initialized by SCons/Main.py for regular use;
+# the unit test will set to a capturing version for testing.
+_warningOut: Optional[Callable] = None
+
+
class SConsWarning(SCons.Errors.UserError):
- pass
+ """Base class for all SCons warnings."""
class WarningOnByDefault(SConsWarning):
- pass
+ """Base class for SCons warnings that are enabled by default."""
+SConsWarningOnByDefault = WarningOnByDefault # transition to new name
-# NOTE: If you add a new warning class, add it to the man page, too!
-# Not all warnings are defined here, some are defined in the location of use
-class TargetNotBuiltWarning(SConsWarning): # Should go to OnByDefault
- pass
+class LinkWarning(WarningOnByDefault):
+ """Base class for linker warnings."""
+
+# NOTE: If you add a new warning class here, add it to the man page, too!
+
+# General warnings
class CacheVersionWarning(WarningOnByDefault):
- pass
+ """The derived-file cache directory has an out of date config."""
class CacheWriteErrorWarning(SConsWarning):
- pass
+ """Problems writing a derived file to the cache."""
class CacheCleanupErrorWarning(SConsWarning):
- pass
+ """Problems removing retrieved target prior to rebuilding."""
class CorruptSConsignWarning(WarningOnByDefault):
- pass
+ """Problems decoding the contents of the sconsign database."""
class DependencyWarning(SConsWarning):
- pass
+ """A scanner identified a dependency but did not add it."""
class DevelopmentVersionWarning(WarningOnByDefault):
- pass
+ """Use of a deprecated feature."""
class DuplicateEnvironmentWarning(WarningOnByDefault):
- pass
+ """A target appears in more than one consenv with identical actions.
-class FutureReservedVariableWarning(WarningOnByDefault):
- pass
+ A duplicate target with different rules cannot be built;
+ with the same rule it can, but this could indicate a problem in
+ the build configuration.
+ """
-class LinkWarning(WarningOnByDefault):
- pass
+class FortranCxxMixWarning(LinkWarning):
+ """Fortran and C++ objects appear together in a link line.
+
+ Some compilers support this, others do not.
+ """
+
+class FutureReservedVariableWarning(WarningOnByDefault):
+ """Setting a variable marked to become reserved in a future release."""
class MisleadingKeywordsWarning(WarningOnByDefault):
- pass
+ """Use of possibly misspelled kwargs in Builder calls."""
-# TODO: no longer needed, now an error instead of warning. Leave for a bit.
class MissingSConscriptWarning(WarningOnByDefault):
- pass
+ """The script specified in an SConscript() call was not found.
+
+ TODO: this is now an error, so no need for a warning. Left in for
+ a while in case anyone is using, remove eventually.
+
+ Manpage entry removed in 4.6.0.
+ """
class NoObjectCountWarning(WarningOnByDefault):
- pass
+ """Object counting (debug mode) could not be enabled."""
class NoParallelSupportWarning(WarningOnByDefault):
- pass
+ """Fell back to single-threaded build, as no thread support found."""
class ReservedVariableWarning(WarningOnByDefault):
- pass
+ """Attempt to set reserved construction variable names."""
class StackSizeWarning(WarningOnByDefault):
- pass
+ """Requested thread stack size could not be set."""
+
+class TargetNotBuiltWarning(SConsWarning): # TODO: should go to OnByDefault
+ """A target build indicated success but the file is not found."""
class VisualCMissingWarning(WarningOnByDefault):
- pass
+ """Requested MSVC version not found and policy is to not fail."""
-# Used when MSVC_VERSION and MSVS_VERSION do not point to the
-# same version (MSVS_VERSION is deprecated)
class VisualVersionMismatch(WarningOnByDefault):
- pass
+ """``MSVC_VERSION`` and ``MSVS_VERSION`` do not match.
-class VisualStudioMissingWarning(SConsWarning):
- pass
+ Note ``MSVS_VERSION`` is deprecated, use ``MSVC_VERSION``.
+ """
-class FortranCxxMixWarning(LinkWarning):
+class VisualStudioMissingWarning(SConsWarning): # TODO: unused
pass
# Deprecation warnings
class FutureDeprecatedWarning(SConsWarning):
- pass
+ """Base class for features that will become deprecated in a future release."""
class DeprecatedWarning(SConsWarning):
- pass
+ """Base class for deprecated features, will be removed in future."""
class MandatoryDeprecatedWarning(DeprecatedWarning):
- pass
+ """Base class for deprecated features where warning cannot be disabled."""
# Special case; base always stays DeprecatedWarning
class PythonVersionWarning(DeprecatedWarning):
- pass
-
-class DeprecatedSourceCodeWarning(FutureDeprecatedWarning):
- pass
-
-class TaskmasterNeedsExecuteWarning(DeprecatedWarning):
- pass
+ """SCons was run with a deprecated Python version."""
class DeprecatedOptionsWarning(MandatoryDeprecatedWarning):
- pass
+ """Options that are deprecated."""
class DeprecatedDebugOptionsWarning(MandatoryDeprecatedWarning):
- pass
+ """Option-arguments to --debug that are deprecated."""
-class DeprecatedMissingSConscriptWarning(DeprecatedWarning):
+class ToolQtDeprecatedWarning(DeprecatedWarning): # TODO: unused
pass
-class ToolQtDeprecatedWarning(DeprecatedWarning):
- pass
-
-# The below is a list of 2-tuples. The first element is a class object.
-# The second element is true if that class is enabled, false if it is disabled.
-_enabled = []
-
-# If set, raise the warning as an exception
-_warningAsException = False
-
-# If not None, a function to call with the warning
-_warningOut = None
def suppressWarningClass(clazz) -> None:
- """Suppresses all warnings of type clazz or derived from clazz."""
+ """Suppresses all warnings of type *clazz* or derived from *clazz*."""
_enabled.insert(0, (clazz, False))
def enableWarningClass(clazz) -> None:
- """Enables all warnings of type clazz or derived from clazz."""
+ """Enables all warnings of type *clazz* or derived from *clazz*."""
_enabled.insert(0, (clazz, True))
-def warningAsException(flag: bool=True):
- """Set global _warningAsExeption flag.
+def warningAsException(flag: bool = True) -> bool:
+ """Sets global :data:`_warningAsExeption` flag.
+
+ If true, any enabled warning will cause an exception to be raised.
Args:
- flag: value to set warnings-as-exceptions to [default: True]
+ flag: new value for warnings-as-exceptions.
Returns:
The previous value.
"""
- global _warningAsException
+ global _warningAsException # pylint: disable=global-statement
old = _warningAsException
_warningAsException = flag
return old
-def warn(clazz, *args):
+def warn(clazz, *args) -> None:
"""Issue a warning, accounting for SCons rules.
- Check if warnings for this class are enabled.
- If warnings are treated as exceptions, raise exception.
- Use the global warning-emitter _warningOut, which allows selecting
- different ways of presenting a traceback (see Script/Main.py)
+ Check if warnings for this class are enabled. If warnings are treated
+ as exceptions, raise exception. Use the global warning emitter
+ :data:`_warningOut`, which allows selecting different ways of
+ presenting a traceback (see Script/Main.py).
"""
warning = clazz(args)
for cls, flag in _enabled:
@@ -182,30 +234,32 @@ def warn(clazz, *args):
if _warningAsException:
raise warning
- if _warningOut:
+ if _warningOut is not None:
_warningOut(warning)
break
-def process_warn_strings(arguments) -> None:
+def process_warn_strings(arguments: Sequence[str]) -> None:
"""Process requests to enable/disable warnings.
- The requests are strings passed to the --warn option or the
- SetOption('warn') function.
+ The requests come from the option-argument string passed to the
+ ``--warn`` command line option or as the value passed to the
+ ``SetOption`` function with a first argument of ``warn``;
- An argument to this option should be of the form "warning-class"
- or "no-warning-class". The warning class is munged and has
- the suffix "Warning" added in order to get an actual class name
- from the classes above, which we need to pass to the
- {enable,disable}WarningClass() functions.
- For example, "deprecated" will enable the DeprecatedWarning class.
- "no-dependency" will disable the DependencyWarning class.
+ The arguments are expected to be as documented in the SCons manual
+ page for the ``--warn`` option, in the style ``some-type``,
+ which is converted here to a camel-case name like ``SomeTypeWarning``,
+ to try to match the warning classes defined here, which are then
+ passed to :func:`enableWarningClass` or :func:`suppressWarningClass`.
- As a special case, --warn=all and --warn=no-all will enable or
- disable (respectively) the base class of all SCons warnings.
- """
+ For example, a string``"deprecated"`` enables the
+ :exc:`DeprecatedWarning` class, while a string``"no-dependency"``
+ disables the :exc:`DependencyWarning` class.
- def _classmunge(s):
+ As a special case, the string ``"all"`` disables all warnings and
+ a the string ``"no-all"`` disables all warnings.
+ """
+ def _classmunge(s: str) -> str:
"""Convert a warning argument to SConsCase.
The result is CamelCase, except "Scons" is changed to "SCons"
@@ -215,7 +269,10 @@ def process_warn_strings(arguments) -> None:
for arg in arguments:
enable = True
- if arg.startswith("no-"):
+ if arg.startswith("no-") and arg not in (
+ "no-object-count",
+ "no-parallel-support",
+ ):
enable = False
arg = arg[len("no-") :]
if arg == 'all':
@@ -225,13 +282,12 @@ def process_warn_strings(arguments) -> None:
try:
clazz = globals()[class_name]
except KeyError:
- sys.stderr.write("No warning type: '%s'\n" % arg)
+ sys.stderr.write(f"No warning type: {arg!r}\n")
else:
if enable:
enableWarningClass(clazz)
elif issubclass(clazz, MandatoryDeprecatedWarning):
- fmt = "Can not disable mandataory warning: '%s'\n"
- sys.stderr.write(fmt % arg)
+ sys.stderr.write(f"Can not disable mandataory warning: {arg!r}\n")
else:
suppressWarningClass(clazz)
diff --git a/SCons/WarningsTests.py b/SCons/WarningsTests.py
index 85dbb6e..70b4598 100644
--- a/SCons/WarningsTests.py
+++ b/SCons/WarningsTests.py
@@ -21,11 +21,28 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+"""Test Warnings module."""
+
import unittest
import SCons.Warnings
+from SCons.Warnings import (
+ DependencyWarning,
+ DeprecatedWarning,
+ MandatoryDeprecatedWarning,
+ SConsWarning,
+ WarningOnByDefault,
+)
+
class TestOutput:
+ """Callable class to use as ``_warningOut`` for capturing test output.
+
+ If we've already been called can reset by ``instance.out = None``
+ """
+ def __init__(self) -> None:
+ self.out = None
+
def __call__(self, x) -> None:
args = x.args[0]
if len(args) == 1:
@@ -33,92 +50,73 @@ class TestOutput:
self.out = str(args)
class WarningsTestCase(unittest.TestCase):
+ def setUp(self) -> None:
+ # Setup global state
+ SCons.Warnings._enabled = []
+ SCons.Warnings._warningAsException = False
+ to = TestOutput()
+ SCons.Warnings._warningOut = to
+
def test_Warning(self) -> None:
"""Test warn function."""
+ to = SCons.Warnings._warningOut
- # Reset global state
- SCons.Warnings._enabled = []
- SCons.Warnings._warningAsException = 0
-
- to = TestOutput()
- SCons.Warnings._warningOut=to
- SCons.Warnings.enableWarningClass(SCons.Warnings.SConsWarning)
- SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
- "Foo")
+ SCons.Warnings.enableWarningClass(SConsWarning)
+ SCons.Warnings.warn(DeprecatedWarning, "Foo")
assert to.out == "Foo", to.out
- SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
- "Foo", 1)
+ SCons.Warnings.warn(DependencyWarning, "Foo", 1)
assert to.out == "('Foo', 1)", to.out
def test_WarningAsExc(self) -> None:
"""Test warnings as exceptions."""
-
- # Reset global state
- SCons.Warnings._enabled = []
- SCons.Warnings._warningAsException = 0
-
- SCons.Warnings.enableWarningClass(SCons.Warnings.SConsWarning)
- old = SCons.Warnings.warningAsException()
- assert old == 0, old
- exc_caught = 0
- try:
- SCons.Warnings.warn(SCons.Warnings.SConsWarning, "Foo")
- except:
- exc_caught = 1
- assert exc_caught == 1
-
- old = SCons.Warnings.warningAsException(old)
- assert old == 1, old
- exc_caught = 0
- try:
- SCons.Warnings.warn(SCons.Warnings.SConsWarning, "Foo")
- except:
- exc_caught = 1
- assert exc_caught == 0
+ to = SCons.Warnings._warningOut
+
+ SCons.Warnings.enableWarningClass(WarningOnByDefault)
+ old = SCons.Warnings.warningAsException(True)
+ self.assertFalse(old)
+ # an enabled warning should raise exception
+ self.assertRaises(SConsWarning, SCons.Warnings.warn, WarningOnByDefault, "Foo")
+ to.out = None
+ # a disabled exception should not raise
+ SCons.Warnings.warn(DeprecatedWarning, "Foo")
+ assert to.out == None, to.out
+
+ # make sure original behavior can be restored
+ prev = SCons.Warnings.warningAsException(old)
+ self.assertTrue(prev)
+ SCons.Warnings.warn(WarningOnByDefault, "Foo")
+ assert to.out == "Foo", to.out
def test_Disable(self) -> None:
"""Test disabling/enabling warnings."""
-
- # Reset global state
- SCons.Warnings._enabled = []
- SCons.Warnings._warningAsException = 0
-
- to = TestOutput()
- SCons.Warnings._warningOut=to
- to.out = None
+ to = SCons.Warnings._warningOut
# No warnings by default
- SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
- "Foo")
+ SCons.Warnings.warn(DeprecatedWarning, "Foo")
assert to.out is None, to.out
- SCons.Warnings.enableWarningClass(SCons.Warnings.SConsWarning)
- SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
- "Foo")
+ SCons.Warnings.enableWarningClass(SConsWarning)
+ SCons.Warnings.warn(DeprecatedWarning, "Foo")
assert to.out == "Foo", to.out
to.out = None
- SCons.Warnings.suppressWarningClass(SCons.Warnings.DeprecatedWarning)
- SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
- "Foo")
+ SCons.Warnings.suppressWarningClass(DeprecatedWarning)
+ SCons.Warnings.warn(DeprecatedWarning, "Foo")
assert to.out is None, to.out
- SCons.Warnings.warn(SCons.Warnings.MandatoryDeprecatedWarning,
- "Foo")
+ SCons.Warnings.warn(MandatoryDeprecatedWarning, "Foo")
assert to.out is None, to.out
# Dependency warnings should still be enabled though
- SCons.Warnings.enableWarningClass(SCons.Warnings.SConsWarning)
- SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
- "Foo")
+ SCons.Warnings.enableWarningClass(SConsWarning)
+ SCons.Warnings.warn(DependencyWarning, "Foo")
assert to.out == "Foo", to.out
# Try reenabling all warnings...
- SCons.Warnings.enableWarningClass(SCons.Warnings.SConsWarning)
+ SCons.Warnings.enableWarningClass(SConsWarning)
- SCons.Warnings.enableWarningClass(SCons.Warnings.SConsWarning)
- SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
- "Foo")
+ SCons.Warnings.enableWarningClass(SConsWarning)
+ SCons.Warnings.warn(DeprecatedWarning, "Foo")
assert to.out == "Foo", to.out
if __name__ == "__main__":
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index 1ef6186..21d1ce3 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -2174,6 +2174,15 @@ These warnings are disabled by default.</para>
</varlistentry>
<varlistentry>
+ <term><emphasis role="bold">cache-cleanup-error</emphasis></term>
+ <listitem>
+<para>Warnings about errors when a file retrieved
+from the derived-file cache could not be removed.
+</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><emphasis role="bold">corrupt-sconsign</emphasis></term>
<listitem>
<para>Warnings about unfamiliar signature data in
@@ -2228,6 +2237,16 @@ which can yield unpredictable behavior with some compilers.</para>
</varlistentry>
<varlistentry>
+ <term><emphasis role="bold">future-reserved-variable</emphasis></term>
+ <listitem>
+<para>Warnings about construction variables which
+are currently allowed,
+but will become reserved variables in a future release.
+</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><emphasis role="bold">future-deprecated</emphasis></term>
<listitem>
<para>Warnings about features
@@ -2296,6 +2315,10 @@ feature not working when
is run with the &Python;
<option>-O</option>
option or from optimized &Python; (<filename>.pyo</filename>) modules.</para>
+<para>
+Note the "no-" prefix is part of the name of this warning.
+Add an additional "-no" to disable.
+</para>
</listitem>
</varlistentry>
@@ -2307,6 +2330,10 @@ not being able to support parallel builds when the
<option>-j</option>
option is used.
These warnings are enabled by default.</para>
+<para>
+Note the "no-" prefix is part of the name of this warning.
+Add an additional "-no" to disable.
+</para>
</listitem>
</varlistentry>
diff --git a/doc/sphinx/SCons.rst b/doc/sphinx/SCons.rst
index 85f5878..9ab44b0 100644
--- a/doc/sphinx/SCons.rst
+++ b/doc/sphinx/SCons.rst
@@ -143,11 +143,13 @@ SCons.Subst module
SCons.Warnings module
---------------------
+.. Turn off inherited members to quiet fluff from the Python base Exception
.. automodule:: SCons.Warnings
:members:
:undoc-members:
:show-inheritance:
+ :no-inherited-members:
SCons.cpp module
----------------