Using MyST-NB-Bokeh#
MyST-NB-Bokeh provides one function that glues a Bokeh figure into the Notebook and provides hooks for MyST-NB to read the glued data and paste it into a Markdown cell.
Adding to Configuration#
To use this extension, you will need to install it into your environment and then modify your Sphinx configuration. How you do this depends on what interface to Sphinx you’re using.
Standard Sphinx Site#
If you are building a standard Sphinx site, then the myst_nb_bokeh extension should be added to your conf.py file:
extensions = [
...
"myst_nb_bokeh",
...
]
Note that the three dots indicate other values that may be in your extensions list, you should not copy them.
JupyterBook#
If you’re building a JupyterBook, then you’ll need to modify your Sphinx configuration in your _config.yml file. In this case, you need to add a custom Sphinx extension:
sphinx:
extra_extensions:
- myst_nb_bokeh
Configuring MyST-NB-Bokeh#
MyST-NB-Bokeh has no configuration options 🎉
When the extension is added to your Sphinx configuration, it will set one MyST-NB-bokeh specific configuration values:
nb_mime_priority_overrides: This configuration value determines the priority of the mimetypes in the cell output. This extension sets our custom JS+Bokeh MIME type to be the highest priority for HTML rendering.
Gluing and Pasting Bokeh Figures#
To glue and paste Bokeh figures in your Notebooks, you need to import one function from this package: glue_bokeh(). You do not need to run the show() or any other output_* (for example, output_notebook()) functions to be able to glue and paste Bokeh figures. The BokehJS library is automatically added to the page if necessary, whenever a Bokeh figure is pasted.
glue_bokeh() takes two mandatory and one optional argument, so it has the same arguments as the glue() function from MyST-NB:
name: Mandatory, the name under which the Bokeh figure should be stored.variable: Mandatory, the Bokeh figure to glue into the output.display: Optional, defaultFalse. WhenTrue, the plot will be displayed in the output of the cell where gluing takes place. This is convenient to inspect the plot.
Once a Bokeh figure has been glued into the Notebook, you can use all the same pasting directive functionality as is available from MyST-NB. Note that you cannot paste Bokeh plots inline to the text, because of how the plot is displayed.
The example below shows how to glue and paste Bokeh figures.
from bokeh.plotting import figure
from myst_nb_bokeh import glue_bokeh
p = figure(width=300, height=300)
p.circle(list(range(1, 10)), list(range(10, 1, -1)));
# When display is False or omitted, the figure is not displayed in this cell's output
glue_bokeh("bokeh_plot", p)
---------------------------------------------------------------------------
UnsetValueError Traceback (most recent call last)
Cell In[3], line 2
1 # When display is False or omitted, the figure is not displayed in this cell's output
----> 2 glue_bokeh("bokeh_plot", p)
File ~/checkouts/readthedocs.org/user_builds/myst-nb-bokeh/envs/latest/lib/python3.11/site-packages/myst_nb_bokeh/__init__.py:87, in glue_bokeh(name, variable, display)
84 mime_prefix = "" if display else MYST_NB_GLUE_PREFIX
85 metadata = {"scrapbook": dict(name=name, mime_prefix=mime_prefix, has_bokeh=True)}
86 ipy_display(
---> 87 {mime_prefix + JB_BOKEH_MIMETYPE: json.dumps(json_item(variable), separators=(",", ":"))},
88 raw=True,
89 metadata=metadata,
90 )
91 if display:
92 script, div = components(variable, wrap_script=False)
File ~/checkouts/readthedocs.org/user_builds/myst-nb-bokeh/envs/latest/lib/python3.11/site-packages/bokeh/embed/standalone.py:428, in json_item(model, target, theme)
370 def json_item(model: Model, target: ID | None = None, theme: ThemeLike = None) -> StandaloneEmbedJson:
371 ''' Return a JSON block that can be used to embed standalone Bokeh content.
372
373 Args:
(...) 426
427 '''
--> 428 with OutputDocumentFor([model], apply_theme=theme) as doc:
429 doc.title = ""
430 [doc_json] = standalone_docs_json([model]).values()
File ~/.asdf/installs/python/3.11.14/lib/python3.11/contextlib.py:137, in _GeneratorContextManager.__enter__(self)
135 del self.args, self.kwds, self.func
136 try:
--> 137 return next(self.gen)
138 except StopIteration:
139 raise RuntimeError("generator didn't yield") from None
File ~/checkouts/readthedocs.org/user_builds/myst-nb-bokeh/envs/latest/lib/python3.11/site-packages/bokeh/embed/util.py:153, in OutputDocumentFor(objs, apply_theme, always_new)
151 doc = _new_doc()
152 for model in objs:
--> 153 doc.add_root(model)
155 # handle a single shared document
156 elif len(docs) == 1:
File ~/checkouts/readthedocs.org/user_builds/myst-nb-bokeh/envs/latest/lib/python3.11/site-packages/bokeh/document/document.py:338, in Document.add_root(self, model, setter)
335 if model in self._roots:
336 return
--> 338 with self.models.freeze():
339 self._roots.append(model)
341 self.callbacks.trigger_on_change(RootAddedEvent(self, model, setter))
File ~/.asdf/installs/python/3.11.14/lib/python3.11/contextlib.py:144, in _GeneratorContextManager.__exit__(self, typ, value, traceback)
142 if typ is None:
143 try:
--> 144 next(self.gen)
145 except StopIteration:
146 return False
File ~/checkouts/readthedocs.org/user_builds/myst-nb-bokeh/envs/latest/lib/python3.11/site-packages/bokeh/document/models.py:135, in DocumentModelManager.freeze(self)
133 self._push_freeze()
134 yield
--> 135 self._pop_freeze()
File ~/checkouts/readthedocs.org/user_builds/myst-nb-bokeh/envs/latest/lib/python3.11/site-packages/bokeh/document/models.py:288, in DocumentModelManager._pop_freeze(self)
286 self._freeze_count -= 1
287 if self._freeze_count == 0:
--> 288 self.recompute()
File ~/checkouts/readthedocs.org/user_builds/myst-nb-bokeh/envs/latest/lib/python3.11/site-packages/bokeh/document/models.py:215, in DocumentModelManager.recompute(self)
213 new_models: set[Model] = set()
214 for mr in document.roots:
--> 215 new_models |= mr.references()
217 old_models = set(self._models.values())
219 to_detach = old_models - new_models
File ~/checkouts/readthedocs.org/user_builds/myst-nb-bokeh/envs/latest/lib/python3.11/site-packages/bokeh/model/model.py:496, in Model.references(self)
492 def references(self) -> set[Model]:
493 ''' Returns all ``Models`` that this object has references to.
494
495 '''
--> 496 return set(collect_models(self))
File ~/checkouts/readthedocs.org/user_builds/myst-nb-bokeh/envs/latest/lib/python3.11/site-packages/bokeh/model/util.py:139, in collect_models(*input_values)
123 def collect_models(*input_values: Any) -> list[Model]:
124 ''' Collect a duplicate-free list of all other Bokeh models referred to by
125 this model, or by any of its references, etc.
126
(...) 137
138 '''
--> 139 return collect_filtered_models(None, *input_values)
File ~/checkouts/readthedocs.org/user_builds/myst-nb-bokeh/envs/latest/lib/python3.11/site-packages/bokeh/model/util.py:119, in collect_filtered_models(discard, *input_values)
117 ids.add(obj.id)
118 collected.append(obj)
--> 119 visit_immediate_value_references(obj, queue_one)
121 return collected
File ~/checkouts/readthedocs.org/user_builds/myst-nb-bokeh/envs/latest/lib/python3.11/site-packages/bokeh/model/util.py:185, in visit_immediate_value_references(value, visitor)
183 if isinstance(value, HasProps):
184 for attr in value.properties_with_refs():
--> 185 child = getattr(value, attr)
186 visit_value_and_its_immediate_references(child, visitor)
187 else:
File ~/checkouts/readthedocs.org/user_builds/myst-nb-bokeh/envs/latest/lib/python3.11/site-packages/bokeh/core/property/descriptors.py:282, in PropertyDescriptor.__get__(self, obj, owner)
280 value = self._get(obj)
281 if value is Undefined:
--> 282 raise UnsetValueError(f"{obj}.{self.name} doesn't have a value set")
283 return value
285 elif owner is not None:
UnsetValueError: Circle(id='p1043', ...).radius doesn't have a value set
Once the figure is glued into the output, you can paste it using the {glue:}, {glue:any}, and {glue:figure} directives. MyST has the triple-backtick and colon-fence ways of using directives; we’re using the colon-fence method in this document. The following Markdown will paste the bokeh_plot that we glued in the last cell:
We can also use the {glue:figure} directive to add a caption and cross-referencing.
You can paste the same figure multiple times.
Each version of the figure is completely independent of each other.