以编程方式运行散景服务器以在本地浏览器中显示 [英] Running bokeh server programmatically to show in browser locally

查看:26
本文介绍了以编程方式运行散景服务器以在本地浏览器中显示的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 bokeh (0.12.6) 实用程序进行交互式数据操作,我将在包中部署该实用程序.这个想法是,用户可以运行一些例程 module.utility(),它将启动Bokeh Server,在浏览器中启动应用程序,当标签或浏览器关闭时,服务器将是停止了.

如果我运行 bokeh serve --show myapp,我的应用程序可以正常启动,但是在使用我下面描述的方法连接到本地主机时它会挂起.我检查了处理程序,一切看起来都应该如此.

<块引用>

这样做是否合理,我的做法是否正确?

目录格式

<安装模块路径>/myapp└── main.py

./myapp 将驻留在 venv/lib/python3.5/site-packages/mymodule 等中的位置

ma​​in.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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆