Skip to content
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

Minimal shared library spec #32

Open
jsignell opened this issue Jul 13, 2019 · 10 comments
Open

Minimal shared library spec #32

jsignell opened this issue Jul 13, 2019 · 10 comments

Comments

@jsignell
Copy link
Member

There was discussion at SciPy sprints about having a shared API for accessing objects from various plotting libraries. We think there could be a spec for common actions such as save, show, html_repr. This issue is to start that discussion. @munkm @tacaswell @jonmmease

@jsignell jsignell assigned jsignell and unassigned jsignell Jul 13, 2019
@jonmmease
Copy link
Collaborator

Thanks @jsignell, here are some API entry points in plotly.py (as of version 4, which is in release candidate stage at the moment) that might be candidates for inclusion in a spec (and definitely open to renaming if the concepts make sense but other names are preferred). I've numbered the ideas to aid discussion.

.show() method

  1. The top-level figure class has a .show() method that can be called without arguments to display the figure as a side-effect.
  2. The optional renderer kwarg can be used to override the current default renderer. e.g.:
    • fig.show(renderer='png') to display the figure as a static png image/
    • fig.show(renderer='browser') to display the figure in a browser tab. This works in non-jupyter/IPython contexts.

Self-displaying figures

  1. When figures are displayed in an ipython context, we use the _ipython_display_ protocol to call fig.show().

Exporting figures

Figures have write_* and to_* methods for exporting/converting figures. We don't currently have a .save method because it would be a bit ambiguous in our case.

  1. The write_* methods write the figure to a file (or writable object) and return None. If the first argument to a write_* method is a string, then it is treated as a local file path that can be opened with the standard Python open function. Otherwise, the object passed as the first argument is assumed to be writable (i.e. to have a .write method). Beyond the first required argument, each export approach can have its own optional configuration kwargs. For example
    • .write_image('/path/to/output.png')
    • .write_image('/path/to/output.png', width=800, height=600)
    • .write_json('/path/to/output.json')
    • .write_html('/path/to/output.html')
    • .write_html('/path/to/output.html', include_plotlyjs='cdn')

Or with a writable object

with open('/path/to/output.html', 'w') as f:
    fig.write_html(f, include_plotlyjs='cdn')
  1. The to_* methods return an in-memory representation of the exported figure and don't write any files. They should have the same signature as the corresponding .write method without the first file argument. Ideally, these methods can be called with no arguments and reasonable defaults are chosen. For example:
    • .to_image returns the static image bytes object.
    • .to_html returns a string containing the HTML representation of the figure.
    • .to_json returns a string containing the JSON representation of the figure.

Accessing root figure

  1. Every object in the figure hierarchy (plotly.py calls these "graph objects") has a .figure property that returns the parent figure, if any, or None if the graph object does not belong to a figure.

@jbednar
Copy link
Member

jbednar commented Jul 15, 2019

That sounds really cool! I'd go further and say that such a spec should get a name and revision and have an associated repo in the pyviz organization, with automated tests that define whether each library meets the spec up to revision X (which can be shown in a status page like pyviz.org/tools.html). Seems like a great way to use the pyviz organization!

@jbednar
Copy link
Member

jbednar commented Jul 15, 2019

I'd also propose that the spec include support for the various IPython rich display methods, i.e. repr_html, repr_png, etc. so that each library could be treated the same in how things are displayed inline.

@djhoese
Copy link
Collaborator

djhoese commented Jul 15, 2019

Very interesting idea. Always a big fan of consistency. I have some questions though: Would this spec only apply to plotting interfaces of a library that provide a specific "figure" object? Do most plotting or visualization libraries provide that? What about generic visualizations or animation that may or may not be a "plot" (axes, ticks, etc)?

If a library has every method, but doesn't have a JSON representation for its figures (so no to_json) does it now look "less complete" on the PyViz site because it can't implement every piece of the spec?

@jbednar
Copy link
Member

jbednar commented Jul 15, 2019

I would say that if a given operation doesn't make sense for that library, then it could satisfy the spec by simply having that method return a message to that effect ("JSON support not available in x-viz").

I don't think any of the operations involved here would require axes, ticks, etc., just a viewable representation.

I'm not sure about the semantics required of "figure"; to me that would just be "some object that has a visible representation but which can also be saved, exported, etc.".

@jakevdp
Copy link
Collaborator

jakevdp commented Jul 15, 2019

This is a great idea. I'm happy to implement in Altair as soon as there's consensus on the spec.

@jakevdp
Copy link
Collaborator

jakevdp commented Jul 15, 2019

Another thing that might be useful: a uniform way to enable various libraries in jupyter notebook & jupyterlab. For example, matplotlib has %matplotlib inline, bokeh has bokeh.output_notebook(), altair has alt.renderers.enable('notebook'), etc. If every library agreed to define something like %enable_{LIBNAME}, things would be much more predictable for the user.

@jbednar
Copy link
Member

jbednar commented Jul 15, 2019

A uniform way to enable various libraries in jupyter is a great idea, but I would argue that such a specification should not rely on magics. We routinely use Jupyter notebooks as literate programming environments for developing and sharing code that we need to be usable both within and outside of jupyter; e.g. with many of our notebooks we can do panel serve nb.ipynb to launch them as standalone dashboards without Jupyter involved at all. We can always add special cases to deal with magics, but I don't think that's a good idea; I greatly prefer just to have a normal Python call that can register things with Jupyter if it's available but doesn't otherwise cause syntax errors (if not skipped) or missing functionality (if skipped) outside of Jupyter/IPython.

@jakevdp
Copy link
Collaborator

jakevdp commented Jul 15, 2019

Sure, makes sense.

@jsignell
Copy link
Member Author

I wrote up these initial ideas at https://github.com/pyviz/spec/tree/01_02_03 and made you all admin of that repo. Please comment over there. Ping me if you want to be part of that discussion and haven't been added.

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

No branches or pull requests

5 participants