From 302aca21da8c426d4755200ec84ffbfd36614759 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Tue, 6 Dec 2022 14:57:26 -0500 Subject: [PATCH 01/12] BUG: Import Python.h before stdlib headers Python.h defines several visibility macros, which only work if defined before any stdlib header is included. This is needed for things like pybind11's strdup to get declared. --- src/mplutils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mplutils.h b/src/mplutils.h index 39d98ed02e8f..2eb9e2f563f3 100644 --- a/src/mplutils.h +++ b/src/mplutils.h @@ -6,6 +6,7 @@ #define MPLUTILS_H #define PY_SSIZE_T_CLEAN +#include #include #ifdef _POSIX_C_SOURCE @@ -27,7 +28,6 @@ #endif #endif -#include inline double mpl_round(double v) { From db71b51d230bc91b6cf3dc1eb3a5942eaa8a2ef3 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Mon, 5 Dec 2022 15:03:12 -0500 Subject: [PATCH 02/12] TST: Catch fork failures and mark them as xfail instead of fail. Not a pass, but not the fault of the test. TST,BUG: Move pytest import to just before its use. Hopefully this fixes the doc builds. TST: Cygwin: Mark more fork failures as xfail. Not a pass, but not matplotlib's fault. --- lib/matplotlib/testing/__init__.py | 27 ++++++++++++-------- lib/matplotlib/tests/test_preprocess_data.py | 7 ++++- lib/matplotlib/tests/test_pyplot.py | 9 +++++-- lib/matplotlib/tests/test_sphinxext.py | 23 +++++++++++++---- 4 files changed, 48 insertions(+), 18 deletions(-) diff --git a/lib/matplotlib/testing/__init__.py b/lib/matplotlib/testing/__init__.py index 6f54a4866c18..bd3ba0096f35 100644 --- a/lib/matplotlib/testing/__init__.py +++ b/lib/matplotlib/testing/__init__.py @@ -66,16 +66,23 @@ def subprocess_run_helper(func, *args, timeout, extra_env=None): """ target = func.__name__ module = func.__module__ - proc = subprocess.run( - [sys.executable, - "-c", - f"from {module} import {target}; {target}()", - *args], - env={**os.environ, "SOURCE_DATE_EPOCH": "0", **(extra_env or {})}, - timeout=timeout, check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) + try: + proc = subprocess.run( + [sys.executable, + "-c", + f"from {module} import {target}; {target}()", + *args], + env={**os.environ, "SOURCE_DATE_EPOCH": "0", **(extra_env or {})}, + timeout=timeout, check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + except BlockingIOError: + if sys.platform == "cygwin": + import pytest + pytest.xfail("Fork failure") + else: + raise return proc diff --git a/lib/matplotlib/tests/test_preprocess_data.py b/lib/matplotlib/tests/test_preprocess_data.py index a95a72e7f78d..2ef3ac11b2af 100644 --- a/lib/matplotlib/tests/test_preprocess_data.py +++ b/lib/matplotlib/tests/test_preprocess_data.py @@ -259,7 +259,12 @@ def test_data_parameter_replacement(): "import matplotlib.pyplot as plt" ) cmd = [sys.executable, "-c", program] - completed_proc = subprocess.run(cmd, text=True, capture_output=True) + try: + completed_proc = subprocess.run(cmd, text=True, capture_output=True) + except BlockingIOError: + if sys.platform == "cygwin": + pytest.xfail("Fork failure") + raise assert 'data parameter docstring error' not in completed_proc.stderr diff --git a/lib/matplotlib/tests/test_pyplot.py b/lib/matplotlib/tests/test_pyplot.py index 95e3174d8ae8..78840acfb2df 100644 --- a/lib/matplotlib/tests/test_pyplot.py +++ b/lib/matplotlib/tests/test_pyplot.py @@ -20,8 +20,13 @@ def test_pyplot_up_to_date(tmpdir): plt_file = tmpdir.join('pyplot.py') plt_file.write_text(orig_contents, 'utf-8') - subprocess.run([sys.executable, str(gen_script), str(plt_file)], - check=True) + try: + subprocess.run([sys.executable, str(gen_script), str(plt_file)], + check=True) + except BlockingIOError: + if sys.platform == "cygwin": + pytest.xfail("Fork failure") + raise new_contents = plt_file.read_text('utf-8') if orig_contents != new_contents: diff --git a/lib/matplotlib/tests/test_sphinxext.py b/lib/matplotlib/tests/test_sphinxext.py index 41575d3a3ce1..591644c2af5b 100644 --- a/lib/matplotlib/tests/test_sphinxext.py +++ b/lib/matplotlib/tests/test_sphinxext.py @@ -19,8 +19,14 @@ def build_sphinx_html(source_dir, doctree_dir, html_dir, extra_args=None): extra_args = [] if extra_args is None else extra_args cmd = [sys.executable, '-msphinx', '-W', '-b', 'html', '-d', str(doctree_dir), str(source_dir), str(html_dir), *extra_args] - proc = Popen(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True, - env={**os.environ, "MPLBACKEND": ""}) + try: + proc = Popen(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True, + env={**os.environ, "MPLBACKEND": ""}) + except BlockingIOError: + if sys.platform == "cygwin": + pytest.xfail("Fork failure") + else: + raise out, err = proc.communicate() assert proc.returncode == 0, \ @@ -44,9 +50,16 @@ def test_tinypages(tmp_path): # On CI, gcov emits warnings (due to agg headers being included with the # same name in multiple extension modules -- but we don't care about their # coverage anyways); hide them using GCOV_ERROR_FILE. - proc = Popen( - cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True, - env={**os.environ, "MPLBACKEND": "", "GCOV_ERROR_FILE": os.devnull}) + try: + proc = Popen( + cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True, + env={**os.environ, "MPLBACKEND": "", "GCOV_ERROR_FILE": os.devnull} + ) + except BlockingIOError: + if sys.platform == "cygwin": + pytest.xfail("Fork failure") + else: + raise out, err = proc.communicate() # Build the pages with warnings turned into errors From e5e0e2fde61e62f56529d6d7d4ff1fa004343026 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 6 May 2022 20:08:24 -0400 Subject: [PATCH 03/12] CI: Add a Cygwin run to GHA CI. This involved a good bit of debugging of the build process, especially for the bundled FreeType. Eventually removing the debugging flags let it just work. There are still fork failures, but I've done everything I know to do. CYGWIN=detect_bloda may reveal more, but may not. parent 48e59344de37d7e4589fb9a4d3858e0b3ab63050 author DWesl <22566757+DWesl@users.noreply.github.com> 1651882104 -0400 committer DWesl <22566757+DWesl@users.noreply.github.com> 1670678538 -0500 parent 48e59344de37d7e4589fb9a4d3858e0b3ab63050 author DWesl <22566757+DWesl@users.noreply.github.com> 1651882104 -0400 committer DWesl <22566757+DWesl@users.noreply.github.com> 1670678484 -0500 CI: Add a Cygwin run to GHA CI. Let's see if I translated the workflow even close to correctly. CI: Pin Cygwin setuptools<60 and NumPy !=1.21 setuptools>60 breaks Pillow install, and may cause problems for NumPy. NumPy 1.21 has intermittent segfaults on GHA runners, so let's just skip that. CI: Pin Cygwin setuptools<60 and avoid problem upgrades setuptools>=60 runs into python-pillow/pillow#6216 and pypa/setuptools#3304. NumPy==1.21.* has intermittent segfaults due to SIMD attempts on unaligned memory. kiwisolver, numpy, and pillow have problems installing, so just verify that they are present, don't try to upgrade them. CI: Upgrade Cygwin pip CI: Install g++ and print info on Python.h Hopefully one of these will convince NumPy to compile. CI: Install libjpeg-devel for Pillow install Let's see if it keeps setuptools old enough for this to work. CI: BLD: Use system freetype and qhull Given that I'm checking a build problem at the moment, this should allow that to show up faster. This should probably be reverted before merging. CI: Specify shell for Cygwin pytest step. CI: Rebase Matplotlib DLLs before running pytest Try to avoid "BlockingIOError: Resource Unavailable" with a fork() error on stderr. CI: Compile against bundled freetype. See if this fixes the various test errors. It will take a few tries to get freetype to actually compile, I think. CI: Install make for libfreetype build Let's see if this lets the build run to completion. BLD: Run autoconf before installing bundled freetype Let's see if this fixes the install problems. CI: Try to get Cygwin build of FreeType working. It doesn't fail the same way locally. CI: Revert to actually-configured python alternatives. BLD: Fix autoconf call on Cygwin. BLD: Fix directory for Cygwin autoreconf. CI: Fix directory for Cygwin autoconf in FreeType. BLD: Fix autoreconf command line. BLD: Run only autoconf on Cygwin, not autoreconf. libtoolize still fails; let's see if this at least gets through configure now. BLD: Run verbose libtoolize on bundled FreeType Let's see if this produces useful information. BLD: Copy the libtool files and ensure write permissions Copy instead of symlinking, and make sure the current user has write permissions for the target files. BLD: Add print statements to update on Autotools progress Let's see if I this narrows down where the error happens. BLD: Add permissions for everybody to fix libtoolize It's the only thing I can think of that would make the copy fail. CI: Try to clean up error reporting. Errors importing matplotlib are distinct from test failures; pip's logs of compile failures have a lot of extra stuff not related to the actual compile failure CI: Skip broken FreeType build on Cygwin I'll have to update many tests, but that should be doable. CI: Add FreeType and QHull to Cygwin CI I'm going the hard way through the testing process. BLD: Use auto-typed result from dlsym Try to avoid the Cygwin-vs-Linux dlsym signature incompatibility. (Cygwin returns FARPROC aka `long long int (*)()`, Linux returns void *) CI: Add Cygwin dependencies and try to rebase more Let's hope this sidesteps the BlockingIOErrors in subprocess.run CI: Split Cygwin rebase step One step for finding the files. a separate step for rebasing, using only rebase. CI: Only rebase files in /usr/ and /usr/local/ GHA gives permissions to write to /usr/local, so pip puts the new files there. CI: Add premissions to the image comparisons See if this lets the artifact upload step work. CI: Use the bundled FreeType on Cygwin again CI: Try to change rebase to avoid fork failures BLD: Fix definition of FT_CONFIG_CONFIG_H on Cygwin The default definition doesn't actually define everything assumed to be defined by that header. It was changed a few versions before, but they seem to have only checked whether it worked a few places. BLD: Add autoconf and libtoolize back in I'd forgotten configure crashed. BLD: Continue on libtoolize failure CI: Try to get more information from make CI: Uses Cygwin FreeType package again. CI: Try doubled backslashes for result directory. Let's see if this allows upload-artifact to succeed. BUG: Rebase DLLs in ediable install I skipped those earlier, which doesn't do anything useful. CI, BUG: Install git on Cygwin runner. Some of the tests use setuptools_scm, which needs git. CI, BUG: Run actions/checkout before Cygwin install Otherwise git panics about someone else owning the repository midway through cloning. CI, BUG: Mark the repo safe again Git is apparently picky about how I specify Windows paths. CI, DBG: Print Windows permissions for image dir Inspired by https://social.technet.microsoft.com/wiki/contents/articles/31139.powershell-how-to-get-folder-permissions.aspx Apparently Windows "dir" doesn't have an option to output permissions, that's a separate utility. CI, DBG: Print more permissions for result_images Try printing permissions of the images that fail to upload. The directory seems to deny only write and delete permissions, which shouldn't cause problems. CI, DBG: Give everyone all permissions to result img dir Maybe this will let upload-artifact succeed. CI: Use dash for /bin/sh on Cygwin This seems to help libtoolize finish without crashing. CI: Try to compile bundled freetype on Cygwin again This should get farther. CI: FIX: Specify proper shell when setting shell. CI, FIX: Spell shell options correctly to set /bin/sh CI, FIX: Use dash to set dash as /bin/sh on Cygwin BLD: Specify /bin/dash explicitly for Cygwin FreeType build TODO: Remember to revert the configure change. CI: Keep bash around when making dash /bin/sh CI: Make /bin/sh a symlink to /bin/dash CI: Set Cygwin tempdir to /tmp CI: Explicitly install m4 on Cygwin. CI, DBG: Clarify what /bin/sh is around def change. I want to see how they change before and after I change it from /bin/bash to /bin/dash CI, FIX: Use bash to change /bin/sh to /bin/dash CI, FIX: Clean up syntax to set sh to dash. Also avoid duplicate work for a failure. BLD: Reduce verbosity of FreeType setup on Cygwin Use autoreconf instead of autoconf --verbose Should help me figure out where the new failure is. BLD: Stop running autoreconf on Cygwin It doesn't seem to help much CI, DBG: Only run upload-artifact debuggers if tests ran Don't try to examine images if there hasn't been anything to create them. CI, FIX: Use single quotes for condition. Double quotes cause problems CI: Allow Cygwin CI to run on patch-2 BLD: Revert most changes to setupext.py Still unconditionally rebuild ./configure on Cygwin, to try to update build system. BLD: Revert the rest of the changes to setupext.py FreeType seems to build on unmodified matplotlib locally, so this shouldn't be needed. CI: Install matplotlib in Cygwin CI Hopefully this pulls in the last dependencies and keeps the FreeType build from failing. CI: Specify MAKEFLAGS, not MAKE https://www.gnu.org/software/make/manual/make.html#Options_002fRecursion suggests specifying MAKEFLAGS to ensue options are passed to make, not MAKE. This might help with the weird "directory not found" errors. CI, BUG: Specify --coverage in CPPFLAGS and LDFLAGS Specifying in CPPFLAGS only causes link to fail. LDFLAGS=-lgcov should also work, but this should be more general. STY: Remove trainling whitespace Get pre-commit to stop sending me emails about trailing whitespace. CI,BUG: Make sure coverage library gets linked There may be a problem in the configtest procedure. CI: Cut separate build step It's failing in the pip install step, and I want to be sure that's not because there's already a build tree. CI: Cygwin: Don't add coverage tracing to matplotlib It causes pip install to fail. CI: Install importlib_resources Apparently not listed in the requirements*.txt BUG: Fix spelling CI: Cygwin: Drop LaTeX packages The PGF tests mostly fail. CI: Set ffmpeg path so Cygwin tests don't use Windows executable CI: Append to mplrc, don't overwrite. Let's see if this fixes the key lookup error on import CI: Cygwin: Create new mplrc file instead of overwriting old Appending creates load errors in forked tests; replacing the line should fix that, and creating a new version should fix the remaining problems. CI,FIX: Cygwin: Create mpl config directories CI: Cygwin: Don't install WX, do install ipython WX imports keep failing. One test uses IPython and fails, let's make sure that's not because IPython isn't installed. CI: Cygwin: Uninstall wxPython It shouldn't be installed, so this should be fast. CI: Cygwin: Try to get remaining tests working. Rebase executables in /bin as well as those in /usr/bin Remove default ACLs on /tmp CI,FIX: Fix syntax and style of previous commit //bin is not POSIX-compliant and doesn't work on Cygwin. Delete trailing whitespace. CI: Cygwin: Set username to root and uid to 0 This should let checks for "can I access all files regardless of permissions" work the way Linux expects. CI,FIX: Cygwin: Try to fix sed expression for euid=0 Sed expression #2, character 0: error: no previous regex. Not really useful, sed. CI: Cygwin: Stop changing username, just change uid CI: Stop rebasing binaries in /bin They are already covered in /usr/bin, and probably the important information is in the file, not a database somewhere (I mean, it's also in a database, but you can turn that off). CI: Cygwin: Upload code coverage data CI: Cygwin: Update run condition for upstream repo I changed it so I'd get quicker feedback on tests some time ago and forgot to change it back. Update .github/workflows/tests.yml Co-authored-by: Elliott Sales de Andrade Update .github/workflows/tests.yml Co-authored-by: Elliott Sales de Andrade CI: Cygwin: Incorporate suggestions from review. CI: Incorporate suggestions from review. CI: Cygwin: Use dash for /bin/sh instead of bash. Faster, and doesn't fail the FreeType build. CI: Test python 3.8 and 3.9 on Cygwin Co-authored-by: Thomas A Caswell CI: Cygwin: Correct matplotlibrc location Apparently it doesn't check ~/.config/matplotlib DOC,CI: Explain why CI runs tests with gid set to 0. The other option is to look into cygutils-extra's cygdrop. I have no idea how to use that, so I'm sticking with this. --- .github/workflows/tests.yml | 199 ++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 78dfefdbe7aa..f28c26f7ccf9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -291,6 +291,205 @@ jobs: name: "${{ matrix.python-version }} ${{ matrix.os }} ${{ matrix.name-suffix }} result images" path: ./result_images + test-cygwin: + runs-on: windows-latest + name: Python 3.${{ matrix.python-minor-version }} on Cygwin + if: | + github.event_name == 'workflow_dispatch' || + ( + github.repository == 'matplotlib/matplotlib' && + !contains(github.event.head_commit.message, '[ci skip]') && + !contains(github.event.head_commit.message, '[skip ci]') && + !contains(github.event.head_commit.message, '[skip github]') + ) + env: + SHELLOPTS: igncr + CYGWIN_NOWINPATH: 1 + CHERE_INVOKING: 1 + TMP: /tmp + TEMP: /tmp + strategy: + matrix: + python-minor-version: [8, 9] + + steps: + - name: Fix line endings + run: git config --global core.autocrlf input + + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - uses: cygwin/cygwin-install-action@v2 + with: + packages: >- + ccache gcc-g++ gdb git graphviz libcairo-devel libffi-devel + libgeos-devel libQt5Core-devel pkgconf libglib2.0-devel + noto-cjk-fonts + python3${{ matrix.python-minor-version }}-devel + python3${{ matrix.python-minor-version }}-pip + python3${{ matrix.python-minor-version }}-wheel + python3${{ matrix.python-minor-version }}-setuptools + python3${{ matrix.python-minor-version }}-cycler + python3${{ matrix.python-minor-version }}-dateutil + python3${{ matrix.python-minor-version }}-fonttools + python3${{ matrix.python-minor-version }}-imaging + python3${{ matrix.python-minor-version }}-kiwisolver + python3${{ matrix.python-minor-version }}-numpy + python3${{ matrix.python-minor-version }}-packaging + python3${{ matrix.python-minor-version }}-pyparsing + python3${{ matrix.python-minor-version }}-sip + python3${{ matrix.python-minor-version }}-sphinx + python-cairo-devel + python3${{ matrix.python-minor-version }}-cairo + python3${{ matrix.python-minor-version }}-gi + python3${{ matrix.python-minor-version }}-matplotlib + xorg-server-extra libxcb-icccm4 libxcb-image0 + libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 + libxcb-xinerama0 + make autoconf autoconf2.5 automake automake1.10 libtool m4 + libqhull-devel libfreetype-devel + libjpeg-devel libwebp-devel + + - name: Set runner username to root and id to 0 + shell: bash.exe -eo pipefail -o igncr "{0}" + # GitHub Actions runs everything as Administrator. I don't + # know how to test for this, so set the uid for the CI job so + # that the existing unix root detection will work. + run: | + /bin/mkpasswd.exe -c | sed -e "s/$(id -u)/0/" >/etc/passwd + + - name: Mark test repo safe + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + git.exe config --global --add safe.directory /proc/cygdrive/d/a/matplotlib/matplotlib + git config --global --add safe.directory /cygdrive/d/a/matplotlib/matplotlib + C:/cygwin/bin/git.exe config --global --add safe.directory D:/a/matplotlib/matplotlib + /usr/bin/git config --global --add safe.directory /cygdrive/d/a/matplotlib/matplotlib + + - name: Use dash for /bin/sh + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + ls -l /bin/sh.exe /bin/bash.exe /bin/dash.exe + /bin/rm -f /bin/sh.exe || exit 1 + cp -sf /bin/dash.exe /bin/sh.exe || exit 1 + ls -l /bin/sh.exe /bin/bash.exe /bin/dash.exe + # FreeType build fails with bash, succeeds with dash + + - name: Cache pip + uses: actions/cache@v3 + with: + path: C:\cygwin\home\runneradmin\.cache\pip + key: Cygwin-py3.${{ matrix.python-minor-version }}-pip-${{ hashFiles('requirements/*/*.txt') }} + restore-keys: | + ${{ matrix.os }}-py3.${{ matrix.python-minor-version }}-pip- + + - name: Cache ccache + uses: actions/cache@v3 + with: + path: C:\cygwin\home\runneradmin\.ccache + key: Cygwin-py3.${{ matrix.python-minor-version }}-ccache-${{ hashFiles('src/*') }} + restore-keys: Cygwin-py3.${{ matrix.python-minor-version }}-ccache- + + - name: Cache Matplotlib + uses: actions/cache@v3 + with: + path: | + C:\cygwin\home\runneradmin/.cache/matplotlib + !C:\cygwin\home\runneradmin/.cache/matplotlib/tex.cache + !C:\cygwin\home\runneradmin/.cache/matplotlib/test_cache + key: 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl-${{ github.ref }}-${{ github.sha }} + restore-keys: | + 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl-${{ github.ref }}- + 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl- + + - name: Ensure correct python version + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + /usr/sbin/alternatives --set python /usr/bin/python3.${{ matrix.python-minor-version }} + /usr/sbin/alternatives --set python3 /usr/bin/python3.${{ matrix.python-minor-version }} + + - name: Install Python dependencies + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + python -m pip install --upgrade pip 'setuptools<60' wheel + python -m pip install kiwisolver 'numpy!=1.21.*' pillow importlib_resources + grep -v -F -e psutil requirements/testing/all.txt >requirements_test.txt + python -m pip install --upgrade 'contourpy>=1.0.1' cycler fonttools \ + packaging pyparsing python-dateutil setuptools-scm \ + -r requirements_test.txt sphinx ipython + python -m pip install --upgrade pycairo 'cairocffi>=0.8' PyGObject && + python -c 'import gi; gi.require_version("Gtk", "3.0"); from gi.repository import Gtk' && + echo 'PyGObject is available' || + echo 'PyGObject is not available' + python -m pip install --upgrade pyqt5 && + python -c 'import PyQt5.QtCore' && + echo 'PyQt5 is available' || + echo 'PyQt5 is not available' + python -mpip install --upgrade pyside2 && + python -c 'import PySide2.QtCore' && + echo 'PySide2 is available' || + echo 'PySide2 is not available' + python -m pip uninstall --yes wxpython || echo 'wxPython already uninstalled' + + - name: Install matplotlib + shell: bash.exe -eo pipefail -o igncr "{0}" + env: + AUTOCONF: /usr/bin/autoconf-2.69 + MAKEFLAGS: dw + run: | + ccache -s + git describe + cat <> mplsetup.cfg + [rc_options] + backend=Agg + + [libs] + system_freetype = False + system_qhull = True + EOT + cat mplsetup.cfg + # All dependencies must have been pre-installed, so that the minver + # constraints are held. + python -m pip install --no-deps -ve . + + - name: Find DLLs to rebase + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + find {/usr,/usr/local}/{bin,lib/python3.*/site-packages} /usr/lib/lapack . -name \*.exe -o -name \*.dll -print >files_to_rebase.txt + + - name: Rebase DLL list + shell: ash.exe "{0}" + run: "rebase --database --filelist=files_to_rebase.txt" + # Inplace modification of DLLs to assign non-overlapping load + # addresses so fork() works as expected. Ash is used as it + # does not link against any Cygwin DLLs that might need to be + # rebased. + + - name: Check that Matplotlib imports + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + /usr/bin/python -c "import matplotlib as mpl; import matplotlib.pyplot as plt" + + - name: Set ffmpeg path + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + oldmplrc=$(python -c "from matplotlib import matplotlib_fname as mplrc_file; print(mplrc_file())") + echo "${oldmplrc}" + mkdir -p ~/.matplotlib/ + sed -E -e 's~#animation\.ffmpeg_path:.+~animation.ffmpeg_path: /usr/bin/ffmpeg.exe~' "${oldmplrc}" >~/.matplotlib/matplotlibrc + + - name: Run pytest + shell: bash.exe -eo pipefail -o igncr "{0}" + id: cygwin-run-pytest + run: | + xvfb-run python -mpytest -raR -n auto \ + --maxfail=50 --timeout=300 --durations=25 \ + --cov-report=xml --cov=lib --log-level=DEBUG --color=yes + + - name: Upload code coverage + uses: codecov/codecov-action@v3 + # Separate dependent job to only upload one issue from the matrix of jobs create-issue: if: ${{ failure() && github.event_name == 'schedule' }} From 382a6b87033a9036fbd93a7d2b69cf3cb8b335df Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Wed, 4 Jan 2023 09:58:56 -0500 Subject: [PATCH 04/12] CI: Move Cygwin CI to new file, trigger on label. I think I did this right, we will find out. --- .github/workflows/cygwin.yml | 249 +++++++++++++++++++++++++++++++++++ .github/workflows/tests.yml | 199 ---------------------------- 2 files changed, 249 insertions(+), 199 deletions(-) create mode 100644 .github/workflows/cygwin.yml diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml new file mode 100644 index 000000000000..189343cac85b --- /dev/null +++ b/.github/workflows/cygwin.yml @@ -0,0 +1,249 @@ +--- +name: Cygwin Tests +concurrency: + group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event.ref }} + cancel-in-progress: true + +on: + push: + branches: + - main + - v[0-9]+.[0-9]+.[0-9x]+.x + tags: + - v* + paths: + - 'src/**' + pull_request: + types: + - opened + - synchronize + - reopened + - labeled + branches-ignore: + - v[0-9]+.[0-9]+.[0-9x]+-doc + paths: + - 'src/**' + schedule: + # 5:47 UTC on Saturdays + - cron: "47 5 * * 6" + workflow_dispatch: + workflow: "*" + +permissions: + contents: read + +env: + NO_AT_BRIDGE: 1 # Necessary for GTK3 interactive test. + OPENBLAS_NUM_THREADS: 1 + PYTHONFAULTHANDLER: 1 + SHELLOPTS: igncr + CYGWIN_NOWINPATH: 1 + CHERE_INVOKING: 1 + TMP: /tmp + TEMP: /tmp + +jobs: + + test-cygwin: + runs-on: windows-latest + name: Python 3.${{ matrix.python-minor-version }} on Cygwin + if: | + github.event_name == 'workflow_dispatch' || + ( + github.repository == 'matplotlib/matplotlib' && + !contains(github.event.head_commit.message, '[ci skip]') && + !contains(github.event.head_commit.message, '[skip ci]') && + !contains(github.event.head_commit.message, '[skip github]') && + ( + github.event_name == 'push' || + github.event_name == 'pull_request && ( + ( + github.event.action == 'labeled' && + github.event.label.name == 'Run cygwin' + ) || + contains(github.event.pull_request.labels.*.name, 'Run cygwin') + ) + ) + ) + env: + strategy: + matrix: + python-minor-version: [8, 9] + + steps: + - name: Fix line endings + run: git config --global core.autocrlf input + + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - uses: cygwin/cygwin-install-action@v2 + with: + packages: >- + ccache gcc-g++ gdb git graphviz libcairo-devel libffi-devel + libgeos-devel libQt5Core-devel pkgconf libglib2.0-devel + noto-cjk-fonts + python3${{ matrix.python-minor-version }}-devel + python3${{ matrix.python-minor-version }}-pip + python3${{ matrix.python-minor-version }}-wheel + python3${{ matrix.python-minor-version }}-setuptools + python3${{ matrix.python-minor-version }}-cycler + python3${{ matrix.python-minor-version }}-dateutil + python3${{ matrix.python-minor-version }}-fonttools + python3${{ matrix.python-minor-version }}-imaging + python3${{ matrix.python-minor-version }}-kiwisolver + python3${{ matrix.python-minor-version }}-numpy + python3${{ matrix.python-minor-version }}-packaging + python3${{ matrix.python-minor-version }}-pyparsing + python3${{ matrix.python-minor-version }}-sip + python3${{ matrix.python-minor-version }}-sphinx + python-cairo-devel + python3${{ matrix.python-minor-version }}-cairo + python3${{ matrix.python-minor-version }}-gi + python3${{ matrix.python-minor-version }}-matplotlib + xorg-server-extra libxcb-icccm4 libxcb-image0 + libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 + libxcb-xinerama0 + make autoconf autoconf2.5 automake automake1.10 libtool m4 + libqhull-devel libfreetype-devel + libjpeg-devel libwebp-devel + + - name: Set runner username to root and id to 0 + shell: bash.exe -eo pipefail -o igncr "{0}" + # GitHub Actions runs everything as Administrator. I don't + # know how to test for this, so set the uid for the CI job so + # that the existing unix root detection will work. + run: | + /bin/mkpasswd.exe -c | sed -e "s/$(id -u)/0/" >/etc/passwd + + - name: Mark test repo safe + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + git.exe config --global --add safe.directory /proc/cygdrive/d/a/matplotlib/matplotlib + git config --global --add safe.directory /cygdrive/d/a/matplotlib/matplotlib + C:/cygwin/bin/git.exe config --global --add safe.directory D:/a/matplotlib/matplotlib + /usr/bin/git config --global --add safe.directory /cygdrive/d/a/matplotlib/matplotlib + + - name: Use dash for /bin/sh + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + ls -l /bin/sh.exe /bin/bash.exe /bin/dash.exe + /bin/rm -f /bin/sh.exe || exit 1 + cp -sf /bin/dash.exe /bin/sh.exe || exit 1 + ls -l /bin/sh.exe /bin/bash.exe /bin/dash.exe + # FreeType build fails with bash, succeeds with dash + + - name: Cache pip + uses: actions/cache@v3 + with: + path: C:\cygwin\home\runneradmin\.cache\pip + key: Cygwin-py3.${{ matrix.python-minor-version }}-pip-${{ hashFiles('requirements/*/*.txt') }} + restore-keys: | + ${{ matrix.os }}-py3.${{ matrix.python-minor-version }}-pip- + + - name: Cache ccache + uses: actions/cache@v3 + with: + path: C:\cygwin\home\runneradmin\.ccache + key: Cygwin-py3.${{ matrix.python-minor-version }}-ccache-${{ hashFiles('src/*') }} + restore-keys: Cygwin-py3.${{ matrix.python-minor-version }}-ccache- + + - name: Cache Matplotlib + uses: actions/cache@v3 + with: + path: | + C:\cygwin\home\runneradmin/.cache/matplotlib + !C:\cygwin\home\runneradmin/.cache/matplotlib/tex.cache + !C:\cygwin\home\runneradmin/.cache/matplotlib/test_cache + key: 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl-${{ github.ref }}-${{ github.sha }} + restore-keys: | + 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl-${{ github.ref }}- + 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl- + + - name: Ensure correct python version + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + /usr/sbin/alternatives --set python /usr/bin/python3.${{ matrix.python-minor-version }} + /usr/sbin/alternatives --set python3 /usr/bin/python3.${{ matrix.python-minor-version }} + + - name: Install Python dependencies + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + python -m pip install --upgrade pip 'setuptools<60' wheel + python -m pip install kiwisolver 'numpy!=1.21.*' pillow importlib_resources + grep -v -F -e psutil requirements/testing/all.txt >requirements_test.txt + python -m pip install --upgrade 'contourpy>=1.0.1' cycler fonttools \ + packaging pyparsing python-dateutil setuptools-scm \ + -r requirements_test.txt sphinx ipython + python -m pip install --upgrade pycairo 'cairocffi>=0.8' PyGObject && + python -c 'import gi; gi.require_version("Gtk", "3.0"); from gi.repository import Gtk' && + echo 'PyGObject is available' || + echo 'PyGObject is not available' + python -m pip install --upgrade pyqt5 && + python -c 'import PyQt5.QtCore' && + echo 'PyQt5 is available' || + echo 'PyQt5 is not available' + python -mpip install --upgrade pyside2 && + python -c 'import PySide2.QtCore' && + echo 'PySide2 is available' || + echo 'PySide2 is not available' + python -m pip uninstall --yes wxpython || echo 'wxPython already uninstalled' + + - name: Install matplotlib + shell: bash.exe -eo pipefail -o igncr "{0}" + env: + AUTOCONF: /usr/bin/autoconf-2.69 + MAKEFLAGS: dw + run: | + ccache -s + git describe + cat <> mplsetup.cfg + [rc_options] + backend=Agg + + [libs] + system_freetype = False + system_qhull = True + EOT + cat mplsetup.cfg + # All dependencies must have been pre-installed, so that the minver + # constraints are held. + python -m pip install --no-deps -ve . + + - name: Find DLLs to rebase + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + find {/usr,/usr/local}/{bin,lib/python3.*/site-packages} /usr/lib/lapack . -name \*.exe -o -name \*.dll -print >files_to_rebase.txt + + - name: Rebase DLL list + shell: ash.exe "{0}" + run: "rebase --database --filelist=files_to_rebase.txt" + # Inplace modification of DLLs to assign non-overlapping load + # addresses so fork() works as expected. Ash is used as it + # does not link against any Cygwin DLLs that might need to be + # rebased. + + - name: Check that Matplotlib imports + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + /usr/bin/python -c "import matplotlib as mpl; import matplotlib.pyplot as plt" + + - name: Set ffmpeg path + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + oldmplrc=$(python -c "from matplotlib import matplotlib_fname as mplrc_file; print(mplrc_file())") + echo "${oldmplrc}" + mkdir -p ~/.matplotlib/ + sed -E -e 's~#animation\.ffmpeg_path:.+~animation.ffmpeg_path: /usr/bin/ffmpeg.exe~' "${oldmplrc}" >~/.matplotlib/matplotlibrc + + - name: Run pytest + shell: bash.exe -eo pipefail -o igncr "{0}" + id: cygwin-run-pytest + run: | + xvfb-run python -mpytest -raR -n auto \ + --maxfail=50 --timeout=300 --durations=25 \ + --cov-report=xml --cov=lib --log-level=DEBUG --color=yes + + - name: Upload code coverage + uses: codecov/codecov-action@v3 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f28c26f7ccf9..78dfefdbe7aa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -291,205 +291,6 @@ jobs: name: "${{ matrix.python-version }} ${{ matrix.os }} ${{ matrix.name-suffix }} result images" path: ./result_images - test-cygwin: - runs-on: windows-latest - name: Python 3.${{ matrix.python-minor-version }} on Cygwin - if: | - github.event_name == 'workflow_dispatch' || - ( - github.repository == 'matplotlib/matplotlib' && - !contains(github.event.head_commit.message, '[ci skip]') && - !contains(github.event.head_commit.message, '[skip ci]') && - !contains(github.event.head_commit.message, '[skip github]') - ) - env: - SHELLOPTS: igncr - CYGWIN_NOWINPATH: 1 - CHERE_INVOKING: 1 - TMP: /tmp - TEMP: /tmp - strategy: - matrix: - python-minor-version: [8, 9] - - steps: - - name: Fix line endings - run: git config --global core.autocrlf input - - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - uses: cygwin/cygwin-install-action@v2 - with: - packages: >- - ccache gcc-g++ gdb git graphviz libcairo-devel libffi-devel - libgeos-devel libQt5Core-devel pkgconf libglib2.0-devel - noto-cjk-fonts - python3${{ matrix.python-minor-version }}-devel - python3${{ matrix.python-minor-version }}-pip - python3${{ matrix.python-minor-version }}-wheel - python3${{ matrix.python-minor-version }}-setuptools - python3${{ matrix.python-minor-version }}-cycler - python3${{ matrix.python-minor-version }}-dateutil - python3${{ matrix.python-minor-version }}-fonttools - python3${{ matrix.python-minor-version }}-imaging - python3${{ matrix.python-minor-version }}-kiwisolver - python3${{ matrix.python-minor-version }}-numpy - python3${{ matrix.python-minor-version }}-packaging - python3${{ matrix.python-minor-version }}-pyparsing - python3${{ matrix.python-minor-version }}-sip - python3${{ matrix.python-minor-version }}-sphinx - python-cairo-devel - python3${{ matrix.python-minor-version }}-cairo - python3${{ matrix.python-minor-version }}-gi - python3${{ matrix.python-minor-version }}-matplotlib - xorg-server-extra libxcb-icccm4 libxcb-image0 - libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 - libxcb-xinerama0 - make autoconf autoconf2.5 automake automake1.10 libtool m4 - libqhull-devel libfreetype-devel - libjpeg-devel libwebp-devel - - - name: Set runner username to root and id to 0 - shell: bash.exe -eo pipefail -o igncr "{0}" - # GitHub Actions runs everything as Administrator. I don't - # know how to test for this, so set the uid for the CI job so - # that the existing unix root detection will work. - run: | - /bin/mkpasswd.exe -c | sed -e "s/$(id -u)/0/" >/etc/passwd - - - name: Mark test repo safe - shell: bash.exe -eo pipefail -o igncr "{0}" - run: | - git.exe config --global --add safe.directory /proc/cygdrive/d/a/matplotlib/matplotlib - git config --global --add safe.directory /cygdrive/d/a/matplotlib/matplotlib - C:/cygwin/bin/git.exe config --global --add safe.directory D:/a/matplotlib/matplotlib - /usr/bin/git config --global --add safe.directory /cygdrive/d/a/matplotlib/matplotlib - - - name: Use dash for /bin/sh - shell: bash.exe -eo pipefail -o igncr "{0}" - run: | - ls -l /bin/sh.exe /bin/bash.exe /bin/dash.exe - /bin/rm -f /bin/sh.exe || exit 1 - cp -sf /bin/dash.exe /bin/sh.exe || exit 1 - ls -l /bin/sh.exe /bin/bash.exe /bin/dash.exe - # FreeType build fails with bash, succeeds with dash - - - name: Cache pip - uses: actions/cache@v3 - with: - path: C:\cygwin\home\runneradmin\.cache\pip - key: Cygwin-py3.${{ matrix.python-minor-version }}-pip-${{ hashFiles('requirements/*/*.txt') }} - restore-keys: | - ${{ matrix.os }}-py3.${{ matrix.python-minor-version }}-pip- - - - name: Cache ccache - uses: actions/cache@v3 - with: - path: C:\cygwin\home\runneradmin\.ccache - key: Cygwin-py3.${{ matrix.python-minor-version }}-ccache-${{ hashFiles('src/*') }} - restore-keys: Cygwin-py3.${{ matrix.python-minor-version }}-ccache- - - - name: Cache Matplotlib - uses: actions/cache@v3 - with: - path: | - C:\cygwin\home\runneradmin/.cache/matplotlib - !C:\cygwin\home\runneradmin/.cache/matplotlib/tex.cache - !C:\cygwin\home\runneradmin/.cache/matplotlib/test_cache - key: 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl-${{ github.ref }}-${{ github.sha }} - restore-keys: | - 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl-${{ github.ref }}- - 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl- - - - name: Ensure correct python version - shell: bash.exe -eo pipefail -o igncr "{0}" - run: | - /usr/sbin/alternatives --set python /usr/bin/python3.${{ matrix.python-minor-version }} - /usr/sbin/alternatives --set python3 /usr/bin/python3.${{ matrix.python-minor-version }} - - - name: Install Python dependencies - shell: bash.exe -eo pipefail -o igncr "{0}" - run: | - python -m pip install --upgrade pip 'setuptools<60' wheel - python -m pip install kiwisolver 'numpy!=1.21.*' pillow importlib_resources - grep -v -F -e psutil requirements/testing/all.txt >requirements_test.txt - python -m pip install --upgrade 'contourpy>=1.0.1' cycler fonttools \ - packaging pyparsing python-dateutil setuptools-scm \ - -r requirements_test.txt sphinx ipython - python -m pip install --upgrade pycairo 'cairocffi>=0.8' PyGObject && - python -c 'import gi; gi.require_version("Gtk", "3.0"); from gi.repository import Gtk' && - echo 'PyGObject is available' || - echo 'PyGObject is not available' - python -m pip install --upgrade pyqt5 && - python -c 'import PyQt5.QtCore' && - echo 'PyQt5 is available' || - echo 'PyQt5 is not available' - python -mpip install --upgrade pyside2 && - python -c 'import PySide2.QtCore' && - echo 'PySide2 is available' || - echo 'PySide2 is not available' - python -m pip uninstall --yes wxpython || echo 'wxPython already uninstalled' - - - name: Install matplotlib - shell: bash.exe -eo pipefail -o igncr "{0}" - env: - AUTOCONF: /usr/bin/autoconf-2.69 - MAKEFLAGS: dw - run: | - ccache -s - git describe - cat <> mplsetup.cfg - [rc_options] - backend=Agg - - [libs] - system_freetype = False - system_qhull = True - EOT - cat mplsetup.cfg - # All dependencies must have been pre-installed, so that the minver - # constraints are held. - python -m pip install --no-deps -ve . - - - name: Find DLLs to rebase - shell: bash.exe -eo pipefail -o igncr "{0}" - run: | - find {/usr,/usr/local}/{bin,lib/python3.*/site-packages} /usr/lib/lapack . -name \*.exe -o -name \*.dll -print >files_to_rebase.txt - - - name: Rebase DLL list - shell: ash.exe "{0}" - run: "rebase --database --filelist=files_to_rebase.txt" - # Inplace modification of DLLs to assign non-overlapping load - # addresses so fork() works as expected. Ash is used as it - # does not link against any Cygwin DLLs that might need to be - # rebased. - - - name: Check that Matplotlib imports - shell: bash.exe -eo pipefail -o igncr "{0}" - run: | - /usr/bin/python -c "import matplotlib as mpl; import matplotlib.pyplot as plt" - - - name: Set ffmpeg path - shell: bash.exe -eo pipefail -o igncr "{0}" - run: | - oldmplrc=$(python -c "from matplotlib import matplotlib_fname as mplrc_file; print(mplrc_file())") - echo "${oldmplrc}" - mkdir -p ~/.matplotlib/ - sed -E -e 's~#animation\.ffmpeg_path:.+~animation.ffmpeg_path: /usr/bin/ffmpeg.exe~' "${oldmplrc}" >~/.matplotlib/matplotlibrc - - - name: Run pytest - shell: bash.exe -eo pipefail -o igncr "{0}" - id: cygwin-run-pytest - run: | - xvfb-run python -mpytest -raR -n auto \ - --maxfail=50 --timeout=300 --durations=25 \ - --cov-report=xml --cov=lib --log-level=DEBUG --color=yes - - - name: Upload code coverage - uses: codecov/codecov-action@v3 - # Separate dependent job to only upload one issue from the matrix of jobs create-issue: if: ${{ failure() && github.event_name == 'schedule' }} From 3709884f29445ae3f081a788f4eeca3e924d9898 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Wed, 4 Jan 2023 17:00:21 -0500 Subject: [PATCH 05/12] CI: Also run Cygwin CI when workflow changed. --- .github/workflows/cygwin.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index 189343cac85b..030e6c4be15d 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -13,6 +13,7 @@ on: - v* paths: - 'src/**' + - '.github/workflows/cygwin.yml' pull_request: types: - opened @@ -23,6 +24,7 @@ on: - v[0-9]+.[0-9]+.[0-9x]+-doc paths: - 'src/**' + - '.github/workflows/cygwin.yml' schedule: # 5:47 UTC on Saturdays - cron: "47 5 * * 6" From 848c35b490efde9623caa0e10a8e24bd5590536b Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 7 Jan 2023 09:02:35 -0500 Subject: [PATCH 06/12] STY,FIX: Apply suggestions from code review It might be good to make all the "try fork; xfail on BlockingIOError if running on Cygwin" code look like each other, or all use a single helper function somewhere. Co-authored-by: Elliott Sales de Andrade --- .github/workflows/cygwin.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index 030e6c4be15d..56306707cb8c 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -8,7 +8,7 @@ on: push: branches: - main - - v[0-9]+.[0-9]+.[0-9x]+.x + - v[0-9]+.[0-9]+.[0-9x]+ tags: - v* paths: @@ -67,7 +67,6 @@ jobs: ) ) ) - env: strategy: matrix: python-minor-version: [8, 9] @@ -155,15 +154,15 @@ jobs: uses: actions/cache@v3 with: path: | - C:\cygwin\home\runneradmin/.cache/matplotlib - !C:\cygwin\home\runneradmin/.cache/matplotlib/tex.cache - !C:\cygwin\home\runneradmin/.cache/matplotlib/test_cache + C:\cygwin\home\runneradmin\.cache\matplotlib + !C:\cygwin\home\runneradmin\.cache\matplotlib\tex.cache + !C:\cygwin\home\runneradmin\.cache\matplotlib\test_cache key: 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl-${{ github.ref }}-${{ github.sha }} restore-keys: | 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl-${{ github.ref }}- 1-Cygwin-py3.${{ matrix.python-minor-version }}-mpl- - - name: Ensure correct python version + - name: Ensure correct Python version shell: bash.exe -eo pipefail -o igncr "{0}" run: | /usr/sbin/alternatives --set python /usr/bin/python3.${{ matrix.python-minor-version }} @@ -192,7 +191,7 @@ jobs: echo 'PySide2 is not available' python -m pip uninstall --yes wxpython || echo 'wxPython already uninstalled' - - name: Install matplotlib + - name: Install Matplotlib shell: bash.exe -eo pipefail -o igncr "{0}" env: AUTOCONF: /usr/bin/autoconf-2.69 From 3bc5b3d6b6bc0cbbce6445c80c0e1ad6b89d6f5d Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Mon, 9 Jan 2023 11:32:26 -0500 Subject: [PATCH 07/12] FIX: Include closing quote in conditional. --- .github/workflows/cygwin.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index 56306707cb8c..7b5096ef3e19 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -58,7 +58,8 @@ jobs: !contains(github.event.head_commit.message, '[skip github]') && ( github.event_name == 'push' || - github.event_name == 'pull_request && ( + github.event_name == 'pull_request' && + ( ( github.event.action == 'labeled' && github.event.label.name == 'Run cygwin' From 9f2e3ba5b27645606d85999c400c4bc4c5f9503e Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Mon, 9 Jan 2023 18:14:28 -0500 Subject: [PATCH 08/12] 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. --- lib/matplotlib/testing/__init__.py | 84 ++++++++++++++++---- lib/matplotlib/tests/test_preprocess_data.py | 11 +-- lib/matplotlib/tests/test_pyplot.py | 12 +-- lib/matplotlib/tests/test_sphinxext.py | 33 +++----- 4 files changed, 87 insertions(+), 53 deletions(-) diff --git a/lib/matplotlib/testing/__init__.py b/lib/matplotlib/testing/__init__.py index bd3ba0096f35..336a855eed99 100644 --- a/lib/matplotlib/testing/__init__.py +++ b/lib/matplotlib/testing/__init__.py @@ -50,6 +50,63 @@ def setup(): set_reproducibility_for_testing() +def subprocess_run_for_testing( + command: "list[str]", + env: "dict[str, str]", + timeout=float, + stdout=None, + stderr=None, + check=False, + universal_newlines=True, + capture_output=False +) -> "subprocess.Popen": + """ + Create and run a subprocess. + + Thin wrapper around subprocess.run, intended for testing. + + Arguments + --------- + args : list of str + env : dict[str, str] + timeout : float + stdout, stderr + check : bool + universal_newlines : bool + capture_output : bool + Set stdout and stderr to subprocess.PIPE + + Returns + ------- + proc : subprocess.Popen + + See Also + -------- + subprocess.run + + Raises + ------ + pytest.xfail + If platform is Cygwin and subprocess reports a fork() failure. + """ + if capture_output: + stdout = stderr = subprocess.PIPE + try: + proc = subprocess.run( + command, env=env, + timeout=timeout, check=check, + stdout=stdout, stderr=stderr, + universal_newlines=universal_newlines + ) + except BlockingIOError: + if sys.platform == "cygwin": + # Might want to make this more specific + import pytest + pytest.xfail("Fork failure") + raise + return proc + + def subprocess_run_helper(func, *args, timeout, extra_env=None): """ Run a function in a sub-process. @@ -66,23 +123,16 @@ def subprocess_run_helper(func, *args, timeout, extra_env=None): """ target = func.__name__ module = func.__module__ - try: - proc = subprocess.run( - [sys.executable, - "-c", - f"from {module} import {target}; {target}()", - *args], - env={**os.environ, "SOURCE_DATE_EPOCH": "0", **(extra_env or {})}, - timeout=timeout, check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) - except BlockingIOError: - if sys.platform == "cygwin": - import pytest - pytest.xfail("Fork failure") - else: - raise + proc = subprocess_run_for_testing( + [sys.executable, + "-c", + f"from {module} import {target}; {target}()", + *args], + env={**os.environ, "SOURCE_DATE_EPOCH": "0", **(extra_env or {})}, + timeout=timeout, check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) return proc diff --git a/lib/matplotlib/tests/test_preprocess_data.py b/lib/matplotlib/tests/test_preprocess_data.py index 2ef3ac11b2af..693385ee5b40 100644 --- a/lib/matplotlib/tests/test_preprocess_data.py +++ b/lib/matplotlib/tests/test_preprocess_data.py @@ -1,5 +1,4 @@ import re -import subprocess import sys import numpy as np @@ -7,6 +6,7 @@ from matplotlib import _preprocess_data from matplotlib.axes import Axes +from matplotlib.testing import subprocess_run_for_testing from matplotlib.testing.decorators import check_figures_equal # Notes on testing the plotting functions itself @@ -259,12 +259,9 @@ def test_data_parameter_replacement(): "import matplotlib.pyplot as plt" ) cmd = [sys.executable, "-c", program] - try: - completed_proc = subprocess.run(cmd, text=True, capture_output=True) - except BlockingIOError: - if sys.platform == "cygwin": - pytest.xfail("Fork failure") - raise + completed_proc = subprocess_run_for_testing( + cmd, text=True, capture_output=True + ) assert 'data parameter docstring error' not in completed_proc.stderr diff --git a/lib/matplotlib/tests/test_pyplot.py b/lib/matplotlib/tests/test_pyplot.py index 78840acfb2df..292f81f1292e 100644 --- a/lib/matplotlib/tests/test_pyplot.py +++ b/lib/matplotlib/tests/test_pyplot.py @@ -1,13 +1,13 @@ import difflib import numpy as np -import subprocess import sys from pathlib import Path import pytest import matplotlib as mpl +from matplotlib.testing import subprocess_run_for_testing from matplotlib import pyplot as plt from matplotlib._api import MatplotlibDeprecationWarning @@ -20,13 +20,9 @@ def test_pyplot_up_to_date(tmpdir): plt_file = tmpdir.join('pyplot.py') plt_file.write_text(orig_contents, 'utf-8') - try: - subprocess.run([sys.executable, str(gen_script), str(plt_file)], - check=True) - except BlockingIOError: - if sys.platform == "cygwin": - pytest.xfail("Fork failure") - raise + subprocess_run_for_testing( + [sys.executable, str(gen_script), str(plt_file)], + check=True) new_contents = plt_file.read_text('utf-8') if orig_contents != new_contents: diff --git a/lib/matplotlib/tests/test_sphinxext.py b/lib/matplotlib/tests/test_sphinxext.py index 591644c2af5b..f95538799f09 100644 --- a/lib/matplotlib/tests/test_sphinxext.py +++ b/lib/matplotlib/tests/test_sphinxext.py @@ -4,9 +4,9 @@ import os from pathlib import Path import shutil -from subprocess import Popen, PIPE import sys +from matplotlib.testing import subprocess_run_for_testing import pytest @@ -19,15 +19,11 @@ def build_sphinx_html(source_dir, doctree_dir, html_dir, extra_args=None): extra_args = [] if extra_args is None else extra_args cmd = [sys.executable, '-msphinx', '-W', '-b', 'html', '-d', str(doctree_dir), str(source_dir), str(html_dir), *extra_args] - try: - proc = Popen(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True, - env={**os.environ, "MPLBACKEND": ""}) - except BlockingIOError: - if sys.platform == "cygwin": - pytest.xfail("Fork failure") - else: - raise - out, err = proc.communicate() + proc = subprocess_run_for_testing( + cmd, capture_output=True, universal_newlines=True, + env={**os.environ, "MPLBACKEND": ""}) + out = proc.stdout + err = proc.stderr assert proc.returncode == 0, \ f"sphinx build failed with stdout:\n{out}\nstderr:\n{err}\n" @@ -50,17 +46,12 @@ def test_tinypages(tmp_path): # On CI, gcov emits warnings (due to agg headers being included with the # same name in multiple extension modules -- but we don't care about their # coverage anyways); hide them using GCOV_ERROR_FILE. - try: - proc = Popen( - cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True, - env={**os.environ, "MPLBACKEND": "", "GCOV_ERROR_FILE": os.devnull} - ) - except BlockingIOError: - if sys.platform == "cygwin": - pytest.xfail("Fork failure") - else: - raise - out, err = proc.communicate() + proc = subprocess_run_for_testing( + cmd, capture_output=True, universal_newlines=True, + env={**os.environ, "MPLBACKEND": "", "GCOV_ERROR_FILE": os.devnull} + ) + out = proc.stdout + err = proc.stderr # Build the pages with warnings turned into errors build_sphinx_html(tmp_path, doctree_dir, html_dir) From 87f9ccccfbd18ff796841febf3e9a95f8d59a6ae Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Mon, 9 Jan 2023 18:28:46 -0500 Subject: [PATCH 09/12] FIX: Fix type and default for argument of new testing helper. --- lib/matplotlib/testing/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/testing/__init__.py b/lib/matplotlib/testing/__init__.py index 336a855eed99..f7b7fb2d380b 100644 --- a/lib/matplotlib/testing/__init__.py +++ b/lib/matplotlib/testing/__init__.py @@ -53,7 +53,7 @@ def setup(): def subprocess_run_for_testing( command: "list[str]", env: "dict[str, str]", - timeout=float, + timeout: float = None, stdout=None, stderr=None, check=False, From a08f7d6e1be8de86edee5c4acded4a998fd711ea Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Mon, 9 Jan 2023 22:38:20 -0500 Subject: [PATCH 10/12] TST,FIX: Provide default env and switch to more recent name for argument. universal_newlines is apparently the back-compatibility name; also text describes the biggest difference in the return types. --- lib/matplotlib/testing/__init__.py | 25 ++++++++++++++++--------- lib/matplotlib/tests/test_sphinxext.py | 4 ++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/matplotlib/testing/__init__.py b/lib/matplotlib/testing/__init__.py index f7b7fb2d380b..b9086d149a1e 100644 --- a/lib/matplotlib/testing/__init__.py +++ b/lib/matplotlib/testing/__init__.py @@ -52,12 +52,12 @@ def setup(): def subprocess_run_for_testing( command: "list[str]", - env: "dict[str, str]", + env: "dict[str, str]" = None, timeout: float = None, stdout=None, stderr=None, check=False, - universal_newlines=True, + text=True, capture_output=False ) -> "subprocess.Popen": """ @@ -72,7 +72,11 @@ def subprocess_run_for_testing( timeout : float stdout, stderr check : bool - universal_newlines : bool + text : bool + Also called `universal_newlines` in subprocess. I chose this + name since the main effect is returning bytes (False) vs. str + (True), though it also tries to normalize newlines across + platforms. capture_output : bool Set stdout and stderr to subprocess.PIPE @@ -96,7 +100,7 @@ def subprocess_run_for_testing( command, env=env, timeout=timeout, check=check, stdout=stdout, stderr=stderr, - universal_newlines=universal_newlines + text=text ) except BlockingIOError: if sys.platform == "cygwin": @@ -124,15 +128,18 @@ def subprocess_run_helper(func, *args, timeout, extra_env=None): target = func.__name__ module = func.__module__ proc = subprocess_run_for_testing( - [sys.executable, - "-c", - f"from {module} import {target}; {target}()", - *args], + [ + sys.executable, + "-c", + f"from {module} import {target}; {target}()", + *args + ], env={**os.environ, "SOURCE_DATE_EPOCH": "0", **(extra_env or {})}, timeout=timeout, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True) + text=True + ) return proc diff --git a/lib/matplotlib/tests/test_sphinxext.py b/lib/matplotlib/tests/test_sphinxext.py index f95538799f09..669723be1d55 100644 --- a/lib/matplotlib/tests/test_sphinxext.py +++ b/lib/matplotlib/tests/test_sphinxext.py @@ -20,7 +20,7 @@ def build_sphinx_html(source_dir, doctree_dir, html_dir, extra_args=None): cmd = [sys.executable, '-msphinx', '-W', '-b', 'html', '-d', str(doctree_dir), str(source_dir), str(html_dir), *extra_args] proc = subprocess_run_for_testing( - cmd, capture_output=True, universal_newlines=True, + cmd, capture_output=True, text=True, env={**os.environ, "MPLBACKEND": ""}) out = proc.stdout err = proc.stderr @@ -47,7 +47,7 @@ def test_tinypages(tmp_path): # same name in multiple extension modules -- but we don't care about their # coverage anyways); hide them using GCOV_ERROR_FILE. proc = subprocess_run_for_testing( - cmd, capture_output=True, universal_newlines=True, + cmd, capture_output=True, text=True, env={**os.environ, "MPLBACKEND": "", "GCOV_ERROR_FILE": os.devnull} ) out = proc.stdout From eb1d7076ad909f452878a573313a210b0c7f7b08 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Tue, 10 Jan 2023 11:54:16 -0500 Subject: [PATCH 11/12] DOC,FIX: Numpydoc section name is Parameters, not Arguments. --- lib/matplotlib/testing/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/testing/__init__.py b/lib/matplotlib/testing/__init__.py index b9086d149a1e..96879c3afbc3 100644 --- a/lib/matplotlib/testing/__init__.py +++ b/lib/matplotlib/testing/__init__.py @@ -65,8 +65,8 @@ def subprocess_run_for_testing( Thin wrapper around subprocess.run, intended for testing. - Arguments - --------- + Parameters + ---------- args : list of str env : dict[str, str] timeout : float From 614f922510b9683a03077cd7f30ad923be05cd31 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Wed, 11 Jan 2023 13:17:38 -0500 Subject: [PATCH 12/12] DOC,STY,FIX: Fix syntax for names keyword argument. Also add a few reference links and types. --- lib/matplotlib/testing/__init__.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/testing/__init__.py b/lib/matplotlib/testing/__init__.py index 96879c3afbc3..2df6814efa64 100644 --- a/lib/matplotlib/testing/__init__.py +++ b/lib/matplotlib/testing/__init__.py @@ -56,14 +56,16 @@ def subprocess_run_for_testing( timeout: float = None, stdout=None, stderr=None, - check=False, - text=True, - capture_output=False + check: bool = False, + text: bool = True, + capture_output: bool = False ) -> "subprocess.Popen": """ Create and run a subprocess. - Thin wrapper around subprocess.run, intended for testing. + Thin wrapper around `subprocess.run`, intended for testing. Will + mark fork() failures on Cygwin as expected failures: not a + success, but not indicating a problem with the code either. Parameters ---------- @@ -73,9 +75,9 @@ def subprocess_run_for_testing( stdout, stderr check : bool text : bool - Also called `universal_newlines` in subprocess. I chose this - name since the main effect is returning bytes (False) vs. str - (True), though it also tries to normalize newlines across + Also called ``universal_newlines`` in subprocess. I chose this + name since the main effect is returning bytes (`False`) vs. str + (`True`), though it also tries to normalize newlines across platforms. capture_output : bool Set stdout and stderr to subprocess.PIPE