Skip to content

Fix gallery image and legend option compatibility [mpl compatibility] - #334

Merged
Alek99 merged 11 commits into
mainfrom
agent/gallery-compat-round-2
Jul 27, 2026
Merged

Fix gallery image and legend option compatibility [mpl compatibility]#334
Alek99 merged 11 commits into
mainfrom
agent/gallery-compat-round-2

Conversation

@sselvakumaran

@sselvakumaran sselvakumaran commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This is the first grouped compatibility batch after #280/#281. It fixes exact-gallery execution regressions and blockers without adding review assets or compatibility-ledger edits to the PR:

  • histogram legends no longer interpret per-bin data widths as line stroke widths;
  • legend(handlelength=...) and handletextpad use Matplotlib legend-font units in browser, SVG, and PNG output;
  • Line2D.set_dash_capstyle("round") survives through the fixed-round renderer path;
  • imshow(interpolation="auto") / "antialiased" use Matplotlib 3.11's adaptive nearest/Hanning and data/RGBA-stage rules;
  • CSS named colors such as limegreen work for colormap extremes;
  • resampling preserves the original M×N image extent, and ringing truecolor filters saturate channel overshoot instead of wrapping panels to black;
  • legend reversal, full format-string color detection, non-string errorbar labels, and horizontal ring-hatch sizing now follow Matplotlib semantics.

Exact gallery evidence

The PNGs live on an immutable review-only evidence commit, not in this PR or main.

Dashed-line legend handle geometry

Matplotlib, exact XY base exception, and XY after for line_demo_dash_control

Adaptive image interpolation

Matplotlib, exact XY base exception, and XY after for image_antialiasing auto

Histogram legend regression

Matplotlib, exact XY base exception, and XY after for histogram_bihistogram

Verification

Current integration state:

  • current base 4c26d97, current head fd31f02;
  • 198/198 focused tests across every Python test file changed by the merged PR;
  • 26/26 focused format/color tests after the final non-finite-color review fix;
  • Ruff lint and format checks pass;
  • git diff --check passes;
  • no unresolved review threads;
  • no compat.md, compatibility changelog, shim ledger, pr-assets, or evidence PNG changes in the net PR diff;
  • no Rust/core change.

The exact gallery sweep predates the current-main merge and is retained as visual evidence, not presented as a fresh post-merge run:

  • original batch base a2ac10e: 79/88 paired examples, 136 paired figures;
  • original batch head a916022: 83/88 paired examples, 155 paired figures;
  • exact image_antialiasing.py: 11/11 figures, 4 near / 7 similar / 0 divergent;
  • prior full pyplot suite: 803 passed, 65 skipped;
  • prior JavaScript typecheck and production bundles: passed.

The expensive exact-gallery/browser capture was not rerun after the conflict-free current-main merge. Fresh GitHub Actions are the final integration gate.

Out of scope for this PR

These selected-gallery areas remain separate work:

  1. fill_spiral.py — general non-simple polygon filling;
  2. pie_and_donut_labels.py — annotation/pie geometry;
  3. log_demo.py — separate minor log ticks/grid plus base/nonpositive scale options;
  4. time_series_histogram.py — logarithmic scalar normalization and automatic colorbar layout;
  5. subplots_adjust.py — explicit colorbar(cax=...) placement.

This PR does not claim blanket pixel equivalence for the full gallery.

Summary by CodeRabbit

  • New Features

    • Added configurable legend handle length and spacing, with improved responsive sizing.
    • Preserved outlines for hollow legend swatches across renderers.
    • Added broader color support, including XKCD and CSS color names.
    • Added interpolation="auto" and improved image resampling behavior.
    • Added legend reversal support and improved error-bar and stacked histogram labeling.
    • Improved histogram hatch rendering, including ring and dot patterns.
  • Bug Fixes

    • Corrected image extents, color-channel overshoot, legend ordering, and unsupported line styling issues.
    • Added support for legend configuration through standard plotting defaults.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Legend sizing and stroke rendering, image interpolation, color parsing, histogram ordering, hatch geometry, errorbar legend handling, and renderer compatibility are updated across Python and browser paths with regression tests and styling documentation.

Changes

Legend behavior

Layer / File(s) Summary
Legend options and item layout
python/xy/pyplot/_rc.py, python/xy/pyplot/_axes.py, python/xy/pyplot/_artists.py, python/xy/_svg.py, tests/pyplot/test_grid_legend_contracts.py, tests/pyplot/test_line_legend_gallery_compat.py
Legend handle dimensions are configurable, validated, font-scaled, propagated into payloads, and applied only to supported legend item kinds.
Outlined backend swatches
python/xy/_raster.py, python/xy/_svg.py, spec/api/styling.md, tests/pyplot/test_line_legend_gallery_compat.py
Raster and SVG patch swatches preserve positive-width strokes, including hollow handles, with updated slot documentation and tests.
Responsive browser swatches
js/src/20_theme.ts, js/src/50_chartview.ts, tests/test_text_weight_defaults.py
Browser legend swatches use configurable em geometry, responsive SVG widths, theme-controlled spacing, and inline borders for strokes.

Image and color compatibility

Layer / File(s) Summary
Adaptive image sampling
python/xy/pyplot/_axes.py, tests/pyplot/test_matplotlib_mismatch_regressions.py
imshow() resolves adaptive interpolation stages and dimensions, preserves source extents, clamps RGBA output, and updates Kaiser resampling behavior.
Color parsing and format detection
python/xy/pyplot/_colors.py, python/xy/pyplot/_fmt.py, tests/pyplot/test_axes_charts.py, tests/pyplot/test_fmt.py, tests/pyplot/test_matplotlib_mismatch_regressions.py
Color resolution accepts expanded extreme and XKCD forms, while complete color format strings are detected through is_color_like().

Plot and rendering compatibility

Layer / File(s) Summary
Histogram ordering and hatch geometry
python/xy/pyplot/_axes.py, python/xy/pyplot/_plot_types.py, tests/pyplot/test_gallery_statistics_semantics.py, tests/pyplot/test_launch_compat.py, tests/pyplot/test_gallery_hist_errorbar_compat.py
Stacked step histogram entries follow draw order, and dot/ring hatches render as bounded segment geometry instead of scatter points.
Errorbar labels and legend ordering
python/xy/pyplot/_artists.py, python/xy/pyplot/_axes.py, tests/pyplot/test_gallery_hist_errorbar_compat.py, tests/pyplot/test_grid_legend_contracts.py, tests/pyplot/test_axes_charts.py
Errorbar containers expose labels, legend handles resolve to containers, reverse ordering is supported, and stairs/ECDF paths are expanded for best-location scoring.
Renderer keyword boundary
python/xy/pyplot/_axes.py, tests/pyplot/test_line_legend_gallery_compat.py
Unsupported dash_capstyle values are removed before line, axline, and step renderer calls.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Axes
  participant LegendBuilder
  participant LegendLayout
  participant SVGRenderer
  participant BrowserChartView
  Axes->>LegendBuilder: compose validated legend options
  LegendBuilder->>LegendLayout: calculate handle length and text gap
  LegendLayout->>SVGRenderer: emit sized swatches and strokes
  LegendBuilder->>BrowserChartView: send legend options and item styles
  BrowserChartView->>BrowserChartView: apply responsive SVG and border geometry
Loading

Possibly related PRs

Suggested reviewers: alek99

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main compatibility-focused changes around gallery images and legend options.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/gallery-compat-round-2

Comment @coderabbitai help to get the list of available commands.

@sselvakumaran
sselvakumaran force-pushed the agent/gallery-compat-round-2 branch from b51b611 to a916022 Compare July 27, 2026 05:27
@codspeed-hq

codspeed-hq Bot commented Jul 27, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 103 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing agent/gallery-compat-round-2 (fd31f02) with main (4c26d97)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
python/xy/pyplot/_axes.py (1)

2450-2470: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

**dash_capstyle also needs to be dropped for step/drawstyle="steps-*"handles.**Line2D.set_dash_capstyle()writes the kw into the entry, and the@markdispatch forwards it straight intoxy.step(...)`, which doesn't accept it; a mutated step-based line will still fail at render time.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/xy/pyplot/_axes.py` around lines 2450 - 2470, Update the
step/drawstyle="steps-*" handle-processing path to remove dash_capstyle before
the `@mark` dispatch calls xy.step(...). Ensure Line2D.set_dash_capstyle()
mutations are filtered consistently with the existing unsupported keyword
handling so step-based lines render without forwarding dash_capstyle.
🧹 Nitpick comments (1)
python/xy/pyplot/_axes.py (1)

2519-2537: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Adaptive interpolation/resample-resolution choice is not recorded in the emitted spec.

_resolve_imshow_sampling silently decides the interpolation kernel (nearest vs. hanning) and intermediate resample resolution; only the resulting pixel grid ships in the heatmap entry, with no field recording which method/decision was applied. As per coding guidelines, "Record every decimation and tier decision in the specification; decisions must never be silent." Since this decision is baked once into static pixel data (unlike the interactive density-tier sampling, which the JS client surfaces via "sampled N of M" badges), the practical need for recording it is less clear-cut, but worth confirming intent against the guideline.

Also applies to: 7314-7357

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/xy/pyplot/_axes.py` around lines 2519 - 2537, Update the heatmap
specification emission associated with _resolve_imshow_sampling so it records
the resolved interpolation method, stage, and intermediate resample dimensions
returned by that function, rather than only emitting the resulting pixel grid.
Apply the same metadata recording to the corresponding flow around the
additionally affected section, preserving the existing sampling behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@python/xy/pyplot/_axes.py`:
- Around line 2450-2470: Update the step/drawstyle="steps-*" handle-processing
path to remove dash_capstyle before the `@mark` dispatch calls xy.step(...).
Ensure Line2D.set_dash_capstyle() mutations are filtered consistently with the
existing unsupported keyword handling so step-based lines render without
forwarding dash_capstyle.

---

Nitpick comments:
In `@python/xy/pyplot/_axes.py`:
- Around line 2519-2537: Update the heatmap specification emission associated
with _resolve_imshow_sampling so it records the resolved interpolation method,
stage, and intermediate resample dimensions returned by that function, rather
than only emitting the resulting pixel grid. Apply the same metadata recording
to the corresponding flow around the additionally affected section, preserving
the existing sampling behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 470f495c-4ddc-4c41-834c-7e0d4de9d678

📥 Commits

Reviewing files that changed from the base of the PR and between a2ac10e and b51b611.

📒 Files selected for processing (12)
  • js/src/50_chartview.ts
  • python/xy/_svg.py
  • python/xy/pyplot/_artists.py
  • python/xy/pyplot/_axes.py
  • python/xy/pyplot/_colors.py
  • python/xy/pyplot/_rc.py
  • spec/matplotlib/compat.md
  • tests/pyplot/test_gallery_statistics_semantics.py
  • tests/pyplot/test_grid_legend_contracts.py
  • tests/pyplot/test_line_legend_gallery_compat.py
  • tests/pyplot/test_matplotlib_mismatch_regressions.py
  • tests/test_text_weight_defaults.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/pyplot/test_line_legend_gallery_compat.py (1)

298-300: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert the filtered payload, not only its trace kind.

The test confirms that the step entry becomes kind == "line", but it never verifies that dash_capstyle is removed before renderer dispatch. A regression could preserve the same trace kind while leaking the Matplotlib-only keyword. Inspect the serialized renderer kwargs and assert that dash_capstyle is absent.

Also applies to: 302-313

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/pyplot/test_line_legend_gallery_compat.py` around lines 298 - 300,
Extend the assertions in the test around _build_chart and build_payload to
inspect the serialized renderer kwargs for the first trace, and verify that
dash_capstyle is absent after filtering. Keep the existing kind == "line"
assertion while validating the payload passed to renderer dispatch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/pyplot/test_line_legend_gallery_compat.py`:
- Around line 298-300: Extend the assertions in the test around _build_chart and
build_payload to inspect the serialized renderer kwargs for the first trace, and
verify that dash_capstyle is absent after filtering. Keep the existing kind ==
"line" assertion while validating the payload passed to renderer dispatch.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7f2442fd-5801-4ffd-82d8-0125dabb9666

📥 Commits

Reviewing files that changed from the base of the PR and between a916022 and a16b946.

📒 Files selected for processing (3)
  • python/xy/pyplot/_axes.py
  • tests/pyplot/test_line_legend_gallery_compat.py
  • tests/test_text_weight_defaults.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/test_text_weight_defaults.py
  • python/xy/pyplot/_axes.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@python/xy/pyplot/_artists.py`:
- Around line 966-972: Update ErrorbarContainer.set_label to store str(value) in
the name field, matching Artist.set_label’s normalization while preserving the
existing _touch() call.

In `@python/xy/pyplot/_plot_types.py`:
- Around line 1979-1986: Update the extent/span computation used by ring() to
track edge-span and value-span separately, then select the span paired with each
rectangle axis according to orientation. In the horizontal branch, use the value
span for rect_width and the edge span for rect_height; retain the existing
edge-to-width and value-to-height pairing for vertical orientation. Apply this
consistently to the related hatch sizing logic around ring() and the non-fill
horizontal path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 22dc6cba-8b50-495f-8e02-ce9d5e9ad8f9

📥 Commits

Reviewing files that changed from the base of the PR and between a16b946 and a384918.

📒 Files selected for processing (15)
  • js/src/50_chartview.ts
  • python/xy/_raster.py
  • python/xy/_svg.py
  • python/xy/pyplot/_artists.py
  • python/xy/pyplot/_axes.py
  • python/xy/pyplot/_plot_types.py
  • spec/api/styling.md
  • spec/matplotlib/compat-changelog.md
  • spec/matplotlib/compat.md
  • tests/pyplot/test_gallery_hist_errorbar_compat.py
  • tests/pyplot/test_gallery_statistics_semantics.py
  • tests/pyplot/test_grid_legend_contracts.py
  • tests/pyplot/test_line_legend_gallery_compat.py
  • tests/pyplot/test_matplotlib_mismatch_regressions.py
  • tests/test_text_weight_defaults.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/pyplot/test_grid_legend_contracts.py
  • spec/matplotlib/compat.md
  • tests/pyplot/test_matplotlib_mismatch_regressions.py
  • js/src/50_chartview.ts

Comment thread python/xy/pyplot/_artists.py
Comment thread python/xy/pyplot/_plot_types.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@python/xy/pyplot/_colors.py`:
- Around line 371-388: The is_color_like function currently lets OverflowError
escape when resolve_rgba processes non-finite RGB(A) channels. Update the
exception handling around resolve_rgba to treat OverflowError like TypeError and
ValueError, returning False for such inputs, and add a regression test covering
non-finite RGB(A) values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cb91c778-4a76-45f2-a72d-8653c143c25a

📥 Commits

Reviewing files that changed from the base of the PR and between a384918 and e849586.

📒 Files selected for processing (13)
  • python/xy/_raster.py
  • python/xy/_svg.py
  • python/xy/pyplot/_artists.py
  • python/xy/pyplot/_axes.py
  • python/xy/pyplot/_colors.py
  • python/xy/pyplot/_fmt.py
  • python/xy/pyplot/_plot_types.py
  • spec/api/styling.md
  • tests/pyplot/test_axes_charts.py
  • tests/pyplot/test_fmt.py
  • tests/pyplot/test_gallery_hist_errorbar_compat.py
  • tests/pyplot/test_gallery_statistics_semantics.py
  • tests/pyplot/test_launch_compat.py
🚧 Files skipped from review as they are similar to previous changes (7)
  • python/xy/_svg.py
  • tests/pyplot/test_gallery_statistics_semantics.py
  • python/xy/pyplot/_artists.py
  • spec/api/styling.md
  • python/xy/_raster.py
  • python/xy/pyplot/_plot_types.py
  • python/xy/pyplot/_axes.py

Comment thread python/xy/pyplot/_colors.py
@Alek99
Alek99 merged commit 24b8277 into main Jul 27, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants