Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
204 commits
Select commit Hold shift + click to select a range
658492a
Add support for linewidth in legend frame parsing
K-Mirembe-Mercy Mar 21, 2026
79eafbd
add s and unittest (#400)
cvanelteren Nov 2, 2025
3ee22b4
redo with new ticker (#411)
cvanelteren Nov 16, 2025
7b41afb
Hotfix: bar labels cause limit to reset for unaffected axis. (#413)
cvanelteren Nov 18, 2025
8c54dbe
fix: change default `reduce_C_function` to `np.sum` for `hexbin` (#408)
beckermr Nov 19, 2025
4bd430d
Add external context mode for axes (#406)
cvanelteren Nov 20, 2025
97ee1a9
Bump actions/checkout from 5 to 6 in the github-actions group (#415)
dependabot[bot] Dec 1, 2025
a0bf1d8
[pre-commit.ci] pre-commit autoupdate (#416)
pre-commit-ci[bot] Dec 1, 2025
95aecb0
Add placement of legend to axes within a figure (#418)
cvanelteren Dec 7, 2025
9b239d7
There's a typo about zerotrim in doc. (#420)
gepcel Dec 8, 2025
d007e6a
Fix references in documentation for clarity (#421)
gepcel Dec 10, 2025
2bca02f
fix links to apply_norm (#423)
cvanelteren Dec 11, 2025
0fb8307
[Feature] add lon lat labelrotation (#426)
cvanelteren Dec 12, 2025
4a4ab66
fix boundary check for ticks
cvanelteren Dec 13, 2025
a35a2aa
Revert "fix boundary check for ticks"
cvanelteren Dec 13, 2025
ff1a002
Fix: Boundary labels now visible when setting lonlim/latlim (#429)
cvanelteren Dec 15, 2025
9ffa0e4
Add Copernicus Publications figure standard widths. (#433)
Holmgren825 Dec 16, 2025
517975f
Fix unequal slicing for Gridspec (#435)
cvanelteren Dec 17, 2025
9d53f37
Fix GeoAxes panel alignment with aspect-constrained projections (#432)
cvanelteren Dec 29, 2025
048e14c
Bump the github-actions group with 2 updates (#444)
dependabot[bot] Jan 1, 2026
b184271
Fix dualx alignment on log axes (#443)
cvanelteren Jan 4, 2026
507f829
Subset label sharing and implicit slice labels for axis groups (#440)
cvanelteren Jan 4, 2026
bffd369
Preserve log formatter when setting log scales (#437)
cvanelteren Jan 4, 2026
e7b1aff
added inference of labels for spanning legends (#447)
cvanelteren Jan 4, 2026
940ea3c
[pre-commit.ci] pre-commit autoupdate (#449)
pre-commit-ci[bot] Jan 5, 2026
bbadfa1
Avoid title overlap with abc labels (#442)
cvanelteren Jan 7, 2026
4e47506
Guard abc/title width calc when text detached (#452)
cvanelteren Jan 9, 2026
759bb59
Refactor GeoAxes gridliner handling and format flow; remove cartopy m…
cvanelteren Jan 11, 2026
b027648
Swap visual tests with hash comparison (#427)
cvanelteren Jan 13, 2026
ce8396e
Remove xdist from image compare (#462)
cvanelteren Jan 13, 2026
980fd58
Fix 'What's New' page formatting and generation (#464)
cvanelteren Jan 13, 2026
9ac61ba
Fix SubplotGrid indexing and enhance legend placement with 'ref' argu…
cvanelteren Jan 14, 2026
26f09ce
Add ridgeline plot feature (#451)
cvanelteren Jan 14, 2026
431c285
Fix/axes alpha doc formatting (#467)
cvanelteren Jan 15, 2026
367db2f
Run image compare single thread
cvanelteren Jan 15, 2026
3f882b8
Fix legend span inference with panels (#469)
cvanelteren Jan 15, 2026
a5082a2
Hopefully this fixes the api indexing (#471)
cvanelteren Jan 15, 2026
50d464d
Introduce internal dataclass to restructure the craziest loop that ex…
cvanelteren Jan 15, 2026
a62a165
Fix share label group overrides (#473)
cvanelteren Jan 15, 2026
8eb1e35
Lazy-load top-level imports and defer setup (#439)
cvanelteren Jan 15, 2026
cec9ca1
add make.bat (#475)
gepcel Jan 16, 2026
140971a
Add gallery (#465)
cvanelteren Jan 16, 2026
6234d48
Remove local test.py from repo
cvanelteren Jan 16, 2026
33dfae3
Ignore ipynb for based pyright
cvanelteren Jan 16, 2026
da9170c
Add two files and one folder from doc building to git ignoring (#482)
gepcel Jan 19, 2026
c12379e
Refactor format in sensible blocks (#484)
cvanelteren Jan 20, 2026
105ed1a
Fix: Correct label size calculation in _update_outer_abc_loc (#485)
cvanelteren Jan 20, 2026
094bd18
Fix: Isolate inset and panel format from figure format (#486)
cvanelteren Jan 20, 2026
eaa1864
Fix: Move locator back to top level (#490)
cvanelteren Jan 21, 2026
3563daf
Fix baseline cache invalidation (#492)
cvanelteren Jan 22, 2026
4e33268
Fix/baseline cache refresh 2 (#493)
cvanelteren Jan 22, 2026
6f3b055
Feature: Sankey diagrams (#478)
cvanelteren Jan 22, 2026
b4ed787
Add UltraLayout for non-orthogonal subplot positioning
cvanelteren Jan 16, 2026
ef974d8
Restore base docstrings to main
cvanelteren Jan 16, 2026
36117b6
Handle list input in _parse_level_lim
cvanelteren Jan 16, 2026
b35ecd4
Update GridSpec indexing and label-sharing behavior
cvanelteren Jan 17, 2026
c4c2f5a
Improve UltraLayout layout handling
cvanelteren Jan 17, 2026
9a8834c
Round axes size to pixel grid for ref sizing
cvanelteren Jan 17, 2026
3f4d5f8
Honor ref sizing in axes size calculations
cvanelteren Jan 17, 2026
b8c61c0
Adding more tests
cvanelteren Jan 18, 2026
24837cb
Fix test
cvanelteren Jan 18, 2026
de2154b
Add constraint-based inset colorbar reflow
cvanelteren Jan 18, 2026
966448d
Fix inset colorbar frame reflow sizing
cvanelteren Jan 18, 2026
ad7fdb9
Add test for inset colorbar frame reflow
cvanelteren Jan 18, 2026
aa387c0
Extend inset colorbar frame reflow test
cvanelteren Jan 18, 2026
c78e5fc
Add UltraLayout: Advanced constraint-based positioning for non-orthog…
cvanelteren Jan 22, 2026
0457f1c
Feature: Add container to encapsulate external axes (#422)
cvanelteren Jan 22, 2026
960e2e9
Fix font lazy load (#498)
cvanelteren Jan 23, 2026
2af0a9f
Fix test_get_size_inches_rounding_and_reference_override (#499)
cvanelteren Jan 23, 2026
4f4cdd6
Remove -x from mpl pytest runs (#500)
cvanelteren Jan 23, 2026
1423779
Ci test selection (#502)
cvanelteren Jan 23, 2026
692e4d7
Update GitHub workflows (#505)
cvanelteren Jan 25, 2026
22fd792
CI: log test status before exit
cvanelteren Jan 26, 2026
fbf976e
CI: stop canceling in-progress matrix jobs
cvanelteren Jan 26, 2026
227eb42
CI: make baseline comparison non-blocking (#508)
cvanelteren Jan 26, 2026
25210d3
CI: remove redundant pytest run (#509)
cvanelteren Jan 26, 2026
affbb82
Fix log formatter tickrange crash (#507)
cvanelteren Jan 27, 2026
6affb09
Add nox setup
cvanelteren Jan 27, 2026
cd05bf1
add .nox to gitignore
cvanelteren Jan 27, 2026
25ce182
add return fig to test (#510)
cvanelteren Jan 27, 2026
a6857a3
CI: set default mpl image tolerance (#511)
cvanelteren Jan 27, 2026
daed3c1
Fix pytest-mpl default tolerance hook (#512)
cvanelteren Jan 27, 2026
cfff4d0
Delegate external axes methods with guardrails (#514)
cvanelteren Jan 27, 2026
946fa5f
Fix ternary tri* delegation and add example (#513)
cvanelteren Jan 27, 2026
0788931
Separate docs dependencies from user dependencies (#515)
cvanelteren Jan 28, 2026
47ccbb3
Skip micromamba post cleanup to avoid GHA deinit error (#521)
cvanelteren Jan 28, 2026
b307a39
Fix/gha micromamba (#522)
cvanelteren Jan 28, 2026
34dc7a4
Use checked-in condarc for micromamba (#523)
cvanelteren Jan 28, 2026
eba2142
Keep gridline labels when updating ticklen (#520)
cvanelteren Jan 29, 2026
d07d06a
Fix environment.yml pip block (#525)
cvanelteren Jan 29, 2026
b64a9c4
Preserve `set_extent` across ticklen format (#518)
cvanelteren Jan 29, 2026
a90ffb1
Feature: integrate pycirclize into UltraPlot (#495)
cvanelteren Jan 29, 2026
3d2f8f2
correct removal of lines (#526)
cvanelteren Jan 30, 2026
98be528
Fix geo label side order and prune corner labels (#527)
cvanelteren Jan 30, 2026
96f6580
Fix idle draw animation (#504)
cvanelteren Jan 30, 2026
0dc4b3d
Stabilize pytest-mpl styling and colormap lookups (#528)
cvanelteren Feb 1, 2026
c9f9ab7
Limit mpl baseline generation to selected tests (#533)
cvanelteren Feb 1, 2026
e072859
Fix/polar tight layout (#534)
cvanelteren Feb 1, 2026
96842a5
Use PR-selected nodeids for mpl baselines and skip empty selections (…
cvanelteren Feb 1, 2026
a252eb9
Fix UltraLayout spans for GridSpec slices (#532)
cvanelteren Feb 1, 2026
733cef2
Bump actions/cache from 4 to 5 in the github-actions group (#536)
dependabot[bot] Feb 1, 2026
3ebdbac
Add threaded rc cycle consistency test
cvanelteren Jan 31, 2026
fc1159a
Add debug logging for selected mpl tests
cvanelteren Feb 1, 2026
d4d5a3b
Fix/revert debug and clean (#537)
cvanelteren Feb 1, 2026
4c00ba7
Revert "Fix/revert debug and clean (#537)"
cvanelteren Feb 1, 2026
11b2e9a
Reapply "Fix/revert debug and clean (#537)"
cvanelteren Feb 1, 2026
79978a4
Remove merge markers in test_config
cvanelteren Feb 1, 2026
c48ef53
Add debug output for selected mpl tests (#538)
cvanelteren Feb 1, 2026
265e5a7
Debug/select tests logging (#539)
cvanelteren Feb 1, 2026
b99b9cb
Align test-map cache key between generator and selector (#540)
cvanelteren Feb 1, 2026
65472f7
Fix/test map coverage (#541)
cvanelteren Feb 1, 2026
e03dafe
Generate test map on PR cache miss (#542)
cvanelteren Feb 1, 2026
45828a2
Install pytest tools for PR test-map build (#543)
cvanelteren Feb 1, 2026
0ad28f5
Use conda env for PR test-map build (#544)
cvanelteren Feb 1, 2026
18ce2a3
Normalize pytest-cov contexts and parallelize map build
cvanelteren Feb 1, 2026
c4378bf
Normalize unparameterized nodeids to file paths
cvanelteren Feb 1, 2026
8b62f30
Parallelize mpl baseline and comparison runs
cvanelteren Feb 1, 2026
4eac803
CI: stabilize mpl selection + map generation, parallelize image tests…
cvanelteren Feb 1, 2026
7b99040
ci: reduce memory usage to prevent job cancellations
cvanelteren Feb 1, 2026
f21ff9a
ci: add memory monitoring to diagnose CI issues
cvanelteren Feb 1, 2026
f758d3f
ci: ignore empty results directory in artifact upload
cvanelteren Feb 1, 2026
d2c7bf5
ci: fix git checkout after baseline generation and improve error hand…
cvanelteren Feb 1, 2026
45a37ac
fix: add dict support to _to_string for YAML serialization
cvanelteren Feb 1, 2026
f87c896
[pre-commit.ci] pre-commit autoupdate (#546)
pre-commit-ci[bot] Feb 2, 2026
25a9dbb
Add rc default for text borderstyle (#549)
cvanelteren Feb 4, 2026
3a77731
Surpress warnings on docs (#551)
cvanelteren Feb 5, 2026
3ac2a69
Fix UltraLayout gaps for spanning axes (#555)
cvanelteren Feb 6, 2026
c3505a9
Preserve tight-layout gaps for inner y-labels (#556)
cvanelteren Feb 6, 2026
8a5dc92
Guard inset colorbar frame bounds (#554)
cvanelteren Feb 6, 2026
59f7ba5
Fix ridgeline spacing for histogram mode (#553)
cvanelteren Feb 6, 2026
a3bdd77
Docs: show figures and silence warnings (#552)
cvanelteren Feb 6, 2026
fcaba40
Fix different y range yielding odd results in docs (#558)
cvanelteren Feb 6, 2026
4accc56
Add opt-in pixel snapping for subplot layout (#561)
cvanelteren Feb 6, 2026
2ff8a7a
CI: preserve selected nodeids as JSON arrays (#562)
cvanelteren Feb 6, 2026
8250000
CI: avoid heredoc EOF parsing in nodeid step (#563)
cvanelteren Feb 6, 2026
27e1afe
Fix/ci nodeid json safe (#564)
cvanelteren Feb 6, 2026
a561787
CI: fix workflow run block for nodeid parser (#565)
cvanelteren Feb 7, 2026
2986c6e
Limit pixel snapping to main axes (#567)
cvanelteren Feb 7, 2026
408ef61
Fix rc init when matplotlib is imported first (#569)
cvanelteren Feb 7, 2026
8366dc6
Tests: pin outside-label panel baseline to explicit share mode (#572)
cvanelteren Feb 8, 2026
6be6f1d
Fix suptitle spacing for non-bottom vertical alignment (#574)
cvanelteren Feb 9, 2026
0fce8e8
Stabilize outside-label panel image test tolerance (#575)
cvanelteren Feb 9, 2026
136238b
Fix YAML syntax in compare-baseline workflow step (#576)
cvanelteren Feb 9, 2026
ace8a32
CI: stabilize compare-baseline exits and determinism (#577)
cvanelteren Feb 9, 2026
8b37341
Feature: add curved annotation (#550)
cvanelteren Feb 10, 2026
a027443
Black formatting
cvanelteren Feb 10, 2026
203112d
Stabilize shared-axis ordering to fix flaky centered legend image com…
cvanelteren Feb 10, 2026
849246f
Fix GeoAxes._toggle_ticks to support bool and sequence label specs (#…
cvanelteren Feb 11, 2026
6e60040
CI: resolve PR baseline from live base branch tip (#580)
cvanelteren Feb 11, 2026
8a423af
Test: use latlabels for right-side geo panel labels
cvanelteren Feb 11, 2026
d267460
Feature: Auto-share default with compatibility-aware grouping (#560)
cvanelteren Feb 11, 2026
88d14b5
Feature: Add top-aligned ribbon flow plot type (#559)
cvanelteren Feb 12, 2026
ff2358f
Feature: Add histtype option for ridgeline histograms (#557)
cvanelteren Feb 12, 2026
9bbb1b2
Fix double-counted margins for shared spanning labels at space=0 (#584)
cvanelteren Feb 13, 2026
977c9c3
Internal: Refactor colorbar to decouple from axis. Introduces UltraCo…
cvanelteren Feb 14, 2026
5b335d0
Feature: add LegendEntry and pie wedge legend handler (#571)
cvanelteren Feb 14, 2026
5b55485
Refactor UltraLegend builder into dedicated module (#570)
cvanelteren Feb 15, 2026
5995358
CI: run representative Python/Matplotlib matrix combos (#587)
cvanelteren Feb 15, 2026
576acd2
Bump to python 3.14 (#385)
cvanelteren Feb 15, 2026
4534a4d
Feature: semantic legend API and geo legend support (#586)
cvanelteren Feb 18, 2026
a94ff63
Updated docs theme (#585)
cvanelteren Feb 18, 2026
097f6c4
Make docs theme dependency RTD-only and PyPI-safe
cvanelteren Feb 18, 2026
510bad3
Update build-states to new test-map.yml (#590)
cvanelteren Feb 18, 2026
6cc35dd
Preserve figure dpi in draw_without_rendering (#591)
cvanelteren Feb 22, 2026
b4fd082
Fix cartopy tri default transform for Triangulation inputs (#595)
cvanelteren Feb 25, 2026
866227f
Internal: cache inspect.signature used by pop_params (#596)
cvanelteren Feb 26, 2026
2472941
Bugfix: Deduplicate spanning axes in SubplotGrid slicing (#598)
cvanelteren Feb 26, 2026
9fe824a
Fix inset colorbar frame reflow for refaspect (#593)
cvanelteren Feb 26, 2026
1f47b0f
Exclude ultraplot/demos.py from coverage reports (#602)
cvanelteren Feb 26, 2026
f6aaff9
Fix contour level color mapping with explicit limits (#599)
cvanelteren Feb 26, 2026
472aadd
Bump the github-actions group with 2 updates (#604)
dependabot[bot] Mar 1, 2026
a8dc369
Enable graph plotting on 3D axes (#605)
cvanelteren Mar 5, 2026
8867289
Restore colorbar frame handling (#610)
cvanelteren Mar 11, 2026
d5c67c8
Preserve hatches in geometry legend proxies (#612)
cvanelteren Mar 11, 2026
5486831
Honor cmap for numeric scatter colors (#616)
cvanelteren Mar 17, 2026
b566a47
Fix release metadata and Zenodo flow (#620)
cvanelteren Mar 19, 2026
c175e54
Add core Python/Matplotlib version contract checks
cvanelteren Mar 11, 2026
95a4766
Document core version contract helpers
cvanelteren Mar 11, 2026
d4acb42
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 11, 2026
3014e12
Update ultraplot/tests/test_core_versions.py
cvanelteren Mar 12, 2026
ad2f76c
Make core version support explicit
cvanelteren Mar 12, 2026
aaa01a0
Handle cross-major version filtering
cvanelteren Mar 12, 2026
b166611
Add pip Dependabot updates
cvanelteren Mar 12, 2026
276185c
Format docstring
cvanelteren Mar 19, 2026
7c098a1
Revert "Format docstring"
cvanelteren Mar 19, 2026
2932b43
Publish Zenodo releases via API (#625)
cvanelteren Mar 19, 2026
74af0ec
Support Python 3.10 TOML loading (#626)
cvanelteren Mar 19, 2026
02a8d85
Bump dorny/paths-filter from 3 to 4 in the github-actions group (#624)
dependabot[bot] Mar 19, 2026
9d6ac43
Add choropleth support to GeoAxes (#623)
cvanelteren Mar 19, 2026
f3db41b
CI: re-add Codecov upload (#633)
cvanelteren Mar 19, 2026
95c9874
Fix duplicate shared boxplot tick labels (#630)
cvanelteren Mar 19, 2026
808af0c
CI: restore PR Codecov uploads (#635)
cvanelteren Mar 19, 2026
352fa35
add pytest tag (#637)
cvanelteren Mar 19, 2026
4044db4
Increase coverage to 85% with targeted tests (#636)
cvanelteren Mar 19, 2026
26ef4b3
Cache jupytext conversion for docs builds (#603)
cvanelteren Mar 20, 2026
eb3861a
Improve gallery widget and thumbnail backgrounds (#644)
cvanelteren Mar 20, 2026
4919ea5
Support custom labels in sizelegend (#629)
cvanelteren Mar 20, 2026
999df1c
fix: refresh outdated contributor setup instructions (#638) (#646)
JiwaniZakir Mar 20, 2026
0820a1c
Refresh constructor registries after ticker reload (#645)
cvanelteren Mar 20, 2026
469c587
Honor patch linewidth rc for edgefix (#649)
cvanelteren Mar 20, 2026
5f93bfa
Fix frame kwargs handling without mutating kw_frame
K-Mirembe-Mercy Mar 22, 2026
6428e6b
Apply black formatting
K-Mirembe-Mercy Mar 22, 2026
033be2a
Apply linewidth/lw to legend frame
K-Mirembe-Mercy Mar 22, 2026
b899fd0
Fix legend frame test using lw
K-Mirembe-Mercy Mar 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
groups:
python-dependencies:
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
Expand Down
3 changes: 3 additions & 0 deletions .github/micromamba-condarc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
channels:
- conda-forge
channel_priority: strict
276 changes: 253 additions & 23 deletions .github/workflows/build-ultraplot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ on:
matplotlib-version:
required: true
type: string
test-mode:
required: false
type: string
default: full
test-nodeids:
required: false
type: string
default: ""

env:
LC_ALL: en_US.UTF-8
Expand All @@ -21,15 +29,36 @@ jobs:
defaults:
run:
shell: bash -el {0}
env:
TEST_MODE: ${{ inputs.test-mode }}
TEST_NODEIDS: ${{ inputs.test-nodeids }}
PYTEST_WORKERS: 4
PYTHONHASHSEED: "0"
steps:
- uses: actions/checkout@v5
- name: Set up swap space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 10

- name: Show system memory
run: |
echo "=== System Memory ==="
free -h
echo ""
echo "=== CPU Info ==="
nproc
cat /proc/cpuinfo | grep "model name" | head -1

- uses: actions/checkout@v6
with:
fetch-depth: 0

- uses: mamba-org/setup-micromamba@v2.0.7
with:
environment-file: ./environment.yml
init-shell: bash
condarc-file: ./.github/micromamba-condarc.yml
post-cleanup: none
create-args: >-
--verbose
python=${{ inputs.python-version }}
Expand All @@ -41,64 +70,265 @@ jobs:
run: |
pip install --no-build-isolation --no-deps .

- name: Test Ultraplot
run: |
pytest --cov=ultraplot --cov-branch --cov-report term-missing --cov-report=xml ultraplot

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: Ultraplot/ultraplot

compare-baseline:
name: Compare baseline Python ${{ inputs.python-version }} with MPL ${{ inputs.matplotlib-version }}
runs-on: ubuntu-latest
continue-on-error: true
env:
IS_PR: ${{ github.event_name == 'pull_request' }}
TEST_MODE: ${{ inputs.test-mode }}
TEST_NODEIDS: ${{ inputs.test-nodeids }}
PYTEST_WORKERS: 4
PYTHONHASHSEED: "0"
defaults:
run:
shell: bash -el {0}
steps:
- uses: actions/checkout@v5
- name: Set up swap space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 10

- name: Show system memory
run: |
echo "=== System Memory ==="
free -h
echo ""
echo "=== CPU Info ==="
nproc
cat /proc/cpuinfo | grep "model name" | head -1

- uses: actions/checkout@v6

- uses: mamba-org/setup-micromamba@v2.0.7
with:
environment-file: ./environment.yml
init-shell: bash
condarc-file: ./.github/micromamba-condarc.yml
post-cleanup: none
create-args: >-
--verbose
python=${{ inputs.python-version }}
matplotlib=${{ inputs.matplotlib-version }}
cache-environment: true
cache-downloads: false

- name: Resolve baseline reference
id: baseline-ref
run: |
if [ "${IS_PR}" = "true" ]; then
BASE_REF="${{ github.event.pull_request.base.ref }}"
git fetch origin "${BASE_REF}"
BASE_SHA="$(git rev-parse "origin/${BASE_REF}")"
else
BASE_REF="${GITHUB_REF_NAME:-main}"
BASE_SHA="${GITHUB_SHA}"
fi
echo "base_ref=${BASE_REF}" >> "${GITHUB_OUTPUT}"
echo "base_sha=${BASE_SHA}" >> "${GITHUB_OUTPUT}"
echo "Resolved baseline ref=${BASE_REF} sha=${BASE_SHA}"

# Cache Baseline Figures (Restore step)
- name: Cache Baseline Figures
id: cache-baseline
uses: actions/cache@v5
if: ${{ env.IS_PR }}
with:
path: ./ultraplot/tests/baseline # The directory to cache
# Key is based on OS, Python/Matplotlib versions, and the base commit SHA
key: ${{ runner.os }}-baseline-base-v5-hs${{ env.PYTHONHASHSEED }}-${{ steps.baseline-ref.outputs.base_sha }}-${{ inputs.python-version }}-${{ inputs.matplotlib-version }}
restore-keys: |
${{ runner.os }}-baseline-base-v5-hs${{ env.PYTHONHASHSEED }}-${{ steps.baseline-ref.outputs.base_sha }}-${{ inputs.python-version }}-${{ inputs.matplotlib-version }}-

# Conditional Baseline Generation (Only runs on cache miss)
- name: Generate baseline from main
# Skip this step if the cache was found (cache-hit is true)
if: steps.cache-baseline.outputs.cache-hit != 'true' || !env.IS_PR
run: |
mkdir -p baseline
git fetch origin ${{ github.event.pull_request.base.sha }}
git checkout ${{ github.event.pull_request.base.sha }}
mkdir -p ultraplot/tests/baseline
echo "TEST_MODE=${TEST_MODE}"
echo "IS_PR=${IS_PR}"
echo "PR_BASE_REF=${{ steps.baseline-ref.outputs.base_ref }}"
echo "PR_BASE_SHA=${{ steps.baseline-ref.outputs.base_sha }}"
echo "TEST_NODEIDS=${TEST_NODEIDS}"
# Save PR-selected nodeids for reuse after checkout (if provided)
if [ "${TEST_MODE}" = "selected" ] && [ -n "${TEST_NODEIDS}" ]; then
python -c 'import json,os; raw=os.environ.get("TEST_NODEIDS","").strip(); parsed=json.loads(raw) if raw and raw!="[]" else []; parsed=[parsed] if isinstance(parsed,str) else parsed; nodeids=[item for item in parsed if isinstance(item,str) and item]; open("/tmp/pr_selected_nodeids.txt","w",encoding="utf-8").write("".join(f"{nodeid}\n" for nodeid in nodeids)); print(f"Selected nodeids parsed: {len(nodeids)}")'
else
: > /tmp/pr_selected_nodeids.txt
fi
# Checkout the resolved base-branch tip for PR baseline generation.
if [ "${IS_PR}" = "true" ]; then
git checkout "${{ steps.baseline-ref.outputs.base_sha }}"
fi

# Install the Ultraplot version from the base branch's code
pip install --no-build-isolation --no-deps .

# Generate the baseline images and hash library
python -c "import ultraplot as plt; plt.config.Configurator()._save_yaml('ultraplot.yml')"
pytest -W ignore \
--mpl-generate-path=./baseline/ \
--mpl-default-style="./ultraplot.yml"\
ultraplot/tests
git checkout ${{ github.sha }} # Return to PR branch
if [ "${TEST_MODE}" = "selected" ] && [ -s /tmp/pr_selected_nodeids.txt ]; then
status=0
FILTERED_NODEIDS=()
while IFS= read -r nodeid; do
if [ -z "$nodeid" ]; then
continue
fi
path="${nodeid%%::*}"
if [ -f "$path" ]; then
FILTERED_NODEIDS+=("$nodeid")
fi
done < /tmp/pr_selected_nodeids.txt
echo "FILTERED_NODEIDS_BASE_COUNT=${#FILTERED_NODEIDS[@]}"
if [ "${#FILTERED_NODEIDS[@]}" -eq 0 ]; then
echo "No valid nodeids found on base; skipping baseline generation."
else
echo "=== Memory before baseline generation ===" && free -h
pytest -n ${PYTEST_WORKERS} --dist loadfile --tb=short --disable-warnings -W ignore \
--mpl-generate-path=./ultraplot/tests/baseline/ \
--mpl-default-style="./ultraplot.yml" \
"${FILTERED_NODEIDS[@]}" || status=$?
echo "=== Memory after baseline generation ===" && free -h
if [ "$status" -eq 4 ] || [ "$status" -eq 5 ]; then
echo "No tests collected from selected nodeids on base; skipping baseline generation."
status=0
fi
fi
# Return to the PR branch before continuing
if [ "${IS_PR}" = "true" ]; then
echo "Checking out PR branch: ${{ github.sha }}"
git checkout ${{ github.sha }} || echo "Warning: git checkout failed, but continuing"
fi
if [ "$status" -ne 0 ]; then
echo "Baseline generation failed with status $status"
exit "$status"
fi
else
echo "=== Memory before baseline generation ===" && free -h
pytest -n ${PYTEST_WORKERS} --dist loadfile --tb=short --disable-warnings -W ignore \
--mpl-generate-path=./ultraplot/tests/baseline/ \
--mpl-default-style="./ultraplot.yml" \
ultraplot/tests
echo "=== Memory after baseline generation ===" && free -h
# Return to the PR branch for the rest of the job
if [ "${IS_PR}" = "true" ]; then
echo "Checking out PR branch: ${{ github.sha }}"
git checkout ${{ github.sha }} || echo "Warning: git checkout failed, but continuing"
fi
fi

# Image Comparison (Uses cached or newly generated baseline)
- name: Image Comparison Ultraplot
run: |
set -uo pipefail
# This workflow runs in a login shell (bash -el), which executes
# ~/.bash_logout on exit. Neutralize that file to prevent runner
# teardown commands (e.g. clear_console) from overriding step status.
if [ -f "${HOME}/.bash_logout" ]; then
cp "${HOME}/.bash_logout" "${HOME}/.bash_logout.bak" || true
: > "${HOME}/.bash_logout" || true
fi

# Re-install the Ultraplot version from the current PR branch
pip install --no-build-isolation --no-deps .

mkdir -p results
python -c "import ultraplot as plt; plt.config.Configurator()._save_yaml('ultraplot.yml')"
pytest -W ignore \
echo "TEST_MODE=${TEST_MODE}"
echo "TEST_NODEIDS=${TEST_NODEIDS}"
parse_junit_counts() {
python -c "import sys,xml.etree.ElementTree as ET; root=ET.parse(sys.argv[1]).getroot(); suites=[root] if root.tag=='testsuite' else root.findall('testsuite'); failures=sum(int(s.attrib.get('failures', 0)) for s in suites); errors=sum(int(s.attrib.get('errors', 0)) for s in suites); print(f'{failures} {errors}')" "$1" 2>/dev/null || echo "0 0"
}
if [ "${TEST_MODE}" = "selected" ] && [ -s /tmp/pr_selected_nodeids.txt ]; then
status=0
FILTERED_NODEIDS=()
while IFS= read -r nodeid; do
if [ -z "$nodeid" ]; then
continue
fi
path="${nodeid%%::*}"
if [ -f "$path" ]; then
FILTERED_NODEIDS+=("$nodeid")
fi
done < /tmp/pr_selected_nodeids.txt
echo "FILTERED_NODEIDS_PR_COUNT=${#FILTERED_NODEIDS[@]}"
if [ "${#FILTERED_NODEIDS[@]}" -eq 0 ]; then
echo "No valid nodeids found on PR branch; skipping image comparison."
exit 0
else
status=0
echo "=== Memory before image comparison ===" && free -h
set +e
pytest -n ${PYTEST_WORKERS} --dist loadfile --tb=short --disable-warnings -W ignore \
--mpl \
--mpl-baseline-path=./ultraplot/tests/baseline \
--mpl-results-path=./results/ \
--mpl-generate-summary=html \
--mpl-default-style="./ultraplot.yml" \
--junitxml=./results/junit.xml \
"${FILTERED_NODEIDS[@]}"
status=$?
echo "=== Memory after image comparison ===" && free -h
junit_failures=0
junit_errors=0
if [ -f ./results/junit.xml ]; then
junit_counts="$(parse_junit_counts ./results/junit.xml || echo '0 0')"
junit_failures="${junit_counts%% *}"
junit_errors="${junit_counts##* }"
fi
case "$junit_failures" in ''|*[!0-9]*) junit_failures=0 ;; esac
case "$junit_errors" in ''|*[!0-9]*) junit_errors=0 ;; esac
echo "pytest_status=$status junit_failures=$junit_failures junit_errors=$junit_errors"
if [ "$status" -ne 0 ] && [ "$junit_failures" -eq 0 ] && [ "$junit_errors" -eq 0 ]; then
echo "pytest exited with $status but junit reports no failures/errors; overriding exit status to 0."
status=0
fi
if [ "$status" -eq 4 ] || [ "$status" -eq 5 ]; then
echo "No tests collected from selected nodeids; skipping image comparison."
status=0
fi
fi
exit "$status"
else
status=0
echo "=== Memory before image comparison ===" && free -h
set +e
pytest -n ${PYTEST_WORKERS} --dist loadfile --tb=short --disable-warnings -W ignore \
--mpl \
--mpl-baseline-path=./baseline/ \
--mpl-baseline-path=./ultraplot/tests/baseline \
--mpl-results-path=./results/ \
--mpl-generate-summary=html \
--mpl-default-style="./ultraplot.yml" \
--junitxml=./results/junit.xml \
ultraplot/tests
status=$?
echo "=== Memory after image comparison ===" && free -h
junit_failures=0
junit_errors=0
if [ -f ./results/junit.xml ]; then
junit_counts="$(parse_junit_counts ./results/junit.xml || echo '0 0')"
junit_failures="${junit_counts%% *}"
junit_errors="${junit_counts##* }"
fi
case "$junit_failures" in ''|*[!0-9]*) junit_failures=0 ;; esac
case "$junit_errors" in ''|*[!0-9]*) junit_errors=0 ;; esac
echo "pytest_status=$status junit_failures=$junit_failures junit_errors=$junit_errors"
if [ "$status" -ne 0 ] && [ "$junit_failures" -eq 0 ] && [ "$junit_errors" -eq 0 ]; then
echo "pytest exited with $status but junit reports no failures/errors; overriding exit status to 0."
status=0
fi
if [ "$status" -eq 4 ] || [ "$status" -eq 5 ]; then
echo "No tests collected; skipping image comparison."
status=0
fi
exit "$status"
fi

# Return the html output of the comparison even if failed
- name: Upload comparison failures
if: always()
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v7
with:
name: failed-comparisons-${{ inputs.python-version }}-${{ inputs.matplotlib-version }}-${{ github.sha }}
path: results/*
if-no-files-found: ignore
Loading