Reputation: 817
I'm trying to programmatically generate a notebook, execute it, and convert to HTML using nbformat.
The code is basically this:
import nbformat as nbf
from nbconvert.preprocessors import ExecutePreprocessor
from nbconvert import HTMLExporter
nb = nbf.v4.new_notebook()
nb.cells = [nbf.v4.new_code_cell("""
import altair as alt
alt.renderers.enable("notebook")
# ... load df
alt.Chart(df).mark_point().encode(x='x', y='y')
""")]
ep = ExecutePreprocessor(timeout=600, kernel_name='python3')
ep.preprocess(nb, {'metadata': {'path': '.'}})
html_exporter = HTMLExporter()
html_exporter.template_file = 'basic'
(body, resources) = html_exporter.from_notebook_node(nb)
with open('out.html', 'w') as f:
f.write(body)
The flow is working fine, it generates a notebook and an HTML file and all the code is executed. However, the output of the code cell is the following (no chart):
<vega.vegalite.VegaLite at 0x7f80c4d8a090>
When I write out the notebook as an .ipynb file and execute it manually, in the same environment, the chart is displayed as expected. Is it possible to use Altair in this way? I wonder if the fact that I'm running nbformat inside a Python session makes a difference, maybe it can only render charts in a browser?
Upvotes: 2
Views: 421
Reputation: 86533
The difference between generating a notebook via nbformat and running a notebook live is that nbformat does not have a frontend attached. This is significant here, because for Altair charts, the frontend notebook extension is what renders the chart.
So why doesn't the rendered chart show up when you open the notebook after generating it? It's because of the security model of the Jupyter notebook: the notebook will not execute existing Javascript when opening a new untrusted notebook, because this would be an easy attack path (e.g. an attacker could publish a notebook with javascript that, on load, executes python code that searches your hard drive for sensitive information and mails it to the attacker, before you have a chance to read the notebook contents).
For this reason, upon rendering a chart, the vega notebook extension generates a PNG preview and saves it to the notebook, so when you share it with others they get a static chart preview as a placeholder until they actually execute the notebook. But if you execute the notebook without a frontend, this PNG preview is not generated, so you see nothing when opening the notebook.
The best way around this would be to use JupyterLab rather than Juptyer Notebook. In JupyterLab, Altair charts are saved using the VegaLite Mimetype, which is interpreted by the vega labextension and turned into a chart. This does not involve execution of arbitrary javascript from the notebook, and so charts you create this way will be rendered in the notebook at load time in JupyterLab. This path is not used in the classic notebook because classic does not support mimebundle-based rendering.
If you must use Jupyter Notebook rather than JupterLab, there is not really any way around this issue, aside from maybe using a headless browser engine such as selenium to programatically execute your notebook with a browser frontend that will render the PNG previews.
Upvotes: 3