fix(imshow): make faceted slot assignments fail loudly and legibly - #95
Open
FBumann wants to merge 2 commits into
Open
fix(imshow): make faceted slot assignments fail loudly and legibly#95FBumann wants to merge 2 commits into
FBumann wants to merge 2 commits into
Conversation
imshow with both facet_col and facet_row silently produced the wrong figure in two cases. On plotly < 6.7.0 (px.imshow has no facet_row), auto-assignment quietly dropped the slot, so a 4D array animated its fourth dimension instead of faceting subplot rows -- with no warning -- and a 5D array failed with an unrelated "Unassigned dimension(s)" error. The fallback now warns, naming the dimension and the plotly requirement, and the 5D case raises an error that explains the real cause. On any plotly version, px.imshow honours facet_col_wrap even when facet_row is set: the grid is built but the facet_row subplot titles are dropped. Every other px function ignores the wrap in that case, so drop it here too and warn. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TKhPQzJgQr7khmzDPYAXA9
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Two slot assignments reached px.imshow and crashed inside its own slicing with an error that named nothing the caller had written: - The same dimension in two slots popped the same index twice, giving "IndexError: pop index out of range". - Facet and animation slots consuming all but one dimension left nothing for the second heatmap axis, giving "IndexError: list index out of range". Duplicates that reached the transpose first surfaced as "ValueError: axes don't match array" instead. Validate the assignment before transposing: every slot needs its own dimension, and both y and x must be filled. The error now names the dimension and the two slots that want it, or the slots that consumed the axes. Sweeping all 3125 combinations of auto/None/each dimension across the five slots of a 4D array: every one now either builds a figure or raises a ValueError that says what to change, with no change to the 1056 combinations that already worked. Also drop mypy's python_version = "3.10" pin, which failed CI on the 3.12 and 3.13 matrix entries -- numpy's stubs now use `type` statements that mypy rejects when targeting 3.10. Reproduced on origin/main with no other changes. Unpinned, mypy targets the interpreter it runs under, so each matrix entry checks its own version against the numpy stubs resolved for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TKhPQzJgQr7khmzDPYAXA9
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
Started from "imshow with facet_col and facet_row doesn't work", then grew to cover a second report of
IndexError: pop index out of range.The feature itself is fine on plotly >= 6.7.0 — a 4D array rendered in a headless browser gives a correct facet grid, with both column and row titles, the right data in each panel, and shared color bounds. What was broken is everything around it: four separate ways to get a wrong figure or an error that named nothing the caller had written.
Silent wrong figures
1. plotly < 6.7.0 (
px.imshowhas nofacet_row; the package floor isplotly>=5.0.0, so this is a valid install). Reproduced against 6.3.1:facet_rowslot and animated the fourth dimension instead — 2 traces + 3 frames rather than 6 subplots, with no warning at all.ValueError: Unassigned dimension(s): ['t'], which says nothing about plotly.facet_rowraised the informative error.2.
facet_col_wraptogether withfacet_row(every plotly version).px.imshowhonours the wrap even whenfacet_rowis set: it builds the right grid but silently drops the row titles. Straight frompx.imshow, no wrapper involved:px.lineand the other px functions correctly ignore the wrap here, so imshow was the odd one out — and the docstring claimed plotly ignored it, which it doesn't.Crashes inside px
3. The same dimension in two slots popped the same index twice inside
px.imshow, givingIndexError: pop index out of range. Duplicates that hit our transpose first surfaced asValueError: axes don't match array.4. Facet and animation slots consuming all but one dimension left nothing for the second heatmap axis, giving
IndexError: list index out of range.Changes
xarray_plotly/plotting.py:_validate_imshow_slotsruns before the transpose: every slot needs its own dimension, and bothyandxmust be filled. The error names the dimension and the two slots that want it, or the slots that consumed the axes._handle_unsupported_facet_rowreplaces the pre-assign_slotshack that blankedfacet_row, so it runs after assignment and knows which dimension was intended. An auto-assignedfacet_rowon old plotly still falls back to animating, but warns:facet_row for imshow requires plotly>=6.7.0 (installed: 6.3.1). Dimension 'year' is animated instead of faceted across subplot rows; upgrade plotly to facet it.The 5D case raises an error naming the real cause. An explicitly namedfacet_rowstill raises, unchanged.facet_col_wrapis dropped (with a warning) whenfacet_rowis active, so the row titles survive.Docstrings in
plotting.pyandaccessor.pyupdated to describe the actual behaviour.Verification
Swept all 3125 combinations of
auto/None/ each dimension across the five slots of a 4D array. Every one now either builds a figure or raises aValueErrorthat says what to change — noIndexError, noaxes don't match array— with no change to the 1056 combinations that already worked.Eleven tests added in
tests/test_accessor.py, covering the warning on fallback, the 5D error,facet_col_wrapwith and withoutfacet_row, both duplicate-slot cases, both empty-axis cases, and one asserting that each(facet_col, facet_row)pair lands in its own subplot with the right data.183 tests pass; ruff check, ruff format, and mypy are clean on Python 3.10, 3.12, and 3.13. Behaviour verified against both plotly 7.0.0 and plotly 6.3.1.
The CI failure, which was not from this branch
Reproduced on
origin/mainwith nothing else changed, on the 3.12 and 3.13 matrix entries. numpy's stubs now usetypestatements, which mypy rejects while targetingpython_version = "3.10". The pin is dropped here so the branch can go green: mypy then targets the interpreter it runs under, and each matrix entry checks its own version against the numpy stubs resolved for it — so the 3.10 job still type-checks 3.10 semantics. Say the word if you would rather have this as its own PR.Not changed — your call
pyproject.tomlstill declaresplotly>=5.0.0, and CI never tests against an old plotly (the existing tests monkeypatch the version check). That is why the plotly-version gap went unnoticed. If faceted imshow is meant to just work, the floor probably belongs at>=6.7.0; otherwise a min-deps CI job would catch this class of bug.🤖 Generated with Claude Code
https://claude.ai/code/session_01TKhPQzJgQr7khmzDPYAXA9