diff --git a/petsctools/options.py b/petsctools/options.py index 7bfb4dc..5b9cd5a 100644 --- a/petsctools/options.py +++ b/petsctools/options.py @@ -432,7 +432,11 @@ def __init__(self, parameters: dict, # Replace any Python objects in the parameters dict with appctx entries appmngr = AppContextManager() for key, value in parameters.items(): - if not isinstance(value, _native_petsc_option_types): + # Convert Python objects into their string representation for things + # like 'pc_python_type' and 'snes_python_type' + if key.endswith("python_type") and isinstance(value, type): + parameters[key] = f"{value.__module__}.{value.__name__}" + elif not isinstance(value, _native_petsc_option_types): parameters[key] = appmngr.add(value) self.appmngr = appmngr diff --git a/tests/docs/test_appctx_docs.py b/tests/docs/test_appctx_docs.py index 2d0ddf9..bd34355 100644 --- a/tests/docs/test_appctx_docs.py +++ b/tests/docs/test_appctx_docs.py @@ -100,7 +100,7 @@ def test_appctx_docs(): 'ksp_converged_reason': None, 'ksp_type': 'richardson', 'pc_type': 'python', - 'pc_python_type': f'{__name__}.DiffusionJacobiPC', + 'pc_python_type': DiffusionJacobiPC, 'djacobi_scale': 0.9, 'djacobi_sigma': sigma_p, }, diff --git a/tests/test_options.py b/tests/test_options.py index cb162b7..b279c6e 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -182,7 +182,8 @@ def apply(self, pc, x, y): @pytest.mark.skipnopetsc4py @pytest.mark.parametrize("use_prefix", ["with_prefix", "without_prefix"]) -def test_appctx_context_manager(use_prefix): +@pytest.mark.parametrize("use_pc_class", [False, True]) +def test_appctx_context_manager(use_prefix, use_pc_class): PETSc = petsctools.init() n = 4 sizes = (n, n) @@ -199,10 +200,14 @@ def test_appctx_context_manager(use_prefix): parameters = { 'ksp_type': 'preonly', 'pc_type': 'python', - 'pc_python_type': f'{__name__}.JacobiTestPC', 'jacobi_use_prefixed_appctx': use_prefix == "with_prefix", 'jacobi_scale': diag, } + if use_pc_class: + parameters['pc_python_type'] = JacobiTestPC + else: + parameters['pc_python_type'] = f'{__name__}.JacobiTestPC' + petsctools.set_from_options( ksp, parameters=parameters, options_prefix="myksp" ) @@ -219,6 +224,31 @@ def test_appctx_context_manager(use_prefix): assert (x - xcheck).norm() < 1e-14 +class MyPythonSNES: + pass + + +@pytest.mark.skipnopetsc4py +@pytest.mark.parametrize("use_prefix", + [True, False], + ids=["with_prefix", "without_prefix"]) +def test_python_type_option(use_prefix): + from petsc4py import PETSc + + options = petsctools.OptionsManager( + parameters={ + "snes_type": "python", + "snes_python_type": MyPythonSNES, + }, + options_prefix="prefix_" if use_prefix else None + ) + + with options.inserted_options(): + opts = PETSc.Options(options.options_prefix) + assert opts["snes_python_type"] == f"{__name__}.MyPythonSNES", \ + "Python type name was not inserted into the options dictionary" + + @pytest.mark.skipnopetsc4py def test_python_options(): petsctools.init()