Backport PR #29191: ci: Simplify 3.13t test setup #1594
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
name: Tests | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event.ref }} | |
cancel-in-progress: true | |
on: | |
push: | |
branches-ignore: | |
- auto-backport-of-pr-[0-9]+ | |
- v[0-9]+.[0-9]+.[0-9x]+-doc | |
- dependabot/** | |
pull_request: | |
branches-ignore: | |
- v[0-9]+.[0-9]+.[0-9x]+-doc | |
paths-ignore: | |
# Skip running tests if changes are only in documentation directories | |
- 'doc/**' | |
- 'galleries/**' | |
schedule: | |
# 5:47 UTC on Saturdays | |
- cron: "47 5 * * 6" | |
workflow_dispatch: | |
env: | |
NO_AT_BRIDGE: 1 # Necessary for GTK3 interactive test. | |
OPENBLAS_NUM_THREADS: 1 | |
PYTHONFAULTHANDLER: 1 | |
jobs: | |
test: | |
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]') && | |
!contains(github.event.head_commit.message, '[ci doc]') | |
) | |
permissions: | |
contents: read | |
name: "Python ${{ matrix.python-version }} on ${{ matrix.os }} ${{ matrix.name-suffix }}" | |
runs-on: ${{ matrix.os }} | |
strategy: | |
fail-fast: false | |
matrix: | |
include: | |
- name-suffix: "(Minimum Versions)" | |
os: ubuntu-20.04 | |
python-version: '3.10' | |
extra-requirements: '-c requirements/testing/minver.txt' | |
delete-font-cache: true | |
# Oldest versions with Py3.10 wheels. | |
pyqt5-ver: '==5.15.5 sip==6.3.0' | |
pyqt6-ver: '==6.2.0 PyQt6-Qt6==6.2.0' | |
pyside2-ver: '==5.15.2.1' | |
pyside6-ver: '==6.2.0' | |
- os: ubuntu-20.04 | |
python-version: '3.10' | |
# One CI run tests ipython/matplotlib-inline before backend mapping moved to mpl | |
extra-requirements: | |
-r requirements/testing/extra.txt | |
"ipython==7.29.0" | |
"ipykernel==5.5.6" | |
"matplotlib-inline<0.1.7" | |
CFLAGS: "-fno-lto" # Ensure that disabling LTO works. | |
# https://github.com/matplotlib/matplotlib/pull/26052#issuecomment-1574595954 | |
# https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html | |
pyqt6-ver: '!=6.5.1,!=6.6.0,!=6.7.1' | |
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 | |
pyside6-ver: '!=6.5.1' | |
- os: ubuntu-22.04 | |
python-version: '3.11' | |
# https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html | |
pyqt6-ver: '!=6.6.0' | |
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 | |
pyside6-ver: '!=6.5.1' | |
extra-requirements: '-r requirements/testing/extra.txt' | |
- os: ubuntu-22.04 | |
python-version: '3.12' | |
# https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html | |
pyqt6-ver: '!=6.6.0' | |
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 | |
pyside6-ver: '!=6.5.1' | |
- os: ubuntu-22.04 | |
python-version: '3.13' | |
# https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html | |
pyqt6-ver: '!=6.6.0' | |
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 | |
pyside6-ver: '!=6.5.1' | |
- name-suffix: "Free-threaded" | |
os: ubuntu-22.04 | |
python-version: '3.13t' | |
# https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html | |
pyqt6-ver: '!=6.6.0' | |
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 | |
pyside6-ver: '!=6.5.1' | |
- os: macos-13 # This runner is on Intel chips. | |
python-version: '3.10' | |
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 | |
pyside6-ver: '!=6.5.1' | |
- os: macos-14 # This runner is on M1 (arm64) chips. | |
python-version: '3.12' | |
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 | |
pyside6-ver: '!=6.5.1' | |
- os: macos-14 # This runner is on M1 (arm64) chips. | |
python-version: '3.13' | |
# https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 | |
pyside6-ver: '!=6.5.1' | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- name: Set up Python ${{ matrix.python-version }} | |
uses: actions/setup-python@v5 | |
if: matrix.python-version != '3.13t' | |
with: | |
python-version: ${{ matrix.python-version }} | |
allow-prereleases: true | |
- name: Set up Python ${{ matrix.python-version }} | |
uses: Quansight-Labs/setup-python@b9ab292c751a42bcd2bb465b7fa202ea2c3f5796 # v5.3.1 | |
if: matrix.python-version == '3.13t' | |
with: | |
python-version: ${{ matrix.python-version }} | |
allow-prereleases: true | |
- name: Install OS dependencies | |
run: | | |
case "${{ runner.os }}" in | |
Linux) | |
echo 'Acquire::Retries "3";' | sudo tee /etc/apt/apt.conf.d/80-retries | |
sudo apt-get update -yy | |
sudo apt-get install -yy --no-install-recommends \ | |
ccache \ | |
cm-super \ | |
dvipng \ | |
ffmpeg \ | |
fonts-freefont-otf \ | |
fonts-noto-cjk \ | |
fonts-wqy-zenhei \ | |
gdb \ | |
gir1.2-gtk-3.0 \ | |
graphviz \ | |
inkscape \ | |
language-pack-de \ | |
lcov \ | |
libcairo2 \ | |
libcairo2-dev \ | |
libffi-dev \ | |
libgeos-dev \ | |
libgirepository1.0-dev \ | |
libsdl2-2.0-0 \ | |
libxkbcommon-x11-0 \ | |
libxcb-cursor0 \ | |
libxcb-icccm4 \ | |
libxcb-image0 \ | |
libxcb-keysyms1 \ | |
libxcb-randr0 \ | |
libxcb-render-util0 \ | |
libxcb-xinerama0 \ | |
lmodern \ | |
ninja-build \ | |
pkg-config \ | |
qtbase5-dev \ | |
texlive-fonts-recommended \ | |
texlive-latex-base \ | |
texlive-latex-extra \ | |
texlive-latex-recommended \ | |
texlive-luatex \ | |
texlive-pictures \ | |
texlive-xetex | |
if [[ "${{ matrix.os }}" = ubuntu-20.04 ]]; then | |
sudo apt-get install -yy --no-install-recommends libopengl0 | |
else # ubuntu-22.04 | |
sudo apt-get install -yy --no-install-recommends \ | |
gir1.2-gtk-4.0 libnotify4 | |
fi | |
;; | |
macOS) | |
brew update | |
# Periodically, Homebrew updates Python and fails to overwrite the | |
# existing not-managed-by-Homebrew copy without explicitly being told | |
# to do so. GitHub/Azure continues to avoid fixing their runner images: | |
# https://github.com/actions/runner-images/issues/9966 | |
# so force an overwrite even if there are no Python updates. | |
# We don't even care about Homebrew's Python because we use the one | |
# from actions/setup-python. | |
for python_package in $(brew list | grep python@); do | |
brew unlink ${python_package} | |
brew link --overwrite ${python_package} | |
done | |
# Workaround for https://github.com/actions/runner-images/issues/10984 | |
brew uninstall --ignore-dependencies --force [email protected] | |
brew install ccache ghostscript gobject-introspection gtk4 ninja | |
brew install --cask font-noto-sans-cjk inkscape | |
;; | |
esac | |
- name: Cache pip | |
uses: actions/cache@v4 | |
if: startsWith(runner.os, 'Linux') | |
with: | |
path: ~/.cache/pip | |
key: ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('requirements/*/*.txt') }} | |
restore-keys: | | |
${{ matrix.os }}-py${{ matrix.python-version }}-pip- | |
- name: Cache pip | |
uses: actions/cache@v4 | |
if: startsWith(runner.os, 'macOS') | |
with: | |
path: ~/Library/Caches/pip | |
key: ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('requirements/*/*.txt') }} | |
restore-keys: | | |
${{ matrix.os }}-py${{ matrix.python-version }}-pip- | |
- name: Cache ccache | |
uses: actions/cache@v4 | |
with: | |
path: | | |
~/.ccache | |
key: ${{ matrix.os }}-py${{ matrix.python-version }}-ccache-${{ hashFiles('src/*') }} | |
restore-keys: | | |
${{ matrix.os }}-py${{ matrix.python-version }}-ccache- | |
- name: Cache Matplotlib | |
uses: actions/cache@v4 | |
with: | |
path: | | |
~/.cache/matplotlib | |
!~/.cache/matplotlib/tex.cache | |
!~/.cache/matplotlib/test_cache | |
key: 4-${{ runner.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}-${{ github.sha }} | |
restore-keys: | | |
4-${{ runner.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}- | |
4-${{ runner.os }}-py${{ matrix.python-version }}-mpl- | |
- name: Install Python dependencies | |
run: | | |
# Upgrade pip and setuptools and wheel to get as clean an install as | |
# possible. | |
python -m pip install --upgrade pip setuptools wheel | |
# Install pre-release versions during our weekly upcoming dependency tests. | |
if [[ "${{ github.event_name }}" == 'schedule' | |
&& "${{ matrix.name-suffix }}" != '(Minimum Versions)' ]]; then | |
PRE="--pre" | |
fi | |
# Install dependencies from PyPI. | |
# Preinstall build requirements to enable no-build-isolation builds. | |
python -m pip install --upgrade $PRE \ | |
'contourpy>=1.0.1' cycler fonttools kiwisolver importlib_resources \ | |
packaging pillow 'pyparsing!=3.1.0' python-dateutil setuptools-scm \ | |
'meson-python>=0.13.1' 'pybind11>=2.13.2' \ | |
-r requirements/testing/all.txt \ | |
${{ matrix.extra-requirements }} | |
# Install optional dependencies from PyPI. | |
# Sphinx is needed to run sphinxext tests | |
python -m pip install --upgrade sphinx!=6.1.2 | |
if [[ "${{ matrix.python-version }}" != '3.13t' ]]; then | |
# GUI toolkits are pip-installable only for some versions of Python | |
# so don't fail if we can't install them. Make it easier to check | |
# whether the install was successful by trying to import the toolkit | |
# (sometimes, the install appears to be successful but shared | |
# libraries cannot be loaded at runtime, so an actual import is a | |
# better check). | |
python -m pip install --upgrade pycairo 'cairocffi>=0.8' PyGObject && | |
( | |
python -c 'import gi; gi.require_version("Gtk", "4.0"); from gi.repository import Gtk' && | |
echo 'PyGObject 4 is available' || echo 'PyGObject 4 is not available' | |
) && ( | |
python -c 'import gi; gi.require_version("Gtk", "3.0"); from gi.repository import Gtk' && | |
echo 'PyGObject 3 is available' || echo 'PyGObject 3 is not available' | |
) | |
python -mpip install --upgrade pyqt5${{ matrix.pyqt5-ver }} && | |
python -c 'import PyQt5.QtCore' && | |
echo 'PyQt5 is available' || | |
echo 'PyQt5 is not available' | |
# Even though PySide2 wheels can be installed on Python 3.12+, they are broken and since PySide2 is | |
# deprecated, they are unlikely to be fixed. For the same deprecation reason, there are no wheels | |
# on M1 macOS, so don't bother there either. | |
if [[ "${{ matrix.os }}" != 'macos-14' | |
&& "${{ matrix.python-version }}" != '3.12' && "${{ matrix.python-version }}" != '3.13' ]]; then | |
python -mpip install --upgrade pyside2${{ matrix.pyside2-ver }} && | |
python -c 'import PySide2.QtCore' && | |
echo 'PySide2 is available' || | |
echo 'PySide2 is not available' | |
fi | |
python -mpip install --upgrade pyqt6${{ matrix.pyqt6-ver }} && | |
python -c 'import PyQt6.QtCore' && | |
echo 'PyQt6 is available' || | |
echo 'PyQt6 is not available' | |
python -mpip install --upgrade pyside6${{ matrix.pyside6-ver }} && | |
python -c 'import PySide6.QtCore' && | |
echo 'PySide6 is available' || | |
echo 'PySide6 is not available' | |
python -mpip install --upgrade \ | |
-f "https://extras.wxpython.org/wxPython4/extras/linux/gtk3/${{ matrix.os }}" \ | |
wxPython && | |
python -c 'import wx' && | |
echo 'wxPython is available' || | |
echo 'wxPython is not available' | |
fi # Skip backends on Python 3.13t. | |
- name: Install the nightly dependencies | |
# Only install the nightly dependencies during the scheduled event | |
if: github.event_name == 'schedule' && matrix.name-suffix != '(Minimum Versions)' | |
run: | | |
python -m pip install pytz tzdata # Must be installed for Pandas. | |
python -m pip install \ | |
--index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple \ | |
--upgrade --only-binary=:all: numpy pandas | |
- name: Install Matplotlib | |
run: | | |
ccache -s | |
git describe | |
# Set flag in a delayed manner to avoid issues with installing other | |
# packages | |
if [[ "${{ runner.os }}" == 'macOS' ]]; then | |
export CPPFLAGS='-fprofile-instr-generate=default.%m.profraw' | |
export CPPFLAGS="$CPPFLAGS -fcoverage-mapping" | |
else | |
export CPPFLAGS='--coverage -fprofile-abs-path' | |
fi | |
python -m pip install --no-deps --no-build-isolation --verbose \ | |
--config-settings=setup-args="-DrcParams-backend=Agg" \ | |
--editable .[dev] | |
if [[ "${{ runner.os }}" != 'macOS' ]]; then | |
unset CPPFLAGS | |
fi | |
- name: Clear font cache | |
run: | | |
rm -rf ~/.cache/matplotlib | |
if: matrix.delete-font-cache | |
- name: Run pytest | |
run: | | |
if [[ "${{ matrix.python-version }}" == '3.13t' ]]; then | |
export PYTHON_GIL=0 | |
fi | |
pytest -rfEsXR -n auto \ | |
--maxfail=50 --timeout=300 --durations=25 \ | |
--cov-report=xml --cov=lib --log-level=DEBUG --color=yes | |
- name: Cleanup non-failed image files | |
if: failure() | |
run: | | |
function remove_files() { | |
local extension=$1 | |
find ./result_images -type f -name "*-expected*.$extension" | while read file; do | |
if [[ $file == *"-expected_pdf"* ]]; then | |
base=${file%-expected_pdf.$extension}_pdf | |
elif [[ $file == *"-expected_eps"* ]]; then | |
base=${file%-expected_eps.$extension}_eps | |
elif [[ $file == *"-expected_svg"* ]]; then | |
base=${file%-expected_svg.$extension}_svg | |
else | |
base=${file%-expected.$extension} | |
fi | |
if [[ ! -e "${base}-failed-diff.$extension" ]]; then | |
if [[ -e "$file" ]]; then | |
rm "$file" | |
echo "Removed $file" | |
fi | |
if [[ -e "${base}.$extension" ]]; then | |
rm "${base}.$extension" | |
echo " Removed ${base}.$extension" | |
fi | |
fi | |
done | |
} | |
remove_files "png"; remove_files "svg"; remove_files "pdf"; remove_files "eps"; | |
if [ "$(find ./result_images -mindepth 1 -type d)" ]; then | |
find ./result_images/* -type d -empty -delete | |
fi | |
- name: Filter C coverage | |
if: ${{ !cancelled() && github.event_name != 'schedule' }} | |
run: | | |
if [[ "${{ runner.os }}" != 'macOS' ]]; then | |
lcov --rc lcov_branch_coverage=1 --capture --directory . \ | |
--output-file coverage.info | |
lcov --rc lcov_branch_coverage=1 --output-file coverage.info \ | |
--extract coverage.info $PWD/src/'*' $PWD/lib/'*' | |
lcov --rc lcov_branch_coverage=1 --list coverage.info | |
find . -name '*.gc*' -delete | |
else | |
xcrun llvm-profdata merge -sparse default.*.profraw \ | |
-o default.profdata | |
xcrun llvm-cov export -format="lcov" build/*/src/*.so \ | |
-instr-profile default.profdata > info.lcov | |
fi | |
- name: Upload code coverage | |
if: ${{ !cancelled() && github.event_name != 'schedule' }} | |
uses: codecov/codecov-action@v5 | |
with: | |
name: "${{ matrix.python-version }} ${{ matrix.os }} ${{ matrix.name-suffix }}" | |
token: ${{ secrets.CODECOV_TOKEN }} | |
- uses: actions/upload-artifact@v4 | |
if: failure() | |
with: | |
name: "${{ matrix.python-version }} ${{ matrix.os }} ${{ matrix.name-suffix }} result images" | |
path: ./result_images | |
# Separate dependent job to only upload one issue from the matrix of jobs | |
create-issue: | |
if: ${{ failure() && github.event_name == 'schedule' }} | |
needs: [test] | |
permissions: | |
issues: write | |
runs-on: ubuntu-latest | |
name: "Create issue on failure" | |
steps: | |
- name: Create issue on failure | |
uses: imjohnbo/issue-bot@v3 | |
with: | |
title: "[TST] Upcoming dependency test failures" | |
body: | | |
The weekly build with nightly wheels from numpy and pandas | |
has failed. Check the logs for any updates that need to be | |
made in matplotlib. | |
https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} | |
pinned: false | |
close-previous: false | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |