''' Provide functions to embed Bokeh models (e.g., plots, widget, layouts)
in various different ways.
There are a number of different combinations of options when embedding
Bokeh plots. The data for the plot can be contained in the document,
or on a Bokeh server, or in a sidecar JavaScript file. Likewise, BokehJS
may be inlined in the document, or loaded from CDN or a Bokeh server.
The functions in ``bokeh.embed`` provide functionality to embed in all
these different cases.
'''
from __future__ import absolute_import
from collections import Sequence
from warnings import warn
from six import string_types
from .core.templates import (
AUTOLOAD_JS, AUTOLOAD_TAG, FILE,
NOTEBOOK_DIV, PLOT_DIV, DOC_JS, SCRIPT_TAG
)
from .core.json_encoder import serialize_json
from .document import Document, DEFAULT_TITLE
from .model import Model, _ModelInDocument
from .resources import BaseResources, _SessionCoordinates, EMPTY
from .util.string import encode_utf8
from .util.serialization import make_id
def _wrap_in_function(code):
# indent and wrap Bokeh function def around
code = "\n".join([" " + line for line in code.split("\n")])
return 'Bokeh.$(function() {\n%s\n});' % code
def components(models, resources=None, wrap_script=True, wrap_plot_info=True):
'''
Return HTML components to embed a Bokeh plot. The data for the plot is
stored directly in the returned HTML.
An example can be found in examples/embed/embed_multiple.py
.. note::
The returned components assume that BokehJS resources are
**already loaded**.
Args:
models (Model|list|dict|tuple) :
A single Model, a list/tuple of Models, or a dictionary of keys and Models.
resources :
Deprecated argument
wrap_script (boolean, optional) :
If True, the returned javascript is wrapped in a script tag.
(default: True)
wrap_plot_info (boolean, optional) : If True, returns ``
`` strings.
Otherwise, return dicts that can be used to build your own divs.
(default: True)
If False, the returned dictionary contains the following information:
.. code-block:: python
{
'modelid': 'The model ID, used with Document.get_model_by_id',
'elementid': 'The css identifier the BokehJS will look for to target the plot',
'docid': 'Used by Bokeh to find the doc embedded in the returned script',
}
Returns:
UTF-8 encoded *(script, div[s])* or *(raw_script, plot_info[s])*
Examples:
With default wrapping parameter values:
.. code-block:: python
components(plot)
# => (script, plot_div)
components((plot1, plot2))
# => (script, (plot1_div, plot2_div))
components({"Plot 1": plot1, "Plot 2": plot2})
# => (script, {"Plot 1": plot1_div, "Plot 2": plot2_div})
Examples:
With wrapping parameters set to ``False``:
.. code-block:: python
components(plot, wrap_script=False, wrap_plot_info=False)
# => (javascript, plot_dict)
components((plot1, plot2), wrap_script=False, wrap_plot_info=False)
# => (javascript, (plot1_dict, plot2_dict))
components({"Plot 1": plot1, "Plot 2": plot2}, wrap_script=False, wrap_plot_info=False)
# => (javascript, {"Plot 1": plot1_dict, "Plot 2": plot2_dict})
'''
if resources is not None:
warn('Because the ``resources`` argument is no longer needed, '
'it is deprecated and no longer has any effect',
DeprecationWarning, stacklevel=2)
# 1) Convert single items and dicts into list
was_single_object = isinstance(models, Model) or isinstance(models, Document)
# converts single to list
models = _check_models(models, allow_dict=True)
# now convert dict to list, saving keys in the same order
model_keys = None
if isinstance(models, dict):
model_keys = models.keys()
values = []
# don't just use .values() to ensure we are in the same order as key list
for k in model_keys:
values.append(models[k])
models = values
# 2) Do our rendering
with _ModelInDocument(models):
(docs_json, render_items) = _standalone_docs_json_and_render_items(models)
script = _script_for_render_items(docs_json, render_items, websocket_url=None, wrap_script=wrap_script)
script = encode_utf8(script)
if wrap_plot_info:
results = list(_div_for_render_item(item) for item in render_items)
else:
results = render_items
# 3) convert back to the input shape
if was_single_object:
return script, results[0]
elif model_keys is not None:
result = {}
for (key, value) in zip(model_keys, results):
result[key] = value
return script, result
else:
return script, tuple(results)
def _use_widgets(objs):
from .models.widgets import Widget
def _needs_widgets(obj):
return isinstance(obj, Widget)
for obj in objs:
if isinstance(obj, Document):
if _use_widgets(obj.roots):
return True
else:
if any(_needs_widgets(ref) for ref in obj.references()):
return True
else:
return False
def _use_compiler(objs):
from .models.callbacks import CustomJS
def _needs_compiler(obj):
return hasattr(obj, "__implementation__") or (isinstance(obj, CustomJS) and obj.lang == "coffeescript")
for obj in objs:
if isinstance(obj, Document):
if _use_compiler(obj.roots):
return True
else:
if any(_needs_compiler(ref) for ref in obj.references()):
return True
else:
return False
def _bundle_for_objs_and_resources(objs, resources):
if isinstance(resources, BaseResources):
js_resources = css_resources = resources
elif isinstance(resources, tuple) and len(resources) == 2 and all(r is None or isinstance(r, BaseResources) for r in resources):
js_resources, css_resources = resources
if js_resources and not css_resources:
warn('No Bokeh CSS Resources provided to template. If required you will need to provide them manually.')
if css_resources and not js_resources:
warn('No Bokeh JS Resources provided to template. If required you will need to provide them manually.')
else:
raise ValueError("expected Resources or a pair of optional Resources, got %r" % resources)
from copy import deepcopy
# XXX: force all components on server and in notebook, because we don't know in advance what will be used
use_widgets = _use_widgets(objs) if objs else True
use_compiler = _use_compiler(objs) if objs else True
if js_resources:
js_resources = deepcopy(js_resources)
if not use_widgets and "bokeh-widgets" in js_resources.components:
js_resources.components.remove("bokeh-widgets")
if not use_compiler and "bokeh-compiler" in js_resources.components:
js_resources.components.remove("bokeh-compiler")
bokeh_js = js_resources.render_js()
else:
bokeh_js = None
if css_resources:
css_resources = deepcopy(css_resources)
if not use_widgets and "bokeh-widgets" in css_resources.components:
css_resources.components.remove("bokeh-widgets")
if not use_compiler and "bokeh-compiler" in css_resources.components:
css_resources.components.remove("bokeh-compiler")
bokeh_css = css_resources.render_css()
else:
bokeh_css = None
return bokeh_js, bokeh_css
def notebook_div(model, notebook_comms_target=None):
''' Return HTML for a div that will display a Bokeh plot in an
IPython Notebook
The data for the plot is stored directly in the returned HTML.
Args:
model (Model) : Bokeh object to render
notebook_comms_target (str, optional) :
A target name for a Jupyter Comms object that can update
the document that is rendered to this notebook div
Returns:
UTF-8 encoded HTML text for a ``
``
.. note::
Assumes :func:`~bokeh.util.notebook.load_notebook` or the equivalent
has already been executed.
'''
model = _check_one_model(model)
with _ModelInDocument(model):
(docs_json, render_items) = _standalone_docs_json_and_render_items([model])
item = render_items[0]
item['notebook_comms_target'] = notebook_comms_target
script = _script_for_render_items(docs_json, render_items, wrap_script=False)
resources = EMPTY
js = AUTOLOAD_JS.render(
js_urls = resources.js_files,
css_urls = resources.css_files,
js_raw = resources.js_raw + [script],
css_raw = resources.css_raw_str,
elementid = item['elementid'],
)
div = _div_for_render_item(item)
html = NOTEBOOK_DIV.render(
plot_script = js,
plot_div = div,
)
return encode_utf8(html)
def file_html(models,
resources,
title=None,
template=FILE,
template_variables={}):
'''Return an HTML document that embeds Bokeh Model or Document objects.
The data for the plot is stored directly in the returned HTML.
This is an alias for standalone_html_page_for_models() which
supports customizing the JS/CSS resources independently and
customizing the jinja2 template.
Args:
models (Model or Document or list) : Bokeh object or objects to render
typically a Model or Document
resources (Resources or tuple(JSResources or None, CSSResources or None)) : a resource configuration for Bokeh JS & CSS assets.
title (str, optional) : a title for the HTML document ``
`` tags or None. (default: None)
If None, attempt to automatically find the Document title from the given plot objects.
template (Template, optional) : HTML document template (default: FILE)
A Jinja2 Template, see bokeh.core.templates.FILE for the required
template parameters
template_variables (dict, optional) : variables to be used in the Jinja2
template. If used, the following variable names will be overwritten:
title, bokeh_js, bokeh_css, plot_script, plot_div
Returns:
UTF-8 encoded HTML
'''
models = _check_models(models)
with _ModelInDocument(models):
(docs_json, render_items) = _standalone_docs_json_and_render_items(models)
title = _title_from_models(models, title)
bundle = _bundle_for_objs_and_resources(models, resources)
return _html_page_for_render_items(bundle, docs_json, render_items, title=title,
template=template, template_variables=template_variables)
# TODO rename this "standalone"?
def autoload_static(model, resources, script_path):
''' Return JavaScript code and a script tag that can be used to embed
Bokeh Plots.
The data for the plot is stored directly in the returned JavaScript code.
Args:
model (Model or Document) :
resources (Resources) :
script_path (str) :
Returns:
(js, tag) :
JavaScript code to be saved at ``script_path`` and a ``