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

[MNT]: Improve backend selection API · Issue #26406 · matplotlib/matplotlib · GitHub

Repository navigation

[MNT]: Improve backend selection API #26406

Open

Description

Summary

We currently have:

  • matplotlib.use
  • matplotlib.get_backend
  • pyplot.switch_backend

Issues:

  • The name matplotlib.use is really obscure.
  • The difference between switch_backend and use is not quite clear.

Proposed fix

  • Consider renaming matplotlib.use (and either discourace or long-term deprecate use (with a pending deprecation)).
  • Check whether use and switch_backend can be unified. If not, at least improve the documentation.

Side note: The force parameter in use can be dropped.

matplotlib.use(..., force=True)

is equivalent to

try:
    matplotlib.use(...)
except ImportError:
    pass

It should be rare that users want to ignore that changing the backend fails. And if so, they can do it explicitly. We do not need to provide API for that. The only slight complication is that this the default behavior is force=True; i.e. dropping would change the default behavor. But we can address that in the course of "renaming": We simply provide a new function, e.g. set_backend with the new behavior.

Activity

  1. added
    API: changesChanges to the public API, typically requiring deprecation.
    API: consistencyConsistency of the matplotlib API, including naming, behavior, defaults, …
    on Jul 28, 2023
  2. changed the title [-][MNT]: Backend selection API[/-] [+][MNT]: Improve backend selection API[/+] on Jul 28, 2023
  3. anntzer commented on Aug 7, 2023

    Contributor

    use() is a somewhat obscure name, but it's so widely used that it's probably never removable.

    The notion of having "a" backend is really a pyplot one (because that's basically what plt.figure()/plt.show() dispatch to), so in theory the backend-control functions should really be in pyplot (and in practice the actual logic, i.e. switch_backend, is indeed implemented in pyplot), but for a long time backends were not switchable after pyplot had been imported hence the existence of use() at the toplevel, which could be called (just to register whatever backend the user actually wants) before pyplot gets imported.
    Now the situation is a bit better, and backend switches are possible, but still not perfect (just importing pyplot still tries to import the "current" backend and thus may load PyQt/wxPython/whatnot in the module space, which was e.g. a problem back when we supported PyQt4 and PyQt5 because these two modules cannot be imported in the same process at all, even without starting an event loop; I'd guess ideally we'd only import the backend module when actually calling plt.figure(), i.e. at the last possible moment). So it's still useful to have a backend setting function accessible before importing pyplot.

    To summarize: I think we need to keep an API outside of pyplot (use) unless we further cleanup pyplot to delay even more the actual backend importing (which is probably doable and worthwhile); also that API may be very hard to ever deprecate. I also think it makes sense for the implementation to live in pyplot (because it's fundamentally a pyplot concept), though it could possibly be made private (I'm not sure I'd really want that, though).

    One should also check how this plays with mpl-gui.

  4. tacaswell commented on Aug 7, 2023

    Member

    just importing pyplot still tries to import the "current" backend and thus may load

    This isn't true in 3.7 any more:

    ✔ 17:05:51 [belanna] @ ipython
    Python 3.11.3 (main, Jun  5 2023, 09:32:32) [GCC 13.1.1 20230429]
    Type 'copyright', 'credits' or 'license' for more information
    IPython 8.14.0 -- An enhanced Interactive Python. Type '?' for help.
    
    In [1]: import matplotlib
    
    In [2]: import matplotlib.pyplot as plt
    
    In [3]: type(matplotlib.rcParams)
    Out[3]: matplotlib.RcParams
    
    In [4]: type(matplotlib.rcParams).mro()
    Out[4]:
    [matplotlib.RcParams,
     collections.abc.MutableMapping,
     collections.abc.Mapping,
     collections.abc.Collection,
     collections.abc.Sized,
     collections.abc.Iterable,
     collections.abc.Container,
     dict,
     object]
    
    In [5]: dict.__getitem__(matplotlib.rcParams, 'backend')
    Out[5]: <object at 0x7ff7fd8ffc80>
    
    In [6]: matplotlib.__version__
    Out[6]: '3.7.2'
    
    In [7]:
    

    but if you do anything that iterates over the rcParams (like tab-complete in IPython (🤯)) it will run though the fall-back logic and pick a backend. In IPython picking the backend will also aggressively start the event loop by installing the inputhook.

    I agree with @anntzer "active backend" is really a pyplot concept and if possible we should push it as far into pyplot as we can.

    The real trap we are in is:

    • users can use rcParams to set the backend
    • we want the rcParams['backend'] to always reflect reality
    • we do not want to return the auto-sentinel to users (and we need the auto sentinal to detect "the user is on a headless system and does not care, give them agg" vs "the user asked for Qt, they are on a headless system, raise because we can not do what they asked).

    Giving up any of those three would make the problem a lot simpler. The softest one is probably the third (breaking type stability on rcParams['backend']) and letting the user know "we have not locked a backend yet". If we accept that we can drop the "maybe import pyplot" logic in the rcParams object.

    I think the path would be then to:

    • stop documenting mpl.use and mpl.get_backend() in examples etc (agree with @anntzer that they are too widespread to actually deprecate)
    • highlight plt.switch_backend in our docs as the "right" way to force a backend of the user's choice
    • move the actual selection to only automatically happen when plt.figure is called (now it is that or when rcParams['backend'] or mpl.get_backend() is used).
  5. anntzer commented on Aug 7, 2023

    Contributor

    The softest one is probably the third (breaking type stability on rcParams['backend']) and letting the user know "we have not locked a backend yet"

    By the way when I first implemented the autoselection logic there was also the idea of allowing the user to explicitly set rcParams["backend"] to a list of strings (the list of backends that will be tested when we actually need to select a backend) instead of introducing an opaque object representing the (fixed) builtin fallback order (see discussion in the old threads). The idea was rejected on grounds of type stability, but may be perhaps worth revisiting (... or not). (Reading rcParams["backend"] could also return that list if we haven't selected the backend yet, or (as we do now) force selection and return just the selected backend.)

  6. tacaswell commented on Aug 7, 2023

    Member

    I am coming around the the list version more than when it was first proposed. plt.switch_backend([list, of, options]) also seems nice?

  7. timhoffm commented on Aug 7, 2023

    MemberAuthor

    All of the above makes sense.

    It would also be a lot easier if rcParams["backend"] would not need to reflect the current state, i.e. it's just a (list of) value(s) to look up when needed. Similar to prop_cycle not holding the actual cycler state.
    But I don't see a reasonable migration strategy (other than deprecating rcParams["backend"] and replacing it with rcParams["default_backend"] and deferring all actual setting/retrieving actions to pyplot functions).

  8. tacaswell commented on Aug 9, 2023

    Member

    'proposed_backend' 'preferred_backend', 'backend_priority', 'backend_order'?

  9. timhoffm commented on Aug 10, 2023

    MemberAuthor

    Do the name proposals mean, you consider it worthwile redesigning the rcParams backend interaction (including the implied API changes)?

  10. jklymak commented on Aug 10, 2023

    Member

    I feel that the few things that are not "style" would ideally be segregated from rcParams.

  11. tacaswell commented on Aug 10, 2023

    Member

    Do the name proposals mean, you consider it worthwile redesigning the rcParams backend interaction (including the implied API changes)?

    I think it is worth considering so we can get away from rcParams having to be updated to match the selected backend.

    I feel that the few things that are not "style" would ideally be segregated from rcParams.

    Yes, but the ability to set the preferred backend via configuration file is too useful to give up and I think the cost of having backend information in rcParams is a lesser evil than having a second configuration file.

  12. added this to the v3.9.0 milestone on Aug 10, 2023
  13. removed this from the v3.9.0 milestone on Mar 13, 2024
  14. 5 remaining items

  15. rcomer commented on Oct 30, 2024

    Member

    (Default t.b.d. resolve=True is what we are currently using, but resolve=False is the more sane default - triggering resolution as a side-effect should be opt-in)

    I would think that most end-users who use this are doing some kind of debugging and want to know "which backend will mpl use?". So they would want resolve=True and be confused to get None by default.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    API: changesChanges to the public API, typically requiring deprecation.API: consistencyConsistency of the matplotlib API, including naming, behavior, defaults, …Maintenance

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL