Skip to content

Commit 9f2e3ba

Browse files
committed
TST,STY: Make a subprocess.run helper to xfail fork failures.
This way I have fewer places to get things wrong and fewer places to remember to fix things. Ideally, at least.
1 parent 3bc5b3d commit 9f2e3ba

File tree

4 files changed

+87
-53
lines changed

4 files changed

+87
-53
lines changed

lib/matplotlib/testing/__init__.py

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,63 @@ def setup():
5050
set_reproducibility_for_testing()
5151

5252

53+
def subprocess_run_for_testing(
54+
command: "list[str]",
55+
env: "dict[str, str]",
56+
timeout=float,
57+
stdout=None,
58+
stderr=None,
59+
check=False,
60+
universal_newlines=True,
61+
capture_output=False
62+
) -> "subprocess.Popen":
63+
"""
64+
Create and run a subprocess.
65+
66+
Thin wrapper around subprocess.run, intended for testing.
67+
68+
Arguments
69+
---------
70+
args : list of str
71+
env : dict[str, str]
72+
timeout : float
73+
stdout, stderr
74+
check : bool
75+
universal_newlines : bool
76+
capture_output : bool
77+
Set stdout and stderr to subprocess.PIPE
78+
79+
Returns
80+
-------
81+
proc : subprocess.Popen
82+
83+
See Also
84+
--------
85+
subprocess.run
86+
87+
Raises
88+
------
89+
pytest.xfail
90+
If platform is Cygwin and subprocess reports a fork() failure.
91+
"""
92+
if capture_output:
93+
stdout = stderr = subprocess.PIPE
94+
try:
95+
proc = subprocess.run(
96+
command, env=env,
97+
timeout=timeout, check=check,
98+
stdout=stdout, stderr=stderr,
99+
universal_newlines=universal_newlines
100+
)
101+
except BlockingIOError:
102+
if sys.platform == "cygwin":
103+
# Might want to make this more specific
104+
import pytest
105+
pytest.xfail("Fork failure")
106+
raise
107+
return proc
108+
109+
53110
def subprocess_run_helper(func, *args, timeout, extra_env=None):
54111
"""
55112
Run a function in a sub-process.
@@ -66,23 +123,16 @@ def subprocess_run_helper(func, *args, timeout, extra_env=None):
66123
"""
67124
target = func.__name__
68125
module = func.__module__
69-
try:
70-
proc = subprocess.run(
71-
[sys.executable,
72-
"-c",
73-
f"from {module} import {target}; {target}()",
74-
*args],
75-
env={**os.environ, "SOURCE_DATE_EPOCH": "0", **(extra_env or {})},
76-
timeout=timeout, check=True,
77-
stdout=subprocess.PIPE,
78-
stderr=subprocess.PIPE,
79-
universal_newlines=True)
80-
except BlockingIOError:
81-
if sys.platform == "cygwin":
82-
import pytest
83-
pytest.xfail("Fork failure")
84-
else:
85-
raise
126+
proc = subprocess_run_for_testing(
127+
[sys.executable,
128+
"-c",
129+
f"from {module} import {target}; {target}()",
130+
*args],
131+
env={**os.environ, "SOURCE_DATE_EPOCH": "0", **(extra_env or {})},
132+
timeout=timeout, check=True,
133+
stdout=subprocess.PIPE,
134+
stderr=subprocess.PIPE,
135+
universal_newlines=True)
86136
return proc
87137

88138

lib/matplotlib/tests/test_preprocess_data.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import re
2-
import subprocess
32
import sys
43

54
import numpy as np
65
import pytest
76

87
from matplotlib import _preprocess_data
98
from matplotlib.axes import Axes
9+
from matplotlib.testing import subprocess_run_for_testing
1010
from matplotlib.testing.decorators import check_figures_equal
1111

1212
# Notes on testing the plotting functions itself
@@ -259,12 +259,9 @@ def test_data_parameter_replacement():
259259
"import matplotlib.pyplot as plt"
260260
)
261261
cmd = [sys.executable, "-c", program]
262-
try:
263-
completed_proc = subprocess.run(cmd, text=True, capture_output=True)
264-
except BlockingIOError:
265-
if sys.platform == "cygwin":
266-
pytest.xfail("Fork failure")
267-
raise
262+
completed_proc = subprocess_run_for_testing(
263+
cmd, text=True, capture_output=True
264+
)
268265
assert 'data parameter docstring error' not in completed_proc.stderr
269266

270267

lib/matplotlib/tests/test_pyplot.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import difflib
22

33
import numpy as np
4-
import subprocess
54
import sys
65
from pathlib import Path
76

87
import pytest
98

109
import matplotlib as mpl
10+
from matplotlib.testing import subprocess_run_for_testing
1111
from matplotlib import pyplot as plt
1212
from matplotlib._api import MatplotlibDeprecationWarning
1313

@@ -20,13 +20,9 @@ def test_pyplot_up_to_date(tmpdir):
2020
plt_file = tmpdir.join('pyplot.py')
2121
plt_file.write_text(orig_contents, 'utf-8')
2222

23-
try:
24-
subprocess.run([sys.executable, str(gen_script), str(plt_file)],
25-
check=True)
26-
except BlockingIOError:
27-
if sys.platform == "cygwin":
28-
pytest.xfail("Fork failure")
29-
raise
23+
subprocess_run_for_testing(
24+
[sys.executable, str(gen_script), str(plt_file)],
25+
check=True)
3026
new_contents = plt_file.read_text('utf-8')
3127

3228
if orig_contents != new_contents:

lib/matplotlib/tests/test_sphinxext.py

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
import os
55
from pathlib import Path
66
import shutil
7-
from subprocess import Popen, PIPE
87
import sys
98

9+
from matplotlib.testing import subprocess_run_for_testing
1010
import pytest
1111

1212

@@ -19,15 +19,11 @@ def build_sphinx_html(source_dir, doctree_dir, html_dir, extra_args=None):
1919
extra_args = [] if extra_args is None else extra_args
2020
cmd = [sys.executable, '-msphinx', '-W', '-b', 'html',
2121
'-d', str(doctree_dir), str(source_dir), str(html_dir), *extra_args]
22-
try:
23-
proc = Popen(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True,
24-
env={**os.environ, "MPLBACKEND": ""})
25-
except BlockingIOError:
26-
if sys.platform == "cygwin":
27-
pytest.xfail("Fork failure")
28-
else:
29-
raise
30-
out, err = proc.communicate()
22+
proc = subprocess_run_for_testing(
23+
cmd, capture_output=True, universal_newlines=True,
24+
env={**os.environ, "MPLBACKEND": ""})
25+
out = proc.stdout
26+
err = proc.stderr
3127

3228
assert proc.returncode == 0, \
3329
f"sphinx build failed with stdout:\n{out}\nstderr:\n{err}\n"
@@ -50,17 +46,12 @@ def test_tinypages(tmp_path):
5046
# On CI, gcov emits warnings (due to agg headers being included with the
5147
# same name in multiple extension modules -- but we don't care about their
5248
# coverage anyways); hide them using GCOV_ERROR_FILE.
53-
try:
54-
proc = Popen(
55-
cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True,
56-
env={**os.environ, "MPLBACKEND": "", "GCOV_ERROR_FILE": os.devnull}
57-
)
58-
except BlockingIOError:
59-
if sys.platform == "cygwin":
60-
pytest.xfail("Fork failure")
61-
else:
62-
raise
63-
out, err = proc.communicate()
49+
proc = subprocess_run_for_testing(
50+
cmd, capture_output=True, universal_newlines=True,
51+
env={**os.environ, "MPLBACKEND": "", "GCOV_ERROR_FILE": os.devnull}
52+
)
53+
out = proc.stdout
54+
err = proc.stderr
6455

6556
# Build the pages with warnings turned into errors
6657
build_sphinx_html(tmp_path, doctree_dir, html_dir)

0 commit comments

Comments
 (0)