I provide a tool as part of my python package that visualizes a parameter space using bokeh. The normal way to launch it would be:
$ bokeh serve --show my_package/tools/my_tool.py
Which opens a browser and shows an interactive plot. However, when I install this package using pip/ PyPI users can not easily access this folder, so I would like to provide an entry point for this in my setup.py.
Package Layout:folder
|
|--- my_package
| |
| |- __init__.py
| |- __main__.py
| |- some_code.py
| |
| +--- tools
| |
| +--- my_tool.py
|
+--setup.py
In my setup.py I already specify an entry point to my main method:
setup.pyfrom setuptools import setup, find_packages
setup(
name = "my_package",
packages = find_packages(),
entry_points = {
'console_scripts': [
'my_package = my_package.__main__:main'
]
},
[...],
)
However, the only way I found to launch the bokeh server and show the interface was to create another python script like this
import os
from subprocess import call
def main():
p = os.path.realpath(__file__)
prefix, _ = os.path.split(p)
bokeh_server_file = os.path.join(prefix, "my_tool.py")
call(["bokeh", "serve", "--show", bokeh_server_file])
if __name__ == "__main__":
main()
place it in the tools
folder and create an entry point for this scripts main method. *shudder* There has to be a better way to do this.
Is it possible to provide this kind of entry point using setuptools or is there another way to achieve this behavior?
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 an optional component that can be used to provide additional capabilities, such as: hosting and publishing Bokeh plots for wider audiences. streaming data to plots so that they automatically update. interactively visualizing very large datasets by employing downsampling and abstract rendering.
The easiest way to install Bokeh is to use conda . Conda is part of the Anaconda Python Distribution, which is designed with scientific and data analysis applications like Bokeh in mind. If you use Anaconda on your system, installing with conda is the recommended method. Otherwise, use pip .
To expand on the answer of @jxramos, I modified the standalone embed example given on the page. By managing the tornado IO loop yourself in the main function you can make your bokeh script behave like a normal python script that automatically starts a browser when executed.
from tornado.ioloop import IOLoop
from bokeh.application.handlers import FunctionHandler
from bokeh.application import Application
from bokeh.server.server import Server
def modify_doc(doc):
"""Function that modifies a document."""
# [...] create a plot
doc.add_root(plot)
doc.title = "Test Plot"
def main():
"""Launch the server and connect to it."""
io_loop = IOLoop.current()
bokeh_app = Application(FunctionHandler(modify_doc)) # pass the function that assembles your document here.
server = Server({"/": bokeh_app}, io_loop=io_loop)
server.start()
print("Opening Bokeh application on http://localhost:5006/")
io_loop.add_callback(server.show, "/")
io_loop.start()
main()
This script can then be called from the command line
$ python my_tool.py
and hence be used for an entry point as expected:
[...]
entry_points = {
'console_scripts': [
'my_package = my_package.tools.my_tool:main'
]
},
[...]
The following is a more complete example that I ported over from a Documentation example I wrote. Since Documentation is being phased out, I think it is better suited as part of this answer.
To allow a bokeh application to be executed like a normal .py file, you need to handle the tornado IOloop in your application, as described here. A standalone bokeh application like this can be used to implement a console script entry point in setup.py
.
However, this requires bokeh version >= 0.12.4.
Consider the file local_server.py
:
from tornado.ioloop import IOLoop
from bokeh.application.handlers import FunctionHandler
from bokeh.application import Application
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.server.server import Server
def modify_doc(doc):
"""Add a plotted function to the document.
Arguments:
doc: A bokeh document to which elements can be added.
"""
x_values = range(10)
y_values = [x ** 2 for x in x_values]
data_source = ColumnDataSource(data=dict(x=x_values, y=y_values))
plot = figure(title="f(x) = x^2",
tools="crosshair,pan,reset,save,wheel_zoom",)
plot.line('x', 'y', source=data_source, line_width=3, line_alpha=0.6)
doc.add_root(plot)
doc.title = "Test Plot"
def main():
"""Launch the server and connect to it.
"""
print("Preparing a bokeh application.")
io_loop = IOLoop.current()
bokeh_app = Application(FunctionHandler(modify_doc))
server = Server({"/": bokeh_app}, io_loop=io_loop)
server.start()
print("Opening Bokeh application on http://localhost:5006/")
io_loop.add_callback(server.show, "/")
io_loop.start()
main()
This file can be executed
$ python local_server.py
which run the server and automatically launch a browser to show the document.
In order to provide a script that can be easily installed and called using the setup.py. Consider the following folder structure:
project
├── setup.py
└── my_package
├── __init__.py
└── local_server.py
Content of setup.py
:
from setuptools import setup
setup(
name = "my_package",
entry_points={
"console_scripts": ["my_script = my_package.local_server:main"],
},
)
When installing the package using
$ python setup.py install
you can then use the call
$ my_script
to launch the bokeh application and automatically start a browser displaying the document.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With