Skip to content

refactor(plot)!: adopt cleopatra 0.31 RgbBands for the ArrayGlyph constructor - #972

Merged
MAfarrag merged 8 commits into
mainfrom
chore/cleopatra-0.31-migration
Aug 12, 2026
Merged

refactor(plot)!: adopt cleopatra 0.31 RgbBands for the ArrayGlyph constructor#972
MAfarrag merged 8 commits into
mainfrom
chore/cleopatra-0.31-migration

Conversation

@MAfarrag

@MAfarrag MAfarrag commented Aug 12, 2026

Copy link
Copy Markdown
Member

Description

Adopts cleopatra 0.31.0 (released on PyPI). 0.31 landed one consumer-facing breaking change to what pyramids
consumes — cleopatra serapeum-org/cleopatra#291 grouped ArrayGlyph's four loose RGB band-prep constructor
keywords (rgb / surface_reflectance / cutoff / percentile) into a single RgbBands object, so
ArrayGlyph.__init__ now takes only rgb_bands=. This PR wires pyramids' one ArrayGlyph construction site onto
the new object and bumps the pin.

The single call site. render_array (src/pyramids/dataset/_plot_helpers.py) is the only place pyramids
constructs a cleopatra ArrayGlyph. It now builds an RgbBands from the resolved band indices and stretch
controls on the RGB path:

  • RGB path: rgb_bands = RgbBands(rgb, surface_reflectance=…, cutoff=…, percentile=…).
  • Single-band and animate paths: rgb_bands=None. The animate compositor still uses
    ArrayGlyph.prepare_array(...), which keeps its loose keyword signature in 0.31, so the per-frame RGB
    compositing is unchanged; the constructor's stretch params are nulled after compositing as before.

Every public facade (Dataset.plot, DatasetCollection.plot, NetCDF.plot, Analysis.plot) forwards to
pyramids' own render_array, whose signature is unchanged — so this is the only source change. pyramids' user
surface (rgb_options= on the raster facades) is untouched; RgbBands is an internal detail.

Not affected. The rest of cleopatra#291 (ColorBar.to_options / resolve, PointOverlay.draw,
FrameLabel / PanelLabels method moves, the removed base Glyph._plot_point_values, the new
DataStyle.for_apply_style) is internal cleopatra refactoring that pyramids never called — it drives the grouped
render objects through the public plot / animate / facet API, whose shape is unchanged. cleopatra#298 was
docs/housekeeping. No notebook or test needed changes: the rgb_options facade is pyramids' only RGB entry point,
and nothing constructs a cleopatra ArrayGlyph with loose rgb= outside render_array.

Install source. cleopatra is the published cleopatra[tiles]>=0.31.0 via the [viz] extra; pixi.lock bumped
0.30.0 → 0.31.0 across every environment.

Issues

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

  • Full plot surface against cleopatra 0.31.0:
    tests/dataset/plot tests/netcdf/plot tests/ugrid tests/basemap tests/feature/test_plot_cleopatra.py tests/dataset/collection/test_plot_labels.py720 passed.
  • RGB path specifically (the RgbBands construction): the -k "rgb or percentile or reflect" subset →
    39 passed — single-band, RGB percentile/reflectance stretch, and RGB-animate composites all render.
  • mypy clean on the changed module (cleopatra is typed via the follow_untyped_imports override, so the
    RgbBands construction is type-checked).
  • cleopatra 0.31.0 resolves and imports; ArrayGlyph.__init__ exposes rgb_bands, no loose rgb.

Migration notes (breaking)

Only the pin changes for downstream users of the pyramids raster facades — the pyramids RGB surface is unchanged
(ds.plot(rgb_options={"rgb": [...], "surface_reflectance": ..., "cutoff": ..., "percentile": ...})). The break is
purely the minimum cleopatra version: requires cleopatra >=0.31.0 (0.30.x no longer satisfies the pin, and
0.30's loose ArrayGlyph(rgb=...) constructor is gone upstream).

Behaviour change carried in by the bump (not a pyramids code change). cleopatra 0.31 also ships a semantic
fix in serapeum-org/cleopatra#291: the RGB surface-reflectance cutoff now clips each band's data rather than the
integer band index. Callers that pass cutoff in rgb_options (still-RGB and RGB-animate paths alike) will render
slightly different pixels than on cleopatra 0.30 — this is an upstream correctness improvement, surfaced here so it
is discoverable. No other pyramids-visible output changes with the bump.

Checklist:

  • updated version number in pyproject.toml. (release version is bumped by commitizen in CI, not by hand)
  • added changes to History.rst. (change log is commitizen-generated)
  • updated the latest version in README file. (n/a)
  • I have added tests that prove my fix is effective or that my feature works. (existing RGB suite covers the
    new path; 720 plot tests green)
  • New and existing unit tests pass locally with my changes.
  • documentation are updated. (no user-facing surface changed)

…structor

cleopatra 0.31.0 (serapeum-org/cleopatra#291) grouped the four loose RGB
band-prep constructor keywords (rgb / surface_reflectance / cutoff /
percentile) into a single RgbBands object; ArrayGlyph.__init__ now takes only
rgb_bands=. render_array builds an RgbBands from the resolved band indices and
stretch controls on the RGB path (and passes rgb_bands=None on the single-band
and animate paths, where prepare_array — which keeps its loose signature —
already consumed the stretch). Bump the viz extra to cleopatra[tiles]>=0.31.0.

BREAKING CHANGE: requires cleopatra >=0.31.0.
@MAfarrag MAfarrag changed the title refactor(plot)!: adopt cleopatra 0.31 RgbBands for the ArrayGlyph con… refactor(plot)!: adopt cleopatra 0.31 RgbBands for the ArrayGlyph constructor Aug 12, 2026
…int (L1)

render_array requires cleopatra >= 0.31 for RgbBands, but require_cleopatra()
only checks presence, so a stale cleopatra <0.31 raised a bare
ImportError: cannot import name 'RgbBands'. Translate that ImportError into
OptionalPackageDoesNotExist with the same [viz]-upgrade hint pyramids uses for
other optional-extra version mismatches.
…or (M1, L2)

No test referenced RgbBands. Add two mocked-ArrayGlyph routing tests: the RGB
plot path builds an RgbBands carrying the band indices and all three stretch
controls (surface_reflectance / cutoff / percentile — the L2 gap), and the
single-band and animate paths pass rgb_bands=None. They construct a real
RgbBands so they carry the module's plot marker (render_array needs the [viz]
extra); CI runs the plot lane on every PR.
…rsion (L1, N1)

Import ArrayGlyph/PanelLabels/PointOverlay unguarded and wrap only the RgbBands
import, so a genuine cleopatra module-load failure keeps its real ImportError
instead of being mis-branded as 'missing RgbBands' (L1). Drop the hardcoded
cleopatra version from the error message — the floor lives solely in
pyproject.toml's [viz] pin, which the upgrade hint points at (N1).
…are absent (N1, N2)

Update the too-old-cleopatra test to match the version-free message, and assert
the RGB routing test that the removed loose ctor keywords (rgb/surface_reflectance/
cutoff/percentile) do not leak onto the ArrayGlyph constructor.
@sonarqubecloud

Copy link
Copy Markdown

@MAfarrag
MAfarrag merged commit 04736a4 into main Aug 12, 2026
30 checks passed
@MAfarrag
MAfarrag deleted the chore/cleopatra-0.31-migration branch August 12, 2026 22:49
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.

bug(plot): cleopatra 0.31 removed the loose RGB ArrayGlyph kwargs, breaking all plotting

1 participant