我想在jupyterhub(AKA是一个身份验证代理)后面的jupyter笔记本实例上开发散景应用程序。我想让交互式散景应用程序回调到笔记本内核。我不想使用笔记本小部件等因为我希望能够将笔记本导出为python文件并且可以使用散景服务器提供。
我的笔记本中的以下代码给出了一个没有错误的空输出:
from bokeh.layouts import row
from bokeh.models.widgets import Button
from bokeh.io import show, output_notebook
from bokeh.application.handlers import FunctionHandler
from bokeh.application import Application
output_notebook()
# Create the Document Application
def modify_doc(doc):
layout = row(Button(label="Hello,"),Button(label="world!"))
doc.add_root(layout)
handler = FunctionHandler(modify_doc)
app = Application(handler)
# Output = BokehJS 0.12.10 successfully loaded.
# New cell
show(app, notebook_url="my-jupyterhub-url.com:80")
# Output = "empty" cell
检查单元格是否添加了脚本标记:
<script src="http://my-jupyterhub-url.com:46249/autoload.js?bokeh-autoload-element=f8fa3bd0-9caf-473d-87a5-6c7b9680648b&bokeh-absolute-url=http://my-jupyterhub-url.com:46249" id="f8fa3bd0-9caf-473d-87a5-6c7b9680648b" data-bokeh-model-id="" data-bokeh-doc-id=""></script>
这不起作用,因为端口46249
未在jupyterhub代理上打开。此外,路由到我的 jupyter实例的路径为my-jupyterhub-url.com/user/my-username/
,因此my-jupyterhub-url.com/autoload.js
不会路由到任何地方。
这个感觉就像它可能是一个常见的要求,但是搜索还没有找到解决方案。
有什么想法吗?
答案 0 :(得分:1)
所以我找到了一个我不满意的解决方案,但只是工作......
首先在Jupyter实例上安装nbserverproxy
。这允许您通过JupyterHub(您在其中进行身份验证)代理Jupyter机器/容器上的任意端口。我通过从Jupyter Web前端打开终端并键入:
pip install git+https://github.com/jupyterhub/nbserverproxy --user
jupyter serverextension enable --py nbserverproxy --user
然后重新启动服务器。对于我安装的JupyterHub,这是control panel
- &gt; stop my server
等待start my server
。
最后,我修改了Ipython.display.publish_display_data
(因为源代码显示bokeh
在笔记本中调用show
时使用了这个),就像这样。
from unittest.mock import patch
from IPython.display import publish_display_data
orig = publish_display_data
import re
def proxy_replacer(display_data):
for key, item in display_data.items():
if isinstance(item, str):
item= re.sub(r'(/user/tam203)/?:([0-9]+)', r'\1/proxy/\2', item)
item = re.sub(r'http:' , 'https:', item)
display_data[key] = item
return display_data
def mock(data, metadata=None, source=None):
data = proxy_replacer(data) if data else data
metadata = proxy_replacer(metadata) if metadata else metadata
return orig(data, metadata=metadata, source=source)
patcher = patch('IPython.display.publish_display_data', new=mock)
patcher.start()
随着这一切的完成,我可以运行以下内容,看到一个很好的动态更新图。
import random
from bokeh.io import output_notebook
output_notebook()
from bokeh.io import show
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.plotting import figure, ColumnDataSource
def make_document(doc):
source = ColumnDataSource({'x': [], 'y': [], 'color': []})
def update():
new = {'x': [random.random()],
'y': [random.random()],
'color': [random.choice(['red', 'blue', 'green'])]}
source.stream(new)
doc.add_periodic_callback(update, 100)
fig = figure(title='Streaming Circle Plot!', sizing_mode='scale_width',
x_range=[0, 1], y_range=[0, 1])
fig.circle(source=source, x='x', y='y', color='color', size=10)
doc.title = "Now with live updating!"
doc.add_root(fig)
app = Application(FunctionHandler(make_document))
show(app, notebook_url="<my-domain>.co.uk/user/tam203/")
因此,虽然我很高兴能找到一份解决方案,但并不是真正的解决方案。我认为散景中的一个微小变化可以解决这个问题(类似于url模板字符串,你可以在其中指定路径和端口)。
答案 1 :(得分:0)
根据官方bokeh documentation show(obj, notebook_url=remote_jupyter_proxy_url)
接受notebook_url参数值。显然,这可以是接受端口参数值的函数。
通过在jupyterhub / jupyterlab和代理扩展的上下文中提供功能remote_jupyter_proxy_url
的参考实现,文档进一步发展了。