以编程方式运行散景服务器以在本地浏览器中显示 [英] Running bokeh server programmatically to show in browser locally
问题描述
我正在使用 bokeh
(0.12.6
) 实用程序进行交互式数据操作,我将在包中部署该实用程序.这个想法是,用户可以运行一些例程 module.utility()
,它将启动Bokeh Server,在浏览器中启动应用程序,当标签或浏览器关闭时,服务器将是停止了.
如果我运行 bokeh serve --show myapp
,我的应用程序可以正常启动,但是在使用我下面描述的方法连接到本地主机时它会挂起.我检查了处理程序,一切看起来都应该如此.
这样做是否合理,我的做法是否正确?
目录格式
<安装模块路径>/myapp└── main.py
./myapp
将驻留在 venv/lib/python3.5/site-packages/mymodule
等中的位置
main.py
from bokeh.io import curdoc从 bokeh.layouts 导入列来自 bokeh.plotting 导入图从 bokeh.models.sources 导入 ColumnDataSourcesource = ColumnDataSource(dict(x=list(range(5)), y=list(range(5))))p = 图(宽度=300,高度=300,工具=[],工具栏位置=无)p.line(x='x', y='y', source=source)curdoc().add_root(column(p, sizing_mode='scale_width'))
运行脚本
def run_single_server(abs_app_path, port=5000):'''从服务器为单个会话运行散景应用程序`'''从 bokeh.application 导入应用程序从 bokeh.application.handlers 导入 DirectoryHandler从 bokeh.server.server 导入服务器导入操作系统app_name = os.path.split(abs_app_path)[1]url = '/{}'.format(app_name)# 启动散景服务器应用程序 = {url:Application(DirectoryHandler(filename=abs_app_path))}服务器 = 服务器(应用程序,端口 = 端口)服务器启动()server.show(网址)# 以某种方式等待会话结束,可能使用 `server_lifecycle.py`服务器停止()返回定义实用程序():导入我的模块module_path = os.path.split(mymodule.__file__)[0]abs_app_path = os.path.join(module_path, 'myapp')run_single_server(abs_app_path, port=5000)返回
也许在主 __init__.py
中有这个例程,并让它像这样工作:
import mymodulemymodule.utility()# 1. 浏览器启动# 2. 用户做事# 3. 用户关闭窗口# 4. 散景服务器关闭
<小时>
更新我找到了 build_single_handler_application
例程并尝试过,但它似乎也挂了.
from bokeh.command.util import build_single_handler_application导入操作系统app_name = os.path.split(abs_app_path)[1]url = '/{}'.format(app_name)# 启动散景服务器应用程序 = build_single_handler_application(abs_app_path)
看起来我遇到了一些问题.我最终找到并修改了我在邮件组 此处 用于我的用例.
我通过使用单独的进程来使一切正常工作:1) 启动服务器,2) 使用 webbrowser
启动应用程序 url,以及 3) 检查已关闭的连接并关闭.>
我想我也许可以像在我改编的烧瓶示例中那样启动 tornado
服务器实例,但我在这里很高兴.
注意:此示例使用单文件应用程序,但您也可以传递目录格式应用程序的路径.
def create_bokeh_server(io_loop, files, argvs, host, port):'''使用应用程序路径启动散景服务器'''从 bokeh.server.server 导入服务器从 bokeh.command.util 导入 build_single_handler_applications# 将文件路径转换为散景应用程序应用程序 = build_single_handler_applications(文件,argvs)# kwargs 从散景服务调用中提升到服务器,并创建了 io_loop夸格 = {'io_loop':io_loop,'generate_session_ids':真,'redirect_root':真,'use_x_headers':假,'secret_key':无,'num_procs':1,'主机':主机,'sign_sessions':错误,'发展':错误,'端口':端口,'use_index':真}服务器 = 服务器(应用程序,** kwargs)返回服务器def run_single_app(files, port=5000, new='tab'):def start_bokeh(io_loop):'''启动`io_loop`'''io_loop.start()返回无def launch_app(host, app_name, new):'''在浏览器中启动应用程序理想情况下,这将是 `bokeh.util.browser.view()`,但它不起作用'''导入浏览器# 将方法字符串映射到浏览器方法选项 = {'当前':0,'窗口':1,'标签':2}# 连接url并在浏览器中打开,创建一个会话app_url = 'http://{}/{}'.format(host, app_name)print('在浏览器中打开`{}`'.format(app_url))webbrowser.open(app_url, new=options[new])返回无def server_loop(server, io_loop):'''创建会话后检查连接并在断开连接时关闭'''导入时间连接 = [真,]session_loaded = 假而任何(连接):# 检查服务器上是否没有启动会话会话 = server.get_sessions()如果不是 session_loaded:如果会话:session_loaded = 真# 一旦 1+ 个会话开始,检查是否没有连接别的:# 每个会话的布尔值列表连接 = [True,]*len(sessions)# 设置 `connected` 项为 false 会话上没有连接对于我在范围内(len(会话)):如果会话[i].connection_count == 0:连接 [i] = 假# 放慢节奏时间.sleep(2)# 一旦打开会话连接关闭,停止服务器io_loop.stop()返回无导入操作系统进口螺纹导入 tornado.ioloop导入 tornado.autoreload导入时间# 初始化一些值,清理散景图的路径argvs = {}app_names = []对于文件中的路径:argvs[路径] = 无app_names.append(os.path.splitext(os.path.split(path)[1])[0])# 连接主机名/端口以创建处理程序、启动应用程序主机 = '本地主机:{}'.格式(端口)# 初始化 Tornado 服务器io_loop = tornado.ioloop.IOLoop.instance()tornado.autoreload.start(io_loop)# 将 io_loop 添加到散景服务器server = run_bokeh_server(io_loop,文件,argvs,主机,端口)print('在{}'上启动服务器'.format(host))args = (io_loop,)th_startup = threading.Thread(target=start_bokeh, args=args)th_startup.start()# 在自己的选项卡或窗口中启动每个应用程序th_launch = [None,]*len(app_names)对于我在范围内(len(app_names)):args = (host, app_names[i], new)th_launch[i] = threading.Thread(target=launch_app, args=args)th_launch[i].start()# 延迟以允许选项卡在同一浏览器窗口中打开时间.sleep(2)# 运行会话连接测试,然后停止`io_loop`args =(服务器,io_loop)th_shutdown = threading.Thread(target=server_loop, args=args)th_shutdown.start()返回无如果 __name__ == "__main__":导入操作系统files = [os.path.join('bokeh', fname) for fname in ['ex1.py','ex2.py']]run_single_app(文件,端口=5006)
I am making an interactive data manipulation with bokeh
(0.12.6
) utility that I will deploy within a package. The idea is that a user can run a some routine module.utility()
that will start the bokeh server, launch the application in a browser, and when the tab or browser are closed, the server will be stopped.
My application launches fine if I run bokeh serve --show myapp
, but it hangs when connecting to the localhost using my method described below. I've inspected the handlers, everything looks as it should.
Is this a reasonable thing to do, and am I going about it correctly?
Directory format
<installed module path>/myapp
└── main.py
Where ./myapp
will reside in venv/lib/python3.5/site-packages/mymodule
etc.
main.py
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.plotting import figure
from bokeh.models.sources import ColumnDataSource
source = ColumnDataSource(dict(x=list(range(5)), y=list(range(5))))
p = figure(width=300, height=300, tools=[], toolbar_location=None)
p.line(x='x', y='y', source=source)
curdoc().add_root(column(p, sizing_mode='scale_width'))
Run script
def run_single_server(abs_app_path, port=5000):
'''Run bokeh application for single session from server`'''
from bokeh.application import Application
from bokeh.application.handlers import DirectoryHandler
from bokeh.server.server import Server
import os
app_name = os.path.split(abs_app_path)[1]
url = '/{}'.format(app_name)
# Start a bokeh server
apps = {url:Application(DirectoryHandler(filename=abs_app_path))}
server = Server(apps, port=port)
server.start()
server.show(url)
# somehow wait for session to end, perhaps using `server_lifecycle.py`
server.stop()
return
def utility():
import mymodule
module_path = os.path.split(mymodule.__file__)[0]
abs_app_path = os.path.join(module_path, 'myapp')
run_single_server(abs_app_path, port=5000)
return
Perhaps have that routine in the main __init__.py
, and have it work like this:
import mymodule
mymodule.utility()
# 1. Browser launches
# 2. user does stuff
# 3. user closes window
# 4. bokeh server is shutdown
Update
I found the build_single_handler_application
routine and tried that, but it also appears to hang.
from bokeh.command.util import build_single_handler_application
import os
app_name = os.path.split(abs_app_path)[1]
url = '/{}'.format(app_name)
# Start a bokeh server
apps = build_single_handler_application(abs_app_path)
It looks like I had a couple of problems. I ended up finding and adapting some code that I found on the mail group here for my use-case.
I managed to get everything to work by using separate process for 1) starting the server, 2) launching the app urls with webbrowser
, and 3) checking for closed connections and shutting down.
I think I could perhaps do away with initiating the tornado
server instance as was done in the flask example I adapted, but I'm happy here.
Note: this example uses single file apps, but you can pass the paths of directory formatted apps as well.
def create_bokeh_server(io_loop, files, argvs, host, port):
'''Start bokeh server with applications paths'''
from bokeh.server.server import Server
from bokeh.command.util import build_single_handler_applications
# Turn file paths into bokeh apps
apps = build_single_handler_applications(files, argvs)
# kwargs lifted from bokeh serve call to Server, with created io_loop
kwargs = {
'io_loop':io_loop,
'generate_session_ids':True,
'redirect_root':True,
'use_x_headers':False,
'secret_key':None,
'num_procs':1,
'host': host,
'sign_sessions':False,
'develop':False,
'port':port,
'use_index':True
}
server = Server(apps,**kwargs)
return server
def run_single_app(files, port=5000, new='tab'):
def start_bokeh(io_loop):
'''Start the `io_loop`'''
io_loop.start()
return None
def launch_app(host, app_name, new):
'''Lauch app in browser
Ideally this would `bokeh.util.browser.view()`, but it doesn't work
'''
import webbrowser
# Map method strings to webbrowser method
options = {'current':0, 'window':1, 'tab':2}
# Concatenate url and open in browser, creating a session
app_url = 'http://{}/{}'.format(host, app_name)
print('Opening `{}` in browser'.format(app_url))
webbrowser.open(app_url, new=options[new])
return None
def server_loop(server, io_loop):
'''Check connections once session created and close on disconnect'''
import time
connected = [True,]
session_loaded = False
while any(connected):
# Check if no session started on server
sessions = server.get_sessions()
if not session_loaded:
if sessions:
session_loaded = True
# Once 1+ sessions started, check for no connections
else:
# List of bools for each session
connected = [True,]*len(sessions)
# Set `connected` item false no connections on session
for i in range(len(sessions)):
if sessions[i].connection_count == 0:
connected[i] = False
# Keep the pace down
time.sleep(2)
# Stop server once opened session connections closed
io_loop.stop()
return None
import os
import threading
import tornado.ioloop
import tornado.autoreload
import time
# Initialize some values, sanatize the paths to the bokeh plots
argvs = {}
app_names = []
for path in files:
argvs[path] = None
app_names.append(os.path.splitext(os.path.split(path)[1])[0])
# Concate hostname/port for creating handlers, launching apps
host = 'localhost:{}'.format(port)
# Initialize the tornado server
io_loop = tornado.ioloop.IOLoop.instance()
tornado.autoreload.start(io_loop)
# Add the io_loop to the bokeh server
server = run_bokeh_server(io_loop, files, argvs, host, port)
print('Starting the server on {}'.format(host))
args = (io_loop,)
th_startup = threading.Thread(target=start_bokeh, args=args)
th_startup.start()
# Launch each application in own tab or window
th_launch = [None,]*len(app_names)
for i in range(len(app_names)):
args = (host, app_names[i], new)
th_launch[i] = threading.Thread(target=launch_app, args=args)
th_launch[i].start()
# Delay to allow tabs to open in same browser window
time.sleep(2)
# Run session connection test, then stop `io_loop`
args = (server, io_loop)
th_shutdown = threading.Thread(target=server_loop, args=args)
th_shutdown.start()
return None
if __name__ == "__main__":
import os
files = [os.path.join('bokeh', fname) for fname in ['ex1.py','ex2.py']]
run_single_app(files, port=5006)
这篇关于以编程方式运行散景服务器以在本地浏览器中显示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!