Skip to content

Close leftover figure in plot/cplot/splot on error (fixes #1007) - #1123

Merged
skirpichev merged 2 commits into
mpmath:masterfrom
apoorvdarshan:fix-1007-stale-figure
Jul 10, 2026
Merged

Close leftover figure in plot/cplot/splot on error (fixes #1007)#1123
skirpichev merged 2 commits into
mpmath:masterfrom
apoorvdarshan:fix-1007-stale-figure

Conversation

@apoorvdarshan

Copy link
Copy Markdown
Contributor

Summary

plot(), cplot() and splot() create the matplotlib figure up front
(pylab.figure() / plt.subplots()) but only call show()/savefig() at the
very end. When the user-supplied function raises an exception that is not in
plot_ignore (for example plot(lambda t: 'a', (0, 2)), which raises
TypeError inside ctx.isnan(v)), the exception propagates out of the function,
but the figure that was already created is never closed. It stays registered in
matplotlib's global figure manager and is then displayed as a stale blank window
on the next successful call. This is issue #1007.

Fix

Wrap the body of each of the three functions in try/except. On any exception,
close the figure we created internally and re-raise. The cleanup only runs for
figures owned by the function (fig is not None); when the caller passes their
own axes=, fig is None and their figure is left untouched. Successful
plotting is unchanged.

Tests / Verification

  • Added test_issue_1007 in mpmath/tests/test_visualization.py. It drives all
    three functions with a function that raises and asserts
    pylab.get_fignums() == [] afterwards. The test fails on the current code
    (a leftover figure [1] remains) and passes with the fix.
  • test_axes and the module doctests still pass.
  • Verified manually that successful plot/cplot/splot calls still save/show
    and that passing a caller-supplied axes does not close the caller's figure,
    either on success or when the function raises.
  • flake518 reports no issues on the changed files.
  • Added a CHANGES entry under Bug fixes.

Disclosure: prepared with AI assistance; reviewed and verified locally.

plot(), cplot() and splot() create the matplotlib figure up front but
only show or save it at the very end. If the user-supplied function
raised an exception that is not in plot_ignore (e.g. plot(lambda t: 'a',
(0, 2))), the figure was created and never closed, so it lingered in
matplotlib's global figure registry and was displayed as a stale blank
window on the next successful call.

Wrap the body of each function in try/except and close the internally
created figure before re-raising, so nothing is left behind. Figures
supplied by the caller via axes= are untouched.

Fixes mpmath#1007.
@skirpichev
skirpichev self-requested a review July 9, 2026 23:27
@skirpichev skirpichev added this to the 1.5 milestone Jul 9, 2026

@skirpichev skirpichev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Sorry, no. This touches too much code without good reason.

Can't you just add another except block after except ctx.plot_ignore? For example (please don't use bare except):

diff --git a/mpmath/visualization.py b/mpmath/visualization.py
index d87beb67..e0c7b1fa 100644
--- a/mpmath/visualization.py
+++ b/mpmath/visualization.py
@@ -81,6 +81,9 @@ def plot(ctx, f, xlim=[-5,5], ylim=None, points=200, file=None, dpi=None,
                 if segment:
                     segments.append(segment)
                 segment = []
+            except Exception:
+                pylab.close(fig)
+                raise
         if segment:
             segments.append(segment)
         for segment in segments:

Comment thread CHANGES Outdated

@skirpichev skirpichev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

OK, I did suggested changes.

@skirpichev
skirpichev merged commit 8697816 into mpmath:master Jul 10, 2026
15 checks passed
@skirpichev

Copy link
Copy Markdown
Collaborator

Thanks.

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