Skip to content

Fix Figure.clear leaving stale state and decouple ui kwarg routing#760

Merged
cvanelteren merged 2 commits into
mainfrom
fix/figure-clear-stale-subplot-state
Jul 14, 2026
Merged

Fix Figure.clear leaving stale state and decouple ui kwarg routing#760
cvanelteren merged 2 commits into
mainfrom
fix/figure-clear-stale-subplot-state

Conversation

@cvanelteren

Copy link
Copy Markdown
Collaborator

Matplotlib's clear() detaches every axes and artist and sets _suptitle to None, but ultraplot kept its own bookkeeping pointing at the destroyed objects: a cleared figure went on handing out dead axes through subplotgrid, _get_subplot, _iter_subplots and _iter_axes, kept a stale gridspec and label counter, leaked figure-level panels, and raised AttributeError from the next format(suptitle=...) because the suptitle artist was gone, so this adds SubplotManager.reset() and overrides Figure.clear() to call it, empty the panel dict, reset the layout flags, and rebuild the label artists via the extracted _init_super_labels() (which also covers clf(), matplotlib's alias for clear()). It further removes a footgun in ui.py, which split figure keywords from subplot keywords by introspecting the signatures of Figure._parse_proj and Figure._add_subplots even though those are pure pass-throughs whose only remaining job was to mirror SubplotManager's parameter list -- a contract nothing enforced, and one that already fired once when collapsing _parse_proj to (*args, **kwargs) made _pop_params see no projection parameters and silently routed proj to the figure, raising from Figure.set(); _pop_params now introspects SubplotManager.parse_proj and .add_subplots, which actually own those parameters, so the delegators are free to collapse back to (*args, **kwargs).

Closes #755, closes #756

Matplotlib's `clear()` detaches every axes and artist and sets `_suptitle` to None, but ultraplot kept its own bookkeeping pointing at the destroyed objects: a cleared figure went on handing out dead axes through `subplotgrid`, `_get_subplot`, `_iter_subplots` and `_iter_axes`, kept a stale gridspec and label counter, leaked figure-level panels, and raised `AttributeError` from the next `format(suptitle=...)` because the suptitle artist was gone, so this adds `SubplotManager.reset()` and overrides `Figure.clear()` to call it, empty the panel dict, reset the layout flags, and rebuild the label artists via the extracted `_init_super_labels()` (which also covers `clf()`, matplotlib's alias for `clear()`). It further removes a footgun in `ui.py`, which split figure keywords from subplot keywords by introspecting the signatures of `Figure._parse_proj` and `Figure._add_subplots` even though those are pure pass-throughs whose only remaining job was to mirror `SubplotManager`'s parameter list -- a contract nothing enforced, and one that already fired once when collapsing `_parse_proj` to `(*args, **kwargs)` made `_pop_params` see no projection parameters and silently routed `proj` to the figure, raising from `Figure.set()`; `_pop_params` now introspects `SubplotManager.parse_proj` and `.add_subplots`, which actually own those parameters, so the delegators are free to collapse back to `(*args, **kwargs)`.

Closes #755, closes #756
@cvanelteren cvanelteren marked this pull request as ready for review July 14, 2026 03:13
@codecov

codecov Bot commented Jul 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@cvanelteren cvanelteren merged commit 317db84 into main Jul 14, 2026
17 of 18 checks passed
@cvanelteren cvanelteren deleted the fix/figure-clear-stale-subplot-state branch July 14, 2026 03: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

1 participant