Skip to content

Commit 9b39fd5

Browse files
authored
Add support for jupyter wigets (#71)
* Remove and ignore .ipynb_checkpoints * Add support for jupyter widgets * Don't initialize document multiple times * Make this integration work with notebook * Add example notebooks * Add support for binary buffers * Add a bundle for static embedding * Allow to publish pip/conda packages * Update npm dependencies * Use semver compatible dev version * Don't lint to avoid travis ci/node js errors * Rename to {@bokeh/}jupyter_bokeh
1 parent 278177b commit 9b39fd5

27 files changed

+5439
-667
lines changed

.gitignore

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
*.bundle.*
2-
lib/
3-
node_modules/
4-
*.egg-info/
1+
/lib/
2+
/dist/
3+
/node_modules/
4+
/build/
5+
/*.egg-info/
6+
/*.tgz
7+
.ipynb_checkpoints/

.travis.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ install:
1515
- conda config --add channels bokeh
1616
- conda config --add channels conda-forge
1717
- conda info -a
18-
- conda install nodejs selenium jupyterlab notebook geckodriver firefox
18+
- conda install conda-build nodejs selenium jupyterlab notebook geckodriver firefox
1919
- npm set progress false
2020
- npm install -g npm
21+
- conda build conda.recipe/
2122
script:
2223
- npm install
2324
- npm run build
24-
- npm run lint
25+
#- npm run lint
2526
- jupyter labextension install .
2627
- jupyter lab clean
2728
- jupyter labextension link .

LICENSE.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2018, Anaconda, Inc.
1+
Copyright (c) 2012 - 2019, Anaconda, Inc., and Bokeh Contributors
22
All rights reserved.
33

44
Redistribution and use in source and binary forms, with or without modification,

MANIFEST.in

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
include README.md
2+
include LICENSE.txt
3+
include jupyter_bokeh.json
4+
recursive-include jupyter_bokeh/nbextension/static *.js *.js.map *.d.ts

README.md

+27-14
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,61 @@
1-
# jupyterlab_bokeh
1+
# jupyter_bokeh
22

3-
A JupyterLab extension for rendering Bokeh content
3+
A Jupyter extension for rendering Bokeh content.
44

55
## Prerequisites
66

7-
* JupyterLab
7+
* Jupyter Lab or Notebook
88

99
## Installation
1010

11-
To install the latest version:
11+
To install the latest version in Jupyter Lab:
1212

1313
```bash
14-
jupyter labextension install jupyterlab_bokeh
14+
const install -c bokeh jupyter_bokeh
15+
jupyter labextension install @jupyter-widgets/jupyterlab-manager
16+
jupyter labextension install @bokeh/jupyter_bokeh
1517
```
1618

1719
To install a specific version:
1820

1921
```bash
20-
jupyter labextension install [email protected]
22+
jupyter labextension install @bokeh/[email protected]
23+
```
24+
25+
On slow or limited memory systems, one can use `--minimize=False` to reduce compilation
26+
times or make Jupyter Lab build runtime bundles at all.
27+
28+
jupyter_bokeh is automatically installed in Jupyter Notebook. In case of a non-standard
29+
setup, one can install the extensions with:
30+
31+
```bash
32+
jupyter nbextension install --sys-prefix --symlink --py jupyter_bokeh
33+
jupyter nbextension enable jupyter_bokeh --py --sys-prefix
2134
```
2235

2336
## Compatibility
2437

2538
The core [Bokeh](https://github.com/bokeh/bokeh) library is generally version independent of
26-
[JupyterLab](https://github.com/jupyterlab/jupyterlab) and this ``jupyterlab_bokeh`` extension for versions
39+
[JupyterLab](https://github.com/jupyterlab/jupyterlab) and this ``jupyter_bokeh`` extension for versions
2740
of ``bokeh>=0.12.0``.
2841

29-
Our goal is that ``jupyterlab_bokeh`` minor releases (using the [SemVer](https://semver.org/) pattern) are
30-
made to follow JupyterLab minor release bumps and micro releases are for new ``jupyterlab_bokeh`` features
42+
Our goal is that ``jupyter_bokeh`` minor releases (using the [SemVer](https://semver.org/) pattern) are
43+
made to follow JupyterLab minor release bumps and micro releases are for new ``jupyter_bokeh`` features
3144
or bug fix releases. We've been previously inconsistent with having the extension release minor version bumps
3245
track that of JupyterLab, so users seeking to find extension releases that are compatible with their JupyterLab
3346
installation may refer to the below table.
3447

35-
###### Compatible JupyterLab and jupyterlab_bokeh versions
48+
###### Compatible JupyterLab and jupyter_bokeh versions
3649

37-
| JupyterLab | jupyterlab_bokeh |
50+
| JupyterLab | jupyter_bokeh |
3851
| ------------- | ---------------- |
3952
| 0.34.x | 0.6.2 |
4053
| 0.35.x | 0.6.3 |
4154
| 1.0.x | 1.0.0 |
4255

4356
## Development
4457

45-
For a development install (requires npm version 4 or later), do the following in the repository directory:
58+
For a development install (requires npm version 6 or later), do the following in the repository directory:
4659

4760
```bash
4861
npm install
@@ -74,6 +87,6 @@ please make a micro release bump (i.e. 0.6.3 -> 0.6.4).
7487

7588
## Testing
7689

77-
There is a directory named ``test_cases`` which contains a collection of notebooks that cover the various ``jupyterlab_bokeh``
90+
There is a directory named ``test_cases`` which contains a collection of notebooks that cover the various ``jupyter_bokeh``
7891
functionalities. If you update the extension for new JupyterLab releases, please manually execute each and check that the
79-
expected behavior occurs. If you extend the ``jupyterlab_bokeh``, please add a new notebook that covers the new functionality.
92+
expected behavior occurs. If you extend the ``jupyter_bokeh``, please add a new notebook that covers the new functionality.

conda.recipe/meta.yaml

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{% set data = load_setup_py_data() %}
2+
3+
package:
4+
name: jupyter_bokeh
5+
version: {{ data["version"] }}
6+
7+
source:
8+
path: ..
9+
10+
build:
11+
noarch: python
12+
number: 0
13+
script: python setup.py build_js install --single-version-externally-managed --record record.txt
14+
15+
extra:
16+
channels:
17+
- bokeh
18+
- conda-forge
19+
20+
requirements:
21+
build:
22+
- python
23+
- setuptools
24+
- nodejs >=8.8
25+
run:
26+
- python
27+
- bokeh >=1.3.4
28+
- ipywidgets >=7.0.0
29+
30+
test:
31+
imports:
32+
- jupyter_bokeh
33+
34+
about:
35+
home: {{ data["url"] }}
36+
license: {{ data["license"] }}
37+
summary: {{ data["description"] }}

jupyter_bokeh.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"load_extensions": {
3+
"jupyter_bokeh/extension": true
4+
}
5+
}

jupyter_bokeh/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .nbextension import _jupyter_nbextension_paths
2+
3+
from .widgets import BokehModel

jupyter_bokeh/nbextension/__init__.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
def _jupyter_nbextension_paths():
2+
return [{
3+
"section": "notebook",
4+
"src": "nbextension/static",
5+
"dest": "jupyter_bokeh",
6+
"require": "jupyter_bokeh/extension",
7+
}]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/index.js
2+
/index.js.map
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
define(function() {
2+
"use strict";
3+
4+
window['requirejs'].config({
5+
map: {
6+
'*': {
7+
'@bokeh/jupyter_bokeh': 'nbextensions/jupyter_bokeh/index',
8+
},
9+
}
10+
});
11+
// Export the required load_ipython_extension function.
12+
return {
13+
load_ipython_extension: function() {},
14+
};
15+
});

jupyter_bokeh/widgets.py

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#-----------------------------------------------------------------------------
2+
# Copyright (c) 2012 - 2019, 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+
Defines a Bokeh model wrapper for Jupyter notebook/lab, which renders Bokeh models
9+
and performs bi-directional syncing just like bokeh server does.
10+
11+
"""
12+
13+
#-----------------------------------------------------------------------------
14+
# Boilerplate
15+
#-----------------------------------------------------------------------------
16+
from __future__ import absolute_import, division, print_function, unicode_literals
17+
18+
# Standard library imports
19+
import json
20+
21+
# External imports
22+
from ipywidgets import DOMWidget
23+
from traitlets import Unicode, Dict
24+
25+
# Bokeh imports
26+
from bokeh.core.json_encoder import serialize_json
27+
from bokeh.models import LayoutDOM
28+
from bokeh.document import Document
29+
from bokeh.protocol import Protocol
30+
from bokeh.util.dependencies import import_optional
31+
from bokeh.embed.elements import div_for_render_item
32+
from bokeh.embed.util import standalone_docs_json_and_render_items
33+
34+
#-----------------------------------------------------------------------------
35+
# Globals and constants
36+
#-----------------------------------------------------------------------------
37+
38+
__all__ = (
39+
"BokehModel",
40+
)
41+
42+
_module_name = "@bokeh/jupyter_bokeh"
43+
_module_version = "1.1.0-dev.1"
44+
45+
#-----------------------------------------------------------------------------
46+
# General API
47+
#-----------------------------------------------------------------------------
48+
49+
class BokehModel(DOMWidget):
50+
51+
_model_name = Unicode("BokehModel").tag(sync=True)
52+
_model_module = Unicode(_module_name).tag(sync=True)
53+
_model_module_version = Unicode(_module_version).tag(sync=True)
54+
55+
_view_name = Unicode("BokehView").tag(sync=True)
56+
_view_module = Unicode(_module_name).tag(sync=True)
57+
_view_module_version = Unicode(_module_version).tag(sync=True)
58+
59+
render_bundle = Dict().tag(sync=True, to_json=lambda obj, _: serialize_json(obj))
60+
61+
@property
62+
def _document(self):
63+
return self._model.document
64+
65+
def __init__(self, model, **kwargs):
66+
assert isinstance(model, LayoutDOM)
67+
self.update_from_model(model)
68+
super(BokehModel, self).__init__(**kwargs)
69+
self.on_msg(self._sync_model)
70+
71+
def close(self):
72+
if self._document is not None:
73+
self._document.remove_on_change(self)
74+
75+
@classmethod
76+
def _model_to_traits(cls, model):
77+
if model.document is None:
78+
document = Document()
79+
document.add_root(model)
80+
(docs_json, [render_item]) = standalone_docs_json_and_render_items([model], True)
81+
render_bundle = dict(
82+
docs_json=docs_json,
83+
render_items=[render_item.to_json()],
84+
div=div_for_render_item(render_item),
85+
)
86+
return render_bundle
87+
88+
def update_from_model(self, model):
89+
self._model = model
90+
self.render_bundle = self._model_to_traits(model)
91+
self._document.on_change_dispatch_to(self)
92+
93+
def _document_patched(self, event):
94+
msg = Protocol("1.0").create("PATCH-DOC", [event])
95+
96+
self.send({"msg": "patch", "payload": msg.header_json})
97+
self.send({"msg": "patch", "payload": msg.metadata_json})
98+
self.send({"msg": "patch", "payload": msg.content_json})
99+
for header, buffer in msg.buffers:
100+
self.send({"msg": "patch", "payload": json.dumps(header)})
101+
self.send({"msg": "patch"}, [buffer])
102+
103+
def _sync_model(self, _, content, _buffers):
104+
if content.get("event", "") != "jsevent":
105+
return
106+
new, old, attr = content["new"], content["old"], content["attr"]
107+
submodel = self._model.select_one({"id": content["id"]})
108+
try:
109+
setattr(submodel, attr, new)
110+
except Exception:
111+
return
112+
for cb in submodel._callbacks.get(attr, []):
113+
cb(attr, old, new)
114+
115+
#-----------------------------------------------------------------------------
116+
# Dev API
117+
#-----------------------------------------------------------------------------

0 commit comments

Comments
 (0)