From f73c1e8e4e0be255a4436668a1a2550b4ae24bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Sat, 5 Jul 2025 10:56:42 +0300 Subject: [PATCH] Run subprocess-starting tests in a separate xdist loadgroup I suspect that we are running out of process IDs, or the scheduler is not letting all subprocesses execute, leading to timeouts in tests that start subprocesses when other long-running tests are executing. This happens often with Python 3.14 tests for some reason. Put the tests that start a subprocess in a separate loadgroup so that no two of them get executed in parallel. Alternative to #30264. --- .github/workflows/tests.yml | 2 +- azure-pipelines.yml | 3 +-- lib/matplotlib/testing/_markers.py | 1 + lib/matplotlib/tests/test_backend_inline.py | 2 ++ lib/matplotlib/tests/test_backend_nbagg.py | 2 ++ lib/matplotlib/tests/test_backend_webagg.py | 2 ++ lib/matplotlib/tests/test_backends_interactive.py | 4 ++++ lib/matplotlib/tests/test_basic.py | 2 ++ lib/matplotlib/tests/test_determinism.py | 6 +++++- lib/matplotlib/tests/test_font_manager.py | 2 ++ lib/matplotlib/tests/test_matplotlib.py | 3 +++ lib/matplotlib/tests/test_preprocess_data.py | 3 +++ lib/matplotlib/tests/test_pyplot.py | 3 +++ lib/matplotlib/tests/test_rcparams.py | 4 ++++ lib/matplotlib/tests/test_sphinxext.py | 3 +++ lib/matplotlib/tests/test_texmanager.py | 2 ++ 16 files changed, 40 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 85ace93445b6..e8dad10d1714 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -337,7 +337,7 @@ jobs: if [[ "${{ matrix.python-version }}" == '3.13t' ]]; then export PYTHON_GIL=0 fi - pytest -rfEsXR -n auto \ + pytest -rfEsXR -n auto --dist=loadgroup \ --maxfail=50 --timeout=300 --durations=25 \ --cov-report=xml --cov=lib --log-level=DEBUG --color=yes diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d68a9d36f0d3..d12dd5d73f83 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -119,8 +119,7 @@ stages: VS_VER=2022 echo "##vso[task.setvariable variable=VS_COVERAGE_TOOL]$TOOL" - - PYTHONFAULTHANDLER=1 pytest -rfEsXR -n 2 \ + PYTHONFAULTHANDLER=1 pytest -rfEsXR -n 2 --dist=loadgroup \ --maxfail=50 --timeout=300 --durations=25 \ --junitxml=junit/test-results.xml --cov-report=xml --cov=lib diff --git a/lib/matplotlib/testing/_markers.py b/lib/matplotlib/testing/_markers.py index c7ef8687a8b3..0000cbff836c 100644 --- a/lib/matplotlib/testing/_markers.py +++ b/lib/matplotlib/testing/_markers.py @@ -47,3 +47,4 @@ def _checkdep_usetex() -> bool: needs_usetex = pytest.mark.skipif( not _checkdep_usetex(), reason="This test needs a TeX installation") +starts_subprocess = pytest.mark.xdist_group("subprocess") diff --git a/lib/matplotlib/tests/test_backend_inline.py b/lib/matplotlib/tests/test_backend_inline.py index 997e1e7186b1..70e68ac2fb43 100644 --- a/lib/matplotlib/tests/test_backend_inline.py +++ b/lib/matplotlib/tests/test_backend_inline.py @@ -5,11 +5,13 @@ import pytest from matplotlib.testing import subprocess_run_for_testing +from matplotlib.testing._markers import starts_subprocess nbformat = pytest.importorskip('nbformat') pytest.importorskip('nbconvert') pytest.importorskip('ipykernel') pytest.importorskip('matplotlib_inline') +pytestmark = starts_subprocess def test_ipynb(): diff --git a/lib/matplotlib/tests/test_backend_nbagg.py b/lib/matplotlib/tests/test_backend_nbagg.py index ccf74df20aab..eaf711502c44 100644 --- a/lib/matplotlib/tests/test_backend_nbagg.py +++ b/lib/matplotlib/tests/test_backend_nbagg.py @@ -3,12 +3,14 @@ from tempfile import TemporaryDirectory import pytest +from matplotlib.testing._markers import starts_subprocess from matplotlib.testing import subprocess_run_for_testing nbformat = pytest.importorskip('nbformat') pytest.importorskip('nbconvert') pytest.importorskip('ipykernel') +pytestmark = starts_subprocess # From https://blog.thedataincubator.com/2016/06/testing-jupyter-notebooks/ diff --git a/lib/matplotlib/tests/test_backend_webagg.py b/lib/matplotlib/tests/test_backend_webagg.py index 1d6769494ef9..50100883747d 100644 --- a/lib/matplotlib/tests/test_backend_webagg.py +++ b/lib/matplotlib/tests/test_backend_webagg.py @@ -3,9 +3,11 @@ import pytest import matplotlib.backends.backend_webagg_core +from matplotlib.testing._markers import starts_subprocess from matplotlib.testing import subprocess_run_for_testing +@starts_subprocess @pytest.mark.parametrize("backend", ["webagg", "nbagg"]) def test_webagg_fallback(backend): pytest.importorskip("tornado") diff --git a/lib/matplotlib/tests/test_backends_interactive.py b/lib/matplotlib/tests/test_backends_interactive.py index a27783fa4be1..0819c95a4f18 100644 --- a/lib/matplotlib/tests/test_backends_interactive.py +++ b/lib/matplotlib/tests/test_backends_interactive.py @@ -20,6 +20,10 @@ from matplotlib import _c_internal_utils from matplotlib.backend_tools import ToolToggleBase from matplotlib.testing import subprocess_run_helper as _run_helper, is_ci_environment +from matplotlib.testing._markers import starts_subprocess + + +pytestmark = starts_subprocess class _WaitForStringPopen(subprocess.Popen): diff --git a/lib/matplotlib/tests/test_basic.py b/lib/matplotlib/tests/test_basic.py index f6aa1e458555..976fba23c0ef 100644 --- a/lib/matplotlib/tests/test_basic.py +++ b/lib/matplotlib/tests/test_basic.py @@ -3,6 +3,7 @@ import sys import textwrap +from matplotlib.testing._markers import starts_subprocess from matplotlib.testing import subprocess_run_for_testing @@ -28,6 +29,7 @@ def test_override_builtins(): assert overridden <= ok_to_override +@starts_subprocess def test_lazy_imports(): source = textwrap.dedent(""" import sys diff --git a/lib/matplotlib/tests/test_determinism.py b/lib/matplotlib/tests/test_determinism.py index 2ecc40dbd3c0..28684b17efc7 100644 --- a/lib/matplotlib/tests/test_determinism.py +++ b/lib/matplotlib/tests/test_determinism.py @@ -16,11 +16,15 @@ from matplotlib.patches import Circle, PathPatch from matplotlib.path import Path from matplotlib.testing import subprocess_run_for_testing -from matplotlib.testing._markers import needs_ghostscript, needs_usetex +from matplotlib.testing._markers import ( + needs_ghostscript, needs_usetex, starts_subprocess +) import matplotlib.testing.compare from matplotlib.text import TextPath from matplotlib.transforms import IdentityTransform +pytestmark = starts_subprocess + def _save_figure(objects='mhip', fmt="pdf", usetex=False): mpl.use(fmt) diff --git a/lib/matplotlib/tests/test_font_manager.py b/lib/matplotlib/tests/test_font_manager.py index 97ee8672b1d4..858fb85e3902 100644 --- a/lib/matplotlib/tests/test_font_manager.py +++ b/lib/matplotlib/tests/test_font_manager.py @@ -18,6 +18,7 @@ MSUserFontDirectories, _get_fontconfig_fonts, ttfFontProperty) from matplotlib import cbook, ft2font, pyplot as plt, rc_context, figure as mfigure from matplotlib.testing import subprocess_run_helper, subprocess_run_for_testing +from matplotlib.testing._markers import starts_subprocess has_fclist = shutil.which('fc-list') is not None @@ -288,6 +289,7 @@ def test_fontcache_thread_safe(): subprocess_run_helper(_test_threading, timeout=10) +@starts_subprocess def test_lockfilefailure(tmp_path): # The logic here: # 1. get a temp directory from pytest diff --git a/lib/matplotlib/tests/test_matplotlib.py b/lib/matplotlib/tests/test_matplotlib.py index d0a3f8c617e1..911ccf65d20e 100644 --- a/lib/matplotlib/tests/test_matplotlib.py +++ b/lib/matplotlib/tests/test_matplotlib.py @@ -7,6 +7,7 @@ import matplotlib from matplotlib.testing import subprocess_run_for_testing +from matplotlib.testing._markers import starts_subprocess @pytest.mark.parametrize('version_str, version_tuple', [ @@ -19,6 +20,7 @@ def test_parse_to_version_info(version_str, version_tuple): assert matplotlib._parse_to_version_info(version_str) == version_tuple +@starts_subprocess @pytest.mark.skipif(sys.platform == "win32", reason="chmod() doesn't work as is on Windows") @pytest.mark.skipif(sys.platform != "win32" and os.geteuid() == 0, @@ -37,6 +39,7 @@ def test_tmpconfigdir_warning(tmp_path): os.chmod(tmp_path, mode) +@starts_subprocess def test_importable_with_no_home(tmp_path): subprocess_run_for_testing( [sys.executable, "-c", diff --git a/lib/matplotlib/tests/test_preprocess_data.py b/lib/matplotlib/tests/test_preprocess_data.py index c983d78786e1..44db2733f10d 100644 --- a/lib/matplotlib/tests/test_preprocess_data.py +++ b/lib/matplotlib/tests/test_preprocess_data.py @@ -8,6 +8,8 @@ from matplotlib.axes import Axes from matplotlib.testing import subprocess_run_for_testing from matplotlib.testing.decorators import check_figures_equal +from matplotlib.testing._markers import starts_subprocess + # Notes on testing the plotting functions itself # * the individual decorated plotting functions are tested in 'test_axes.py' @@ -245,6 +247,7 @@ def funcy(ax, x, y, z, t=None): funcy.__doc__) +@starts_subprocess def test_data_parameter_replacement(): """ Test that the docstring contains the correct *data* parameter stub diff --git a/lib/matplotlib/tests/test_pyplot.py b/lib/matplotlib/tests/test_pyplot.py index 55f7c33cb52e..be4709e45634 100644 --- a/lib/matplotlib/tests/test_pyplot.py +++ b/lib/matplotlib/tests/test_pyplot.py @@ -11,7 +11,10 @@ from matplotlib.testing import subprocess_run_for_testing from matplotlib import pyplot as plt +from matplotlib.testing._markers import starts_subprocess + +@starts_subprocess def test_pyplot_up_to_date(tmp_path): pytest.importorskip("black") diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index 2235f98b720f..afa36347d354 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -31,6 +31,7 @@ _validate_linestyle, _listify_validator) from matplotlib.testing import subprocess_run_for_testing +from matplotlib.testing._markers import starts_subprocess def test_rcparams(tmp_path): @@ -528,6 +529,7 @@ def test_rcparams_reset_after_fail(): assert mpl.rcParams['text.usetex'] is False +@starts_subprocess @pytest.mark.skipif(sys.platform != "linux", reason="Linux only") def test_backend_fallback_headless_invalid_backend(tmp_path): env = {**os.environ, @@ -545,6 +547,7 @@ def test_backend_fallback_headless_invalid_backend(tmp_path): env=env, check=True, stderr=subprocess.DEVNULL) +@starts_subprocess @pytest.mark.skipif(sys.platform != "linux", reason="Linux only") def test_backend_fallback_headless_auto_backend(tmp_path): # specify a headless mpl environment, but request a graphical (tk) backend @@ -567,6 +570,7 @@ def test_backend_fallback_headless_auto_backend(tmp_path): assert backend.strip().lower() == "agg" +@starts_subprocess @pytest.mark.skipif( sys.platform == "linux" and not _c_internal_utils.xdisplay_is_valid(), reason="headless") diff --git a/lib/matplotlib/tests/test_sphinxext.py b/lib/matplotlib/tests/test_sphinxext.py index ede3166a2e1b..5f99d0090879 100644 --- a/lib/matplotlib/tests/test_sphinxext.py +++ b/lib/matplotlib/tests/test_sphinxext.py @@ -7,6 +7,8 @@ import sys from matplotlib.testing import subprocess_run_for_testing +from matplotlib.testing._markers import starts_subprocess + import pytest @@ -16,6 +18,7 @@ tinypages = Path(__file__).parent / 'data/tinypages' +@starts_subprocess def build_sphinx_html(source_dir, doctree_dir, html_dir, extra_args=None): # Build the pages with warnings turned into errors extra_args = [] if extra_args is None else extra_args diff --git a/lib/matplotlib/tests/test_texmanager.py b/lib/matplotlib/tests/test_texmanager.py index 64dcbf46456d..12033b7402df 100644 --- a/lib/matplotlib/tests/test_texmanager.py +++ b/lib/matplotlib/tests/test_texmanager.py @@ -9,6 +9,7 @@ from matplotlib.testing import subprocess_run_for_testing from matplotlib.testing._markers import needs_usetex from matplotlib.texmanager import TexManager +from matplotlib.testing._markers import starts_subprocess def test_fontconfig_preamble(): @@ -63,6 +64,7 @@ def test_unicode_characters(): fig.canvas.draw() +@starts_subprocess @needs_usetex def test_openin_any_paranoid(): completed = subprocess_run_for_testing( pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy