We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
custom latex examples do not run
Cannot get either example under `custom` to function with 2.1.0rc3 with a simple python call to the script. Both seem to compile things fine, but then report 
> Unhandled Promise Rejection: Error: Model 'latex_label.LatexLabel' does not exist. This could be due to a widget or a custom model not being registered before first usage.

in the browser.  Using this node on OSX:
```
nodejs                    14.4.0               h2c41780_0    conda-forge
```

If there is something beyond a simple `python foo.py` that is necessary to run these, then this issue should be re-tasked to add a README to each. Otherwise it can be changed to `bug`. If there is some unsupported range of build tools we need to find a way to warn better. 

To note: the `examples/models/file/latex_extension.py` example **does*** work.

cc @mattpap 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bokeh/embed/bundle.py`
Content:
```
1 #-----------------------------------------------------------------------------
2 # Copyright (c) 2012 - 2020, Anaconda, Inc., and Bokeh Contributors.
3 # All rights reserved.
4 #
5 # The full license is in the file LICENSE.txt, distributed with this software.
6 #-----------------------------------------------------------------------------
7 '''
8 
9 '''
10 
11 #-----------------------------------------------------------------------------
12 # Boilerplate
13 #-----------------------------------------------------------------------------
14 import logging # isort:skip
15 log = logging.getLogger(__name__)
16 
17 #-----------------------------------------------------------------------------
18 # Imports
19 #-----------------------------------------------------------------------------
20 
21 # Standard library imports
22 import json
23 from os.path import abspath, basename, dirname, exists, join, normpath
24 from typing import Dict, List, NamedTuple, Optional
25 from warnings import warn
26 
27 # Bokeh imports
28 from ..core.templates import CSS_RESOURCES, JS_RESOURCES
29 from ..document.document import Document
30 from ..model import Model
31 from ..resources import BaseResources, Resources
32 from ..settings import settings
33 from ..util.compiler import bundle_models
34 
35 #-----------------------------------------------------------------------------
36 # Globals and constants
37 #-----------------------------------------------------------------------------
38 
39 __all__ = (
40     'Bundle',
41     'bundle_for_objs_and_resources',
42 )
43 
44 #-----------------------------------------------------------------------------
45 # General API
46 #-----------------------------------------------------------------------------
47 
48 #-----------------------------------------------------------------------------
49 # Dev API
50 #-----------------------------------------------------------------------------
51 
52 
53 class ScriptRef:
54     def __init__(self, url, type="text/javascript"):
55         self.url = url
56         self.type = type
57 
58 
59 class Script:
60     def __init__(self, content, type="text/javascript"):
61         self.content = content
62         self.type = type
63 
64 
65 class StyleRef:
66     def __init__(self, url):
67         self.url = url
68 
69 
70 class Style:
71     def __init__(self, content):
72         self.content = content
73 
74 
75 class Bundle:
76     @classmethod
77     def of(cls, js_files, js_raw, css_files, css_raw, hashes):
78         return cls(js_files=js_files, js_raw=js_raw, css_files=css_files, css_raw=css_raw, hashes=hashes)
79 
80     def __init__(self, **kwargs):
81         self.js_files = kwargs.get("js_files", [])
82         self.js_raw = kwargs.get("js_raw", [])
83         self.css_files = kwargs.get("css_files", [])
84         self.css_raw = kwargs.get("css_raw", [])
85         self.hashes = kwargs.get("hashes", {})
86 
87     def __iter__(self):
88         yield self._render_js()
89         yield self._render_css()
90 
91     def _render_js(self):
92         return JS_RESOURCES.render(js_files=self.js_files, js_raw=self.js_raw, hashes=self.hashes)
93 
94     def _render_css(self):
95         return CSS_RESOURCES.render(css_files=self.css_files, css_raw=self.css_raw)
96 
97     def scripts(self, tag=True):
98         if tag:
99             return JS_RESOURCES.render(js_raw=self.js_raw, js_files=[])
100         else:
101             return "\n".join(self.js_raw)
102 
103     @property
104     def js_urls(self):
105         return self.js_files
106 
107     @property
108     def css_urls(self):
109         return self.css_files
110 
111     def add(self, artifact):
112         if isinstance(artifact, ScriptRef):
113             self.js_files.append(artifact.url)
114         elif isinstance(artifact, Script):
115             self.js_raw.append(artifact.content)
116         elif isinstance(artifact, StyleRef):
117             self.css_files.append(artifact.url)
118         elif isinstance(artifact, Style):
119             self.css_raw.append(artifact.content)
120 
121 def bundle_for_objs_and_resources(objs, resources):
122     ''' Generate rendered CSS and JS resources suitable for the given
123     collection of Bokeh objects
124 
125     Args:
126         objs (seq[Model or Document]) :
127 
128         resources (BaseResources or tuple[BaseResources])
129 
130     Returns:
131         Bundle
132 
133     '''
134     # Any env vars will overide a local default passed in
135     resources = settings.resources(default=resources)
136     if isinstance(resources, str):
137         resources = Resources(mode=resources)
138 
139     if resources is None or isinstance(resources, BaseResources):
140         js_resources = css_resources = resources
141     elif isinstance(resources, tuple) and len(resources) == 2 and all(r is None or isinstance(r, BaseResources) for r in resources):
142         js_resources, css_resources = resources
143 
144         if js_resources and not css_resources:
145             warn('No Bokeh CSS Resources provided to template. If required you will need to provide them manually.')
146 
147         if css_resources and not js_resources:
148             warn('No Bokeh JS Resources provided to template. If required you will need to provide them manually.')
149     else:
150         raise ValueError("expected Resources or a pair of optional Resources, got %r" % resources)
151 
152     from copy import deepcopy
153 
154     # XXX: force all components on server and in notebook, because we don't know in advance what will be used
155     use_widgets = _use_widgets(objs) if objs else True
156     use_tables  = _use_tables(objs)  if objs else True
157 
158     js_files = []
159     js_raw = []
160     css_files = []
161     css_raw = []
162 
163     if js_resources:
164         js_resources = deepcopy(js_resources)
165         if not use_widgets and "bokeh-widgets" in js_resources.js_components:
166             js_resources.js_components.remove("bokeh-widgets")
167         if not use_tables and "bokeh-tables" in js_resources.js_components:
168             js_resources.js_components.remove("bokeh-tables")
169 
170         js_files.extend(js_resources.js_files)
171         js_raw.extend(js_resources.js_raw)
172 
173     if css_resources:
174         css_resources = deepcopy(css_resources)
175         css_files.extend(css_resources.css_files)
176         css_raw.extend(css_resources.css_raw)
177 
178     if js_resources:
179         extensions = _bundle_extensions(objs, js_resources)
180         mode = js_resources.mode if resources is not None else "inline"
181         if mode == "inline":
182             js_raw.extend([ Resources._inline(bundle.artifact_path) for bundle in extensions ])
183         elif mode == "server":
184             js_files.extend([ bundle.server_url for bundle in extensions ])
185         elif mode == "cdn":
186             js_files.extend([ bundle.cdn_url for bundle in extensions if bundle.cdn_url is not None ])
187         else:
188             js_files.extend([ bundle.artifact_path for bundle in extensions ])
189 
190     models = [ obj.__class__ for obj in _all_objs(objs) ] if objs else None
191     ext = bundle_models(models)
192     if ext is not None:
193         js_raw.append(ext)
194 
195     return Bundle.of(js_files, js_raw, css_files, css_raw, js_resources.hashes if js_resources else {})
196 
197 #-----------------------------------------------------------------------------
198 # Private API
199 #-----------------------------------------------------------------------------
200 
201 def _query_extensions(objs, query):
202     names = set()
203 
204     for obj in _all_objs(objs):
205         if hasattr(obj, "__implementation__"):
206             continue
207         name = obj.__view_module__.split(".")[0]
208         if name == "bokeh":
209             continue
210         if name in names:
211             continue
212         names.add(name)
213 
214         for model in Model.model_class_reverse_map.values():
215             if model.__module__.startswith(name):
216                 if query(model):
217                     return True
218 
219     return False
220 
221 _default_cdn_host = "https://unpkg.com"
222 
223 class ExtensionEmbed(NamedTuple):
224     artifact_path: str
225     server_url: str
226     cdn_url: Optional[str] = None
227 
228 extension_dirs: Dict[str, str] = {} # name -> path
229 
230 def _bundle_extensions(objs, resources: Resources) -> List[ExtensionEmbed]:
231     names = set()
232     bundles = []
233 
234     extensions = [".min.js", ".js"] if resources.minified else [".js"]
235 
236     for obj in _all_objs(objs) if objs is not None else Model.model_class_reverse_map.values():
237         if hasattr(obj, "__implementation__"):
238             continue
239         name = obj.__view_module__.split(".")[0]
240         if name == "bokeh":
241             continue
242         if name in names:
243             continue
244         names.add(name)
245         module = __import__(name)
246         this_file = abspath(module.__file__)
247         base_dir = dirname(this_file)
248         dist_dir = join(base_dir, "dist")
249 
250         ext_path = join(base_dir, "bokeh.ext.json")
251         if not exists(ext_path):
252             continue
253 
254         server_prefix = f"{resources.root_url}static/extensions"
255         package_path = join(base_dir, "package.json")
256 
257         pkg: Optional[str] = None
258 
259         if exists(package_path):
260             with open(package_path) as io:
261                 try:
262                     pkg = json.load(io)
263                 except json.decoder.JSONDecodeError:
264                     pass
265 
266         artifact_path: str
267         server_url: str
268         cdn_url: Optional[str] = None
269 
270         if pkg is not None:
271             pkg_name = pkg["name"]
272             pkg_version = pkg.get("version", "latest")
273             pkg_main = pkg.get("module", pkg.get("main", None))
274             if pkg_main is not None:
275                 cdn_url = f"{_default_cdn_host}/{pkg_name}@^{pkg_version}/{pkg_main}"
276             else:
277                 pkg_main = join(dist_dir, f"{name}.js")
278             artifact_path = join(base_dir, normpath(pkg_main))
279             artifacts_dir = dirname(artifact_path)
280             artifact_name = basename(artifact_path)
281             server_path = f"{name}/{artifact_name}"
282         else:
283             for ext in extensions:
284                 artifact_path = join(dist_dir, f"{name}{ext}")
285                 artifacts_dir = dist_dir
286                 server_path = f"{name}/{name}{ext}"
287                 if exists(artifact_path):
288                     break
289             else:
290                 raise ValueError(f"can't resolve artifact path for '{name}' extension")
291 
292         extension_dirs[name] = artifacts_dir
293         server_url = f"{server_prefix}/{server_path}"
294         embed = ExtensionEmbed(artifact_path, server_url, cdn_url)
295         bundles.append(embed)
296 
297     return bundles
298 
299 def _all_objs(objs):
300     all_objs = set()
301 
302     for obj in objs:
303         if isinstance(obj, Document):
304             for root in obj.roots:
305                 all_objs |= root.references()
306         else:
307             all_objs |= obj.references()
308 
309     return all_objs
310 
311 def _any(objs, query):
312     ''' Whether any of a collection of objects satisfies a given query predicate
313 
314     Args:
315         objs (seq[Model or Document]) :
316 
317         query (callable)
318 
319     Returns:
320         True, if ``query(obj)`` is True for some object in ``objs``, else False
321 
322     '''
323     for obj in objs:
324         if isinstance(obj, Document):
325             if _any(obj.roots, query):
326                 return True
327         else:
328             if any(query(ref) for ref in obj.references()):
329                 return True
330     return False
331 
332 def _use_tables(objs):
333     ''' Whether a collection of Bokeh objects contains a TableWidget
334 
335     Args:
336         objs (seq[Model or Document]) :
337 
338     Returns:
339         bool
340 
341     '''
342     from ..models.widgets import TableWidget
343     return _any(objs, lambda obj: isinstance(obj, TableWidget)) or _ext_use_tables(objs)
344 
345 def _use_widgets(objs):
346     ''' Whether a collection of Bokeh objects contains a any Widget
347 
348     Args:
349         objs (seq[Model or Document]) :
350 
351     Returns:
352         bool
353 
354     '''
355     from ..models.widgets import Widget
356     return _any(objs, lambda obj: isinstance(obj, Widget)) or _ext_use_widgets(objs)
357 
358 def _ext_use_tables(objs):
359     from ..models.widgets import TableWidget
360     return _query_extensions(objs, lambda cls: issubclass(cls, TableWidget))
361 
362 def _ext_use_widgets(objs):
363     from ..models.widgets import Widget
364     return _query_extensions(objs, lambda cls: issubclass(cls, Widget))
365 
366 #-----------------------------------------------------------------------------
367 # Code
368 #-----------------------------------------------------------------------------
369
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/bokeh/embed/bundle.py b/bokeh/embed/bundle.py
--- a/bokeh/embed/bundle.py
+++ b/bokeh/embed/bundle.py
@@ -183,7 +183,11 @@
         elif mode == "server":
             js_files.extend([ bundle.server_url for bundle in extensions ])
         elif mode == "cdn":
-            js_files.extend([ bundle.cdn_url for bundle in extensions if bundle.cdn_url is not None ])
+            for bundle in extensions:
+                if bundle.cdn_url is not None:
+                    js_files.append(bundle.cdn_url)
+                else:
+                    js_raw.append(Resources._inline(bundle.artifact_path))
         else:
             js_files.extend([ bundle.artifact_path for bundle in extensions ])

{"golden_diff": "diff --git a/bokeh/embed/bundle.py b/bokeh/embed/bundle.py\n--- a/bokeh/embed/bundle.py\n+++ b/bokeh/embed/bundle.py\n@@ -183,7 +183,11 @@\n         elif mode == \"server\":\n             js_files.extend([ bundle.server_url for bundle in extensions ])\n         elif mode == \"cdn\":\n-            js_files.extend([ bundle.cdn_url for bundle in extensions if bundle.cdn_url is not None ])\n+            for bundle in extensions:\n+                if bundle.cdn_url is not None:\n+                    js_files.append(bundle.cdn_url)\n+                else:\n+                    js_raw.append(Resources._inline(bundle.artifact_path))\n         else:\n             js_files.extend([ bundle.artifact_path for bundle in extensions ])\n", "issue": "custom latex examples do not run\nCannot get either example under `custom` to function with 2.1.0rc3 with a simple python call to the script. Both seem to compile things fine, but then report \r\n> Unhandled Promise Rejection: Error: Model 'latex_label.LatexLabel' does not exist. This could be due to a widget or a custom model not being registered before first usage.\r\n\r\nin the browser.  Using this node on OSX:\r\n```\r\nnodejs                    14.4.0               h2c41780_0    conda-forge\r\n```\r\n\r\nIf there is something beyond a simple `python foo.py` that is necessary to run these, then this issue should be re-tasked to add a README to each. Otherwise it can be changed to `bug`. If there is some unsupported range of build tools we need to find a way to warn better. \r\n\r\nTo note: the `examples/models/file/latex_extension.py` example **does*** work.\r\n\r\ncc @mattpap \n", "before_files": [{"content": "#-----------------------------------------------------------------------------\n# Copyright (c) 2012 - 2020, Anaconda, Inc., and Bokeh Contributors.\n# All rights reserved.\n#\n# The full license is in the file LICENSE.txt, distributed with this software.\n#-----------------------------------------------------------------------------\n'''\n\n'''\n\n#-----------------------------------------------------------------------------\n# Boilerplate\n#-----------------------------------------------------------------------------\nimport logging # isort:skip\nlog = logging.getLogger(__name__)\n\n#-----------------------------------------------------------------------------\n# Imports\n#-----------------------------------------------------------------------------\n\n# Standard library imports\nimport json\nfrom os.path import abspath, basename, dirname, exists, join, normpath\nfrom typing import Dict, List, NamedTuple, Optional\nfrom warnings import warn\n\n# Bokeh imports\nfrom ..core.templates import CSS_RESOURCES, JS_RESOURCES\nfrom ..document.document import Document\nfrom ..model import Model\nfrom ..resources import BaseResources, Resources\nfrom ..settings import settings\nfrom ..util.compiler import bundle_models\n\n#-----------------------------------------------------------------------------\n# Globals and constants\n#-----------------------------------------------------------------------------\n\n__all__ = (\n    'Bundle',\n    'bundle_for_objs_and_resources',\n)\n\n#-----------------------------------------------------------------------------\n# General API\n#-----------------------------------------------------------------------------\n\n#-----------------------------------------------------------------------------\n# Dev API\n#-----------------------------------------------------------------------------\n\n\nclass ScriptRef:\n    def __init__(self, url, type=\"text/javascript\"):\n        self.url = url\n        self.type = type\n\n\nclass Script:\n    def __init__(self, content, type=\"text/javascript\"):\n        self.content = content\n        self.type = type\n\n\nclass StyleRef:\n    def __init__(self, url):\n        self.url = url\n\n\nclass Style:\n    def __init__(self, content):\n        self.content = content\n\n\nclass Bundle:\n    @classmethod\n    def of(cls, js_files, js_raw, css_files, css_raw, hashes):\n        return cls(js_files=js_files, js_raw=js_raw, css_files=css_files, css_raw=css_raw, hashes=hashes)\n\n    def __init__(self, **kwargs):\n        self.js_files = kwargs.get(\"js_files\", [])\n        self.js_raw = kwargs.get(\"js_raw\", [])\n        self.css_files = kwargs.get(\"css_files\", [])\n        self.css_raw = kwargs.get(\"css_raw\", [])\n        self.hashes = kwargs.get(\"hashes\", {})\n\n    def __iter__(self):\n        yield self._render_js()\n        yield self._render_css()\n\n    def _render_js(self):\n        return JS_RESOURCES.render(js_files=self.js_files, js_raw=self.js_raw, hashes=self.hashes)\n\n    def _render_css(self):\n        return CSS_RESOURCES.render(css_files=self.css_files, css_raw=self.css_raw)\n\n    def scripts(self, tag=True):\n        if tag:\n            return JS_RESOURCES.render(js_raw=self.js_raw, js_files=[])\n        else:\n            return \"\\n\".join(self.js_raw)\n\n    @property\n    def js_urls(self):\n        return self.js_files\n\n    @property\n    def css_urls(self):\n        return self.css_files\n\n    def add(self, artifact):\n        if isinstance(artifact, ScriptRef):\n            self.js_files.append(artifact.url)\n        elif isinstance(artifact, Script):\n            self.js_raw.append(artifact.content)\n        elif isinstance(artifact, StyleRef):\n            self.css_files.append(artifact.url)\n        elif isinstance(artifact, Style):\n            self.css_raw.append(artifact.content)\n\ndef bundle_for_objs_and_resources(objs, resources):\n    ''' Generate rendered CSS and JS resources suitable for the given\n    collection of Bokeh objects\n\n    Args:\n        objs (seq[Model or Document]) :\n\n        resources (BaseResources or tuple[BaseResources])\n\n    Returns:\n        Bundle\n\n    '''\n    # Any env vars will overide a local default passed in\n    resources = settings.resources(default=resources)\n    if isinstance(resources, str):\n        resources = Resources(mode=resources)\n\n    if resources is None or isinstance(resources, BaseResources):\n        js_resources = css_resources = resources\n    elif isinstance(resources, tuple) and len(resources) == 2 and all(r is None or isinstance(r, BaseResources) for r in resources):\n        js_resources, css_resources = resources\n\n        if js_resources and not css_resources:\n            warn('No Bokeh CSS Resources provided to template. If required you will need to provide them manually.')\n\n        if css_resources and not js_resources:\n            warn('No Bokeh JS Resources provided to template. If required you will need to provide them manually.')\n    else:\n        raise ValueError(\"expected Resources or a pair of optional Resources, got %r\" % resources)\n\n    from copy import deepcopy\n\n    # XXX: force all components on server and in notebook, because we don't know in advance what will be used\n    use_widgets = _use_widgets(objs) if objs else True\n    use_tables  = _use_tables(objs)  if objs else True\n\n    js_files = []\n    js_raw = []\n    css_files = []\n    css_raw = []\n\n    if js_resources:\n        js_resources = deepcopy(js_resources)\n        if not use_widgets and \"bokeh-widgets\" in js_resources.js_components:\n            js_resources.js_components.remove(\"bokeh-widgets\")\n        if not use_tables and \"bokeh-tables\" in js_resources.js_components:\n            js_resources.js_components.remove(\"bokeh-tables\")\n\n        js_files.extend(js_resources.js_files)\n        js_raw.extend(js_resources.js_raw)\n\n    if css_resources:\n        css_resources = deepcopy(css_resources)\n        css_files.extend(css_resources.css_files)\n        css_raw.extend(css_resources.css_raw)\n\n    if js_resources:\n        extensions = _bundle_extensions(objs, js_resources)\n        mode = js_resources.mode if resources is not None else \"inline\"\n        if mode == \"inline\":\n            js_raw.extend([ Resources._inline(bundle.artifact_path) for bundle in extensions ])\n        elif mode == \"server\":\n            js_files.extend([ bundle.server_url for bundle in extensions ])\n        elif mode == \"cdn\":\n            js_files.extend([ bundle.cdn_url for bundle in extensions if bundle.cdn_url is not None ])\n        else:\n            js_files.extend([ bundle.artifact_path for bundle in extensions ])\n\n    models = [ obj.__class__ for obj in _all_objs(objs) ] if objs else None\n    ext = bundle_models(models)\n    if ext is not None:\n        js_raw.append(ext)\n\n    return Bundle.of(js_files, js_raw, css_files, css_raw, js_resources.hashes if js_resources else {})\n\n#-----------------------------------------------------------------------------\n# Private API\n#-----------------------------------------------------------------------------\n\ndef _query_extensions(objs, query):\n    names = set()\n\n    for obj in _all_objs(objs):\n        if hasattr(obj, \"__implementation__\"):\n            continue\n        name = obj.__view_module__.split(\".\")[0]\n        if name == \"bokeh\":\n            continue\n        if name in names:\n            continue\n        names.add(name)\n\n        for model in Model.model_class_reverse_map.values():\n            if model.__module__.startswith(name):\n                if query(model):\n                    return True\n\n    return False\n\n_default_cdn_host = \"https://unpkg.com\"\n\nclass ExtensionEmbed(NamedTuple):\n    artifact_path: str\n    server_url: str\n    cdn_url: Optional[str] = None\n\nextension_dirs: Dict[str, str] = {} # name -> path\n\ndef _bundle_extensions(objs, resources: Resources) -> List[ExtensionEmbed]:\n    names = set()\n    bundles = []\n\n    extensions = [\".min.js\", \".js\"] if resources.minified else [\".js\"]\n\n    for obj in _all_objs(objs) if objs is not None else Model.model_class_reverse_map.values():\n        if hasattr(obj, \"__implementation__\"):\n            continue\n        name = obj.__view_module__.split(\".\")[0]\n        if name == \"bokeh\":\n            continue\n        if name in names:\n            continue\n        names.add(name)\n        module = __import__(name)\n        this_file = abspath(module.__file__)\n        base_dir = dirname(this_file)\n        dist_dir = join(base_dir, \"dist\")\n\n        ext_path = join(base_dir, \"bokeh.ext.json\")\n        if not exists(ext_path):\n            continue\n\n        server_prefix = f\"{resources.root_url}static/extensions\"\n        package_path = join(base_dir, \"package.json\")\n\n        pkg: Optional[str] = None\n\n        if exists(package_path):\n            with open(package_path) as io:\n                try:\n                    pkg = json.load(io)\n                except json.decoder.JSONDecodeError:\n                    pass\n\n        artifact_path: str\n        server_url: str\n        cdn_url: Optional[str] = None\n\n        if pkg is not None:\n            pkg_name = pkg[\"name\"]\n            pkg_version = pkg.get(\"version\", \"latest\")\n            pkg_main = pkg.get(\"module\", pkg.get(\"main\", None))\n            if pkg_main is not None:\n                cdn_url = f\"{_default_cdn_host}/{pkg_name}@^{pkg_version}/{pkg_main}\"\n            else:\n                pkg_main = join(dist_dir, f\"{name}.js\")\n            artifact_path = join(base_dir, normpath(pkg_main))\n            artifacts_dir = dirname(artifact_path)\n            artifact_name = basename(artifact_path)\n            server_path = f\"{name}/{artifact_name}\"\n        else:\n            for ext in extensions:\n                artifact_path = join(dist_dir, f\"{name}{ext}\")\n                artifacts_dir = dist_dir\n                server_path = f\"{name}/{name}{ext}\"\n                if exists(artifact_path):\n                    break\n            else:\n                raise ValueError(f\"can't resolve artifact path for '{name}' extension\")\n\n        extension_dirs[name] = artifacts_dir\n        server_url = f\"{server_prefix}/{server_path}\"\n        embed = ExtensionEmbed(artifact_path, server_url, cdn_url)\n        bundles.append(embed)\n\n    return bundles\n\ndef _all_objs(objs):\n    all_objs = set()\n\n    for obj in objs:\n        if isinstance(obj, Document):\n            for root in obj.roots:\n                all_objs |= root.references()\n        else:\n            all_objs |= obj.references()\n\n    return all_objs\n\ndef _any(objs, query):\n    ''' Whether any of a collection of objects satisfies a given query predicate\n\n    Args:\n        objs (seq[Model or Document]) :\n\n        query (callable)\n\n    Returns:\n        True, if ``query(obj)`` is True for some object in ``objs``, else False\n\n    '''\n    for obj in objs:\n        if isinstance(obj, Document):\n            if _any(obj.roots, query):\n                return True\n        else:\n            if any(query(ref) for ref in obj.references()):\n                return True\n    return False\n\ndef _use_tables(objs):\n    ''' Whether a collection of Bokeh objects contains a TableWidget\n\n    Args:\n        objs (seq[Model or Document]) :\n\n    Returns:\n        bool\n\n    '''\n    from ..models.widgets import TableWidget\n    return _any(objs, lambda obj: isinstance(obj, TableWidget)) or _ext_use_tables(objs)\n\ndef _use_widgets(objs):\n    ''' Whether a collection of Bokeh objects contains a any Widget\n\n    Args:\n        objs (seq[Model or Document]) :\n\n    Returns:\n        bool\n\n    '''\n    from ..models.widgets import Widget\n    return _any(objs, lambda obj: isinstance(obj, Widget)) or _ext_use_widgets(objs)\n\ndef _ext_use_tables(objs):\n    from ..models.widgets import TableWidget\n    return _query_extensions(objs, lambda cls: issubclass(cls, TableWidget))\n\ndef _ext_use_widgets(objs):\n    from ..models.widgets import Widget\n    return _query_extensions(objs, lambda cls: issubclass(cls, Widget))\n\n#-----------------------------------------------------------------------------\n# Code\n#-----------------------------------------------------------------------------\n", "path": "bokeh/embed/bundle.py"}], "after_files": [{"content": "#-----------------------------------------------------------------------------\n# Copyright (c) 2012 - 2020, Anaconda, Inc., and Bokeh Contributors.\n# All rights reserved.\n#\n# The full license is in the file LICENSE.txt, distributed with this software.\n#-----------------------------------------------------------------------------\n'''\n\n'''\n\n#-----------------------------------------------------------------------------\n# Boilerplate\n#-----------------------------------------------------------------------------\nimport logging # isort:skip\nlog = logging.getLogger(__name__)\n\n#-----------------------------------------------------------------------------\n# Imports\n#-----------------------------------------------------------------------------\n\n# Standard library imports\nimport json\nfrom os.path import abspath, basename, dirname, exists, join, normpath\nfrom typing import Dict, List, NamedTuple, Optional\nfrom warnings import warn\n\n# Bokeh imports\nfrom ..core.templates import CSS_RESOURCES, JS_RESOURCES\nfrom ..document.document import Document\nfrom ..model import Model\nfrom ..resources import BaseResources, Resources\nfrom ..settings import settings\nfrom ..util.compiler import bundle_models\n\n#-----------------------------------------------------------------------------\n# Globals and constants\n#-----------------------------------------------------------------------------\n\n__all__ = (\n    'Bundle',\n    'bundle_for_objs_and_resources',\n)\n\n#-----------------------------------------------------------------------------\n# General API\n#-----------------------------------------------------------------------------\n\n#-----------------------------------------------------------------------------\n# Dev API\n#-----------------------------------------------------------------------------\n\n\nclass ScriptRef:\n    def __init__(self, url, type=\"text/javascript\"):\n        self.url = url\n        self.type = type\n\n\nclass Script:\n    def __init__(self, content, type=\"text/javascript\"):\n        self.content = content\n        self.type = type\n\n\nclass StyleRef:\n    def __init__(self, url):\n        self.url = url\n\n\nclass Style:\n    def __init__(self, content):\n        self.content = content\n\n\nclass Bundle:\n    @classmethod\n    def of(cls, js_files, js_raw, css_files, css_raw, hashes):\n        return cls(js_files=js_files, js_raw=js_raw, css_files=css_files, css_raw=css_raw, hashes=hashes)\n\n    def __init__(self, **kwargs):\n        self.js_files = kwargs.get(\"js_files\", [])\n        self.js_raw = kwargs.get(\"js_raw\", [])\n        self.css_files = kwargs.get(\"css_files\", [])\n        self.css_raw = kwargs.get(\"css_raw\", [])\n        self.hashes = kwargs.get(\"hashes\", {})\n\n    def __iter__(self):\n        yield self._render_js()\n        yield self._render_css()\n\n    def _render_js(self):\n        return JS_RESOURCES.render(js_files=self.js_files, js_raw=self.js_raw, hashes=self.hashes)\n\n    def _render_css(self):\n        return CSS_RESOURCES.render(css_files=self.css_files, css_raw=self.css_raw)\n\n    def scripts(self, tag=True):\n        if tag:\n            return JS_RESOURCES.render(js_raw=self.js_raw, js_files=[])\n        else:\n            return \"\\n\".join(self.js_raw)\n\n    @property\n    def js_urls(self):\n        return self.js_files\n\n    @property\n    def css_urls(self):\n        return self.css_files\n\n    def add(self, artifact):\n        if isinstance(artifact, ScriptRef):\n            self.js_files.append(artifact.url)\n        elif isinstance(artifact, Script):\n            self.js_raw.append(artifact.content)\n        elif isinstance(artifact, StyleRef):\n            self.css_files.append(artifact.url)\n        elif isinstance(artifact, Style):\n            self.css_raw.append(artifact.content)\n\ndef bundle_for_objs_and_resources(objs, resources):\n    ''' Generate rendered CSS and JS resources suitable for the given\n    collection of Bokeh objects\n\n    Args:\n        objs (seq[Model or Document]) :\n\n        resources (BaseResources or tuple[BaseResources])\n\n    Returns:\n        Bundle\n\n    '''\n    # Any env vars will overide a local default passed in\n    resources = settings.resources(default=resources)\n    if isinstance(resources, str):\n        resources = Resources(mode=resources)\n\n    if resources is None or isinstance(resources, BaseResources):\n        js_resources = css_resources = resources\n    elif isinstance(resources, tuple) and len(resources) == 2 and all(r is None or isinstance(r, BaseResources) for r in resources):\n        js_resources, css_resources = resources\n\n        if js_resources and not css_resources:\n            warn('No Bokeh CSS Resources provided to template. If required you will need to provide them manually.')\n\n        if css_resources and not js_resources:\n            warn('No Bokeh JS Resources provided to template. If required you will need to provide them manually.')\n    else:\n        raise ValueError(\"expected Resources or a pair of optional Resources, got %r\" % resources)\n\n    from copy import deepcopy\n\n    # XXX: force all components on server and in notebook, because we don't know in advance what will be used\n    use_widgets = _use_widgets(objs) if objs else True\n    use_tables  = _use_tables(objs)  if objs else True\n\n    js_files = []\n    js_raw = []\n    css_files = []\n    css_raw = []\n\n    if js_resources:\n        js_resources = deepcopy(js_resources)\n        if not use_widgets and \"bokeh-widgets\" in js_resources.js_components:\n            js_resources.js_components.remove(\"bokeh-widgets\")\n        if not use_tables and \"bokeh-tables\" in js_resources.js_components:\n            js_resources.js_components.remove(\"bokeh-tables\")\n\n        js_files.extend(js_resources.js_files)\n        js_raw.extend(js_resources.js_raw)\n\n    if css_resources:\n        css_resources = deepcopy(css_resources)\n        css_files.extend(css_resources.css_files)\n        css_raw.extend(css_resources.css_raw)\n\n    if js_resources:\n        extensions = _bundle_extensions(objs, js_resources)\n        mode = js_resources.mode if resources is not None else \"inline\"\n        if mode == \"inline\":\n            js_raw.extend([ Resources._inline(bundle.artifact_path) for bundle in extensions ])\n        elif mode == \"server\":\n            js_files.extend([ bundle.server_url for bundle in extensions ])\n        elif mode == \"cdn\":\n            for bundle in extensions:\n                if bundle.cdn_url is not None:\n                    js_files.append(bundle.cdn_url)\n                else:\n                    js_raw.append(Resources._inline(bundle.artifact_path))\n        else:\n            js_files.extend([ bundle.artifact_path for bundle in extensions ])\n\n    models = [ obj.__class__ for obj in _all_objs(objs) ] if objs else None\n    ext = bundle_models(models)\n    if ext is not None:\n        js_raw.append(ext)\n\n    return Bundle.of(js_files, js_raw, css_files, css_raw, js_resources.hashes if js_resources else {})\n\n#-----------------------------------------------------------------------------\n# Private API\n#-----------------------------------------------------------------------------\n\ndef _query_extensions(objs, query):\n    names = set()\n\n    for obj in _all_objs(objs):\n        if hasattr(obj, \"__implementation__\"):\n            continue\n        name = obj.__view_module__.split(\".\")[0]\n        if name == \"bokeh\":\n            continue\n        if name in names:\n            continue\n        names.add(name)\n\n        for model in Model.model_class_reverse_map.values():\n            if model.__module__.startswith(name):\n                if query(model):\n                    return True\n\n    return False\n\n_default_cdn_host = \"https://unpkg.com\"\n\nclass ExtensionEmbed(NamedTuple):\n    artifact_path: str\n    server_url: str\n    cdn_url: Optional[str] = None\n\nextension_dirs: Dict[str, str] = {} # name -> path\n\ndef _bundle_extensions(objs, resources: Resources) -> List[ExtensionEmbed]:\n    names = set()\n    bundles = []\n\n    extensions = [\".min.js\", \".js\"] if resources.minified else [\".js\"]\n\n    for obj in _all_objs(objs) if objs is not None else Model.model_class_reverse_map.values():\n        if hasattr(obj, \"__implementation__\"):\n            continue\n        name = obj.__view_module__.split(\".\")[0]\n        if name == \"bokeh\":\n            continue\n        if name in names:\n            continue\n        names.add(name)\n        module = __import__(name)\n        this_file = abspath(module.__file__)\n        base_dir = dirname(this_file)\n        dist_dir = join(base_dir, \"dist\")\n\n        ext_path = join(base_dir, \"bokeh.ext.json\")\n        if not exists(ext_path):\n            continue\n\n        server_prefix = f\"{resources.root_url}static/extensions\"\n        package_path = join(base_dir, \"package.json\")\n\n        pkg: Optional[str] = None\n\n        if exists(package_path):\n            with open(package_path) as io:\n                try:\n                    pkg = json.load(io)\n                except json.decoder.JSONDecodeError:\n                    pass\n\n        artifact_path: str\n        server_url: str\n        cdn_url: Optional[str] = None\n\n        if pkg is not None:\n            pkg_name = pkg[\"name\"]\n            pkg_version = pkg.get(\"version\", \"latest\")\n            pkg_main = pkg.get(\"module\", pkg.get(\"main\", None))\n            if pkg_main is not None:\n                cdn_url = f\"{_default_cdn_host}/{pkg_name}@^{pkg_version}/{pkg_main}\"\n            else:\n                pkg_main = join(dist_dir, f\"{name}.js\")\n            artifact_path = join(base_dir, normpath(pkg_main))\n            artifacts_dir = dirname(artifact_path)\n            artifact_name = basename(artifact_path)\n            server_path = f\"{name}/{artifact_name}\"\n        else:\n            for ext in extensions:\n                artifact_path = join(dist_dir, f\"{name}{ext}\")\n                artifacts_dir = dist_dir\n                server_path = f\"{name}/{name}{ext}\"\n                if exists(artifact_path):\n                    break\n            else:\n                raise ValueError(f\"can't resolve artifact path for '{name}' extension\")\n\n        extension_dirs[name] = artifacts_dir\n        server_url = f\"{server_prefix}/{server_path}\"\n        embed = ExtensionEmbed(artifact_path, server_url, cdn_url)\n        bundles.append(embed)\n\n    return bundles\n\ndef _all_objs(objs):\n    all_objs = set()\n\n    for obj in objs:\n        if isinstance(obj, Document):\n            for root in obj.roots:\n                all_objs |= root.references()\n        else:\n            all_objs |= obj.references()\n\n    return all_objs\n\ndef _any(objs, query):\n    ''' Whether any of a collection of objects satisfies a given query predicate\n\n    Args:\n        objs (seq[Model or Document]) :\n\n        query (callable)\n\n    Returns:\n        True, if ``query(obj)`` is True for some object in ``objs``, else False\n\n    '''\n    for obj in objs:\n        if isinstance(obj, Document):\n            if _any(obj.roots, query):\n                return True\n        else:\n            if any(query(ref) for ref in obj.references()):\n                return True\n    return False\n\ndef _use_tables(objs):\n    ''' Whether a collection of Bokeh objects contains a TableWidget\n\n    Args:\n        objs (seq[Model or Document]) :\n\n    Returns:\n        bool\n\n    '''\n    from ..models.widgets import TableWidget\n    return _any(objs, lambda obj: isinstance(obj, TableWidget)) or _ext_use_tables(objs)\n\ndef _use_widgets(objs):\n    ''' Whether a collection of Bokeh objects contains a any Widget\n\n    Args:\n        objs (seq[Model or Document]) :\n\n    Returns:\n        bool\n\n    '''\n    from ..models.widgets import Widget\n    return _any(objs, lambda obj: isinstance(obj, Widget)) or _ext_use_widgets(objs)\n\ndef _ext_use_tables(objs):\n    from ..models.widgets import TableWidget\n    return _query_extensions(objs, lambda cls: issubclass(cls, TableWidget))\n\ndef _ext_use_widgets(objs):\n    from ..models.widgets import Widget\n    return _query_extensions(objs, lambda cls: issubclass(cls, Widget))\n\n#-----------------------------------------------------------------------------\n# Code\n#-----------------------------------------------------------------------------\n", "path": "bokeh/embed/bundle.py"}]}