Skip to content

Instantly share code, notes, and snippets.

@disarticulate
Last active May 7, 2016 22:55
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save disarticulate/dfe91dbbee6fa8daa5add53d71b083ff to your computer and use it in GitHub Desktop.
Save disarticulate/dfe91dbbee6fa8daa5add53d71b083ff to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# OBREROS\n",
"1. Inject Cell Folding and other notebook functions\n",
"2. Create a parent server application with JINJA template injections\n",
"3. Define routes (endpoints)\n",
"4. Build the application (load the IPython Notebook source file)\n",
"5. Test the application in the Notebook\n",
"6. Test the application in Docker"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## 1. Inject Cell Folding and other notebook functions"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false,
"scrolled": true
},
"outputs": [
{
"data": {
"application/javascript": [
"//./js/foldjupytercells.js\n",
"function fold(t) {\n",
" var sibs = $(t.target).siblings();\n",
" sibs[0].click()\n",
" if(t.target.innerHTML == \"-\"){\n",
" t.target.innerHTML=\"+\"\n",
" $(\"#run_cell\").click()\n",
" } else {\n",
" t.target.innerHTML=\"-\"\n",
" }\n",
" for(var i=0;sibs.length>i;i++){\n",
" var d = $(sibs[i])[0].style.display\n",
" if(d == \"none\") {\n",
" $(sibs[i])[0].style.display = \"\"\n",
" } else {\n",
" $(sibs[i])[0].style.display = \"none\"\n",
" };\n",
" };\n",
" \n",
"};\n",
"function hasButton(d) {\n",
" var has = false\n",
" for(var i=0;d.children.length > i;i++) {\n",
" if(d.children[i].className==\"toggle\") {\n",
" has = true\n",
" }; \n",
" };\n",
" return has\n",
"};\n",
"function addFold() {\n",
" var inputcells = $(\".cell .input_area,.cell .output\")\n",
" for(var i=0;inputcells.length > i;i++) {\n",
" if(hasButton(inputcells[i])==false) {\n",
" var e = document.createElement(\"button\")\n",
" e.className = \"toggle\"\n",
" e.innerHTML = \"-\"\n",
" e.style.height=\".5em\"\n",
" $(inputcells[i]).prepend(e)\n",
" $(e).click(this,fold)\n",
" };\n",
" };\n",
"};\n",
"$('document').ready(addFold);\n",
"\n",
"require.config({paths: {\n",
" d3: \"https://cdn.rawgit.com/ElDeveloper/a6fc14f8993a1b8ad843/raw/ca8d8c12125cd93ccf6902c4dacce473e4cf2c3b/d3.v3.min\",\n",
" topojson: \"https://cdn.rawgit.com/ElDeveloper/7d4f4a12683b03008435/raw/7ab04a76821117541a86764036e41edb00fae4f3/topojson.v1.min\",\n",
" datamaps: \"https://cdn.rawgit.com/ElDeveloper/61b29679961d29e94db1/raw/d05e3e44485a414b4f32b16457c5bcef6de1a6a7/datamaps.all\",\n",
" handlebars: \"https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.amd\"\n",
" }\n",
" }\n",
" );\n",
"\n",
"require([\"d3\", \n",
" \"topojson\", \n",
" \"datamaps\",\n",
" \"handlebars\"], \n",
" function(a, b, c, d, e, f) {\n",
" window.Handlebars = d;\n",
"});\n",
"\n",
"var pvar = \"obreros_name\"\n",
"var kernel = IPython.notebook.kernel;\n",
"var thename = window.document.getElementById(\"notebook_name\").innerHTML;\n",
"var command = pvar + \" = \" + \"'\" + thename + \"'\";\n",
"kernel.execute(command);\n",
"\n",
"this.element.append(\"<H2>Added Tabs, Loaded Jupyter Libraries</H2>\")\n",
"this.element.append(\"<h3>Notebook name: <b>\"+thename+\"</b></h3>\")\n",
"this.element.append(\"<h4>Use variable notebook name: <b>\"+pvar+\"</b></h4>\")\n",
"this.element.append(\"<p>Using code folding; loading libraries; folding each cell executes the cell</p>\")"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%javascript\n",
"//./js/foldjupytercells.js\n",
"function fold(t) {\n",
" var sibs = $(t.target).siblings();\n",
" sibs[0].click()\n",
" if(t.target.innerHTML == \"-\"){\n",
" t.target.innerHTML=\"+\"\n",
" $(\"#run_cell\").click()\n",
" } else {\n",
" t.target.innerHTML=\"-\"\n",
" }\n",
" for(var i=0;sibs.length>i;i++){\n",
" var d = $(sibs[i])[0].style.display\n",
" if(d == \"none\") {\n",
" $(sibs[i])[0].style.display = \"\"\n",
" } else {\n",
" $(sibs[i])[0].style.display = \"none\"\n",
" };\n",
" };\n",
" \n",
"};\n",
"function hasButton(d) {\n",
" var has = false\n",
" for(var i=0;d.children.length > i;i++) {\n",
" if(d.children[i].className==\"toggle\") {\n",
" has = true\n",
" }; \n",
" };\n",
" return has\n",
"};\n",
"function addFold() {\n",
" var inputcells = $(\".cell .input_area,.cell .output\")\n",
" for(var i=0;inputcells.length > i;i++) {\n",
" if(hasButton(inputcells[i])==false) {\n",
" var e = document.createElement(\"button\")\n",
" e.className = \"toggle\"\n",
" e.innerHTML = \"-\"\n",
" e.style.height=\".5em\"\n",
" $(inputcells[i]).prepend(e)\n",
" $(e).click(this,fold)\n",
" };\n",
" };\n",
"};\n",
"$('document').ready(addFold);\n",
"\n",
"require.config({paths: {\n",
" d3: \"https://cdn.rawgit.com/ElDeveloper/a6fc14f8993a1b8ad843/raw/ca8d8c12125cd93ccf6902c4dacce473e4cf2c3b/d3.v3.min\",\n",
" topojson: \"https://cdn.rawgit.com/ElDeveloper/7d4f4a12683b03008435/raw/7ab04a76821117541a86764036e41edb00fae4f3/topojson.v1.min\",\n",
" datamaps: \"https://cdn.rawgit.com/ElDeveloper/61b29679961d29e94db1/raw/d05e3e44485a414b4f32b16457c5bcef6de1a6a7/datamaps.all\",\n",
" handlebars: \"https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.amd\"\n",
" }\n",
" }\n",
" );\n",
"\n",
"require([\"d3\", \n",
" \"topojson\", \n",
" \"datamaps\",\n",
" \"handlebars\"], \n",
" function(a, b, c, d, e, f) {\n",
" window.Handlebars = d;\n",
"});\n",
"\n",
"var pvar = \"obreros_name\"\n",
"var kernel = IPython.notebook.kernel;\n",
"var thename = window.document.getElementById(\"notebook_name\").innerHTML;\n",
"var command = pvar + \" = \" + \"'\" + thename + \"'\";\n",
"kernel.execute(command);\n",
"\n",
"this.element.append(\"<H2>Added Tabs, Loaded Jupyter Libraries</H2>\")\n",
"this.element.append(\"<h3>Notebook name: <b>\"+thename+\"</b></h3>\")\n",
"this.element.append(\"<h4>Use variable notebook name: <b>\"+pvar+\"</b></h4>\")\n",
"this.element.append(\"<p>Using code folding; loading libraries; folding each cell executes the cell</p>\")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Defined functions\n"
]
}
],
"source": [
"def findkeys(node, kv):\n",
" if isinstance(node, list):\n",
" for i in node:\n",
" for x in findkeys(i, kv):\n",
" yield x\n",
" elif isinstance(node, dict):\n",
" if kv in node:\n",
" yield node[kv]\n",
" for j in node.values():\n",
" for x in findkeys(j, kv):\n",
" yield x\n",
"print(\"Defined functions\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## 2. Create a parent server application with JINJA template injections"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from pprint import pprint\n",
"import json"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Main server application\n",
"========\n",
"> Uses JINJA string literals for template injections of routes\n",
"\n",
"> Injects the statements after we've rendered the template using a try block\n",
"\n",
"> Otherwise our notebook gets a SyntaxError; but this allows us to test\n",
"\n",
"`try: exec(JINJA)`\n",
"\n",
"`except SyntaxError: pass`\n",
"\n",
"## _NOT SECURE FOR PRODUCTION_"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'file': './server.py',\n",
" 'name': 'root',\n",
" 'route': '/',\n",
" 'templatefile': './server.jinja',\n",
" 'type': 'template'}\n"
]
}
],
"source": [
"METADATA = {\"name\":\"root\",\"file\":\"./server.py\",\"templatefile\":\"./server.jinja\",\"route\":\"/\",\"type\":\"template\"}\n",
"\"\"\"SERVER APP TEMPLATE\"\"\"\n",
"\n",
"from bottle import Bottle\n",
"from pprint import pprint\n",
"\n",
"root = Bottle()\n",
"\n",
"\n",
"\"\"\"NOT SUITABLE OR SECURE FOR PRODUCTION\"\"\"\n",
"JINJA = \"\"\"\n",
"{% for route in routes %}\n",
"import routes.{{route.name}}\n",
"{%- endfor %}\n",
"\"\"\"\n",
"try: exec(JINJA)\n",
"except SyntaxError: pass\n",
"\n",
"@root.route(METADATA['route'])\n",
"def index():\n",
" return '<i>Application Suite Home Page</i>'\n",
"\n",
"\"\"\"NOT SUITABLE OR SECURE FOR PRODUCTION\"\"\"\n",
"JINJA = \"\"\"\n",
"{% for route in routes %}\n",
"root.merge(routes.{{route.name}}.app)\n",
"{%- endfor %}\n",
"\"\"\"\n",
"try: exec(JINJA)\n",
"except SyntaxError: pass\n",
"\n",
"pprint(METADATA)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Now we can try our route `index()` using Jupyter's [`IPython.display.HTML` ](http://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html?highlight=html#IPython.display.HTML)\n",
"-----\n",
"### ([or whatever you want your API to display Javascript, JSON, SVG ...](http://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html))\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false,
"scrolled": true
},
"outputs": [
{
"data": {
"text/html": [
"<i>Application Suite Home Page</i>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.display import HTML, Javascript, FileLink\n",
"HTML(index())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## 3. Define routes (endpoints)\n",
"### Routes:\n",
"\n",
"> Note the METADATA tag at the head.\n",
"\n",
"> Each will get printed when we use [`Bottle.merge()`](http://bottlepy.org/docs/dev/api.html#the-bottle-class)\n",
"\n",
"> We printout the METADATA at the end to verify our route loaded\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'file': './routes/hello.py',\n",
" 'name': 'hello',\n",
" 'route': '/hello',\n",
" 'type': 'route'}\n"
]
}
],
"source": [
"METADATA = {\"name\":\"hello\",\"file\":\"./routes/hello.py\",\"route\":\"/hello\",\"type\":\"route\"}\n",
"\n",
"from bottle import Bottle\n",
"from pprint import pprint\n",
"\n",
"app = Bottle()\n",
"\n",
"@app.route(METADATA['route'])\n",
"def hello():\n",
" return \"Hello world no.1\"\n",
"\n",
"pprint(METADATA)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'file': './routes/hellojson.py',\n",
" 'name': 'hellojson',\n",
" 'route': '/hellojson',\n",
" 'type': 'route'}\n"
]
}
],
"source": [
"METADATA = {\"name\":\"hellojson\", \"file\":\"./routes/hellojson.py\",\"route\":\"/hellojson\",\"type\":\"route\"}\n",
"\n",
"from bottle import Bottle\n",
"from pprint import pprint\n",
"\n",
"app = Bottle()\n",
"\n",
"@app.route(METADATA['route'])\n",
"def hellojson():\n",
" return {\"title\":\"hello\",\"attribute\":\"json\"}\n",
"\n",
"pprint(METADATA)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Dockerfile\n",
"> can be extended\n",
"\n",
"> doesn't currently use a requirements.txt"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"METADATA = {\"name\":\"Dockerfile\", \"file\":\"./Dockerfile\",\"type\":\"content\"}\n",
"METADATA[\"content\"] = \"\"\"\n",
"FROM python:3.5.1-alpine\n",
"RUN pip install bottle\n",
"COPY . /app\n",
"WORKDIR /app\n",
"CMD python -m bottle --debug --reload server:root -b 0.0.0.0:8000\n",
"\"\"\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## 4. Build the application (load the IPython Notebook source file)\n",
"\n",
"### First some more Javascript magic"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"application/javascript": [
"//THIS'LL SAVE YOUR NOTEBOOK\n",
"$(\"#save_checkpoint > a\").click()\n",
"\n",
"//THIS'LL TIMESTAMP IT INCASE YOU FORGET WHEN\n",
"var thename = window.document.getElementById(\"notebook_name\").innerHTML;\n",
"var today = new Date();\n",
"var dd = today.getDate();\n",
"var mm = today.getMonth()+1; //January is 0!\n",
"var yyyy = today.getFullYear();\n",
"\n",
"this.element.append(\"<H2>Obreros file input: </H2>\"+ thename)\n",
"this.element.append(\"<p>Saved: \"+ today+\"</p>\")"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%javascript\n",
"//THIS'LL SAVE YOUR NOTEBOOK\n",
"$(\"#save_checkpoint > a\").click()\n",
"\n",
"//THIS'LL TIMESTAMP IT INCASE YOU FORGET WHEN\n",
"var thename = window.document.getElementById(\"notebook_name\").innerHTML;\n",
"var today = new Date();\n",
"var dd = today.getDate();\n",
"var mm = today.getMonth()+1; //January is 0!\n",
"var yyyy = today.getFullYear();\n",
"\n",
"this.element.append(\"<H2>Obreros file input: </H2>\"+ thename)\n",
"this.element.append(\"<p>Saved: \"+ today+\"</p>\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# OBREROS STARTS\n",
" > ## `server.py` defined\n",
"> ## routes defined\n",
"\n",
"### The following Ipython cell injects the name of the notebook into Ipython:\n",
"\n",
"```\n",
"%%javascript\n",
"var pvar = \"obreros_name\"\n",
"var kernel = IPython.notebook.kernel;\n",
"var thename = window.document.getElementById(\"notebook_name\").innerHTML;\n",
"var command = pvar + \" = \" + \"'\" + thename + \"'\";\n",
"kernel.execute(command);\n",
"```\n",
"> Note that you'll get an error if you try to run all the cells, as the %%javascript doesn't appear to run `kernel.execute(command)` on restart\n",
"\n",
"### We use `exec(METADATA[0])` to run the first line of _ANY_ `source` with METADATA \n",
"```\n",
"if \"METADATA\" in source[0]:\n",
" #We inject (INSECURE) the variable into Ipython\n",
" exec(source[0])\n",
"```\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loaded Obreros\n"
]
}
],
"source": [
"obreros = json.load(open(obreros_name +'.ipynb','r')) \n",
"print(\"Loaded Obreros\")"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Templates:\n",
"[{'file': './server.py',\n",
" 'name': 'root',\n",
" 'route': '/',\n",
" 'templatefile': './server.jinja',\n",
" 'type': 'template'}]\n",
"Routes:\n",
"[{'file': './routes/hello.py',\n",
" 'name': 'hello',\n",
" 'route': '/hello',\n",
" 'type': 'route'},\n",
" {'file': './routes/hellojson.py',\n",
" 'name': 'hellojson',\n",
" 'route': '/hellojson',\n",
" 'type': 'route'}]\n",
"Contents:\n",
"[{'content': '\\n'\n",
" 'FROM python:3.5.1-alpine\\n'\n",
" 'RUN pip install bottle\\n'\n",
" 'COPY . /app\\n'\n",
" 'WORKDIR /app\\n'\n",
" 'CMD python -m bottle --debug --reload server:root -b '\n",
" '0.0.0.0:8000\\n',\n",
" 'file': './Dockerfile',\n",
" 'name': 'Dockerfile',\n",
" 'type': 'content'}]\n"
]
}
],
"source": [
"templates = []\n",
"routes = []\n",
"contents = []\n",
"# We look for the \"source\" cells in the notebook\n",
"for source in findkeys(obreros,\"source\"):\n",
" try: \n",
" #We check to see if the first line has a METADATA variable\n",
" if \"METADATA\" in source[0]:\n",
" #We inject (INSECURE) the variable into Ipython\n",
" exec(source[0])\n",
" with open(METADATA['file'],\"w\") as f:\n",
" #we have to execute the entire cell (non-python files)\n",
" if METADATA['type'] == 'content':\n",
" src = ''.join(source)\n",
" exec(src)\n",
" f.write(METADATA['content'])\n",
" else:\n",
" #We write each line into the file name\n",
" [f.write(line) for line in source]\n",
" #We collect our templates and routes\n",
" if METADATA['type'] == 'template':\n",
" templates.append(METADATA)\n",
" if METADATA['type'] == 'route':\n",
" routes.append(METADATA)\n",
" if METADATA['type'] == 'content':\n",
" contents.append(METADATA)\n",
"\n",
" \n",
" except: pass\n",
"print(\"Templates:\")\n",
"pprint(templates)\n",
"print(\"Routes:\")\n",
"pprint(routes)\n",
"print(\"Contents:\")\n",
"pprint(contents)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Templates using Jinja\n",
"> Everything is a template, if you really want it."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"METADATA = {\"name\":\"root\",\"file\":\"./server.py\",\"templatefile\":\"./server.jinja\",\"route\":\"/\",\"type\":\"template\"}\n",
"\"\"\"SERVER APP TEMPLATE\"\"\"\n",
"\n",
"from bottle import Bottle\n",
"from pprint import pprint\n",
"\n",
"root = Bottle()\n",
"\n",
"\n",
"\"\"\"NOT SUITABLE OR SECURE FOR PRODUCTION\"\"\"\n",
"JINJA = \"\"\"\n",
"\n",
"import routes.hello\n",
"import routes.hellojson\n",
"\"\"\"\n",
"try: exec(JINJA)\n",
"except SyntaxError: pass\n",
"\n",
"@root.route(METADATA['route'])\n",
"def index():\n",
" return '<i>Application Suite Home Page</i>'\n",
"\n",
"\"\"\"NOT SUITABLE OR SECURE FOR PRODUCTION\"\"\"\n",
"JINJA = \"\"\"\n",
"\n",
"root.merge(routes.hello.app)\n",
"root.merge(routes.hellojson.app)\n",
"\"\"\"\n",
"try: exec(JINJA)\n",
"except SyntaxError: pass\n",
"\n",
"pprint(METADATA)\n"
]
}
],
"source": [
"from jinja2 import Template, Environment, FileSystemLoader\n",
"#Everything is a template\n",
"obreros_env = Environment(loader=FileSystemLoader('.'))\n",
"for t in templates:\n",
" obreros_template = obreros_env.get_template(t['file']).render(routes=routes)\n",
" with open(t['file'], \"w\") as f:\n",
" f.write(obreros_template)\n",
" print(obreros_template)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## 5. Test the application in the Notebook\n",
"\n",
"### Server testing\n",
"> Note the print outs for each route\n",
"\n",
"> \"!\" indicates a shell command: **`!python -m bottle --debug --reload server:root`**\n",
"\n",
"> Interrupt Kernel to quit your cell"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'file': './routes/hello.py',\n",
" 'name': 'hello',\n",
" 'route': '/hello',\n",
" 'type': 'route'}\n",
"{'file': './routes/hellojson.py',\n",
" 'name': 'hellojson',\n",
" 'route': '/hellojson',\n",
" 'type': 'route'}\n",
"{'file': './server.py',\n",
" 'name': 'root',\n",
" 'route': '/',\n",
" 'templatefile': './server.jinja',\n",
" 'type': 'template'}\n",
"Bottle v0.12.9 server starting up (using WSGIRefServer())...\n",
"Listening on http://localhost:8080/\n",
"Hit Ctrl-C to quit.\n",
"\n",
"^C\n"
]
}
],
"source": [
"!python -m bottle --debug --reload server:root"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"## 6. Test the application in Docker\n",
"### Inject the code into Docker so we can interact\n",
"> **`!docker build .`** is a shell command to execute Dockerfile in current working directory\n",
"\n",
"> **`OBREROS_BUILD`** captures the stdout of docker\n",
"\n",
"> **`imageid`** can be used to track the image. You can use a tag also.\n",
"\n",
"> **`containerid`** can be used to start/stop containers"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"#Shell stdout is captured\n",
"OBREROS_BUILD = !docker build ."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['Sending build context to Docker daemon 557.1 kB',\n",
" 'Sending build context to Docker daemon 1.114 MB',\n",
" 'Sending build context to Docker daemon 1.671 MB',\n",
" 'Sending build context to Docker daemon 2.228 MB',\n",
" 'Sending build context to Docker daemon 2.785 MB',\n",
" 'Sending build context to Docker daemon 3.342 MB',\n",
" 'Sending build context to Docker daemon 3.899 MB',\n",
" 'Sending build context to Docker daemon 4.456 MB',\n",
" 'Sending build context to Docker daemon 5.014 MB',\n",
" 'Sending build context to Docker daemon 5.571 MB',\n",
" 'Sending build context to Docker daemon 6.128 MB',\n",
" 'Sending build context to Docker daemon 6.685 MB',\n",
" 'Sending build context to Docker daemon 7.242 MB',\n",
" 'Sending build context to Docker daemon 7.799 MB',\n",
" 'Sending build context to Docker daemon 8.356 MB',\n",
" 'Sending build context to Docker daemon 8.913 MB',\n",
" 'Sending build context to Docker daemon 9.47 MB',\n",
" 'Sending build context to Docker daemon 10.03 MB',\n",
" 'Sending build context to Docker daemon 10.58 MB',\n",
" 'Sending build context to Docker daemon 11.14 MB',\n",
" 'Sending build context to Docker daemon 11.7 MB',\n",
" 'Sending build context to Docker daemon 12.26 MB',\n",
" 'Sending build context to Docker daemon 12.81 MB',\n",
" 'Sending build context to Docker daemon 13.37 MB',\n",
" 'Sending build context to Docker daemon 13.93 MB',\n",
" 'Sending build context to Docker daemon 14.48 MB',\n",
" 'Sending build context to Docker daemon 15.04 MB',\n",
" 'Sending build context to Docker daemon 15.6 MB',\n",
" 'Sending build context to Docker daemon 16.15 MB',\n",
" 'Sending build context to Docker daemon 16.71 MB',\n",
" 'Sending build context to Docker daemon 17.27 MB',\n",
" 'Sending build context to Docker daemon 17.83 MB',\n",
" 'Sending build context to Docker daemon 18.38 MB',\n",
" 'Sending build context to Docker daemon 18.94 MB',\n",
" 'Sending build context to Docker daemon 19.5 MB',\n",
" 'Sending build context to Docker daemon 20.05 MB',\n",
" 'Sending build context to Docker daemon 20.61 MB',\n",
" 'Sending build context to Docker daemon 21.17 MB',\n",
" 'Sending build context to Docker daemon 21.73 MB',\n",
" 'Sending build context to Docker daemon 22.28 MB',\n",
" 'Sending build context to Docker daemon 22.84 MB',\n",
" 'Sending build context to Docker daemon 23.4 MB',\n",
" 'Sending build context to Docker daemon 23.95 MB',\n",
" 'Sending build context to Docker daemon 24.51 MB',\n",
" 'Sending build context to Docker daemon 25.07 MB',\n",
" 'Sending build context to Docker daemon 25.62 MB',\n",
" 'Sending build context to Docker daemon 26.18 MB',\n",
" 'Sending build context to Docker daemon 26.74 MB',\n",
" 'Sending build context to Docker daemon 27.3 MB',\n",
" 'Sending build context to Docker daemon 27.85 MB',\n",
" 'Sending build context to Docker daemon 28.41 MB',\n",
" 'Sending build context to Docker daemon 28.97 MB',\n",
" 'Sending build context to Docker daemon 29.52 MB',\n",
" 'Sending build context to Docker daemon 30.08 MB',\n",
" 'Sending build context to Docker daemon 30.64 MB',\n",
" 'Sending build context to Docker daemon 31.2 MB',\n",
" 'Sending build context to Docker daemon 31.75 MB',\n",
" 'Sending build context to Docker daemon 32.31 MB',\n",
" 'Sending build context to Docker daemon 32.87 MB',\n",
" 'Sending build context to Docker daemon 33.42 MB',\n",
" 'Sending build context to Docker daemon 33.98 MB',\n",
" 'Sending build context to Docker daemon 34.54 MB',\n",
" 'Sending build context to Docker daemon 35.09 MB',\n",
" 'Sending build context to Docker daemon 35.65 MB',\n",
" 'Sending build context to Docker daemon 36.21 MB',\n",
" 'Sending build context to Docker daemon 36.77 MB',\n",
" 'Sending build context to Docker daemon 37.32 MB',\n",
" 'Sending build context to Docker daemon 37.88 MB',\n",
" 'Sending build context to Docker daemon 38.44 MB',\n",
" 'Sending build context to Docker daemon 38.99 MB',\n",
" 'Sending build context to Docker daemon 39.55 MB',\n",
" 'Sending build context to Docker daemon 40.11 MB',\n",
" 'Sending build context to Docker daemon 40.67 MB',\n",
" 'Sending build context to Docker daemon 41.22 MB',\n",
" 'Sending build context to Docker daemon 41.78 MB',\n",
" 'Sending build context to Docker daemon 42.34 MB',\n",
" 'Sending build context to Docker daemon 42.89 MB',\n",
" 'Sending build context to Docker daemon 43.45 MB',\n",
" 'Sending build context to Docker daemon 44.01 MB',\n",
" 'Sending build context to Docker daemon 44.56 MB',\n",
" 'Sending build context to Docker daemon 45.12 MB',\n",
" 'Sending build context to Docker daemon 45.68 MB',\n",
" 'Sending build context to Docker daemon 46.24 MB',\n",
" 'Sending build context to Docker daemon 46.79 MB',\n",
" 'Sending build context to Docker daemon 47.35 MB',\n",
" 'Sending build context to Docker daemon 47.91 MB',\n",
" 'Sending build context to Docker daemon 48.46 MB',\n",
" 'Sending build context to Docker daemon 49.02 MB',\n",
" 'Sending build context to Docker daemon 49.58 MB',\n",
" 'Sending build context to Docker daemon 50.14 MB',\n",
" 'Sending build context to Docker daemon 50.69 MB',\n",
" 'Sending build context to Docker daemon 51.25 MB',\n",
" 'Sending build context to Docker daemon 51.81 MB',\n",
" 'Sending build context to Docker daemon 52.36 MB',\n",
" 'Sending build context to Docker daemon 52.92 MB',\n",
" 'Sending build context to Docker daemon 53.48 MB',\n",
" 'Sending build context to Docker daemon 54.03 MB',\n",
" 'Sending build context to Docker daemon 54.59 MB',\n",
" 'Sending build context to Docker daemon 55.15 MB',\n",
" 'Sending build context to Docker daemon 55.71 MB',\n",
" 'Sending build context to Docker daemon 56.26 MB',\n",
" 'Sending build context to Docker daemon 56.82 MB',\n",
" 'Sending build context to Docker daemon 57.38 MB',\n",
" 'Sending build context to Docker daemon 57.93 MB',\n",
" 'Sending build context to Docker daemon 58.49 MB',\n",
" 'Sending build context to Docker daemon 59.05 MB',\n",
" 'Sending build context to Docker daemon 59.6 MB',\n",
" 'Sending build context to Docker daemon 60.16 MB',\n",
" 'Sending build context to Docker daemon 60.72 MB',\n",
" 'Sending build context to Docker daemon 61.28 MB',\n",
" 'Sending build context to Docker daemon 61.83 MB',\n",
" 'Sending build context to Docker daemon 62.39 MB',\n",
" 'Sending build context to Docker daemon 62.95 MB',\n",
" 'Sending build context to Docker daemon 63.5 MB',\n",
" 'Sending build context to Docker daemon 64.06 MB',\n",
" 'Sending build context to Docker daemon 64.62 MB',\n",
" 'Sending build context to Docker daemon 65.18 MB',\n",
" 'Sending build context to Docker daemon 65.73 MB',\n",
" 'Sending build context to Docker daemon 66.29 MB',\n",
" 'Sending build context to Docker daemon 66.85 MB',\n",
" 'Sending build context to Docker daemon 67.4 MB',\n",
" 'Sending build context to Docker daemon 67.96 MB',\n",
" 'Sending build context to Docker daemon 68.52 MB',\n",
" 'Sending build context to Docker daemon 69.07 MB',\n",
" 'Sending build context to Docker daemon 69.63 MB',\n",
" 'Sending build context to Docker daemon 70.19 MB',\n",
" 'Sending build context to Docker daemon 70.75 MB',\n",
" 'Sending build context to Docker daemon 71.3 MB',\n",
" 'Sending build context to Docker daemon 71.86 MB',\n",
" 'Sending build context to Docker daemon 72.42 MB',\n",
" 'Sending build context to Docker daemon 72.97 MB',\n",
" 'Sending build context to Docker daemon 73.53 MB',\n",
" 'Sending build context to Docker daemon 74.09 MB',\n",
" 'Sending build context to Docker daemon 74.65 MB',\n",
" 'Sending build context to Docker daemon 75.2 MB',\n",
" 'Sending build context to Docker daemon 75.76 MB',\n",
" 'Sending build context to Docker daemon 76.32 MB',\n",
" 'Sending build context to Docker daemon 76.87 MB',\n",
" 'Sending build context to Docker daemon 77.43 MB',\n",
" 'Sending build context to Docker daemon 77.99 MB',\n",
" 'Sending build context to Docker daemon 78.54 MB',\n",
" 'Sending build context to Docker daemon 79.1 MB',\n",
" 'Sending build context to Docker daemon 79.66 MB',\n",
" 'Sending build context to Docker daemon 80.22 MB',\n",
" 'Sending build context to Docker daemon 80.77 MB',\n",
" 'Sending build context to Docker daemon 81.33 MB',\n",
" 'Sending build context to Docker daemon 81.89 MB',\n",
" 'Sending build context to Docker daemon 82.44 MB',\n",
" 'Sending build context to Docker daemon 83 MB',\n",
" 'Sending build context to Docker daemon 83.56 MB',\n",
" 'Sending build context to Docker daemon 84.12 MB',\n",
" 'Sending build context to Docker daemon 84.67 MB',\n",
" 'Sending build context to Docker daemon 85.23 MB',\n",
" 'Sending build context to Docker daemon 85.79 MB',\n",
" 'Sending build context to Docker daemon 86.34 MB',\n",
" 'Sending build context to Docker daemon 86.9 MB',\n",
" 'Sending build context to Docker daemon 87.46 MB',\n",
" 'Sending build context to Docker daemon 88.01 MB',\n",
" 'Sending build context to Docker daemon 88.57 MB',\n",
" 'Sending build context to Docker daemon 89.13 MB',\n",
" 'Sending build context to Docker daemon 89.61 MB',\n",
" '',\n",
" 'Step 1 : FROM python:3.5.1-alpine',\n",
" ' ---> b319ff8bc15a',\n",
" 'Step 2 : RUN pip install bottle',\n",
" ' ---> Using cache',\n",
" ' ---> 3dab1cb3b009',\n",
" 'Step 3 : COPY . /app',\n",
" ' ---> 13196f24d603',\n",
" 'Removing intermediate container ca1ead23bbc6',\n",
" 'Step 4 : WORKDIR /app',\n",
" ' ---> Running in 0e10319d11c4',\n",
" ' ---> a693bc0dcf92',\n",
" 'Removing intermediate container 0e10319d11c4',\n",
" 'Step 5 : CMD python -m bottle --debug --reload server:root -b 0.0.0.0:8000',\n",
" ' ---> Running in d2adc9dbe84d',\n",
" ' ---> df1dbb62fa06',\n",
" 'Removing intermediate container d2adc9dbe84d',\n",
" 'Successfully built df1dbb62fa06']\n",
"Container Image: df1dbb62fa06\n"
]
}
],
"source": [
"#SEEMS MAGICAL, but we're just taking the last line of output, parsing it for the last ID.\n",
"#Someone at docker should just return the id like other commands\n",
"\n",
"imageid = OBREROS_BUILD[-1].split(' ')[-1]\n",
"pprint(OBREROS_BUILD)\n",
"print(\"Container Image: \" + imageid)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"docker run -d -p 8000:8000 df1dbb62fa06\n",
"Docker container: 06a69b7ee744d429da88415bba69173c95c05bbe5b6b581c28bf309b0967aa1f\n"
]
}
],
"source": [
"OBREROS_INPORT=8000\n",
"OBREROS_OUTPORT=8000\n",
"\n",
"!echo docker run -d -p $OBREROS_INPORT:$OBREROS_OUTPORT $imageid\n",
"containerid = !docker run -d -p $OBREROS_INPORT:$OBREROS_OUTPORT $imageid\n",
"containerid = containerid[0]\n",
"print(\"Docker container: \"+containerid)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Test container endpoints"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<i>Application Suite Home Page</i>"
]
}
],
"source": [
"!curl localhost:8000"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"* Trying 127.0.0.1...\r\n",
"* Connected to localhost (127.0.0.1) port 8000 (#0)\r\n",
"> HEAD /hello HTTP/1.1\r",
"\r\n",
"> Host: localhost:8000\r",
"\r\n",
"> User-Agent: curl/7.47.0\r",
"\r\n",
"> Accept: */*\r",
"\r\n",
"> \r",
"\r\n",
"* HTTP 1.0, assume close after body\r\n",
"< HTTP/1.0 200 OK\r",
"\r\n",
"HTTP/1.0 200 OK\r",
"\r\n",
"< Date: Sat, 07 May 2016 16:03:53 GMT\r",
"\r\n",
"Date: Sat, 07 May 2016 16:03:53 GMT\r",
"\r\n",
"< Server: WSGIServer/0.2 CPython/3.5.1\r",
"\r\n",
"Server: WSGIServer/0.2 CPython/3.5.1\r",
"\r\n",
"< Content-Length: 16\r",
"\r\n",
"Content-Length: 16\r",
"\r\n",
"< Content-Type: text/html; charset=UTF-8\r",
"\r\n",
"Content-Type: text/html; charset=UTF-8\r",
"\r\n",
"\r",
"\r\n",
"< \r",
"\r\n",
"* Closing connection 0\r\n"
]
}
],
"source": [
"!curl -I -v localhost:8000/hello"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"* Trying 127.0.0.1...\r\n",
"* Connected to localhost (127.0.0.1) port 8000 (#0)\r\n",
"> HEAD /hellojson HTTP/1.1\r",
"\r\n",
"> Host: localhost:8000\r",
"\r\n",
"> User-Agent: curl/7.47.0\r",
"\r\n",
"> Accept: */*\r",
"\r\n",
"> \r",
"\r\n",
"* HTTP 1.0, assume close after body\r\n",
"< HTTP/1.0 200 OK\r",
"\r\n",
"HTTP/1.0 200 OK\r",
"\r\n",
"< Date: Sat, 07 May 2016 16:03:54 GMT\r",
"\r\n",
"Date: Sat, 07 May 2016 16:03:54 GMT\r",
"\r\n",
"< Server: WSGIServer/0.2 CPython/3.5.1\r",
"\r\n",
"Server: WSGIServer/0.2 CPython/3.5.1\r",
"\r\n",
"< Content-Type: application/json\r",
"\r\n",
"Content-Type: application/json\r",
"\r\n",
"< Content-Length: 39\r",
"\r\n",
"Content-Length: 39\r",
"\r\n",
"\r",
"\r\n",
"< \r",
"\r\n",
"* Closing connection 0\r\n"
]
}
],
"source": [
"!curl -I -v localhost:8000/hellojson"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Destroy container"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"8068d669e77be61ebc26887860cb4c0a97a6bddce4369f03eaacb9467b73d37b\r\n"
]
}
],
"source": [
"!docker stop $containerid"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.1+"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
@disarticulate
Copy link
Author

disarticulate commented May 7, 2016

Obreros in a Bottle.

Notebook illustrates multi-part bottle application testing in Jupyter Notebook. Interesting features:

  • Javascript magic for cell folding
  • requireJS dependency injection
  • Jinja2 templating for server.py file
  • INSECURE exec to work around Ipython cell calculations
  • shell commands
  • Docker container startup, build and destroy to interact with Docker containers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment