A pytest plugin for executing tests in parallel with MPI
Project description
mpi-pytest
Pytest plugin that lets you run tests in parallel with MPI.
mpi-pytest provides:
- A
parallelmarker indicating that the test must be run under MPI. - A
parallel_assertfunction for evaluating assertions in a deadlock-safe way.
parallel marker
Writing a parallel test simply requires marking the test with the parallel marker:
@pytest.mark.parallel(nprocs=5) # run in parallel with 5 processes
def test_my_code_on_5_procs():
...
@pytest.mark.parallel(5) # the "nprocs" kwarg is optional
def test_my_code_on_5_procs_again():
...
@pytest.mark.parallel # run in parallel with the default number of processes (3)
def test_my_code_on_3_procs():
...
@pytest.mark.parallel() # the brackets are optional
def test_my_code_on_3_procs_again():
...
One can also mark a test with a sequence of values for nprocs:
@pytest.mark.parallel(nprocs=[1, 2, 3]) # run in parallel on 1, 2 and 3 processes
def test_my_code_on_variable_nprocs():
...
@pytest.mark.parallel([1, 2, 3]) # again the "nprocs" kwarg is optional
def test_my_code_on_variable_nprocs_again():
...
If multiple numbers of processes are requested then the tests are parametrised
and renamed to, in this case, test_my_code_on_variable_nprocs[nprocs=1],
test_my_code_on_variable_nprocs[nprocs=2] and
test_my_code_on_variable_nprocs[nprocs=3].
When running the code with these parallel markers, mpi-pytest adds extra markers
to each test to allow one to select all tests with a particular number of processors.
For example, to select all parallel tests on 3 processors, one should run:
$ mpiexec -n 3 pytest -m "parallel[3]"
Serial tests - those either unmarked or marked @pytest.mark.parallel(1) - can
be selected by running:
$ pytest -m "not parallel or parallel[1]"
Forking mode
mpi-pytest can be used in one of two modes: forking or non-forking. The former
works as follows:
- The user calls
pytest(notmpiexec -n <# proc> pytest). This launches the "parent"pytestprocess. - This parent
pytestprocess collects all the tests and begins to run them. - When a test is found with the
parallelmarker, rather than executing the function as before, a subprocess is forked callingmpiexec -n <# proc> pytest this_specific_test_file.py::this_specific_test. This produces<# proc>'child'pytestprocesses that execute the test together. - If this terminates successfully then the test is considered to have passed.
This is convenient for development for a number of reasons:
- The plugin composes better with other pytest plugins like
pytest-xdist. - It is not necessary to wrap
pytestinvocations withmpiexeccalls, and all parallel and serial tests can be run at once.
There are however a number of downsides:
- Only one mainstream MPI distribution (MPICH) supports
nested calls to
MPI_Init. If your 'parent'pytestprocess initialises MPI (for instance by executingfrom mpi4py import MPI) then this will cause non-MPICH MPI distributions to crash. - Forking a subprocess can be expensive since a completely fresh Python interpreter is launched each time.
- Sandboxing each test means that polluted global state at the end of a test cannot be detected.
Non-forking mode
With these significant limitations in mind, mpi-pytest therefore also supports
a non-forking mode. To use it, one simply needs to wrap the pytest invocation
with mpiexec, no additional configuration is necessary. For example, to run
all of the parallel tests on 2 ranks one needs to execute:
$ mpiexec -n 2 pytest -m "parallel[2]"
parallel_assert
Using regular assert statements can be unsafe in parallel codes. Consider the
code:
@pytest.mark.parallel(2)
def test_something():
# this will only fail on *some* ranks
assert COMM_WORLD.rank == 0
# this will hang
COMM_WORLD.barrier()
One can see that failing assertions on some ranks but not others will violate SPMD
and lead to deadlocks. To avoid this, mpi-pytest provides a parallel_assert
function used as follows:
from pytest_mpi import parallel_assert
@pytest.mark.parallel(2)
def test_something():
# this will fail on *all* ranks
parallel_assert(COMM_WORLD.rank == 0)
...
Configuration
mpi-pytest respects the environment variable PYTEST_MPI_MAX_NPROCS, which defines
the maximum number of processes that can be requested by a parallel marker. If this
value is exceeded an error will be raised.
Copyright
Copyright (C) 2025 Imperial College London and others
mpi-pytest is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
mpi-pytest is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with mpi-pytest. If not, see https://www.gnu.org/licenses/.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mpi_pytest-2025.6.0.tar.gz.
File metadata
- Download URL: mpi_pytest-2025.6.0.tar.gz
- Upload date:
- Size: 10.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5aff3c7ea2a6f955cfcf6ef4b57476a364a2785a494beffcec0e5f4ae512a1fa
|
|
| MD5 |
33b03a745fdcbe0ea0bf787b4f980b73
|
|
| BLAKE2b-256 |
5ae04d1a809e2614e39d5203b308eea506609ede6033de1861288740420ed455
|
Provenance
The following attestation bundles were made for mpi_pytest-2025.6.0.tar.gz:
Publisher:
release.yml on firedrakeproject/mpi-pytest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mpi_pytest-2025.6.0.tar.gz -
Subject digest:
5aff3c7ea2a6f955cfcf6ef4b57476a364a2785a494beffcec0e5f4ae512a1fa - Sigstore transparency entry: 237323832
- Sigstore integration time:
-
Permalink:
firedrakeproject/mpi-pytest@020b3eca1d34466dd34e4e97d63dc89abcbd1a0b -
Branch / Tag:
refs/tags/v2025.6.0 - Owner: https://github.com/firedrakeproject
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@020b3eca1d34466dd34e4e97d63dc89abcbd1a0b -
Trigger Event:
release
-
Statement type:
File details
Details for the file mpi_pytest-2025.6.0-py3-none-any.whl.
File metadata
- Download URL: mpi_pytest-2025.6.0-py3-none-any.whl
- Upload date:
- Size: 11.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7ec0509015c8ca1ef74b1c08619ffe4c617a11b4d34675271043702864ef7c03
|
|
| MD5 |
bf5cffd1ff1368d998f15d471b143e79
|
|
| BLAKE2b-256 |
053b9ea7bb2506794ca2402706945577bbe0ba09f269f2830cb061cd5d9ddf1e
|
Provenance
The following attestation bundles were made for mpi_pytest-2025.6.0-py3-none-any.whl:
Publisher:
release.yml on firedrakeproject/mpi-pytest
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mpi_pytest-2025.6.0-py3-none-any.whl -
Subject digest:
7ec0509015c8ca1ef74b1c08619ffe4c617a11b4d34675271043702864ef7c03 - Sigstore transparency entry: 237323840
- Sigstore integration time:
-
Permalink:
firedrakeproject/mpi-pytest@020b3eca1d34466dd34e4e97d63dc89abcbd1a0b -
Branch / Tag:
refs/tags/v2025.6.0 - Owner: https://github.com/firedrakeproject
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@020b3eca1d34466dd34e4e97d63dc89abcbd1a0b -
Trigger Event:
release
-
Statement type: