FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

[MNT]: Lifetime of pyplot figures · Issue #29849 · matplotlib/matplotlib · GitHub

Repository navigation

[MNT]: Lifetime of pyplot figures #29849

Closed as not planned
Closed as not planned

Description

Summary

Inspired by but technically orthogonal to the recent discussions #29782, #29836: Can we change the lifetime behavior so that (1) users have to care/know less about how pyplot manages the figure - ideally no close() is needed - and (2) we do not break typical existing usage patterns?

Proposed fix

Observations / Questions:

  1. Why does pyplot has to keep a figure reference?
    • 1.1: This prevents garbage collection if the user does not store the figure in a variable.
    • 1.2: It allows managing multiple figures to change the current figure.
    • 1.3: It manages the figure interactions if the figure is shown.
  2. It is unlikely that people manage a lot of figures in parallel through the pyplot state - (if you have 20 figures, you'll propably not organize them through pyplot - or if you do, you at least assign dedicated fignums when creating them)

This combination tells me there is a space for criterions to auto-delete figures:

One interpretation could be: a figure is not needed anymore if all of the following apply:

  • the figure is not shown (no interaction possible)
  • the figure is not the active figure (it's currently not worked on)
  • the figure does not have a user-created fignum (it's not possible to make the figure active again)

This would effectively mean that you only have muliple figures tracked in pyplot if either they are currently shown or they have explicit user-created fignums. Conversely, figures without user-created fignums are auto-deleted when appropriate.
IMHO this is a very reasonable logic and removes the need to understand pyplot tracking or use of close for almost all users.

There's only one minor use case I can imagine that would get broken by this: Users relying on the implictly created fignumbers for changing the active figure:

plt.figure()  # num=1
plt.plot()

plt.figure()  # num=2
plt.plot()

plt.figure(1)  # knowing that the first figure() call auto-assigned num=1

But this behavior is brittle anyway because you have to know that you're in a fresh interpreter. There are three mitigation strategies:

  • deprecate recalling a figure with an implicitly created figure number
  • keep the last N (e.g. 5) implicitly created figures still around. This recall by implicit number may be feasible for one or two figures but nobody can do this for many figures
  • don't care as this is a brittle edge-case, which people should not be using anyway

Activity

  1. rcomer commented on Apr 1, 2025

    Member

    I have some old code that is basically

    for fig in [fig1, fig2]:
        ax = fig.add_subplot(3, 3, plot_num)
        plt.sca(ax)
        ... plot stuff ...

    I likely would not write it using plt.sca now (the blame says it's at least 7 year old!) but there are third party packages that insist on using the current axes, so I think it is not an unreasonable pattern.

    It might be nice if pyplot could register an already-existing figure - I assume there are technical reasons not to let it?

  2. timhoffm commented on Apr 1, 2025

    MemberAuthor

    Good point, that way (or generally by passing a Figure instance plt.figure(fig)) one can make a figure current again. This would if the figure was removed from pyplot according to the above proposal.

    While it's currently prohibited, I don't think there would be a fundamental problem whith registering an already-existing figure. Actually making is possible to add/remove figures from pyplot tracking would make the pyplot code less entangled.


    Edit: Actually, we already can attach an existing figure to pyplot and use that for unpickling Figure:

    if restore_to_pylab:
    # lazy import to avoid circularity
    import matplotlib.pyplot as plt
    import matplotlib._pylab_helpers as pylab_helpers
    allnums = plt.get_fignums()
    num = max(allnums) + 1 if allnums else 1
    backend = plt._get_backend_mod()
    mgr = backend.new_figure_manager_given_figure(num, self)
    pylab_helpers.Gcf._set_new_active_manager(mgr)
    plt.draw_if_interactive()

  3. rcomer commented on Apr 1, 2025

    Member

    Slightly tangential, but I also just quite like the idea that you could decide after the fact that you want your figure in a gui.

    my_fig = matplotlib.figure.Figure(...)
    ax = my_fig.subplots()
    ... add artists ...
    
    plt.figure(my_fig)
    plt.show()
  4. timhoffm commented on Apr 1, 2025

    MemberAuthor

    Slightly tangential, but I also just quite like the idea that you could decide after the fact that you want your figure in a gui.

    I have a prototype working for this 😄

  5. rcomer commented on Apr 2, 2025

    Member

    I think this will also break the case where you do not care how your figures are tracked, but want to show them all together. This is probably quite common.

    for ds, name in zip(my_datasets, dataset_names):
        fig, ax = plt.subplots()
        ax.plot(ds, ...)
        ax.set_title(name)
    
    plt.show()
  6. timhoffm commented on Apr 2, 2025

    MemberAuthor

    This example breaks my assumption that you need to somehow be able to retrieve an individual figure again.

    The two variants with/wo the last show are possible and common:

    for ds, name in zip(my_datasets, dataset_names):
        fig, ax = plt.subplots()
        ax.plot(ds, ...)
        ax.set_title(name)
        plt.savefig(f"{name}.png")
    
    plt.show()  # Comment or uncomment

    We can at no time know whether the figure is still needed because there could always be a plt.show() down the road. This means we cannot detect the "just savefig" case, which is the common cause for memory buildup. 😢

    We cannot just be clever and auto-clean while keeping full backward-compatibility for relevant use cases. One could still think about the reasonable lifetime for pyplot figure management, but there would be breaking changes at least for some usage scenarios, which makes it much less attractive.

  7. timhoffm commented on Oct 13, 2025

    MemberAuthor

    Superseeded by #29855; in particular by switching out the canvas when figures are closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL