@@ -54,6 +54,50 @@ def test_something(rng):
54
54
return np .random .default_rng (seed = SEED )
55
55
56
56
57
+ @pytest .fixture (autouse = True )
58
+ def isolate_mpl_testing ():
59
+ """
60
+ Isolate matplotlib testing for parallel execution.
61
+
62
+ This prevents race conditions in parallel testing (pytest-xdist) where
63
+ multiple processes can interfere with each other's image comparison tests.
64
+ The main issue is that pytest-mpl uses shared temporary directories that
65
+ can conflict between processes.
66
+ """
67
+ import matplotlib as mpl
68
+ import matplotlib .pyplot as plt
69
+ import tempfile
70
+ import os
71
+
72
+ # Store original backend and ensure consistent state
73
+ original_backend = mpl .get_backend ()
74
+ if original_backend != "Agg" :
75
+ mpl .use ("Agg" , force = True )
76
+
77
+ # Clear any existing figures
78
+ plt .close ("all" )
79
+
80
+ # Create process-specific temporary directory for mpl results
81
+ # This prevents file conflicts between parallel processes
82
+ worker_id = os .environ .get ("PYTEST_XDIST_WORKER" , "master" )
83
+ with tempfile .TemporaryDirectory (prefix = f"mpl_test_{ worker_id } _" ) as temp_dir :
84
+ os .environ ["MPL_TEST_TEMP_DIR" ] = temp_dir
85
+
86
+ yield
87
+
88
+ # Clean up after test
89
+ plt .close ("all" )
90
+ uplt .close ("all" )
91
+
92
+ # Remove environment variable
93
+ if "MPL_TEST_TEMP_DIR" in os .environ :
94
+ del os .environ ["MPL_TEST_TEMP_DIR" ]
95
+
96
+ # Restore original backend
97
+ if original_backend != "Agg" :
98
+ mpl .use (original_backend , force = True )
99
+
100
+
57
101
@pytest .fixture (autouse = True )
58
102
def close_figures_after_test ():
59
103
"""Automatically close all figures after each test."""
@@ -139,11 +183,20 @@ def pytest_configure(config):
139
183
- Suppresses verbose matplotlib logging
140
184
- Registers the StoreFailedMplPlugin for enhanced functionality
141
185
- Sets up the plugin regardless of cleanup options (HTML reports always available)
186
+ - Configures process-specific temporary directories for parallel testing
142
187
"""
143
188
# Suppress ultraplot config loading which mpl does not recognize
144
189
logging .getLogger ("matplotlib" ).setLevel (logging .ERROR )
145
190
logging .getLogger ("ultraplot" ).setLevel (logging .WARNING )
146
191
192
+ # Configure process-specific results directory for parallel testing
193
+ worker_id = os .environ .get ("PYTEST_XDIST_WORKER" , "master" )
194
+ if (
195
+ not hasattr (config .option , "mpl_results_path" )
196
+ or not config .option .mpl_results_path
197
+ ):
198
+ config .option .mpl_results_path = f"./mpl-results-{ worker_id } "
199
+
147
200
try :
148
201
# Always register the plugin - it provides enhanced functionality beyond just cleanup
149
202
config .pluginmanager .register (StoreFailedMplPlugin (config ))
0 commit comments