Skip to content

Promote _Backend to public api #19605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ianhi opened this issue Mar 1, 2021 · 4 comments
Closed

Promote _Backend to public api #19605

ianhi opened this issue Mar 1, 2021 · 4 comments
Labels
New feature status: inactive Marked by the “Stale” Github Action status: needs comment/discussion needs consensus on next step

Comments

@ianhi
Copy link
Contributor

ianhi commented Mar 1, 2021

Problem

Discussed on gitter here: https://gitter.im/matplotlib/matplotlib?at=603c83fc44f5a454a447c6e9

The _Backend class added in #8773 would be useful for external backends such as ipympl, and is already used by mplcairo. But it is currently marked as a private.

Proposed Solution

  1. Make _Backend public
  2. Document _Backend as stable.
@anntzer
Copy link
Contributor

anntzer commented Mar 1, 2021

The reason it's already used by mplcairo is because I essentially designed and implemented _Backend for that purpose :-)

I actually think making _Backend public is not best (because @_Backend.export is pretty weird); I have a better solution in mind (starting from something along the lines of #18854, please review :-)). I'll try to post a longer writeup later.


Edit: slightly more elaborated version:

The problem that the _Backend helper is trying to solve is that there is an inheritance-like relationship between backends: if backends were actually classes with FigureCanvas and FigureManager attributes, then backend_qt5agg and backend_qt5cairo could both inherit from backend_qt5 while only overriding FigureCanvas, but keeping the same FigureManager. Previously, this relationship was not really exploited, leading to a lot of duplicated implementations of e.g. new_figure_manager, new_figure_manager_given_canvas, and so on, across backends. I didn't want to further repeat them in mplcairo, so in #8773, I made that inheritance relationship explicit in the (private) _Backend class, while keeping the old API by basically exporting the contents of that class to the module level (via _Backend.export).

I think the better API would be to explicitly acknowledge the inheritance-like relationship in an actual hierarchy of classes. There's two possibilities here:

  • Make _Backend public (e.g. as BackendBase), and state that a matplotlib backend is either a module that has a "Backend" global which subclasses BackendBase (in which case we look up new_figure_canvas, etc. as attributes of Backend), or (for backcompat) a module with new_figure_canvas, etc. functions. This avoids the need for the slightly weird _Backend.export.

  • Move that inheritance hierarchy to the FigureCanvas inheritance hierarchy (basically starting with something like Standardize creation of FigureManager from a given FigureCanvas class. #18854), so that backend-generic code (including pyplot) handles everything through the FigureCanvas subclass provided by the backend module. For example, pyplot would create new managers using FigureCanvas.new_manager(), etc. (as usual, keeping the old approach supported for backcompat).

@anntzer
Copy link
Contributor

anntzer commented Aug 18, 2021

@ianhi Actually, I just realized that there's (I think, probably... but see PS below) a much simpler solution (that should get documented) available for you (because you don't "inherit" from another _Backend). Taking matplotlib/ipympl#305 as a starting point, you can just get rid of _Backend_ipympl, moving FigureCanvas, FigureManager, new_figure_manager_given_figure(), and show() to be toplevel definitions, and that should be all that's needed. (Then your module will not define its own new_figure_manager and draw_if_interactive, which could be considered an API break if you consider that that's public API, but at least Matplotlib will be fine with it because it internally generates a shim to replace it, see

# Backends are implemented as modules, but "inherit" default method
# implementations from backend_bases._Backend. This is achieved by
# creating a "class" that inherits from backend_bases._Backend and whose
# body is filled with the module's globals.
backend_name = cbook._backend_module_name(newbackend)
class backend_mod(matplotlib.backend_bases._Backend):
locals().update(vars(importlib.import_module(backend_name)))
.) In fact, you could perhaps even consider making the additional (transparency handling) code in new_figure_manager_given_figure() to either Canvas.__init__ or FigureManager.__init__ (whichever makes more sense), and then you could then get rid of the definition of new_figure_manager_given_figure as well, again relying on the Matplotlib-generated shim.

(PS: I remembered this point while working on a toy backend; I tried to check whether this works specifically for ipympl but once again I couldn't make Matplotlib work in Jupyter notebooks and I have limited patience in fiddling with that :-))

Copy link

This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

@github-actions github-actions bot added the status: inactive Marked by the “Stale” Github Action label Nov 22, 2023
@anntzer
Copy link
Contributor

anntzer commented Nov 22, 2023

Superseded by #22925, I would say.

@anntzer anntzer closed this as completed Nov 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
New feature status: inactive Marked by the “Stale” Github Action status: needs comment/discussion needs consensus on next step
Projects
None yet
Development

No branches or pull requests

2 participants