Skip to content

Add a backend kwarg to savefig. #15536

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

Merged
merged 1 commit into from
Oct 30, 2019
Merged

Conversation

anntzer
Copy link
Contributor

@anntzer anntzer commented Oct 27, 2019

This makes it easier e.g. to test the pgf backend without globally
switching the backend.

Makes it easier to work on pgf related issues; also helps with #13994 (attn @ArchangeGabriel; see changelog).

PR Summary

PR Checklist

  • Has Pytest style unit tests
  • Code is Flake 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

@tacaswell tacaswell added this to the v3.3.0 milestone Oct 27, 2019
@tacaswell
Copy link
Member

Notionally 👍 one slight behavior question.

Adding a the new parameter which is sometimes None first is a bit odd to me, but I see the argument for that as it is consulted first.

@anntzer
Copy link
Contributor Author

anntzer commented Oct 27, 2019

backend has priority over fmt, so that's why I put them in this order (also, it's private API anyways).

@timhoffm
Copy link
Member

timhoffm commented Oct 27, 2019

I'm a bit hesitant. A backend parameter in savefig() is a big API promise. I really don't claim to understand the whole backend machinery, but, setting the backend only for the savefig() call feels too easy given that we've been jumping a lot of hoops to delay backend selection. Is this really a fully working solution that can live up to the API promised?

  • Does nothing we do in any plotting command (now or in the future) depend on the backend? Otherwise just setting the backend in savefig() is not equivalent to setting the backend earlier, and that would be quite surprising.
  • Following a similar logic, can we then equally do plt.show(backend='QtAgg')?
  • Would a context manager for backends be a reasonable alternative.
  • Do we need checks that the format/extension and the backend are compatible?

@anntzer anntzer force-pushed the backendsavefigkwarg branch from 6650f21 to f647d15 Compare October 27, 2019 20:53
else:
# Return a default canvas for the requested format, if it exists.
canvas_class = get_registered_canvas_class(fmt)
if not hasattr(canvas_class, f"print_{fmt}"):
Copy link
Member

Choose a reason for hiding this comment

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

I think this is in the wrong block of the if/elif/else.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops, fixed

Copy link
Member

Choose a reason for hiding this comment

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

That probably means that a unit test should be written for this :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fair enough, done

@anntzer anntzer force-pushed the backendsavefigkwarg branch from f647d15 to 7852a8f Compare October 27, 2019 20:56
@anntzer
Copy link
Contributor Author

anntzer commented Oct 27, 2019

re @timhoffm:

Does nothing we do in any plotting command (now or in the future) depend on the backend? Otherwise just setting the backend in savefig() is not equivalent to setting the backend earlier, and that would be quite surprising.

I believe not (the backend-dependent logic is in draw(), which is only called by savefig()).

Following a similar logic, can we then equally do plt.show(backend='QtAgg')?

Backend switching is subject to the same rules as use() (i.e. you cannot switch to an interactive backend requiring a GUI event loop once another GUI event loop is running). But switching to a noninteractive backend is fine (and the intended use case) -- note that we already implicitly do so for printing to pdf/ps/svg; this kwarg only makes it explicit and allows you to switch to backend_pgf rather than backend_pdf when printing to pdf.

Would a context manager for backends be a reasonable alternative.

I don't think there is anything else one would want to do within the context manager other than doing the save, which suggests that factoring out the backend switching is not worth it.

Do we need checks that the format/extension and the backend are compatible?

This has been added per @tacaswell's suggestion.

@timhoffm
Copy link
Member

@anntzer thanks for the clarification.

I believe not (the backend-dependent logic is in draw(), which is only called by savefig()).

Are use cases affected that use an intermediate draw to get some position information and use that to position subsequent elements?

Backend switching is subject to the same rules as use() (i.e. you cannot switch to an interactive backend requiring a GUI event loop once another GUI event loop is running). But switching to a noninteractive backend is fine (and the intended use case)

Something like this should be explicitly mentioned in the docstring of the backend parameter; maybe even with a list of supported builtin backends.

The other points are ok.

@anntzer
Copy link
Contributor Author

anntzer commented Oct 28, 2019

Are use cases affected that use an intermediate draw to get some position information and use that to position subsequent elements?

You'd run into the same issues (if any) as if saving to pdf from an interactive canvas, so nothing new.

Something like this should be explicitly mentioned in the docstring of the backend parameter; maybe even with a list of supported builtin backends.

done.

@anntzer anntzer force-pushed the backendsavefigkwarg branch from 7852a8f to 9fd5da5 Compare October 28, 2019 07:59
@ArchangeGabriel
Copy link
Contributor

@anntzer Thanks for this work! Does the PR include a savefig.backend rcParams (it does not seems so), and if not could it be possible to add one?

@anntzer
Copy link
Contributor Author

anntzer commented Oct 28, 2019

It doesn't and fwiw I still don't like the idea.

@anntzer anntzer force-pushed the backendsavefigkwarg branch from 9fd5da5 to d001e38 Compare October 28, 2019 15:03
backend : str, optional
When set, forcibly use this non-interactive backend for rendering.
This can be either a standard backend name ("agg", "pdf", "pgf",
"ps", "svg") or a custom backend name ("module://...").
Copy link
Member

Choose a reason for hiding this comment

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

This could use a comment as to why we would ever want to do this. I'm not sure I understand from the PR what the goal is, so I can imagine users trying to dutifully use this keyword and getting themselves into a mess.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@anntzer anntzer force-pushed the backendsavefigkwarg branch from d001e38 to ab74e58 Compare October 28, 2019 18:13
"ps", "svg") or a custom backend name ("module://..."). The main
use case of this parameter is to render to a format using a backend
that is neither the current backend, nor the default backend for
this format, e.g. for rendering pdfs using the pgf backend.
Copy link
Member

Choose a reason for hiding this comment

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

I'm still not clear. Are there other examples, like cairo instead of agg etc? If there is just one use case, I don't think this should get such a generic sounding kwarg name. If there are other cases, I'm confused about which ones are allowed. If I save to png, can I use pgf? Can I use the pdf backend to save to svg? Where are the compatibilities documented?

Copy link
Contributor Author

@anntzer anntzer Oct 28, 2019

Choose a reason for hiding this comment

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

Indeed, you can also use cairo (either the builtin, so "cairo", or mplcairo ("module://mplcairo.base")) to save to any format.
For the builtin backends the compat table is at https://matplotlib.org/devdocs/tutorials/introductory/usage.html#the-builtin-backends (except that pgf is missing -- will open a separate PR for that -- now at #15555).

Copy link
Member

@jklymak jklymak Oct 28, 2019

Choose a reason for hiding this comment

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

OK, thats more clear. I wonder if we change the kwarg to renderer and refer to this table somehow?

renderer: str, optional
     Use a non-default backend to render the file, i.e. `renderer='cairo'` for a png 
rather than the default 'agg' backend, or 'pgf' for a PDF instead of the default 'pdf'.  
See :doc:`/tutorials/introductory/usage` for a list of valid backends for each file format.  
Note custom backends can be referenced as "module://...".

Copy link
Member

Choose a reason for hiding this comment

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

BTW just to be clear, I'm balking at "backend" because the user will already have likely been subjected to the confusing idea that they have to select a backend, and then see this kwarg and assume the have to make it match up somehow. I think we should try and be clear that 99.9% of the time the user should not need to use this kwarg.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The doc sounds good, mostly used your suggestion with minor edits.
The argument matches the argument to matplotlib.use(), so I think it makes sense to keep the same argument name, so I'd still prefer backend here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

and added "Note that the default backend is normally sufficient."

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good (though you didn't push anything yet, right?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops, missed the push, done now

@anntzer anntzer force-pushed the backendsavefigkwarg branch from ab74e58 to bfe34be Compare October 28, 2019 21:19
@tacaswell
Copy link
Member

Doc failures look real:

/home/circleci/project/lib/matplotlib/figure.py:docstring of matplotlib.figure.Figure.savefig:98: WARNING: Unknown target name: "the-builtin-backends".
/home/circleci/project/lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.savefig:98: WARNING: Unknown target name: "the-builtin-backends".
/home/circleci/project/lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.FigureCanvasBase.print_figure:42: WARNING: Unknown target name: "the-builtin-backends".

Copy link
Member

@tacaswell tacaswell left a comment

Choose a reason for hiding this comment

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

👍 modulo fixing the doc build

This makes it easier e.g. to test the pgf backend without globally
switching the backend.
@anntzer anntzer force-pushed the backendsavefigkwarg branch from bfe34be to 154a312 Compare October 29, 2019 08:32
@anntzer
Copy link
Contributor Author

anntzer commented Oct 29, 2019

doc build fixed

@NelleV NelleV merged commit 3272f8c into matplotlib:master Oct 30, 2019
@anntzer anntzer deleted the backendsavefigkwarg branch October 30, 2019 10:17
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.

6 participants