0
# Server and Applications
1
2
Framework for building interactive web applications with Python callbacks, real-time data updates, and complex application logic. The Bokeh server enables creation of full-featured data applications that respond to user interactions entirely in Python without requiring JavaScript programming.
3
4
## Capabilities
5
6
### Application Framework
7
8
Core classes for creating Bokeh server applications.
9
10
```python { .api }
11
class Application:
12
"""
13
Bokeh application factory for creating documents.
14
15
An Application object is a factory for Document instances. It
16
defines the structure and behavior of server applications.
17
"""
18
def __init__(self, *handlers, **kwargs):
19
"""
20
Parameters:
21
- handlers: FunctionHandler, DirectoryHandler, or ScriptHandler objects
22
- **kwargs: Additional application configuration
23
"""
24
25
# Application lifecycle methods
26
def create_document(self):
27
"""Create a new document instance for a session."""
28
29
def process_request(self, request):
30
"""Process incoming HTTP request."""
31
32
def on_server_loaded(self, server_context):
33
"""Called when application is loaded on server."""
34
35
def on_server_unloaded(self, server_context):
36
"""Called when application is unloaded from server."""
37
38
def on_session_created(self, session_context):
39
"""Called when new session is created."""
40
41
def on_session_destroyed(self, session_context):
42
"""Called when session is destroyed."""
43
44
# Application handlers
45
class Handler:
46
"""Base class for application handlers."""
47
48
class FunctionHandler(Handler):
49
"""Handler that uses a function to create document content."""
50
def __init__(self, func):
51
"""
52
Parameters:
53
- func: Function that takes a Document and modifies it
54
"""
55
56
class ScriptHandler(Handler):
57
"""Handler that loads Python script as application."""
58
def __init__(self, filename):
59
"""
60
Parameters:
61
- filename: Path to Python script file
62
"""
63
64
class DirectoryHandler(Handler):
65
"""Handler that loads application from directory structure."""
66
def __init__(self, path):
67
"""
68
Parameters:
69
- path: Path to application directory
70
"""
71
```
72
73
### Document Management
74
75
Functions and classes for managing the current document context in server applications.
76
77
```python { .api }
78
def curdoc():
79
"""
80
Get current document.
81
82
In server applications, returns the Document instance for the
83
current session. In standalone scripts, creates a new Document.
84
85
Returns:
86
Document: Current document instance
87
"""
88
89
class Document:
90
"""
91
Container for all Bokeh models in an application.
92
93
The Document holds all models and manages their relationships,
94
callbacks, and lifecycle in server applications.
95
"""
96
def __init__(self, **kwargs): ...
97
98
# Model management
99
def add_root(self, model):
100
"""Add a model as a root of the document."""
101
102
def remove_root(self, model):
103
"""Remove a root model from the document."""
104
105
@property
106
def roots(self):
107
"""List of root models in the document."""
108
109
# Callback management
110
def add_periodic_callback(self, callback, period_milliseconds):
111
"""
112
Add a callback to be executed periodically.
113
114
Parameters:
115
- callback: Function to call periodically
116
- period_milliseconds: Callback interval in milliseconds
117
118
Returns:
119
PeriodicCallback: Callback object for management
120
"""
121
122
def add_timeout_callback(self, callback, timeout_milliseconds):
123
"""
124
Add a callback to be executed once after a timeout.
125
126
Parameters:
127
- callback: Function to call after timeout
128
- timeout_milliseconds: Timeout in milliseconds
129
130
Returns:
131
TimeoutCallback: Callback object for management
132
"""
133
134
def add_next_tick_callback(self, callback):
135
"""Add a callback to be executed on the next tick."""
136
137
def remove_periodic_callback(self, callback):
138
"""Remove a periodic callback."""
139
140
def remove_timeout_callback(self, callback):
141
"""Remove a timeout callback."""
142
143
def remove_next_tick_callback(self, callback):
144
"""Remove a next-tick callback."""
145
146
# Property change callbacks
147
def on_change(self, *callbacks):
148
"""Register callbacks for document changes."""
149
150
# Session management
151
@property
152
def session_context(self):
153
"""Current session context."""
154
155
# Serialization
156
def to_json_string(self, include_defaults=True):
157
"""Serialize document to JSON string."""
158
159
def to_json(self, include_defaults=True):
160
"""Serialize document to JSON object."""
161
162
# Title and metadata
163
title: str # Document title
164
template: str # HTML template name
165
template_variables: Dict[str, Any] # Template variables
166
167
class without_document_lock:
168
"""
169
Context manager for operations outside document lock.
170
171
Some operations (like network requests) should be performed
172
outside the document lock to avoid blocking the server.
173
"""
174
def __enter__(self): ...
175
def __exit__(self, exc_type, exc_val, exc_tb): ...
176
```
177
178
### Client Session Management
179
180
Classes for connecting to and managing Bokeh server sessions from client code.
181
182
```python { .api }
183
class ClientSession:
184
"""
185
Client session for connecting to Bokeh server.
186
187
Manages the connection between a client and a server application,
188
handling authentication, document synchronization, and callbacks.
189
"""
190
def __init__(self, session_id=None, websocket_url=None, io_loop=None, **kwargs):
191
"""
192
Parameters:
193
- session_id: Unique session identifier
194
- websocket_url: WebSocket URL for server connection
195
- io_loop: Tornado IOLoop instance
196
"""
197
198
# Connection management
199
def connect(self):
200
"""Connect to the server."""
201
202
def close(self, why="closed"):
203
"""Close the connection."""
204
205
@property
206
def connected(self):
207
"""Whether session is connected to server."""
208
209
# Document access
210
@property
211
def document(self):
212
"""Document associated with this session."""
213
214
# Callback management
215
def on_change(self, *callbacks):
216
"""Register callbacks for session changes."""
217
218
# Request handling
219
def request_server_info(self):
220
"""Request server information."""
221
222
def force_roundtrip(self):
223
"""Force a round-trip to synchronize with server."""
224
225
def pull_session(session_id=None, url=None, io_loop=None, **kwargs):
226
"""
227
Pull session from server.
228
229
Creates a ClientSession connected to an existing server session.
230
231
Parameters:
232
- session_id: Session ID to connect to (None for new session)
233
- url: Server URL
234
- io_loop: Tornado IOLoop instance
235
236
Returns:
237
ClientSession: Connected client session
238
"""
239
240
def push_session(document, session_id=None, url=None, io_loop=None, **kwargs):
241
"""
242
Push session to server.
243
244
Creates a new server session with the given document.
245
246
Parameters:
247
- document: Document to push to server
248
- session_id: Session ID (None for auto-generated)
249
- url: Server URL
250
- io_loop: Tornado IOLoop instance
251
252
Returns:
253
ClientSession: Client session for the pushed document
254
"""
255
256
def show_session(session_id=None, server_url=None, session_url=None,
257
browser=None, new=None, **kwargs):
258
"""
259
Display session in browser.
260
261
Parameters:
262
- session_id: Session ID to display
263
- server_url: Base server URL
264
- session_url: Full session URL
265
- browser: Browser name/path
266
- new: Browser window behavior ('tab', 'window', None)
267
"""
268
269
DEFAULT_SESSION_ID: str # Default session identifier constant
270
```
271
272
### Server Infrastructure
273
274
Classes for running and configuring the Bokeh server.
275
276
```python { .api }
277
class Server:
278
"""
279
Bokeh server for hosting applications.
280
281
The Server class provides the web server infrastructure for
282
hosting Bokeh applications with WebSocket communication.
283
"""
284
def __init__(self, applications, port=5006, address=None, **kwargs):
285
"""
286
Parameters:
287
- applications: Dict mapping URL paths to Application objects
288
- port: Server port number
289
- address: Server bind address
290
"""
291
292
def start(self):
293
"""Start the server."""
294
295
def stop(self):
296
"""Stop the server."""
297
298
def run_until_shutdown(self):
299
"""Run server until shutdown signal."""
300
301
@property
302
def port(self):
303
"""Server port number."""
304
305
@property
306
def address(self):
307
"""Server bind address."""
308
309
# Server lifecycle callbacks
310
class SessionContext:
311
"""Context for server session lifecycle callbacks."""
312
session_id: str
313
server_context: 'ServerContext'
314
destroyed: bool
315
316
class ServerContext:
317
"""Context for server application lifecycle callbacks."""
318
application_context: 'ApplicationContext'
319
sessions: Dict[str, SessionContext]
320
```
321
322
### Callback Types
323
324
Classes for different types of server callbacks.
325
326
```python { .api }
327
class PeriodicCallback:
328
"""Periodic callback that executes at regular intervals."""
329
def __init__(self, callback, period, io_loop=None):
330
"""
331
Parameters:
332
- callback: Function to execute
333
- period: Period in milliseconds
334
- io_loop: Tornado IOLoop instance
335
"""
336
337
def start(self):
338
"""Start the periodic callback."""
339
340
def stop(self):
341
"""Stop the periodic callback."""
342
343
@property
344
def is_running(self):
345
"""Whether callback is currently running."""
346
347
class TimeoutCallback:
348
"""One-time callback that executes after a timeout."""
349
def __init__(self, callback, timeout, io_loop=None):
350
"""
351
Parameters:
352
- callback: Function to execute
353
- timeout: Timeout in milliseconds
354
- io_loop: Tornado IOLoop instance
355
"""
356
357
class NextTickCallback:
358
"""Callback that executes on the next event loop tick."""
359
def __init__(self, callback, io_loop=None):
360
"""
361
Parameters:
362
- callback: Function to execute
363
- io_loop: Tornado IOLoop instance
364
"""
365
```
366
367
## Usage Examples
368
369
### Basic Server Application
370
371
```python
372
# save as: myapp.py
373
from bokeh.plotting import figure, curdoc
374
from bokeh.models import ColumnDataSource, Button, Column
375
from bokeh.events import ButtonClick
376
import numpy as np
377
378
# Create data source
379
source = ColumnDataSource(data=dict(x=[1, 2, 3, 4], y=[1, 4, 2, 3]))
380
381
# Create plot
382
p = figure(width=400, height=400, title="Server Application")
383
line = p.line('x', 'y', source=source, line_width=3)
384
385
# Create button
386
button = Button(label="Update Data", button_type="success")
387
388
def update():
389
"""Update plot with random data."""
390
n = np.random.randint(5, 10)
391
new_data = dict(
392
x=list(range(n)),
393
y=np.random.random(n) * 10
394
)
395
source.data = new_data
396
397
# Handle button clicks
398
button.on_event(ButtonClick, lambda event: update())
399
400
# Create layout
401
layout = Column(button, p)
402
403
# Add to current document
404
curdoc().add_root(layout)
405
curdoc().title = "My Bokeh App"
406
407
# Run with: bokeh serve myapp.py
408
```
409
410
### Periodic Updates
411
412
```python
413
# Real-time data application
414
from bokeh.plotting import figure, curdoc
415
from bokeh.models import ColumnDataSource
416
import numpy as np
417
from datetime import datetime
418
import random
419
420
# Create streaming data source
421
source = ColumnDataSource(data=dict(time=[], value=[]))
422
423
# Create plot
424
p = figure(width=600, height=400, title="Real-Time Data Stream",
425
x_axis_type='datetime')
426
p.line('time', 'value', source=source, line_width=2)
427
428
def update_data():
429
"""Add new data point."""
430
new_time = datetime.now()
431
new_value = random.random() * 100
432
433
# Stream new data (keep last 100 points)
434
source.stream(dict(time=[new_time], value=[new_value]), rollover=100)
435
436
# Add periodic callback for updates every 500ms
437
curdoc().add_periodic_callback(update_data, 500)
438
curdoc().add_root(p)
439
curdoc().title = "Real-Time Dashboard"
440
```
441
442
### Interactive Dashboard with Widgets
443
444
```python
445
from bokeh.plotting import figure, curdoc
446
from bokeh.models import ColumnDataSource, Slider, Select, Column, Row
447
from bokeh.layouts import column, row
448
import numpy as np
449
450
# Data source
451
source = ColumnDataSource(data=dict(x=[], y=[]))
452
453
# Create plot
454
p = figure(width=500, height=400, title="Interactive Dashboard")
455
line = p.line('x', 'y', source=source, line_width=2)
456
457
# Widgets
458
freq_slider = Slider(start=0.1, end=5.0, value=1.0, step=0.1,
459
title="Frequency")
460
func_select = Select(title="Function", value="sin",
461
options=["sin", "cos", "tan"])
462
amplitude_slider = Slider(start=0.1, end=5.0, value=1.0, step=0.1,
463
title="Amplitude")
464
465
def update_plot():
466
"""Update plot based on widget values."""
467
freq = freq_slider.value
468
func_name = func_select.value
469
amplitude = amplitude_slider.value
470
471
x = np.linspace(0, 4*np.pi, 100)
472
473
if func_name == "sin":
474
y = amplitude * np.sin(freq * x)
475
elif func_name == "cos":
476
y = amplitude * np.cos(freq * x)
477
else: # tan
478
y = amplitude * np.tan(freq * x)
479
y = np.clip(y, -10, 10) # Limit tan values
480
481
source.data = dict(x=x, y=y)
482
483
# Widget callbacks
484
def on_change(attr, old, new):
485
update_plot()
486
487
freq_slider.on_change('value', on_change)
488
func_select.on_change('value', on_change)
489
amplitude_slider.on_change('value', on_change)
490
491
# Initial plot
492
update_plot()
493
494
# Layout
495
controls = column(freq_slider, func_select, amplitude_slider)
496
layout = row(controls, p)
497
498
curdoc().add_root(layout)
499
curdoc().title = "Interactive Function Plotter"
500
```
501
502
### Multi-Session Application
503
504
```python
505
from bokeh.plotting import figure, curdoc
506
from bokeh.models import ColumnDataSource, Div, Column
507
import numpy as np
508
import uuid
509
510
# Generate unique session data
511
session_id = str(uuid.uuid4())[:8]
512
session_data = np.random.random(50) * 100
513
514
# Create data source with session-specific data
515
source = ColumnDataSource(data=dict(
516
x=list(range(len(session_data))),
517
y=session_data
518
))
519
520
# Create plot
521
p = figure(width=500, height=300, title=f"Session {session_id}")
522
p.line('x', 'y', source=source, line_width=2)
523
p.circle('x', 'y', source=source, size=8, alpha=0.7)
524
525
# Session info
526
info = Div(text=f"""
527
<h3>Session Information</h3>
528
<p><b>Session ID:</b> {session_id}</p>
529
<p><b>Data Points:</b> {len(session_data)}</p>
530
<p><b>Data Range:</b> {min(session_data):.2f} - {max(session_data):.2f}</p>
531
""")
532
533
layout = Column(info, p)
534
curdoc().add_root(layout)
535
curdoc().title = f"Multi-Session App - {session_id}"
536
537
# Optional: Add periodic callback to update data
538
def update_session_data():
539
"""Update session-specific data."""
540
global session_data
541
# Add some noise to existing data
542
session_data = session_data + np.random.normal(0, 1, len(session_data))
543
source.data = dict(x=list(range(len(session_data))), y=session_data)
544
545
curdoc().add_periodic_callback(update_session_data, 2000) # Update every 2 seconds
546
```
547
548
### Client Connection Example
549
550
```python
551
# Client script to connect to server application
552
from bokeh.client import pull_session, push_session, show_session
553
from bokeh.plotting import figure
554
from bokeh.io import curdoc
555
import numpy as np
556
557
# Method 1: Pull existing session
558
session = pull_session(url="http://localhost:5006/myapp")
559
print(f"Connected to session: {session.session_id}")
560
561
# Access the document
562
doc = session.document
563
print(f"Document has {len(doc.roots)} root models")
564
565
# Method 2: Push new document to server
566
new_doc = curdoc()
567
568
# Create content for new document
569
p = figure(width=400, height=400)
570
x = np.linspace(0, 4*np.pi, 100)
571
p.line(x, np.sin(x))
572
new_doc.add_root(p)
573
574
# Push to server
575
push_session = push_session(new_doc, url="http://localhost:5006/")
576
print(f"Pushed document to session: {push_session.session_id}")
577
578
# Method 3: Show session in browser
579
show_session(session_id=push_session.session_id,
580
server_url="http://localhost:5006")
581
```
582
583
### Application Factory Pattern
584
585
```python
586
# application.py - Reusable application factory
587
from bokeh.application import Application
588
from bokeh.application.handlers import FunctionHandler
589
from bokeh.plotting import figure, curdoc
590
from bokeh.models import ColumnDataSource, Slider, Column
591
import numpy as np
592
593
def create_app(config=None):
594
"""
595
Factory function to create Bokeh application.
596
597
Parameters:
598
- config: Dictionary of configuration options
599
"""
600
config = config or {}
601
602
def modify_doc(doc):
603
"""Function to set up the document."""
604
# Create plot based on config
605
width = config.get('width', 500)
606
height = config.get('height', 400)
607
title = config.get('title', 'Default App')
608
609
source = ColumnDataSource(data=dict(x=[], y=[]))
610
611
p = figure(width=width, height=height, title=title)
612
line = p.line('x', 'y', source=source, line_width=2)
613
614
# Add interactivity
615
slider = Slider(start=0.1, end=5.0, value=1.0, step=0.1,
616
title="Parameter")
617
618
def update(attr, old, new):
619
x = np.linspace(0, 4*np.pi, 100)
620
y = np.sin(new * x)
621
source.data = dict(x=x, y=y)
622
623
slider.on_change('value', update)
624
update(None, None, 1.0) # Initial update
625
626
layout = Column(slider, p)
627
doc.add_root(layout)
628
doc.title = title
629
630
return Application(FunctionHandler(modify_doc))
631
632
# Usage:
633
# app1 = create_app({'title': 'Sin Wave', 'width': 600})
634
# app2 = create_app({'title': 'Different Config', 'height': 500})
635
#
636
# # Run with multiple apps:
637
# # bokeh serve --show application.py:app1 application.py:app2
638
```
639
640
### Command Line Server Usage
641
642
```bash
643
# Basic server commands
644
645
# Serve single application
646
bokeh serve myapp.py
647
648
# Serve on specific port
649
bokeh serve myapp.py --port 8080
650
651
# Serve multiple applications
652
bokeh serve app1.py app2.py --show
653
654
# Serve with auto-reload during development
655
bokeh serve myapp.py --dev
656
657
# Serve from directory
658
bokeh serve myapp/ --show
659
660
# Serve with custom URL prefix
661
bokeh serve myapp.py --prefix /custom/path
662
663
# Allow external connections
664
bokeh serve myapp.py --allow-websocket-origin=*
665
666
# Serve with authentication
667
bokeh serve myapp.py --oauth-provider=github
668
```