8000 Merge pull request #22999 from DWesl/patch-2 · matplotlib/matplotlib@7a909fd · GitHub
[go: up one dir, main page]

Skip to content

Commit 7a909fd

Browse files
authored
Merge pull request #22999 from DWesl/patch-2
CI: Add a Cygwin run to GHA CI.
2 parents eab217f + 614f922 commit 7a909fd

File tree

6 files changed

+344
-20
lines changed

6 files changed

+344
-20
lines changed

.github/workflows/cygwin.yml

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
---
2+
name: Cygwin Tests
3+
concurrency:
4+
group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event.ref }}
5+
cancel-in-progress: true
6+
7+
on:
8+
push:
9+
branches:
10+
- main
11+
- v[0-9]+.[0-9]+.[0-9x]+
12+
tags:
13+
- v*
14+
paths:
15+
- 'src/**'
16+
- '.github/workflows/cygwin.yml'
17+
pull_request:
18+
types:
19+
- opened
20+
- synchronize
21+
- reopened
22+
- labeled
23+
branches-ignore:
24+
- v[0-9]+.[0-9]+.[0-9x]+-doc
25+
paths:
26+
- 'src/**'
27+
- '.github/workflows/cygwin.yml'
28+
schedule:
29+
# 5:47 UTC on Saturdays
30+
- cron: "47 5 * * 6"
31+
workflow_dispatch:
32+
workflow: "*"
33+
34+
permissions:
35+
contents: read
36+
37+
env:
38+
NO_AT_BRIDGE: 1 # Necessary for GTK3 interactive test.
39+
OPENBLAS_NUM_THREADS: 1
40+
PYTHONFAULTHANDLER: 1
41+
SHELLOPTS: igncr
42+
CYGWIN_NOWINPATH: 1
43+
CHERE_INVOKING: 1
44+
TMP: /tmp
45+
TEMP: /tmp
46+
47+
jobs:
48+
49+
test-cygwin:
50+
runs-on: windows-latest
51+
name: Python 3.${{ matrix.python-minor-version }} on Cygwin
52+
if: |
53+
github.event_name == 'workflow_dispatch' ||
54+
(
55+
github.repository == 'matplotlib/matplotlib' &&
56+
!contains(github.event.head_commit.message, '[ci skip]') &&
57+
!contains(github.event.head_commit.message, '[skip ci]') &&
58+
!contains(github.event.head_commit.message, '[skip github]') &&
59+
(
60+
github.event_name == 'push' ||
61+
github.event_name ==  9E81 9;pull_request' &&
62+
(
63+
(
64+
github.event.action == 'labeled' &&
65+
github.event.label.name == 'Run cygwin'
66+
) ||
67+
contains(github.event.pull_request.labels.*.name, 'Run cygwin')
68+
)
69+
)
70+
)
71+
strategy:
72+
matrix:
73+
python-minor-version: [8, 9]
74+
75+
steps:
76+
- name: Fix line endings
77+
run: git config --global core.autocrlf input
78+
79+
- uses: actions/checkout@v3
80+
with:
81+
fetch-depth: 0
82+
83+
- uses: cygwin/cygwin-install-action@v2
84+
with:
85+
packages: >-
86+
ccache gcc-g++ gdb git graphviz libcairo-devel libffi-devel
87+
libgeos-devel libQt5Core-devel pkgconf libglib2.0-devel
88+
noto-cjk-fonts
89+
p F438 ython3${{ matrix.python-minor-version }}-devel
90+
python3${{ matrix.python-minor-version }}-pip
91+
python3${{ matrix.python-minor-version }}-wheel
92+
python3${{ matrix.python-minor-version }}-setuptools
93+
python3${{ matrix.python-minor-version }}-cycler
94+
python3${{ matrix.python-minor-version }}-dateutil
95+
python3${{ matrix.python-minor-version }}-fonttools
96+
python3${{ matrix.python-minor-version }}-imaging
97+
python3${{ matrix.python-minor-version }}-kiwisolver
98+
python3${{ matrix.python-minor-version }}-numpy
99+
python3${{ matrix.python-minor-version }}-packaging
100+
python3${{ matrix.python-minor-version }}-pyparsing
101+
python3${{ matrix.python-minor-version }}-sip
102+
python3${{ matrix.python-minor-version }}-sphinx
103+
python-cairo-devel
104+
python3${{ matrix.python-minor-version }}-cairo
105+
python3${{ matrix.python-minor-version }}-gi
106+
python3${{ matrix.python-minor-version }}-matplotlib
107+
xorg-server-extra libxcb-icccm4 libxcb-image0
108+
libxcb-keysyms1 libxcb-randr0 libxcb-render-util0
109+
libxcb-xinerama0
110+
make autoconf autoconf2.5 automake automake1.10 libtool m4
111+
libqhull-devel libfreetype-devel
112+
libjpeg-devel libwebp-devel
113+
114+
- name: Set runner username to root and id to 0
115+
shell: bash.exe -eo pipefail -o igncr "{0}"
116+
# GitHub Actions runs everything as Administrator. I don't
117+
# know how to test for this, so set the uid for the CI job so
118+
# that the existing unix root detection will work.
119+
run: |
120+
/bin/mkpasswd.exe -c | sed -e "s/$(id -u)/0/" >/etc/passwd
121+
122+
- name: Mark test repo safe
123+
shell: bash.exe -eo pipefail -o igncr "{0}"
124+
run: |
125+
git.exe config --global --add safe.directory /proc/cygdrive/d/a/matplotlib/matplotlib
126+
git config --global --add safe.directory /cygdrive/d/a/matplotlib/matplotlib
127+
C:/cygwin/bin/git.exe config --global --add safe.directory D:/a/matplotlib/matplotlib
128+
/usr/bin/git config --global --add safe.directory /cygdrive/d/a/matplotlib/matplotlib
129+
130+
- name: Use dash for /bin/sh
131+
shell: bash.exe -eo pipefail -o igncr "{0}"
132+
run: |
133+
ls -l /bin/sh.exe /bin/bash.exe /bin/dash.exe
134+
/bin/rm -f /bin/sh.exe || exit 1
135+
cp -sf /bin/dash.exe /bin/sh.exe || exit 1
136+
ls -l /bin/sh.exe /bin/bash.exe /bin/dash.exe
137+
# FreeType build fails with bash, succeeds with dash
138+
139+
- name: Cache pip
140+
uses: actions/cache@v3
141+
with:
142+
path: C:\cygwin\home\runneradmin\.cache\pip
143+
key: Cygwin-py3.${{ matrix.python-minor-version }}-pip-${{ hashFiles('requirements/*/*.txt') }}
144+
restore-keys: |
145+
${{ matrix.os }}-py3.${{ matrix.python-minor-version }}-pip-
146+
147+
- name: Cache ccache
148+
uses: actions/cache@v3
149+
with:
150+
path: C:\cygwin\home\runneradmin\.ccache
151+
key: Cygwin-py3.${{ matrix.python-minor-version }}-ccache-${{ hashFiles('src/*') }}
152+
restore-keys: Cygwin-py3.${{ matrix.python-minor-version }}-ccache-
153+
154+
- name: Cache Matplotlib
155+
uses: actions/cache@v3
156+
with:
157+
path: |
158+
C:\cygwin\home\runneradmin\.cache\matplotlib
159+
!C:\cygwin\home\runneradmin\.cache\matplotlib\tex.cache
160+
!C:\cygwin\home\runneradmin\.cache\matplotlib\test_cache
161+
key: 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl-${{ github.ref }}-${{ github.sha }}
162+
restore-keys: |
163+
1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl-${{ github.ref }}-
164+
1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl-
165+
166+
- name: Ensure correct Python version
167+
shell: bash.exe -eo pipefail -o igncr "{0}"
168+
run: |
169+
/usr/sbin/alternatives --set python /usr/bin/python3.${{ matrix.python-minor-version }}
170+
/usr/sbin/alternatives --set python3 /usr/bin/python3.${{ matrix.python-minor-version }}
171+
172+
- name: Install Python dependencies
173+
shell: bash.exe -eo pipefail -o igncr "{0}"
174+
run: |
175+
python -m pip install --upgrade pip 'setuptools<60' wheel
176+
python -m pip install kiwisolver 'numpy!=1.21.*' pillow importlib_resources
177+
grep -v -F -e psutil requirements/testing/all.txt >requirements_test.txt
178+
python -m pip install --upgrade 'contourpy>=1.0.1' cycler fonttools \
179+
packaging pyparsing python-dateutil setuptools-scm \
180+
-r requirements_test.txt sphinx ipython
181+
python -m pip install --upgrade pycairo 'cairocffi>=0.8' PyGObject &&
182+
python -c 'import gi; gi.require_version("Gtk", "3.0"); from gi.repository import Gtk' &&
183+
echo 'PyGObject is available' ||
184+
echo 'PyGObject is not available'
185+
python -m pip install --upgrade pyqt5 &&
186+
python -c 'import PyQt5.QtCore' &&
187+
echo 'PyQt5 is available' ||
188+
echo 'PyQt5 is not available'
189+
python -mpip install --upgrade pyside2 &&
190+
python -c 'import PySide2.QtCore' &&
191+
echo 'PySide2 is available' ||
192+
echo 'PySide2 is not available'
193+
python -m pip uninstall --yes wxpython || echo 'wxPython already uninstalled'
194+
195+
- name: Install Matplotlib
196+
shell: bash.exe -eo pipefail -o igncr "{0}"
197+
env:
198+
AUTOCONF: /usr/bin/autoconf-2.69
199+
MAKEFLAGS: dw
200+
run: |
201+
ccache -s
202+
git describe
203+
cat <<EOT >> mplsetup.cfg
204+
[rc_options]
205+
backend=Agg
206+
207+
[libs]
208+
system_freetype = False
209+
system_qhull = True
210+
EOT
211+
cat mplsetup.cfg
212+
# All dependencies must have been pre-installed, so that the minver
213+
# constraints are held.
214+
python -m pip install --no-deps -ve .
215+
216+
- name: Find DLLs to rebase
217+
shell: bash.exe -eo pipefail -o igncr "{0}"
218+
run: |
219+
find {/usr,/usr/local}/{bin,lib/python3.*/site-packages} /usr/lib/lapack . -name \*.exe -o -name \*.dll -print >files_to_rebase.txt
220+
221+
- name: Rebase DLL list
222+
shell: ash.exe "{0}"
223+
run: "rebase --database --filelist=files_to_rebase.txt"
224+
# Inplace modification of DLLs to assign non-overlapping load
225+
# addresses so fork() works as expected. Ash is used as it
226+
# does not link against any Cygwin DLLs that might need to be
227+
# rebased.
228+
229+
- name: Check that Matplotlib imports
230+
shell: bash.exe -eo pipefail -o igncr "{0}"
231+
run: |
232+
/usr/bin/python -c "import matplotlib as mpl; import matplotlib.pyplot as plt"
233+
234+
- name: Set ffmpeg path
235+
shell: bash.exe -eo pipefail -o igncr "{0}"
236+
run: |
237+
oldmplrc=$(python -c "from matplotlib import matplotlib_fname as mplrc_file; print(mplrc_file())")
238+
echo "${oldmplrc}"
239+
mkdir -p ~/.matplotlib/
240+
sed -E -e 's~#animation\.ffmpeg_path:.+~animation.ffmpeg_path: /usr/bin/ffmpeg.exe~' "${oldmplrc}" >~/.matplotlib/matplotlibrc
241+
242+
- name: Run pytest
243+
shell: bash.exe -eo pipefail -o igncr "{0}"
244+
id: cygwin-run-pytest
245+
run: |
246+
xvfb-run python -mpytest -raR -n auto \
247+
--maxfail=50 --timeout=300 --durations=25 \
248+
--cov-report=xml --cov=lib --log-level=DEBUG --color=yes
249+
250+
- name: Upload code coverage
251+
uses: codecov/codecov-action@v3

lib/matplotlib/testing/__init__.py

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

5252

53+
def subprocess_run_for_testing(
54+
command: "list[str]",
55+
env: "dict[str, str]" = None,
56+
timeout: float = None,
57+
stdout=None,
58+
stderr=None,
59+
check: bool = False,
60+
text: bool = True,
61+
capture_output: bool = False
62+
) -> "subprocess.Popen":
63+
"""
64+
Create and run a subprocess.
65+
66+
Thin wrapper around `subprocess.run`, intended for testing. Will
67+
mark fork() failures on Cygwin as expected failures: not a
68+
success, but not indicating a problem with the code either.
69+
70+
Parameters
71+
----------
72+
args : list of str
73+
env : dict[str, str]
74+
timeout : float
75+
stdout, stderr
76+
check : bool
77+
text : bool
78+
Also called ``universal_newlines`` in subprocess. I chose this
79+
name since the main effect is returning bytes (`False`) vs. str
80+
(`True`), though it also tries to normalize newlines across
81+
platforms.
82+
capture_output : bool
83+
Set stdout and stderr to subprocess.PIPE
84+
85+
Returns
86+
-------
87+
proc : subprocess.Popen
88+
89+
See Also
90+
--------
91+
subprocess.run
92+
93+
Raises
94+
------
95+
pytest.xfail
96+
If platform is Cygwin and subprocess reports a fork() failure.
97+
"""
98+
if capture_output:
99+
stdout = stderr = subprocess.PIPE
100+
try:
101+
proc = subprocess.run(
102+
command, env=env,
103+
timeout=timeout, check=check,
104+
stdout=stdout, stderr=stderr,
105+
text=text
106+
)
107+
except BlockingIOError:
108+
if sys.platform == "cygwin":
109+
# Might want to make this more specific
110+
import pytest
111+
pytest.xfail("Fork failure")
112+
raise
113+
return proc
114+
115+
53116
def subprocess_run_helper(func, *args, timeout, extra_env=None):
54117
"""
55118
Run a function in a sub-process.
@@ -66,16 +129,19 @@ def subprocess_run_helper(func, *args, timeout, extra_env=None):
66129
"""
67130
target = func.__name__
68131
module = func.__module__
69-
proc = subprocess.run(
70-
[sys.executable,
71-
"-c",
72-
f"from {module} import {target}; {target}()",
73-
*args],
132+
proc = subprocess_run_for_testing(
133+
[
134+
sys.executable,
135+
"-c",
136+
f"from {module} import {target}; {target}()",
137+
*args
138+
],
74139
env={**os.environ, "SOURCE_DATE_EPOCH": "0", **(extra_env or {})},
75140
timeout=timeout, check=True,
76141
stdout=subprocess.PIPE,
77142
stderr=subprocess.PIPE,
78-
universal_newlines=True)
143+
text=True
144+
)
79145
return proc
80146

81147

lib/matplotlib/tests/test_preprocess_data.py

Lines changed: 4 additions & 2 10000 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,7 +259,9 @@ def test_data_parameter_replacement():
259259
"import matplotlib.pyplot as plt"
260260
)
261261
cmd = [sys.executable, "-c", program]
262-
completed_proc = subprocess.run(cmd, text=True, capture_output=True)
262+
completed_proc = subprocess_run_for_testing(
263+
cmd, text=True, capture_output=True
264+
)
263265
assert 'data parameter docstring error' not in completed_proc.stderr
264266

265267

lib/matplotlib/tests/test_pyplot.py

Lines changed: 4 additions & 3 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,8 +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-
subprocess.run([sys.executable, str(gen_script), str(plt_file)],
24-
check=True)
23+
subprocess_run_for_testing(
24+
[sys.executable, str(gen_script), str(plt_file)],
25+
check=True)
2526
new_contents = plt_file.read_text('utf-8')
2627

2728
if orig_contents != new_contents:

0 commit comments

Comments
 (0)
0