From d94eb4373816225582ba922b8d9586b9cd19da59 Mon Sep 17 00:00:00 2001 From: Matthias Diener <mdiener@illinois.edu> Date: Sun, 4 Dec 2022 15:52:46 -0600 Subject: [PATCH] add pytest_raises_on_rank (#162) * add pytest_raises_on_rank * better type annotation * add copyright to mpi.py * add test_mpi.py * fix doc * Clarify test --- doc/conf.py | 3 +++ doc/index.rst | 1 + doc/mpi.rst | 1 + pytools/mpi.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ test/test_mpi.py | 46 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+) create mode 100644 doc/mpi.rst create mode 100644 test/test_mpi.py diff --git a/doc/conf.py b/doc/conf.py index 70a55f0..1338441 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -29,8 +29,11 @@ intersphinx_mapping = { "https://numpy.org/doc/stable": None, "https://documen.tician.de/pymbolic/": None, "https://documen.tician.de/loopy/": None, + "https://docs.pytest.org/en/stable/": None, } nitpick_ignore_regex = [ ["py:class", r"typing_extensions\.(.+)"], ] + +nitpicky = True diff --git a/doc/index.rst b/doc/index.rst index 25f26c6..1a1e9c4 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -11,6 +11,7 @@ Welcome to pytools's documentation! graph tag codegen + mpi misc 🚀 Github <https://github.com/inducer/pytools> 💾 Download Releases <https://pypi.python.org/pypi/pytools> diff --git a/doc/mpi.rst b/doc/mpi.rst new file mode 100644 index 0000000..e7bcae5 --- /dev/null +++ b/doc/mpi.rst @@ -0,0 +1 @@ +.. automodule:: pytools.mpi diff --git a/pytools/mpi.py b/pytools/mpi.py index e165019..9961d76 100644 --- a/pytools/mpi.py +++ b/pytools/mpi.py @@ -1,3 +1,41 @@ +__copyright__ = """ +Copyright (C) 2009-2019 Andreas Kloeckner +Copyright (C) 2022 University of Illinois Board of Trustees +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +__doc__ = """ +MPI helper functionality +======================== + +.. autofunction:: check_for_mpi_relaunch +.. autofunction:: run_with_mpi_ranks +.. autofunction:: pytest_raises_on_rank +""" + +from contextlib import contextmanager, AbstractContextManager +from typing import Generator, Tuple, Union, Type + + def check_for_mpi_relaunch(argv): if argv[1] != "--mpi-relaunch": return @@ -26,3 +64,23 @@ def run_with_mpi_ranks(py_script, ranks, callable_, args=(), kwargs=None): check_call(["mpirun", "-np", str(ranks), sys.executable, py_script, "--mpi-relaunch", callable_and_args], env=newenv) + + +@contextmanager +def pytest_raises_on_rank(my_rank: int, fail_rank: int, + expected_exception: Union[Type[BaseException], + Tuple[Type[BaseException], ...]]) \ + -> Generator[AbstractContextManager, None, None]: + """ + Like :func:`pytest.raises`, but only expect an exception on rank *fail_rank*. + """ + import pytest + from contextlib import nullcontext + + if my_rank == fail_rank: + cm: AbstractContextManager = pytest.raises(expected_exception) + else: + cm = nullcontext() + + with cm as exc: + yield exc diff --git a/test/test_mpi.py b/test/test_mpi.py new file mode 100644 index 0000000..4bbd347 --- /dev/null +++ b/test/test_mpi.py @@ -0,0 +1,46 @@ +__copyright__ = "Copyright (C) 2022 University of Illinois Board of Trustees" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import pytest + + +def test_pytest_raises_on_rank(): + from pytools.mpi import pytest_raises_on_rank + + def fail(my_rank: int, fail_rank: int) -> None: + if my_rank == fail_rank: + raise ValueError("test failure") + + with pytest.raises(ValueError): + fail(0, 0) + + fail(0, 1) + + with pytest_raises_on_rank(0, 0, ValueError): + # Generates an exception, and pytest_raises_on_rank + # expects one. + fail(0, 0) + + with pytest_raises_on_rank(0, 1, ValueError): + # Generates no exception, and pytest_raises_on_rank + # does not expect one. + fail(0, 1) -- GitLab