Skip to content

fix(palette): lazy matplotlib import — unbreaks ggplot2 + makie impl-generate#7713

Merged
MarkusNeusinger merged 2 commits into
mainfrom
fix/palette-lazy-matplotlib-import
May 27, 2026
Merged

fix(palette): lazy matplotlib import — unbreaks ggplot2 + makie impl-generate#7713
MarkusNeusinger merged 2 commits into
mainfrom
fix/palette-lazy-matplotlib-import

Conversation

@MarkusNeusinger
Copy link
Copy Markdown
Owner

Summary

  • Fixes a regression from feat(palette): adopt imprint — new categorical palette + named API + frontend rebuild #7692 where import core.images started transitively requiring matplotlib via core.palette's module-level from matplotlib.colors import LinearSegmentedColormap.
  • That broke the Process plot images (light + dark) step in impl-generate.yml for non-Python libraries (ggplot2 / makie), where the runner only installs PIL helpers — not matplotlib. raincloud-basic R and Julia auto-failed 3× with ModuleNotFoundError: No module named 'matplotlib'.
  • Keeps core/palette.py matplotlib-free at module level. Cmap factory (diverging) and imprint_seq / imprint_div_{light,dark} are constructed lazily on first attribute access via module __getattr__.

Why this is the right layering

core/images.py is documented as PIL-only — the whole point of the reviewer feedback on #7692 was that importing it must not pull matplotlib in as a side effect. R/Julia plot scripts hardcode hex codes; they don't need matplotlib for rendering. But the workflow calls python -m core.images process plot.png afterwards to optimise the rendered PNG with PIL — and that's where the missing matplotlib bites.

Public API preserved

  • from core.palette import imprint_seq ✅ (lazy)
  • core.palette.imprint_seq.name == "imprint_seq"
  • register_with_matplotlib() still registers all three cmaps ✅
  • Re-import in same process still cached after first build ✅

Test plan

  • uv run ruff check . clean
  • uv run ruff format --check . clean
  • Local smoke test: hex constants + lazy cmaps + register_with_matplotlib
  • Local smoke test simulating no-matplotlib env: import core.images succeeds, LIBRARY_COLORS populated
  • CI green
  • Re-trigger raincloud-basic ggplot2 + makie after merge — confirm green

🤖 Generated with Claude Code

PR #7692 introduced a regression: moving `from .palette import …` to
the top of core/images.py made `import core.images` transitively pull
in matplotlib (via palette.py's module-level
`from matplotlib.colors import LinearSegmentedColormap`).

This broke `python -m core.images process` in the impl-generate
workflow for non-Python libraries (ggplot2 / makie), where the runner
only installs PIL helpers — no matplotlib. raincloud-basic R and Julia
both auto-failed 3× on the post-render PNG optimisation step with
ModuleNotFoundError: No module named 'matplotlib'.

Fix: keep palette.py matplotlib-free at module level. The cmap factory
(`diverging`) and the `imprint_seq` / `imprint_div_{light,dark}`
attributes are now constructed lazily on first access via module
`__getattr__`, so importing palette (and transitively, images) costs
nothing for callers that only need the hex constants.

Public API preserved:
- `from core.palette import imprint_seq` still works
- `core.palette.imprint_seq.name == "imprint_seq"`
- `register_with_matplotlib()` still registers all three cmaps

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 27, 2026 05:11
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes the palette import layering regression by keeping core.palette free of module-level matplotlib imports, allowing PIL-only image processing paths for non-Python plot implementations to import core.images without installing matplotlib.

Changes:

  • Moves matplotlib imports into colormap construction paths.
  • Adds lazy module attribute resolution for imprint_seq and diverging colormaps.
  • Updates matplotlib registration to resolve lazy colormaps before registering them.

Comment thread core/palette.py Outdated

imprint_div_light: LinearSegmentedColormap = diverging("light")
imprint_div_dark: LinearSegmentedColormap = diverging("dark")
def __getattr__(name: str):
@codecov
Copy link
Copy Markdown

codecov Bot commented May 27, 2026

Codecov Report

❌ Patch coverage is 30.00000% with 14 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
core/palette.py 30.00% 14 Missing ⚠️

📢 Thoughts on this report? Let us know!

Adds the LinearSegmentedColormap return annotation Copilot flagged on
#7713. With `from __future__ import annotations` active, the annotation
stays as a forward reference and does not trigger a runtime matplotlib
import — `core.palette` (and transitively `core.images`) remains safe
to import without matplotlib installed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@MarkusNeusinger MarkusNeusinger merged commit 6d909e3 into main May 27, 2026
7 checks passed
@MarkusNeusinger MarkusNeusinger deleted the fix/palette-lazy-matplotlib-import branch May 27, 2026 05:28
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.

2 participants