#-----------------------------------------------------------------------------# Copyright (c) Anaconda, Inc., and Bokeh Contributors.# All rights reserved.## The full license is in the file LICENSE.txt, distributed with this software.#-----------------------------------------------------------------------------''''''#-----------------------------------------------------------------------------# Boilerplate#-----------------------------------------------------------------------------from__future__importannotationsimportlogging# isort:skiplog=logging.getLogger(__name__)#-----------------------------------------------------------------------------# Imports#-----------------------------------------------------------------------------# Standard library importsfromtypingimport(TYPE_CHECKING,Any,Literal,Sequence,TypeAlias,TypedDict,cast,overload,)# Bokeh importsfrom..import__version__from..core.templatesimport(AUTOLOAD_JS,AUTOLOAD_TAG,FILE,MACROS,ROOT_DIV,)from..document.documentimportDEFAULT_TITLE,Documentfrom..modelimportModelfrom..resourcesimportResources,ResourcesLikefrom..themesimportThemefrom.bundleimportScript,bundle_for_objs_and_resourcesfrom.elementsimporthtml_page_for_render_items,script_for_render_itemsfrom.utilimport(FromCurdoc,OutputDocumentFor,RenderRoot,standalone_docs_json,standalone_docs_json_and_render_items,)from.wrappersimportwrap_in_onloadifTYPE_CHECKING:fromjinja2importTemplatefrom..core.typesimportIDfrom..document.documentimportDocJson#-----------------------------------------------------------------------------# Globals and constants#-----------------------------------------------------------------------------__all__=('autoload_static','components','file_html','json_item',)ModelLike:TypeAlias=Model|DocumentModelLikeCollection:TypeAlias=Sequence[ModelLike]|dict[str,ModelLike]#-----------------------------------------------------------------------------# General API#-----------------------------------------------------------------------------ThemeLike:TypeAlias=None|Theme|type[FromCurdoc]
[docs]defautoload_static(model:Model|Document,resources:Resources,script_path:str)->tuple[str,str]:''' 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 ``<script>`` tag to load it Raises: ValueError '''# TODO: maybe warn that it's not exactly useful, but technically possible# if resources.mode == 'inline':# raise ValueError("autoload_static() requires non-inline resources")ifisinstance(model,Model):models=[model]elifisinstance(model,Document):models=model.rootselse:raiseValueError("autoload_static expects a single Model or Document")withOutputDocumentFor(models):(docs_json,[render_item])=standalone_docs_json_and_render_items([model])bundle=bundle_for_objs_and_resources(None,resources)bundle.add(Script(script_for_render_items(docs_json,[render_item])))(_,elementid)=next(iter(render_item.roots.to_json().items()))js=wrap_in_onload(AUTOLOAD_JS.render(bundle=bundle,elementid=elementid))tag=AUTOLOAD_TAG.render(src_path=script_path,elementid=elementid,)returnjs,tag
[docs]defcomponents(models:Model|Sequence[Model]|dict[str,Model],wrap_script:bool=True,wrap_plot_info:bool=True,theme:ThemeLike=None)->tuple[str,Any]:''' 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 The returned components assume that BokehJS resources are **already loaded**. The HTML document or template in which they will be embedded needs to include scripts tags, either from a local URL or Bokeh's CDN (replacing ``x.y.z`` with the version of Bokeh you are using): .. code-block:: html <script src="https://cdn.bokeh.org/bokeh/release/bokeh-x.y.z.min.js"></script> <script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-x.y.z.min.js"></script> <script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-x.y.z.min.js"></script> <script src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-x.y.z.min.js"></script> <script src="https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-x.y.z.min.js"></script> Only the Bokeh core library ``bokeh-x.y.z.min.js`` is always required. The other scripts are optional and only need to be included if you want to use corresponding features: * The ``"bokeh-widgets"`` files are only necessary if you are using any of the :ref:`Bokeh widgets <ug_interaction_widgets>`. * The ``"bokeh-tables"`` files are only necessary if you are using Bokeh's :ref:`data tables <ug_interaction_widgets_examples_datatable>`. * The ``"bokeh-api"`` files are required to use the :ref:`BokehJS API <ug_advanced_bokehjs>` and must be loaded *after* the core BokehJS library. * The ``"bokeh-gl"`` files are required to enable :ref:`WebGL support <ug_output_webgl>`. * the ``"bokeh-mathjax"`` files are required to enable :ref:`MathJax support <ug_styling_mathtext>`. Args: models (Model|list|dict|tuple) : A single Model, a list/tuple of Models, or a dictionary of keys and Models. 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 ``<div>`` strings. Otherwise, return :class:`~bokeh.embed.RenderRoot` objects that can be used to build your own divs. (default: True) theme (Theme, optional) : Applies the specified theme when creating the components. If None, or not specified, and the supplied models constitute the full set of roots of a document, applies the theme of that document to the components. Otherwise applies the default theme. 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_root) components((plot1, plot2), wrap_script=False, wrap_plot_info=False) # => (javascript, (plot1_root, plot2_root)) components({"Plot 1": plot1, "Plot 2": plot2}, wrap_script=False, wrap_plot_info=False) # => (javascript, {"Plot 1": plot1_root, "Plot 2": plot2_root}) '''# 1) Convert single items and dicts into list# XXX: was_single_object = isinstance(models, Model) #or isinstance(models, Document)was_single_object=Falseifisinstance(models,Model):was_single_object=Truemodels=[models]models=_check_models_or_docs(models)# type: ignore # XXX: this API needs to be refined# now convert dict to list, saving keys in the same ordermodel_keys=Nonedict_type:type[dict[Any,Any]]=dictifisinstance(models,dict):dict_type=models.__class__model_keys=models.keys()models=list(models.values())# 2) Append models to one document. Either pre-existing or new and renderwithOutputDocumentFor(models,apply_theme=theme):(docs_json,[render_item])=standalone_docs_json_and_render_items(models)bundle=bundle_for_objs_and_resources(None,None)bundle.add(Script(script_for_render_items(docs_json,[render_item])))script=bundle.scripts(tag=wrap_script)defdiv_for_root(root:RenderRoot)->str:returnROOT_DIV.render(root=root,macros=MACROS)results:list[str]|list[RenderRoot]ifwrap_plot_info:results=[div_for_root(root)forrootinrender_item.roots]else:results=list(render_item.roots)# 3) convert back to the input shaperesult:Anyifwas_single_object:result=results[0]elifmodel_keysisnotNone:result=dict_type(zip(model_keys,results))else:result=tuple(results)returnscript,result
[docs]deffile_html(models:Model|Document|Sequence[Model],resources:ResourcesLike|None=None,title:str|None=None,*,template:Template|str=FILE,template_variables:dict[str,Any]={},theme:ThemeLike=None,suppress_callback_warning:bool=False,_always_new:bool=False)->str:''' Return an HTML document that embeds Bokeh Model or Document objects. The data for the plot is stored directly in the returned HTML, with support for customizing the JS/CSS resources independently and customizing the jinja2 template. Args: models (Model or Document or seq[Model]) : Bokeh object or objects to render typically a Model or Document resources (ResourcesLike) : A resources configuration for Bokeh JS & CSS assets. title (str, optional) : A title for the HTML document ``<title>`` 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 theme (Theme, optional) : Applies the specified theme to the created html. If ``None``, or not specified, and the function is passed a document or the full set of roots of a document, applies the theme of that document. Otherwise applies the default theme. suppress_callback_warning (bool, optional) : Normally generating standalone HTML from a Bokeh Document that has Python callbacks will result in a warning stating that the callbacks cannot function. However, this warning can be suppressed by setting this value to True (default: False) Returns: UTF-8 encoded HTML '''models_seq:Sequence[Model]=[]ifisinstance(models,Model):models_seq=[models]elifisinstance(models,Document):iflen(models.roots)==0:raiseValueError("Document has no root Models")models_seq=models.rootselse:models_seq=modelsresources=Resources.build(resources)withOutputDocumentFor(models_seq,apply_theme=theme,always_new=_always_new)asdoc:(docs_json,render_items)=standalone_docs_json_and_render_items(models_seq,suppress_callback_warning=suppress_callback_warning)title=_title_from_models(models_seq,title)bundle=bundle_for_objs_and_resources([doc],resources)returnhtml_page_for_render_items(bundle,docs_json,render_items,title=title,template=template,template_variables=template_variables)
[docs]defjson_item(model:Model,target:ID|None=None,theme:ThemeLike=None)->StandaloneEmbedJson:''' Return a JSON block that can be used to embed standalone Bokeh content. Args: model (Model) : The Bokeh object to embed target (string, optional) A div id to embed the model into. If None, the target id must be supplied in the JavaScript call. theme (Theme, optional) : Applies the specified theme to the created html. If ``None``, or not specified, and the function is passed a document or the full set of roots of a document, applies the theme of that document. Otherwise applies the default theme. Returns: JSON-like This function returns a JSON block that can be consumed by the BokehJS function ``Bokeh.embed.embed_item``. As an example, a Flask endpoint for ``/plot`` might return the following content to embed a Bokeh plot into a div with id *"myplot"*: .. code-block:: python @app.route('/plot') def plot(): p = make_plot('petal_width', 'petal_length') return json.dumps(json_item(p, "myplot")) Then a web page can retrieve this JSON and embed the plot by calling ``Bokeh.embed.embed_item``: .. code-block:: html <script> fetch('/plot') .then(function(response) { return response.json(); }) .then(function(item) { Bokeh.embed.embed_item(item); }) </script> Alternatively, if is more convenient to supply the target div id directly in the page source, that is also possible. If `target_id` is omitted in the call to this function: .. code-block:: python return json.dumps(json_item(p)) Then the value passed to ``embed_item`` is used: .. code-block:: javascript Bokeh.embed.embed_item(item, "myplot"); '''withOutputDocumentFor([model],apply_theme=theme)asdoc:doc.title=""[doc_json]=standalone_docs_json([model]).values()root_id=doc_json["roots"][0]["id"]returnStandaloneEmbedJson(target_id=target,root_id=root_id,doc=doc_json,version=__version__,)
#-----------------------------------------------------------------------------# Dev API#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------# Private API#-----------------------------------------------------------------------------def_check_models_or_docs(models:ModelLike|ModelLikeCollection)->ModelLikeCollection:''' '''input_type_valid=False# Check for single itemifisinstance(models,Model|Document):models=[models]# Check for sequenceifisinstance(models,Sequence)andall(isinstance(x,Model|Document)forxinmodels):input_type_valid=Trueifisinstance(models,dict)and \
all(isinstance(x,str)forxinmodels.keys())and \
all(isinstance(x,Model|Document)forxinmodels.values()):input_type_valid=Trueifnotinput_type_valid:raiseValueError('Input must be a Model, a Document, a Sequence of Models and Document, or a dictionary from string to Model and Document',)returnmodelsdef_title_from_models(models:Sequence[Model|Document],title:str|None)->str:# use override titleiftitleisnotNone:returntitle# use title from any listed documentforpinmodels:ifisinstance(p,Document):returnp.title# use title from any model's documentforpincast(Sequence[Model],models):ifp.documentisnotNone:returnp.document.title# use default titlereturnDEFAULT_TITLE#-----------------------------------------------------------------------------# Code#-----------------------------------------------------------------------------