From ddb0e53371e9d74065835147cff8cacfe7fb09d9 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Wed, 29 Oct 2025 15:52:09 +0000 Subject: [PATCH 01/12] raise a warning for unused petsc options --- petsctools/options.py | 47 +++++++++++++++++++++++++++++++++++++++++-- tests/test_options.py | 31 ++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 tests/test_options.py diff --git a/petsctools/options.py b/petsctools/options.py index 5e5ef58..51fcc33 100644 --- a/petsctools/options.py +++ b/petsctools/options.py @@ -4,7 +4,7 @@ import functools import itertools import warnings -from typing import Any +from typing import Any, Optional import petsc4py @@ -90,7 +90,7 @@ def munge(keys): if option in new: warnings.warn( f"Ignoring duplicate option: {option} (existing value " - f"{new[option]}, new value {value})", + f"{new[option]}, new value {value})", PetscToolsWarning ) new[option] = value return new @@ -240,6 +240,8 @@ def __init__(self, parameters: dict, options_prefix: str | None): if k.startswith(self.options_prefix): self.parameters[k[len(self.options_prefix) :]] = v self._setfromoptions = False + # Keep track of options used between invocations of inserted_options(). + self._used_options = set() def set_default_parameter(self, key: str, val: Any) -> None: """Set a default parameter value. @@ -292,6 +294,8 @@ def inserted_options(self): yield finally: for k in self.to_delete: + if self.options_object.used(self.options_prefix + k): + self._used_options.add(k) del self.options_object[self.options_prefix + k] @functools.cached_property @@ -300,6 +304,25 @@ def options_object(self): return PETSc.Options() + def warn_unused_options(self, options_to_ignore: Optional[set] = None): + """Log a warning for any unused options. + + Parameters + ---------- + options_to_ignore : + List of options for which a warning will not be raised even + if they were not used. Useful for ignoring any default options. + """ + options_to_ignore = options_to_ignore or set() + + unused_options = self.to_delete - (self._used_options + | options_to_ignore) + for option in unused_options: + warnings.warn( + f"PETSc object {self.options_prefix} has unused option: {option}", + PetscToolsWarning + ) + def petscobj2str(obj: petsc4py.PETSc.Object) -> str: """Return a string with a PETSc object type and prefix. @@ -530,3 +553,23 @@ def inserted_options(obj): """ with get_options(obj).inserted_options(): yield + + +def warn_unused_options(obj, options_to_ignore: Optional[set]) + """Log a warning for any unused options. + + Parameters + ---------- + obj : + The object with an OptionsManager attached. + + options_to_ignore : + List of options for which a warning will not be raised even + if they were not used. Useful for ignoring any default options. + + See Also + -------- + OptionsManager + OptionsManager.warn_unused_options + """ + get_options(obj).warn_unused_options(options_to_ignore=options_to_ignore) diff --git a/tests/test_options.py b/tests/test_options.py new file mode 100644 index 0000000..c964206 --- /dev/null +++ b/tests/test_options.py @@ -0,0 +1,31 @@ +import logging +import pytest +import petsctools +PETSc = petsctools.init() + + +def test_unused_options(): + """Check that unused solver options result in a warning in the log.""" + + parameters = { + "used": 1, + "not_used": 2, + "ignored": 3, + } + options = petsctools.OptionsManager(parameters, options_prefix="optobj") + + with options.inserted_options(): + _ = PETSc.Options().getInt(options.options_prefix + "used") + + with pytest.warns() as records: + options.warn_unused_options(options_to_ignore={"ignored"}) + + assert len(records) == 1 + message = str(records[0].message) + # Does the warning include the options prefix? + assert "optobj" in message + # Do we only raise a warning for the unused option? + # Need a space before the option because ("used" in "not_used") == True + assert " not_used" in message + assert " used" not in message + assert " ignored" not in message From 1b363eea1d5b8595296ac33c5a45210d219149cf Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Wed, 29 Oct 2025 16:30:57 +0000 Subject: [PATCH 02/12] grammar --- petsctools/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/petsctools/options.py b/petsctools/options.py index 51fcc33..99af72c 100644 --- a/petsctools/options.py +++ b/petsctools/options.py @@ -555,7 +555,7 @@ def inserted_options(obj): yield -def warn_unused_options(obj, options_to_ignore: Optional[set]) +def warn_unused_options(obj, options_to_ignore: Optional[set]): """Log a warning for any unused options. Parameters From 436c4681db8c50bbd5e95dc95b62b36d6e56ef8b Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Wed, 29 Oct 2025 17:05:47 +0000 Subject: [PATCH 03/12] do not import PETSc at module level in tests --- tests/test_options.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_options.py b/tests/test_options.py index c964206..9bcc740 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -1,11 +1,12 @@ import logging import pytest import petsctools -PETSc = petsctools.init() +@pytest.mark.skipnopetsc4py def test_unused_options(): """Check that unused solver options result in a warning in the log.""" + PETSc = petsctools.init() parameters = { "used": 1, From daedb13e8a0c00655b220d73ea69e9483d8a435d Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Thu, 30 Oct 2025 11:36:12 +0000 Subject: [PATCH 04/12] warn_unused_options message tidy, and remove Optional from type hints --- petsctools/__init__.py | 2 ++ petsctools/options.py | 26 +++++++++++++++++++------- tests/test_options.py | 5 +++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/petsctools/__init__.py b/petsctools/__init__.py index 58293cc..0a701a0 100644 --- a/petsctools/__init__.py +++ b/petsctools/__init__.py @@ -33,6 +33,7 @@ is_set_from_options, inserted_options, set_default_parameter, + warn_unused_options, ) else: @@ -56,6 +57,7 @@ def __getattr__(name): "is_set_from_options", "inserted_options", "set_default_parameter", + "warn_unused_options", } if name in petsc4py_attrs: raise ImportError( diff --git a/petsctools/options.py b/petsctools/options.py index 99af72c..5e5f59e 100644 --- a/petsctools/options.py +++ b/petsctools/options.py @@ -4,7 +4,7 @@ import functools import itertools import warnings -from typing import Any, Optional +from typing import Any, Iterable import petsc4py @@ -304,22 +304,33 @@ def options_object(self): return PETSc.Options() - def warn_unused_options(self, options_to_ignore: Optional[set] = None): + def warn_unused_options(self, options_to_ignore: Iterable | None = None, + obj: Any | None = None): """Log a warning for any unused options. Parameters ---------- options_to_ignore : - List of options for which a warning will not be raised even + Set of options for which a warning will not be raised even if they were not used. Useful for ignoring any default options. + + petsc_obj : + The PETSc object (e.g. SNES, KSP) associated with this OptionsManager. + Used to """ - options_to_ignore = options_to_ignore or set() + options_to_ignore = set(options_to_ignore) or set() unused_options = self.to_delete - (self._used_options | options_to_ignore) + + if obj is None: + object_name = f"object {self.options_prefix}" + else: + object_name = petscobj2str(obj) + for option in unused_options: warnings.warn( - f"PETSc object {self.options_prefix} has unused option: {option}", + f"PETSc {object_name} has unused option: {option}", PetscToolsWarning ) @@ -555,7 +566,7 @@ def inserted_options(obj): yield -def warn_unused_options(obj, options_to_ignore: Optional[set]): +def warn_unused_options(obj, options_to_ignore: Iterable | None = None): """Log a warning for any unused options. Parameters @@ -572,4 +583,5 @@ def warn_unused_options(obj, options_to_ignore: Optional[set]): OptionsManager OptionsManager.warn_unused_options """ - get_options(obj).warn_unused_options(options_to_ignore=options_to_ignore) + get_options(obj).warn_unused_options( + options_to_ignore=options_to_ignore, obj=obj) diff --git a/tests/test_options.py b/tests/test_options.py index 9bcc740..cbd4b9e 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -1,4 +1,3 @@ -import logging import pytest import petsctools @@ -23,9 +22,11 @@ def test_unused_options(): assert len(records) == 1 message = str(records[0].message) + # Does the warning include the options prefix? assert "optobj" in message - # Do we only raise a warning for the unused option? + + # Do we only raise a warning for the unused option that isn't ignored? # Need a space before the option because ("used" in "not_used") == True assert " not_used" in message assert " used" not in message From 8ef0e5f142764b3098f55ec129540d521d3b68fc Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Thu, 30 Oct 2025 13:14:13 +0000 Subject: [PATCH 05/12] optionally only warn on unused arguments if -options_left is set --- petsctools/options.py | 20 +++++++++++++--- tests/test_options.py | 55 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/petsctools/options.py b/petsctools/options.py index 5e5f59e..ea62b54 100644 --- a/petsctools/options.py +++ b/petsctools/options.py @@ -305,7 +305,8 @@ def options_object(self): return PETSc.Options() def warn_unused_options(self, options_to_ignore: Iterable | None = None, - obj: Any | None = None): + obj: Any | None = None, + respect_petsc_options_left: bool = False): """Log a warning for any unused options. Parameters @@ -316,8 +317,16 @@ def warn_unused_options(self, options_to_ignore: Iterable | None = None, petsc_obj : The PETSc object (e.g. SNES, KSP) associated with this OptionsManager. - Used to + Used to specify the warning message. + + respect_petsc_options_left : + If True then warnings will only be raised if "-options_left" is in the + global PETSc.Options() dictionary and the value is >0. """ + if respect_petsc_options_left: + if self.options_object.getInt("options_left", 0) == 0: + return + options_to_ignore = set(options_to_ignore) or set() unused_options = self.to_delete - (self._used_options @@ -566,7 +575,8 @@ def inserted_options(obj): yield -def warn_unused_options(obj, options_to_ignore: Iterable | None = None): +def warn_unused_options(obj, options_to_ignore: Iterable | None = None, + respect_petsc_options_left: bool = False): """Log a warning for any unused options. Parameters @@ -578,6 +588,10 @@ def warn_unused_options(obj, options_to_ignore: Iterable | None = None): List of options for which a warning will not be raised even if they were not used. Useful for ignoring any default options. + respect_petsc_options_left : + If True then warnings will only be raised if "-options_left" is in the + global PETSc.Options() dictionary and the value is >0. + See Also -------- OptionsManager diff --git a/tests/test_options.py b/tests/test_options.py index cbd4b9e..9067f2f 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -1,11 +1,48 @@ +import warnings import pytest import petsctools +@pytest.fixture(autouse=True, scope="module") +def temporarily_remove_options(): + """Remove all options when the module is entered and reinsert them at exit. + This ensures that options in e.g. petscrc files will not pollute the tests. + """ + if petsctools.PETSC4PY_INSTALLED: + PETSc = petsctools.init() + options = PETSc.Options() + previous_options = { + k: v for k, v in options.getAll().items() + } + options.clear() + yield + if petsctools.PETSC4PY_INSTALLED: + for k, v in previous_options.items(): + options[k] = v + + +@pytest.fixture(autouse=True) +def clear_options(): + """Clear any options from the database at the end of each test. + """ + yield + # PETSc already initialised by module scope fixture + from petsc4py import PETSc + PETSc.Options().clear() + + @pytest.mark.skipnopetsc4py -def test_unused_options(): +@pytest.mark.parametrize("respect_petsc_options_left", (True, False), + ids=lambda b: f"respect={b}") +@pytest.mark.parametrize("options_left", (-1, 0, 1), + ids=("no_options_left", "options_left=0", "options_left=1")) +def test_unused_options(respect_petsc_options_left, options_left): """Check that unused solver options result in a warning in the log.""" - PETSc = petsctools.init() + # PETSc already initialised by module scope fixture + from petsc4py import PETSc + + if options_left >= 0: + PETSc.Options()["options_left"] = options_left parameters = { "used": 1, @@ -17,9 +54,21 @@ def test_unused_options(): with options.inserted_options(): _ = PETSc.Options().getInt(options.options_prefix + "used") + # No warnings should be raised in this case. + if respect_petsc_options_left and (options_left <= 0): + with warnings.catch_warnings(): + warnings.simplefilter("error") + options.warn_unused_options( + options_to_ignore={"ignored"}, + respect_petsc_options_left=respect_petsc_options_left) + return + with pytest.warns() as records: - options.warn_unused_options(options_to_ignore={"ignored"}) + options.warn_unused_options( + options_to_ignore={"ignored"}, + respect_petsc_options_left=respect_petsc_options_left) + # Exactly one option is both unused and not ignored assert len(records) == 1 message = str(records[0].message) From 4cd3db3b27f53e7e486b326fe8c50e33e3ab7d0e Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Thu, 30 Oct 2025 13:18:04 +0000 Subject: [PATCH 06/12] warn_unused_options checks in its own options for -options_left --- petsctools/options.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/petsctools/options.py b/petsctools/options.py index ea62b54..ea105eb 100644 --- a/petsctools/options.py +++ b/petsctools/options.py @@ -324,8 +324,9 @@ def warn_unused_options(self, options_to_ignore: Iterable | None = None, global PETSc.Options() dictionary and the value is >0. """ if respect_petsc_options_left: - if self.options_object.getInt("options_left", 0) == 0: - return + with self.inserted_options(): + if self.options_object.getInt("options_left", 0) == 0: + return options_to_ignore = set(options_to_ignore) or set() From 2f5746d6ffcca049f301524ccd3638918459f082 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Thu, 30 Oct 2025 13:36:03 +0000 Subject: [PATCH 07/12] linting --- petsctools/__init__.py | 6 +++++- petsctools/options.py | 10 +++++----- tests/test_options.py | 4 +++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/petsctools/__init__.py b/petsctools/__init__.py index 0a701a0..c01ece0 100644 --- a/petsctools/__init__.py +++ b/petsctools/__init__.py @@ -14,7 +14,11 @@ # is not available then attempting to access these attributes will raise an # informative error. if PETSC4PY_INSTALLED: - from .citation import add_citation, cite, print_citations_at_exit # noqa: F401 + from .citation import ( # noqa: F401 + add_citation, + cite, + print_citations_at_exit, + ) from .config import get_blas_library # noqa: F401 from .init import ( # noqa: F401 InvalidEnvironmentException, diff --git a/petsctools/options.py b/petsctools/options.py index ea105eb..708e2b7 100644 --- a/petsctools/options.py +++ b/petsctools/options.py @@ -238,7 +238,7 @@ def __init__(self, parameters: dict, options_prefix: str | None): # since that does not DTRT for flag options. for k, v in self.options_object.getAll().items(): if k.startswith(self.options_prefix): - self.parameters[k[len(self.options_prefix) :]] = v + self.parameters[k[len(self.options_prefix):]] = v self._setfromoptions = False # Keep track of options used between invocations of inserted_options(). self._used_options = set() @@ -316,12 +316,12 @@ def warn_unused_options(self, options_to_ignore: Iterable | None = None, if they were not used. Useful for ignoring any default options. petsc_obj : - The PETSc object (e.g. SNES, KSP) associated with this OptionsManager. - Used to specify the warning message. + The PETSc object (e.g. SNES, KSP) associated with this + OptionsManager. Used to specify the warning message. respect_petsc_options_left : - If True then warnings will only be raised if "-options_left" is in the - global PETSc.Options() dictionary and the value is >0. + If True then warnings will only be raised if "-options_left" is + in the global PETSc.Options() dictionary and the value is >0. """ if respect_petsc_options_left: with self.inserted_options(): diff --git a/tests/test_options.py b/tests/test_options.py index 9067f2f..16da063 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -35,7 +35,9 @@ def clear_options(): @pytest.mark.parametrize("respect_petsc_options_left", (True, False), ids=lambda b: f"respect={b}") @pytest.mark.parametrize("options_left", (-1, 0, 1), - ids=("no_options_left", "options_left=0", "options_left=1")) + ids=("no_options_left", + "options_left=0", + "options_left=1")) def test_unused_options(respect_petsc_options_left, options_left): """Check that unused solver options result in a warning in the log.""" # PETSc already initialised by module scope fixture From 36aa9d14b095ad2aff1e14f81d13773672a2e900 Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Thu, 30 Oct 2025 13:38:05 +0000 Subject: [PATCH 08/12] actually forward all kwargs for warn_unused_option --- petsctools/options.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/petsctools/options.py b/petsctools/options.py index 708e2b7..bf2835b 100644 --- a/petsctools/options.py +++ b/petsctools/options.py @@ -599,4 +599,5 @@ def warn_unused_options(obj, options_to_ignore: Iterable | None = None, OptionsManager.warn_unused_options """ get_options(obj).warn_unused_options( - options_to_ignore=options_to_ignore, obj=obj) + options_to_ignore=options_to_ignore, obj=obj, + respect_petsc_options_left=respect_petsc_options_left) From aa9908f3b4cecf55de4cadfe0a8431510b85e036 Mon Sep 17 00:00:00 2001 From: JHopeCollins Date: Mon, 3 Nov 2025 11:34:58 +0000 Subject: [PATCH 09/12] raise options_left warnings using weakref.finalize --- petsctools/__init__.py | 2 - petsctools/options.py | 106 +++++++++++++++-------------------------- tests/test_options.py | 23 +++------ 3 files changed, 46 insertions(+), 85 deletions(-) diff --git a/petsctools/__init__.py b/petsctools/__init__.py index c01ece0..878b76b 100644 --- a/petsctools/__init__.py +++ b/petsctools/__init__.py @@ -37,7 +37,6 @@ is_set_from_options, inserted_options, set_default_parameter, - warn_unused_options, ) else: @@ -61,7 +60,6 @@ def __getattr__(name): "is_set_from_options", "inserted_options", "set_default_parameter", - "warn_unused_options", } if name in petsc4py_attrs: raise ImportError( diff --git a/petsctools/options.py b/petsctools/options.py index bf2835b..1e9f13a 100644 --- a/petsctools/options.py +++ b/petsctools/options.py @@ -1,5 +1,6 @@ from __future__ import annotations +import weakref import contextlib import functools import itertools @@ -96,6 +97,37 @@ def munge(keys): return new +def _warn_unused_options(all_options: Iterable, used_options: Iterable, + options_prefix: str | None = None): + """ + Raise warnings for PETSc options which were not used. + + This is meant only as a weakref.finalize callback for the OptionsManager. + + Parameters + ---------- + all_options : + The full set of options passed to the OptionsManager. + used_options : + The options which were used during the OptionsManager's lifetime. + options_prefix : + The options_prefix of the OptionsManager. + + Raises + ------ + PetscToolsWarning : + For every entry in all_options which is not in used_options. + """ + options_prefix = options_prefix or "" + unused_options = set(all_options) - set(used_options) + + for option in unused_options: + warnings.warn( + f"Unused PETSc option: {options_prefix+option}", + PetscToolsWarning + ) + + class OptionsManager: """Class that helps with managing setting PETSc options. @@ -243,6 +275,13 @@ def __init__(self, parameters: dict, options_prefix: str | None): # Keep track of options used between invocations of inserted_options(). self._used_options = set() + # Decide whether to warn for unused options + with self.inserted_options(): + if self.options_object.getInt("options_left", 0) > 0: + weakref.finalize(self, _warn_unused_options, + self.to_delete, self._used_options, + options_prefix=self.options_prefix) + def set_default_parameter(self, key: str, val: Any) -> None: """Set a default parameter value. @@ -304,46 +343,6 @@ def options_object(self): return PETSc.Options() - def warn_unused_options(self, options_to_ignore: Iterable | None = None, - obj: Any | None = None, - respect_petsc_options_left: bool = False): - """Log a warning for any unused options. - - Parameters - ---------- - options_to_ignore : - Set of options for which a warning will not be raised even - if they were not used. Useful for ignoring any default options. - - petsc_obj : - The PETSc object (e.g. SNES, KSP) associated with this - OptionsManager. Used to specify the warning message. - - respect_petsc_options_left : - If True then warnings will only be raised if "-options_left" is - in the global PETSc.Options() dictionary and the value is >0. - """ - if respect_petsc_options_left: - with self.inserted_options(): - if self.options_object.getInt("options_left", 0) == 0: - return - - options_to_ignore = set(options_to_ignore) or set() - - unused_options = self.to_delete - (self._used_options - | options_to_ignore) - - if obj is None: - object_name = f"object {self.options_prefix}" - else: - object_name = petscobj2str(obj) - - for option in unused_options: - warnings.warn( - f"PETSc {object_name} has unused option: {option}", - PetscToolsWarning - ) - def petscobj2str(obj: petsc4py.PETSc.Object) -> str: """Return a string with a PETSc object type and prefix. @@ -574,30 +573,3 @@ def inserted_options(obj): """ with get_options(obj).inserted_options(): yield - - -def warn_unused_options(obj, options_to_ignore: Iterable | None = None, - respect_petsc_options_left: bool = False): - """Log a warning for any unused options. - - Parameters - ---------- - obj : - The object with an OptionsManager attached. - - options_to_ignore : - List of options for which a warning will not be raised even - if they were not used. Useful for ignoring any default options. - - respect_petsc_options_left : - If True then warnings will only be raised if "-options_left" is in the - global PETSc.Options() dictionary and the value is >0. - - See Also - -------- - OptionsManager - OptionsManager.warn_unused_options - """ - get_options(obj).warn_unused_options( - options_to_ignore=options_to_ignore, obj=obj, - respect_petsc_options_left=respect_petsc_options_left) diff --git a/tests/test_options.py b/tests/test_options.py index 16da063..852de47 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -32,13 +32,11 @@ def clear_options(): @pytest.mark.skipnopetsc4py -@pytest.mark.parametrize("respect_petsc_options_left", (True, False), - ids=lambda b: f"respect={b}") @pytest.mark.parametrize("options_left", (-1, 0, 1), ids=("no_options_left", "options_left=0", "options_left=1")) -def test_unused_options(respect_petsc_options_left, options_left): +def test_unused_options(options_left): """Check that unused solver options result in a warning in the log.""" # PETSc already initialised by module scope fixture from petsc4py import PETSc @@ -49,7 +47,6 @@ def test_unused_options(respect_petsc_options_left, options_left): parameters = { "used": 1, "not_used": 2, - "ignored": 3, } options = petsctools.OptionsManager(parameters, options_prefix="optobj") @@ -57,18 +54,14 @@ def test_unused_options(respect_petsc_options_left, options_left): _ = PETSc.Options().getInt(options.options_prefix + "used") # No warnings should be raised in this case. - if respect_petsc_options_left and (options_left <= 0): + if options_left <= 0: with warnings.catch_warnings(): warnings.simplefilter("error") - options.warn_unused_options( - options_to_ignore={"ignored"}, - respect_petsc_options_left=respect_petsc_options_left) + del options return with pytest.warns() as records: - options.warn_unused_options( - options_to_ignore={"ignored"}, - respect_petsc_options_left=respect_petsc_options_left) + del options # Exactly one option is both unused and not ignored assert len(records) == 1 @@ -77,8 +70,6 @@ def test_unused_options(respect_petsc_options_left, options_left): # Does the warning include the options prefix? assert "optobj" in message - # Do we only raise a warning for the unused option that isn't ignored? - # Need a space before the option because ("used" in "not_used") == True - assert " not_used" in message - assert " used" not in message - assert " ignored" not in message + # Do we only raise a warning for the unused option? + assert "optobj_not_used" in message + assert "optobj_used" not in message From 7b1000dd7ebd841644a57d43339292f60ac339bc Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Mon, 3 Nov 2025 12:10:58 +0000 Subject: [PATCH 10/12] Update tests/test_options.py Co-authored-by: Connor Ward --- tests/test_options.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_options.py b/tests/test_options.py index 852de47..c6bd759 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -60,6 +60,7 @@ def test_unused_options(options_left): del options return + # Destroying the object will trigger the unused options warning with pytest.warns() as records: del options From 5cf575e6c99fc148c287df2d33b930b585f04b7d Mon Sep 17 00:00:00 2001 From: JHopeCollins Date: Mon, 3 Nov 2025 12:12:10 +0000 Subject: [PATCH 11/12] default string for warn_unused_options --- petsctools/options.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/petsctools/options.py b/petsctools/options.py index 1e9f13a..ca49fba 100644 --- a/petsctools/options.py +++ b/petsctools/options.py @@ -98,7 +98,7 @@ def munge(keys): def _warn_unused_options(all_options: Iterable, used_options: Iterable, - options_prefix: str | None = None): + options_prefix: str = ""): """ Raise warnings for PETSc options which were not used. @@ -118,7 +118,6 @@ def _warn_unused_options(all_options: Iterable, used_options: Iterable, PetscToolsWarning : For every entry in all_options which is not in used_options. """ - options_prefix = options_prefix or "" unused_options = set(all_options) - set(used_options) for option in unused_options: From c45b05cae85c2cde75e5543ebe84cc619d71989a Mon Sep 17 00:00:00 2001 From: Josh Hope-Collins Date: Mon, 3 Nov 2025 12:14:42 +0000 Subject: [PATCH 12/12] Update petsctools/options.py Co-authored-by: Connor Ward --- petsctools/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/petsctools/options.py b/petsctools/options.py index ca49fba..dc78f4a 100644 --- a/petsctools/options.py +++ b/petsctools/options.py @@ -120,7 +120,7 @@ def _warn_unused_options(all_options: Iterable, used_options: Iterable, """ unused_options = set(all_options) - set(used_options) - for option in unused_options: + for option in sorted(unused_options): warnings.warn( f"Unused PETSc option: {options_prefix+option}", PetscToolsWarning