diff --git a/petsctools/options.py b/petsctools/options.py index ecd715a..0bbe4da 100644 --- a/petsctools/options.py +++ b/petsctools/options.py @@ -1126,7 +1126,14 @@ def get( def getAll(self) -> dict[str, Any]: """Return all entries.""" + # Try to apply type casting to the objects returned by + # super().getAll(). Command line arguments with double dashes e.g. + # "--opt val" will appear as {"opt": "val"} in + # PETSc.Options().getAll(), but PETSc.Options()["opt"] or + # PETSc.Options().get("opt") will raise KeyErrors. Passing self.get() a + # default value from PETSc.Options().getAll() works around this error + # by returning the default when the KeyError is raised. return { - key: self[key] + key: self.get(key, value) for key, value in super().getAll().items() } diff --git a/tests/test_options.py b/tests/test_options.py index ca1d7c2..1d9fcac 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -20,6 +20,8 @@ def temporarily_remove_options(): options = petsctools.Options() previous_options = dict(options.getAll()) + # Note that options.clear() does not actually remove all options from + # the database - definitely a bug. options.clear() previous_option_types = petsctools.options._option_types petsctools.options._option_types = {} @@ -28,6 +30,7 @@ def temporarily_remove_options(): yield + options.clear() for k, v in previous_options.items(): options[k] = v petsctools.options._option_types = previous_option_types @@ -481,3 +484,48 @@ def test_options_missing_types(setter, deleter): for opts in [petsc_opts, petsctools_opts]: with pytest.raises(KeyError): opts["my_option"] + + +@pytest.mark.skipnopetsc4py +def test_getAll_does_type_casting(): + opts = petsctools.Options() + params = { + "integer": 666, + "float": 12.3, + "string": "abc", + "bool": False, + } + for name, value in params.items(): + opts[name] = value + + # Note that opts.getAll() can contain more than one entry because + # PETSc.Options().clear() is bugged, so we get contamination from previous + # tests. + for name, value in params.items(): + assert (name, value) in opts.getAll().items() + + +@pytest.mark.skipnopetsc4py +@pytest.mark.parametrize("backend", ["petsc4py", "petsctools"]) +def test_double_dash_options_do_not_break_getAll(backend): + # PETSc.Options.getAll() will include options passed with double-dashes + # (e.g. --pdb as 'pdb') but any attempt to access the option with + # .get(), __getitem__ etc will raise a KeyError. This test is here to + # check that petsctools.Options.getAll() will run without error when '--' + # options are passed (even though the result itself is bugged). + from petsc4py import PETSc + + opts = petsctools.Options() if backend == "petsctools" else PETSc.Options() + opts["--value"] = "xyz" + + # Note that opts.getAll() can contain more than one entry because + # PETSc.Options().clear() is bugged, so we get contamination from previous + # tests. + assert ("value", "xyz") in opts.getAll().items() + + # Check that PETSc have the odd behaviour that we expect - if this fails + # then we can undo things! + with pytest.raises(KeyError): + opts["value"] + with pytest.raises(KeyError): + opts.getString("value")