☀️ light
🌙 dark
system
irfan toor
engineer . c|eh . data scientist (ai)
Blog
AI
Business
Cyber
Engineering
Life
Hacks
Smart
Projects
title
keyword
category
|
text
images
{"nbformat_minor": 0, "cells": [{"source": "# Run a Web Server in a Notebook\n\nIn this notebook, we show how to run a [Tornado](http://www.tornadoweb.org/en/stable/) or [Flask](http://flask.pocoo.org/) web server within a notebook, and access it from the public Internet. It sounds hacky, but the technique can prove useful:\n\n1. To quickly prototype a REST API for an external web application to consume\n2. To quickly expose a simple web dashboard to select external users\n\nIn this notebook, we'll demonstrate the technique using both Tornado and Flask as the web server. In both cases, the servers will listen for HTTPS connections and use a self-signed certificate. The servers will not authenticate connecting users / clients. (We want to keep things simple for this demo, but such authentication is an obvious next step in securing the web service for real-world use.)\n\n## Define the Demo Scenario\n\nSuppose we have completed a notebook that, among other things, can plot a point-in-time sample of data from an external source. Assume we now want to surface this plot in a very simple UI that has:\n\n1. The title of the demo\n1. The current plot\n2. A refresh button that takes a new sample and updates the plot\n\n## Create the Plotting Function\n\nSuppose we have a function that generates a plot and returns the image as a PNG in a Python string.", "cell_type": "markdown", "metadata": {}}, {"execution_count": 1, "cell_type": "code", "source": "import matplotlib.pyplot as plt\nimport pandas as pd\nimport numpy\nimport io\npd.options.display.mpl_style = 'default'", "outputs": [], "metadata": {"collapsed": true, "trusted": true}}, {"execution_count": 2, "cell_type": "code", "source": "def plot_random_numbers(n=50):\n '''\n Plot random numbers as a line graph.\n '''\n fig, ax = plt.subplots()\n # generate some random numbers\n arr = numpy.random.randn(n)\n ax.plot(arr)\n ax.set_title('Random numbers!')\n # fetch the plot bytes\n output = io.BytesIO()\n plt.savefig(output, format='png')\n png = output.getvalue()\n plt.close()\n return png", "outputs": [], "metadata": {"collapsed": true, "trusted": true}}, {"source": "We can test our function by showing its output inline using the `Image` utility from IPython.", "cell_type": "markdown", "metadata": {}}, {"execution_count": 3, "cell_type": "code", "source": "from IPython.display import Image\nImage(plot_random_numbers())", "outputs": [{"output_type": "stream", "name": "stderr", "text": "/home/notebook/ka_env/lib/python2.7/site-packages/matplotlib/font_manager.py:1282: UserWarning: findfont: Font family [u'monospace'] not found. Falling back to Bitstream Vera Sans\n (prop.get_family(), self.defaultFamily[fontext]))\n"}, {"execution_count": 3, "output_type": "execute_result", "data": {"image/png": "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\nRK5CYII=\n", "text/plain": "
"}, "metadata": {}}], "metadata": {"collapsed": false, "trusted": true}}, {"source": "## Create a Simple Dashboard Page\n\nNow we'll craft a simple dashboard page that includes our plot. We don't *have* to do anything fancy here other than use an `
` tag and a `
`. But to demonstate what's possible, we'll make it pretty with Bootstrap and jQuery, and use a Jinja template that accepts the demo title as a parameter.\n\nNote that the image tag points to a `/plot` resource on the server. Nothing dictates that we must fetch the plot image from our dashboard page. Another application could treat our web server as an API and use it in other ways.", "cell_type": "markdown", "metadata": {}}, {"execution_count": 4, "cell_type": "code", "source": "import jinja2", "outputs": [], "metadata": {"collapsed": true, "trusted": true}}, {"execution_count": 5, "cell_type": "code", "source": "page = jinja2.Template('''\\\n\n\n \n
\n
{{ title }}
\n \n \n
\n
\n
\n
{{ title }}
\n
\n
\n
\n
\n
\n
\n
\n
\n
Refresh Plot
\n
\n
\n \n \n \n''')", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "We can now expose both the plotting function and the template via our web servers (Tornado first, then Flask) using the following endpoints:\n\n* `/` will serve the dashboard HTML.\n* `/plot` will serve the plot PNG.", "cell_type": "markdown", "metadata": {}}, {"source": "## Run Tornado in a Notebook\n\nFirst we create a self-signed certificate using the `openssl` command line library. If we had a real cert, we could use it instead.", "cell_type": "markdown", "metadata": {"collapsed": true}}, {"execution_count": 6, "cell_type": "code", "source": "%%bash\nmkdir -p -m 700 ~/.ssh\nopenssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \\\n -subj \"/C=XX/ST=Unknown/L=Somewhere/O=None/CN=None\" \\\n -keyout /home/notebook/.ssh/notebook.key -out /home/notebook/.ssh/notebook.crt", "outputs": [{"output_type": "stream", "name": "stderr", "text": "Generating a 4096 bit RSA private key\n..................++\n......................++\nwriting new private key to '/home/notebook/.ssh/notebook.key'\n-----\n"}], "metadata": {"collapsed": false, "trusted": true}}, {"source": "Next we import the Tornado models we need.", "cell_type": "markdown", "metadata": {}}, {"execution_count": 7, "cell_type": "code", "source": "import tornado.ioloop\nimport tornado.web\nimport tornado.httpserver", "outputs": [], "metadata": {"collapsed": true, "trusted": true}}, {"source": "Then we define the request handlers for our two endpoints.", "cell_type": "markdown", "metadata": {}}, {"execution_count": 8, "cell_type": "code", "source": "class MainHandler(tornado.web.RequestHandler):\n def get(self):\n '''Renders the template with a title on HTTP GET.'''\n self.finish(page.render(title='Tornado Demo'))", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"execution_count": 9, "cell_type": "code", "source": "class PlotHandler(tornado.web.RequestHandler):\n def get(self):\n '''Creates the plot and returns it on HTTP GET.'''\n self.set_header('content-type', 'image/png')\n png = plot_random_numbers()\n self.finish(png)", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "Now we define the application object which maps the web paths to the handlers.", "cell_type": "markdown", "metadata": {}}, {"execution_count": 10, "cell_type": "code", "source": "application = tornado.web.Application([\n (r\"/\", MainHandler),\n (r\"/plot\", PlotHandler)\n])", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "Finally, we create a new HTTP server bound to a publicly exposed port on our notebook server (e.g., 9000) and using the self-signed certificate with corresponding key.", "cell_type": "markdown", "metadata": {}}, {"source": "
\n
\n
\n
In IBM Knowledge Anyhow Workbench, ports 9000 through 9004 are exposed on a public interface. We can bind our webserver to any of those ports.
\n
\n
", "cell_type": "markdown", "metadata": {}}, {"execution_count": 11, "cell_type": "code", "source": "server = tornado.httpserver.HTTPServer(application, ssl_options = {\n \"certfile\": '/home/notebook/.ssh/notebook.crt',\n \"keyfile\": '/home/notebook/.ssh/notebook.key'\n})\nserver.listen(9000, '0.0.0.0')", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "To see the result, we need to visit the public IP address of our notebook server. For example, if our IP address is 192.168.11.10, we would visit https://192.168.11.10:9000.", "cell_type": "markdown", "metadata": {}}, {"source": "
\n
\n
\n
In IBM Knowledge Anyhow Workbench, we can get our public IP address from an environment variable by executing the code below in our notebook:\n
import os\nos.getenv('HOST_PUBLIC_IP')
\n
\n
\n
", "cell_type": "markdown", "metadata": {}}, {"source": "When we visit the web server in a browser and accept the self-signed cert warning, we should see the resulting dashboard. Clicking *Refresh Plot* in the dashboard shows us a new plot. \n\nNote that since IPython itself is based on Tornado, we are able to run other cells and get ouput while the web server is running. In fact, we can even modify the plotting function and template and see the changes the next time we refresh the dashboard in our browser.\n\nWhen we want to shut the server down, we execute the lines below. Restarting the notebook kernel has the same net effect.", "cell_type": "markdown", "metadata": {}}, {"execution_count": 12, "cell_type": "code", "source": "server.close_all_connections()\nserver.stop()", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "## Run Flask in a Notebook\n\nThe same technique works with Flask, albeit with different pros and cons. First, we need to install Flask since it does not come preinstalled in the notebook environment by default.", "cell_type": "markdown", "metadata": {}}, {"execution_count": 13, "cell_type": "code", "source": "!pip install flask", "outputs": [{"output_type": "stream", "name": "stdout", "text": "Requirement already satisfied (use --upgrade to upgrade): flask in /home/notebook/ka_env/lib/python2.7/site-packages\r\nRequirement already satisfied (use --upgrade to upgrade): Werkzeug>=0.7 in /home/notebook/ka_env/lib/python2.7/site-packages (from flask)\r\nRequirement already satisfied (use --upgrade to upgrade): Jinja2>=2.4 in /home/notebook/ka_env/lib/python2.7/site-packages (from flask)\r\nRequirement already satisfied (use --upgrade to upgrade): itsdangerous>=0.21 in /home/notebook/ka_env/lib/python2.7/site-packages (from flask)\r\nRequirement already satisfied (use --upgrade to upgrade): markupsafe in /usr/local/lib/python2.7/dist-packages (from Jinja2>=2.4->flask)\r\nCleaning up...\r\n"}], "metadata": {"collapsed": false, "trusted": true}}, {"source": "Now we import our Flask requirements, define our app, and create our route mappings.", "cell_type": "markdown", "metadata": {}}, {"execution_count": 14, "cell_type": "code", "source": "from flask import Flask, make_response", "outputs": [], "metadata": {"collapsed": true, "trusted": true}}, {"execution_count": 15, "cell_type": "code", "source": "flask_app = Flask('flask_demo')", "outputs": [], "metadata": {"collapsed": true, "trusted": true}}, {"execution_count": 16, "cell_type": "code", "source": "@flask_app.route('/')\ndef index():\n '''Renders the template with a title on HTTP GET.'''\n return page.render(title='Flask Demo')", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"execution_count": 17, "cell_type": "code", "source": "@flask_app.route('/plot')\ndef get_plot():\n '''Creates the plot and returns it on HTTP GET.'''\n response = make_response(plot_random_numbers())\n response.mimetype = 'image/png'\n return response", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "Finally, we run the Flask web server. Flask supports the generation of an ad-hoc HTTP certificate and key so we don't need to explicitly put one on disk like we did in the case of Tornado.", "cell_type": "markdown", "metadata": {}}, {"execution_count": 18, "cell_type": "code", "source": "flask_app.run(host='0.0.0.0', port=9000, ssl_context='adhoc')", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "Unlike in the Tornado case, the run command above blocks the notebook kernel from returning for as long as the web server is running. To stop the server, we need to interrupt the kernel (*Kernel* → *Interrupt*). \n\n## Run Flask in a Tornado WSGIContainer\n\nIf we are in love with Flask syntax, but miss the cool, non-blocking ability of Tornado, we can run the Flask application in a Tornado WSGIContainer like so.", "cell_type": "markdown", "metadata": {}}, {"execution_count": 21, "cell_type": "code", "source": "from tornado.wsgi import WSGIContainer\nserver = tornado.httpserver.HTTPServer(WSGIContainer(flask_app), ssl_options = {\n \"certfile\": '/home/notebook/.ssh/notebook.crt',\n \"keyfile\": '/home/notebook/.ssh/notebook.key'\n})\nserver.listen(9000, '0.0.0.0')", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "And once we do, we can view the dashboard in a web browser even while executing cells in the notebook. When we're done, we can cleanup with the same logic as in the pure Tornado case.", "cell_type": "markdown", "metadata": {}}, {"execution_count": 22, "cell_type": "code", "source": "server.close_all_connections()\nserver.stop()", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "## Conclusion\n\nIn this notebook, we:\n\n* Defined a simple function that returns a PNG of a plot\n* Defined a template that renders a very simple HTML dashboard\n* Exposed two HTTPS endpoints in Tornado, one for the dashboard HTML and one for the plot\n* Exposed two HTTPS endpoints in Flask for the same resources\n* Exposed two HTTPS endpoints in Flask + Tornado for the same resources\n\nOf course, what we chose to expose was specific to the demo scenario. For example, we could have just as easily created a REST API that accepted feature values for classification and feedback about whether the classification was right or not for future training.\n\nWhile the result is not \"production ready\", it does allow us to expose prototype code to other users without worrying about migrating our work from notebook(s) to other environments.", "cell_type": "markdown", "metadata": {}}, {"source": "
\n
\n
\n
This notebook was created using
IBM Knowledge Anyhow Workbench
. To learn more, visit us at
https://knowledgeanyhow.org
.
\n
\n
", "cell_type": "markdown", "metadata": {"collapsed": true}}], "nbformat": 4, "metadata": {"kernelspec": {"display_name": "Python 2", "name": "python2", "language": "python"}, "language_info": {"mimetype": "text/x-python", "nbconvert_exporter": "python", "version": "2.7.6", "name": "python", "file_extension": ".py", "pygments_lexer": "ipython2", "codemirror_mode": {"version": 2, "name": "ipython"}}}}
Projects
Ask Irfan
: Ask Irfan
RAG
: Retrieval Augmentated Generation
Irfan's Engine
: A bare-minimum PHP framework to create web applications or APIs focussing on the requests and the responses
Exploits SE
A Research Project of Irfan TOOR to do a search of exploits
more ...
Some articles
10 Security Tools
Effects of Radiation
Une IA super-intelligente pourrait arriver dès 2027
more ...
Some example notebooks
instaquery
sklearn
webserver