--- 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: permissions: {} 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-22.04 python-version: '3.11' extra-requirements: '-c ci/minver-requirements.txt' delete-font-cache: true # https://github.com/matplotlib/matplotlib/issues/29844 pygobject-ver: '<3.52.0' - os: ubuntu-22.04 python-version: '3.11' CFLAGS: "-fno-lto" # Ensure that disabling LTO works. extra-requirements: '--group test-extra' # https://github.com/matplotlib/matplotlib/issues/29844 pygobject-ver: '<3.52.0' - name-suffix: "(Extra TeX packages)" os: ubuntu-22.04 python-version: '3.13' extra-packages: 'texlive-fonts-extra texlive-lang-cyrillic' # https://github.com/matplotlib/matplotlib/issues/29844 pygobject-ver: '<3.52.0' - name-suffix: "Free-threaded" os: ubuntu-22.04 python-version: '3.13t' # https://github.com/matplotlib/matplotlib/issues/29844 pygobject-ver: '<3.52.0' - os: ubuntu-24.04 python-version: '3.12' - os: ubuntu-24.04 python-version: '3.14' - os: ubuntu-24.04-arm python-version: '3.12' - os: macos-14 # This runner is on M1 (arm64) chips. python-version: '3.11' # https://github.com/matplotlib/matplotlib/issues/29732 pygobject-ver: '<3.52.0' - os: macos-14 # This runner is on M1 (arm64) chips. python-version: '3.12' # https://github.com/matplotlib/matplotlib/issues/29732 pygobject-ver: '<3.52.0' - os: macos-15 # This runner is on M1 (arm64) chips. python-version: '3.13' # https://github.com/matplotlib/matplotlib/issues/29732 pygobject-ver: '<3.52.0' - os: macos-15 # This runner is on M1 (arm64) chips. python-version: '3.14' # https://github.com/matplotlib/matplotlib/issues/29732 pygobject-ver: '<3.52.0' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 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 \ fonts-freefont-otf \ fonts-noto-cjk \ fonts-wqy-zenhei \ gdb \ gir1.2-gtk-3.0 \ gir1.2-gtk-4.0 \ graphviz \ inkscape \ language-pack-de \ lcov \ libcairo2 \ libcairo2-dev \ libffi-dev \ libgeos-dev \ libnotify4 \ 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 \ ${{ matrix.extra-packages }} if [[ "${{ matrix.name-suffix }}" != '(Minimum Versions)' ]]; then sudo apt-get install -yy --no-install-recommends ffmpeg poppler-utils fi if [[ "${{ matrix.os }}" = ubuntu-22.04 ]]; then sudo apt-get install -yy --no-install-recommends \ libgirepository1.0-dev else # ubuntu-24.04 sudo apt-get install -yy --no-install-recommends \ libgirepository-2.0-dev 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 pkg-config@0.29.2 brew install ccache ffmpeg ghostscript gobject-introspection gtk4 imagemagick ninja brew install --cask font-noto-sans-cjk font-noto-sans-cjk-sc inkscape ;; esac - name: Cache pip uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 if: startsWith(runner.os, 'Linux') with: path: ~/.cache/pip key: | ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('pyproject.toml', 'ci/minver-requirements.txt') }} restore-keys: | ${{ matrix.os }}-py${{ matrix.python-version }}-pip- - name: Cache pip uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 if: startsWith(runner.os, 'macOS') with: path: ~/Library/Caches/pip key: ${{ matrix.os }}-py${{ matrix.python-version }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: | ${{ matrix.os }}-py${{ matrix.python-version }}-pip- - name: Cache ccache uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | ~/.cache/matplotlib !~/.cache/matplotlib/tex.cache !~/.cache/matplotlib/test_cache key: 6-${{ matrix.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}-${{ github.sha }} restore-keys: | 6-${{ matrix.os }}-py${{ matrix.python-version }}-mpl-${{ github.ref }}- 6-${{ matrix.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 --prefer-binary \ --group build --group test ${{ 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${{ matrix.pygobject-ver }}' && ( 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' ) # PyQt5 does not have any wheels for ARM on Linux. if [[ "${{ matrix.os }}" != 'ubuntu-24.04-arm' ]]; then python -mpip install --upgrade --only-binary :all: pyqt5 && python -c 'import PyQt5.QtCore' && echo 'PyQt5 is available' || echo 'PyQt5 is not available' fi # 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.11' ]]; then python -mpip install --upgrade pyside2 && python -c 'import PySide2.QtCore' && echo 'PySide2 is available' || echo 'PySide2 is not available' fi python -mpip install --upgrade --only-binary :all: pyqt6 && python -c 'import PyQt6.QtCore' && echo 'PyQt6 is available' || echo 'PyQt6 is not available' python -mpip install --upgrade --only-binary :all: pyside6 && python -c 'import PySide6.QtCore' && echo 'PySide6 is available' || echo 'PySide6 is not available' python -mpip install --upgrade --only-binary :all: \ -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: | find ./result_images -name "*-expected*.png" | while read file; do if [[ $file == *-expected_???.png ]]; then extension=${file: -7:3} base=${file%*-expected_$extension.png}_$extension else extension="png" base=${file%-expected.png} fi if [[ ! -e ${base}-failed-diff.png ]]; then indent="" list=($file $base.png) if [[ $extension != "png" ]]; then list+=(${base%_$extension}-expected.$extension ${base%_$extension}.$extension) fi for to_remove in "${list[@]}"; do if [[ -e $to_remove ]]; then rm $to_remove echo "${indent}Removed $to_remove" fi indent+=" " done fi done 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_IGNORE_ERRORS=',' # do not ignore any lcov errors by default if [[ "${{ matrix.os }}" = ubuntu-24.04 || "${{ matrix.os }}" = ubuntu-24.04-arm ]]; then # filter mismatch errors detected by lcov 2.x LCOV_IGNORE_ERRORS='mismatch' fi lcov --rc lcov_branch_coverage=1 --ignore-errors $LCOV_IGNORE_ERRORS \ --capture --directory . --exclude $PWD/subprojects --exclude $PWD/build \ --output-file coverage.info lcov --rc lcov_branch_coverage=1 --ignore-errors $LCOV_IGNORE_ERRORS \ --output-file coverage.info --extract coverage.info $PWD/src/'*' lcov --rc lcov_branch_coverage=1 --ignore-errors $LCOV_IGNORE_ERRORS \ --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@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 with: name: "${{ matrix.python-version }} ${{ matrix.os }} ${{ matrix.name-suffix }}" token: ${{ secrets.CODECOV_TOKEN }} - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 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@3188c6ce06249206709d3b1f274d0d4c5a521601 # v3.4.5 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 }}