Run bokeh server from python
Purpose#Note Show
To make this guide easier to follow, consider familiarizing yourself with some of the core concepts of Bokeh in the section Defining key concepts. Bokeh server makes it easy to create interactive web applications that connect front-end UI events to running Python code. Bokeh creates high-level Python models, such as plots, ranges, axes, and glyphs, and then converts these objects to JSON to pass them to its client library, BokehJS. For more information on the latter, see Contributing to BokehJS. This flexible and decoupled design offers some advantages. For instance, it is easy to have other languages, such as R or Scala, drive Bokeh plots and visualizations in the browser. However, keeping these models in sync between the Python environment and the browser would provide further powerful capabilities:
This is where the Bokeh server comes into play: The primary purpose of the Bokeh server is to synchronize data between the underlying Python environment and the BokehJS library running in the browser. Here’s a simple example from demo.bokeh.org that illustrates this behavior. Manipulating the UI controls communicates new values to the backend via Bokeh server. This also triggers callbacks that update the plots with the input in real time. Use case scenarios#Consider a few different scenarios when you might want to use the Bokeh server. Local or individual use#You might want to use the Bokeh server for exploratory data analysis, possibly in a Jupyter notebook, or for a small app that you and your colleagues can run locally. The Bokeh server is very convenient here, allowing for quick and simple deployment through effective use of Bokeh server applications. For more detail, see Building Bokeh applications. Creating deployable applications#You might also want to use the Bokeh server to publish interactive data visualizations and applications to a wider audience, say, on the internet or an internal company network. The Bokeh server also suits this usage well, but you might want to first consult the following:
Shared publishing#Both of the scenarios above involve one person making applications on the server, either for personal use or for consumption by a larger audience. While it is possible for several people to publish different applications to the same server, this does not make for a good use case because hosted applications can execute arbitrary Python code. This raises process isolation and security concerns and makes this kind of shared tenancy prohibitive. One way to support this kind of multi-application environment with multiple users is to build up infrastructure that can run a Bokeh server for each app or at least for each user. The Bokeh project or a third party might create a public service for this kind of usage in the future but such developments are beyond the scope this documentation. Another possibility is to have one app that can access data and other artifacts published by many different people, possibly with access controls. This sort of scenario is possible with the Bokeh server, but often involves integrating it with other web application frameworks. Building Bokeh applications#By far the most flexible way to create interactive
data visualizations with the Bokeh server is to create Bokeh applications and serve them with the The Bokeh server (left) uses the application code to create Bokeh documents. Every new connection from a browser (right) results in the server creating a new document just for that session.# The Bokeh server executes the application code with every new connection and creates a new Bokeh document, syncing it to the browser. The application code also sets up the callbacks that should run whenever properties, such as widget values, change. You can provide the application code in several ways. Single module format#Consider the following complete example. # myapp.py from random import random from bokeh.layouts import column from bokeh.models import Button from bokeh.palettes import RdYlBu3 from bokeh.plotting import figure, curdoc # create a plot and style its properties p = figure(x_range=(0, 100), y_range=(0, 100), toolbar_location=None) p.border_fill_color = 'black' p.background_fill_color = 'black' p.outline_line_color = None p.grid.grid_line_color = None # add a text renderer to the plot (no data yet) r = p.text(x=[], y=[], text=[], text_color=[], text_font_size="26px", text_baseline="middle", text_align="center") i = 0 ds = r.data_source # create a callback that adds a number in a random location def callback(): global i # BEST PRACTICE --- update .data in one step with a new dict new_data = dict() new_data['x'] = ds.data['x'] + [random()*70 + 15] new_data['y'] = ds.data['y'] + [random()*70 + 15] new_data['text_color'] = ds.data['text_color'] + [RdYlBu3[i%3]] new_data['text'] = ds.data['text'] + [str(i)] ds.data = new_data i = i + 1 # add a button widget and configure with the call back button = Button(label="Press Me") button.on_click(callback) # put the button and plot in a layout and add to the document curdoc().add_root(column(button, p)) The code above
doesn’t specify any output or connection method. It is a simple script that creates and updates objects. The bokeh serve --show myapp.py The http://localhost:5006/myapp If you have only one application, the server root will redirect to it. Otherwise, you will see an index of all applications running on the server root: You can disable this index with the In addition to creating Bokeh applications from single Python files, you can also create applications from directories. Directory format#You can create Bokeh apps by creating and populating a filesystem directory with application files. To start an application in a directory named This directory must contain a The following is the directory app structure that the Bokeh server is familiar with: myapp | +---__init__.py +---app_hooks.py +---main.py +---request_handler.py +---static +---theme.yaml +---templates +---index.html Some of the files and subdirectories above are optional.
When
executing your Bokeh also adds the application directory Here’s an example of a more developed directory tree: myapp | +---__init__.py | +---app_hooks.py +---data | +---things.csv | +---helpers.py +---main.py |---models | +---custom.js | +---request_handler.py +---static | +---css | | +---special.css | | | +---images | | +---foo.png | | +---bar.png | | | +---js | +---special.js | |---templates | +---index.html | +---theme.yaml In this case, your code might be similar to the following: from os.path import dirname, join from .helpers import load_data load_data(join(dirname(__file__), 'data', 'things.csv')) The code to load a JavaScript implementation for a custom model from Customizing the application’s Jinja template#The Directory format section mentions that you can override the default Jinja template, which the Bokeh server uses to generate user-facing HTML. This lets you use CSS and JavaScript to tweak the way the application appears in the browser. For more details on how Jinja templating works, see the Jinja project documentation. Embedding figures in the template#To
reference a Bokeh figure in the templated code, you need to set its from bokeh.plotting import curdoc # templates can refer to a configured name value plot = figure(name="bokeh_jinja_figure") curdoc().add_root(plot) You can then use that name in the corresponding Jinja template to reference the figure via the {% extends base %} {% block contents %} <div> {{ embed(roots.bokeh_jinja_figure) }} div> {% endblock %} Defining custom variables#You can pass custom variables to the template with the # set a new single key/value pair curdoc().template_variables["user_id"] = user_id # or update multiple pairs at once curdoc().template_variables.update(first_name="Mary", last_name="Jones") You can then reference the variables in the corresponding Jinja template. {% extends base %} {% block contents %} <div> <p> Hello {{ user_id }}, AKA '{{ last_name }}, {{ first_name }}'! p> div> {% endblock %} Accessing HTTP requests#When creating a session for an application, Bokeh makes the session context available as You can enable additional request attributes as described in Request handler hooks. The following
code accesses the request # request.arguments is a dict that maps argument names to lists of strings, # for example, the query string ?N=10 results in {'N': [b'10']} args = curdoc().session_context.request.arguments try: N = int(args.get('N')[0]) except: N = 200 Warning The request object makes inspecting values, such as Request handler hooks#To provide additional information where full Tornado HTTP requests may not be available, you can define a custom handler hook. To do so, create an app in
directory format and include a file called def process_request(request): '''If present, this function executes when an HTTP request arrives.''' return {} The process then passes Tornado HTTP requests to the handler, which returns a dictionary for Callbacks and events#Before jumping into callbacks and events specifically in the context of the Bokeh server, it’s worth discussing different use cases for callbacks in general. JavaScript callbacks in the browser#Whether you are using the Bokeh server or not, you can create callbacks that execute in the browser with
Python callbacks with Jupyter interactors#When working with Jupyter notebooks, you can use Jupyter interactors to quickly create simple GUI forms. Updates to GUI widgets trigger Python callbacks that execute in the Python kernel of Jupyter. It is often useful to
have these callbacks call Updating from threads#You can make blocking computations in separate threads. However, you must schedule document updates via a next tick callback. This callback executes as soon as possible with the next iteration of the Tornado event loop and automatically acquires necessary locks to safely update the document state. Remember, direct updates to the document state issuing from another thread, whether through other document methods or setting of Bokeh model properties, risk data and protocol corruption. To allow all threads access to the same document, save a local copy of import time from functools import partial from random import random from threading import Thread from bokeh.models import ColumnDataSource from bokeh.plotting import curdoc, figure # only modify from a Bokeh session callback source = ColumnDataSource(data=dict(x=[0], y=[0])) # This is important! Save curdoc() to make sure all threads # see the same document. doc = curdoc() async def update(x, y): source.stream(dict(x=[x], y=[y])) def blocking_task(): while True: # do some blocking computation time.sleep(0.1) x, y = random(), random() # but update the document from a callback doc.add_next_tick_callback(partial(update, x=x, y=y)) p = figure(x_range=[0, 1], y_range=[0,1]) l = p.circle(x='x', y='y', source=source) doc.add_root(p) thread = Thread(target=blocking_task) thread.start() To see this example in action, save the above code to a Python file, for example, bokeh serve --show testapp.py Warning There is currently no locking around adding next tick callbacks to documents. Bokeh should have a more fine-grained locking for callback methods in the future, but for now it is best to have each thread add no more than one callback to the document. Updating from unlocked callbacks#Normally Bokeh session callbacks recursively lock the document until all future work they initiate is completed. However, you may want to drive blocking computations from callbacks using Tornado’s As with the thread example above, all actions that update document state must go through a next tick callback. The following example demonstrates an application that drives a blocking computation from one unlocked Bokeh session callback. It yields to a blocking function that runs on the thread pool executor and then updates with a next tick callback. The example also updates the state simply from a standard locked session callback with a different update rate. import asyncio import time from concurrent.futures import ThreadPoolExecutor from functools import partial from bokeh.document import without_document_lock from bokeh.models import ColumnDataSource from bokeh.plotting import curdoc, figure source = ColumnDataSource(data=dict(x=[0], y=[0], color=["blue"])) i = 0 doc = curdoc() executor = ThreadPoolExecutor(max_workers=2) def blocking_task(i): time.sleep(1) return i # the unlocked callback uses this locked callback to safely update async def locked_update(i): source.stream(dict(x=[source.data['x'][-1]+1], y=[i], color=["blue"])) # this unlocked callback will not prevent other session callbacks from # executing while it is running @without_document_lock async def unlocked_task(): global i i += 1 res = await asyncio.wrap_future(executor.submit(blocking_task, i), loop=None) doc.add_next_tick_callback(partial(locked_update, i=res)) async def update(): source.stream(dict(x=[source.data['x'][-1]+1], y=[i], color=["red"])) p = figure(x_range=[0, 100], y_range=[0, 20]) l = p.circle(x='x', y='y', color='color', source=source) doc.add_periodic_callback(unlocked_task, 1000) doc.add_periodic_callback(update, 200) doc.add_root(p) As before, you can run this example by saving to a Python file and running Lifecycle hooks#You may want to execute code at specific points of server or session runtime. For instance, if you are using a Bokeh server with a Django server, you need to call Bokeh enables this through a set of lifecycle hooks. To use these
hooks, create your application in directory format and include a designated file called def on_server_loaded(server_context): # If present, this function executes when the server starts. pass def on_server_unloaded(server_context): # If present, this function executes when the server shuts down. pass def on_session_created(session_context): # If present, this function executes when the server creates a session. pass def on_session_destroyed(session_context): # If present, this function executes when the server closes a session. pass You can also define doc = Document() def cleanup_session(session_context): # This function executes when the user closes the session. pass doc.on_session_destroyed(cleanup_session) Besides the lifecycle hooks above, you may also define request hooks to access the HTTP requests your users make. For further information, see Request handler hooks. Embedding Bokeh server as a library#It can be
useful to embed the Bokeh Server in a larger Tornado application, or a Jupyter notebook, and use the already existing Tornado from bokeh.server.server import Server server = Server( bokeh_applications, # list of Bokeh applications io_loop=loop, # Tornado IOLoop **server_kwargs # port, num_procs, etc. ) # start timers and services and immediately return server.start() You can also create and control an
Also note that every command line argument for Connecting with bokeh.client#You can directly interact with the Bokeh server via a client API, which you can use to make modifications to Bokeh documents in existing sessions on a Bokeh server. Typically, web browsers connect to the Bokeh server, but you can make a connection from Python by using the This can be useful, for example, to make user-specific customizations to a Bokeh app that is embedded by another web framework, such as Flask or Django. In the following example, a Flask endpoint embeds a “sliders” app already running on the server but changes the plot title before passing the output to the user. from flask import Flask, render_template from bokeh.client import pull_session from bokeh.embed import server_session app = Flask(__name__) @app.route('/', methods=['GET']) def bkapp_page(): with pull_session(url="http://localhost:5006/sliders") as session: # update or customize that session session.document.roots[0].children[1].title.text = "Special sliders for a specific user!" # generate a script to load the customized session script = server_session(session_id=session.id, url='http://localhost:5006/sliders') # use the script in the rendered page return render_template("embed.html", script=script, template="Flask") if __name__ == '__main__': app.run(port=8080) Deployment scenarios#To make your application into a user-friendly service, you’ll have to deploy your work. This subsection explores various aspects of deployment. Standalone Bokeh server#You can have the Bokeh server running on a network for users to interact with your app directly. This can be a simple solution for local network deployment, provided the capabilities of the hardware running the server match your app requirements and the expected number of users. However, if you have authentication, scaling, or uptime requirements, you’ll have to consider more sophisticated deployment configurations. SSH tunnels#To run a standalone instance of the Bokeh server on a host with restricted access, use SSH to “tunnel” to the server. In the simplest scenario, the user accesses the Bokeh server from another location, such as a laptop with no intermediary machines. Run the server as usual on the remote host. Next, issue the following command on the local machine to establish an SSH tunnel to the remote host: ssh -NfL localhost:5006:localhost:5006 Replace user with your username on the remote host and remote.host with the hostname or IP address of the system hosting the Bokeh server.
The remote system may prompt you for login credentials. Once you are connected, you will be able to navigate to A slightly more complicated scenario involves a gateway between the server and the local machine. In that situation, a reverse tunnel must be established from the server to the gateway with another tunnel connecting the gateway with the local machine. Issue the following commands on the remote host where the Bokeh server will be running: nohup bokeh server & ssh -NfR 5006:localhost:5006 Replace user with your username on the gateway and gateway.host with the hostname or IP address of the gateway. The gateway may prompt you for login credentials. To setup the tunnel between the local machine and the gateway, run the following command on the local machine: ssh -NfL localhost:5006:localhost:5006 Again, replace user with your username on the gateway and gateway.host with the hostname or IP address of the gateway. You should now be able to access the Bokeh server from the local machine by navigating to Note We intend to expand this section with more guidance for other tools and configurations. If you have experience with other web deployment scenarios and wish to contribute your knowledge here, please contact us on https://discourse.bokeh.org SSL termination#You can configure the Bokeh server to terminate SSL connections and serve secure HTTPS and WSS sessions directly. To do so, you’ll
have to supply the bokeh serve --ssl-certfile /path/to/cert.pem You can also supply a path to the certificate file by setting the environment variable If the private key is stored separately, you can supply its location by setting the
Alternatively, you may wish to run a Bokeh server behind a proxy and have the proxy terminate SSL connections. See the next subsection for details. Basic reverse proxy setup#To serve a web application to the general internet, you may wish to host your app on an internal network and proxy connections to it through some dedicated HTTP server. This subsection provides guidance on how to configure some common reverse proxies. Nginx#One very common HTTP and reverse-proxying server is Nginx. Here’s an example of a server { listen 80 default_server; server_name _; access_log /tmp/bokeh.access.log; error_log /tmp/bokeh.error.log debug; location / { proxy_pass http://127.0.0.1:5100; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_http_version 1.1; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host:$server_port; proxy_buffering off; } } The above bokeh serve myapp.py --port 5100
The basic server block above does not configure any special handling for static resources, such as Bokeh JS and CSS files. This means that the Bokeh server serves these files directly. Although this is a viable option, it requires that the Bokeh server do extra work that is better handled with Nginx. To serve static assets with Nginx, add the following sub-block to the code above,
substituting the path to your static assets for location /static { alias /path/to/bokeh/server/static; } Make sure that the account running Nginx has permissions to access Bokeh resources. Alternatively, you can copy the resources to a global static directory during the deployment. To communicate cookies and headers across processes, Bokeh may include this information in a JSON web token, sending it via a WebSocket. In certain cases this token can grow very large causing Nginx to drop the request. You may have to work around this by overriding the default Nginx setting large_client_header_buffers: large_client_header_buffers 4 24k; Apache#Another common HTTP server and proxy is Apache. Here is an example configuration for a Bokeh server running behind Apache:
The above configuration aliases /static to the location of the Bokeh static resources directory. However, it is also possible (and probably preferable) to copy the static resources to whatever standard location for static files you configure for Apache as part of the deployment. You may also need to enable some modules for the above configuration: a2enmod proxy a2enmod proxy_http a2enmod proxy_wstunnel apache2ctl restart Depending on your system, you may have to use As before, run the Bokeh server with the following command: bokeh serve myapp.py --port 5100
Reverse proxying with Nginx and SSL#To deploy a Bokeh server behind an SSL-terminated Nginx proxy, you’ll need a few additional customizations. In particular, you’ll have to
configure the Bokeh server with the bokeh serve myapp.py --port 5100 --use-xheaders
The You’ll also have to customize Nginx. In particular, you have to configure Nginx to send The complete details of this
configuration, such as how and where to install SSL certificates and keys, varies by platform and the following is only a reference # redirect HTTP traffic to HTTPS (optional) server { listen 80; server_name foo.com; return 301 https://$server_name$request_uri; } server { listen 443 default_server; server_name foo.com; # adds Strict-Transport-Security to prevent man-in-the-middle attacks add_header Strict-Transport-Security "max-age=31536000"; ssl on; # SSL installation details vary by platform ssl_certificate /etc/ssl/certs/my-ssl-bundle.crt; ssl_certificate_key /etc/ssl/private/my_ssl.key; # enables all versions of TLS, but not the deprecated SSLv2 or v3 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # disables all weak ciphers ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; ssl_prefer_server_ciphers on; location / { proxy_pass http://127.0.0.1:5100; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_http_version 1.1; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host:$server_port; proxy_buffering off; } } This configuration will proxy all incoming HTTPS connections to Load balancing with Nginx#The Bokeh server is scalable by design. If you need more capacity, you can simply run additional servers. In this case, you’ll generally want to run all the Bokeh server instances behind a load balancer so that new connections are distributed among individual servers. The Bokeh server is horizontally scalable. To add more capacity, you can run more servers behind a load balancer.# Nginx can help with load balancing. This section describes some of the basics of one possible configuration, but please also refer to the Nginx load balancer documentation. For instance, there are different strategies available for choosing what server to connect to next. First, you need to add an upstream myapp { least_conn; # Use the least-connected strategy server 127.0.0.1:5100; # Bokeh Server 0 server 127.0.0.1:5101; # Bokeh Server 1 server 127.0.0.1:5102; # Bokeh Server 2 server 127.0.0.1:5103; # Bokeh Server 3 server 127.0.0.1:5104; # Bokeh Server 4 server 127.0.0.1:5105; # Bokeh Server 5 } The rest of the configuration uses the name To run a Bokeh server instance, use commands similar to the following: serve myapp.py --port 5100 serve myapp.py --port 5101 ... Next, in the server { location / { proxy_pass http://myapp; # all other settings unchanged proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_http_version 1.1; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host:$server_port; proxy_buffering off; } } Authentication#The Bokeh server itself does not have any facilities for authentication or authorization. However, you can configure the Bokeh server with an “auth provider” that hooks into Tornado’s underlying capabilities. For background information, see the Tornado docs for Authentication and security. The rest of this section assumes some familiarity with that material. Auth module#You can configure the Bokeh server to only allow authenticated users to connect. To do so, provide a path to the module that implements the necessary functions on the command line. bokeh serve --auth-module=/path/to/auth.py
Alternatively, you can set the The module must contain one of the following two functions that return the current user (or def get_user(request_handler): pass async def get_user_async(request_handler): pass The module passes the function to the Tornado Additionally, the module must specify where to redirect unauthenticated users by including either:
login_url = "..." class LoginHandler(RequestHandler): pass def get_login_url(request_handler): pass If the module provides a relative The To define an endpoint for logging users out, you can also use optional If you don’t provide an authentication module, the configuration will not require any authentication to access Bokeh server endpoints. Warning The configuration executes the contents of the authentication module. Secure cookies#If you want to use Tornado’s
set_secure_cookie and get_secure_cookie functions in your auth module, you’ll have to set a cookie secret. To do so, use the export BOKEH_COOKIE_SECRET= The value should be a long, random sequence of bytes. Security#By default, the Bokeh server will accept any incoming connections with an allowed WebSocket origin. If you specify a session ID, and a session with that ID already exists on the server, the server will connect to that session. Otherwise, the server will automatically create and use a new session. If you are deploying an embedded Bokeh app within a large organization or to the wider internet, you may want to limit who can initiate sessions, and from where. Bokeh lets you manage session creation privileges. WebSocket origin#When a Bokeh server receives an HTTP request, it immediately returns a script that initiates a WebSocket connection. All subsequent communication happens over the WebSocket. To reduce the risk of cross-site misuse, the Bokeh server will only initiate WebSocket connections from the origins that are explicitly allowed. Requests with By default, only bokeh serve --show myapp.py and bokeh serve --show --allow-websocket-origin=localhost:5006 myapp.py
Both of these open your default browser to the default application URL When you embed a Bokeh server in another web page with For example, if a user navigates to your page at You can do so by setting the bokeh serve --show --allow-websocket-origin=acme.com myapp.py
This will prevent other sites from embedding your Bokeh application in their pages because requests from users viewing those pages will report a different origin than Warning Bear in mind that this only prevents other web pages from embedding your Bokeh app without your knowledge. If you require multiple allowed origins, you can pass multiple instances of You can also configure the Bokeh server to allow all connections regardless of origin: bokeh serve --show --allow-websocket-origin='*' myapp.py This option is only suitable for testing, experimentation, and local notebook usage. Signed session IDs#By default, the Bokeh server will automatically create new sessions for all new requests from allowed WebSocket origins, even if you provide no session ID. When embedding a Bokeh app inside another web application, such as Flask or Django, make sure that only your web application is capable of generating viable requests to the Bokeh server, which you can configure to only create sessions with a cryptographically signed session ID. First, use the export BOKEH_SECRET_KEY=`bokeh secret` Then set BOKEH_SIGN_SESSIONS=yes bokeh serve --allow-websocket-origin=acme.com myapp.py Then, in your web application, explicitly provide signed session IDs with from bokeh.util.token import generate_session_id script = server_session(url='http://localhost:5006/bkapp', session_id=generate_session_id()) return render_template("embed.html", script=script, template="Flask") Make sure to set
identical Note Signed session IDs serve as access tokens. As with any token system, security is predicated on keeping the token secret. You should also run the Bokeh server behind a proxy that terminates SSL connections, or configure the Bokeh server to terminate SSL directly. This lets you securely transmit session IDs to the client browsers. XSRF cookies#Bokeh server can use Tornado’s cross-site request forgery protection. To turn this feature on, use the With this setting, you’ll have to properly instrument all PUT, POST, and DELETE operations on custom and login handlers in order for them to function. Typically, this means adding the following code to all HTML form submission templates: {% module xsrf_form_html() %} For full details, see the Tornado documentation on XSRF Cookies. Scaling the server#You can fork multiple server processes with the num-procs option. For example, run the following command to fork 3 processes: bokeh serve --num-procs 3
Note that the forking operation happens in the underlying Tornado server. For further information, see the Tornado docs. Further reading#Now that you are familiar with the concepts of running a Bokeh server, you may be interested in learning more about the internals of the Bokeh server in Understanding Bokeh server. How do you use Bokeh in Python?Bokeh is a data visualization library in Python that provides high-performance interactive charts and plots. Bokeh output can be obtained in various mediums like notebook, html and server. ... . Code #2: Single line. ... . Code #3: Bar Chart. ... . Code #4: Box Plot. ... . Code #5: Histogram. ... . Code #6: Scatter plot.. How do you start a Bokeh server?To run a Bokeh server instance, use commands similar to the following: serve myapp.py --port 5100 serve myapp.py --port 5101 ... Next, in the location stanza for the Bokeh server, change the proxy_pass value to refer to the upstream stanza above. The code below uses proxy_pass http://myapp; .
Which is better Bokeh or Plotly?Though Plotly is good for plotting graphs and visualizing data for insights, it is not good for making dashboards. To make dashboards we can use bokeh and can have very fast dashboards and interactivity. In this comparison of Bokeh vs Plotly, there is no clear winner.
How do you use Bokeh with a flask?Create a new Flask project:. $ mkdir flask-bokeh-example && cd flask-bokeh-example. ... . $ python3 -m venv env $ source env/bin/activate. ... . $ pip install flask==0.12.2. ... . from flask import Flask, render_template app = Flask(__name__) @app. ... . $ pip install bokeh==0.12.6. ... . from bokeh. ... . @app. ... . |