{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# IFastAPI\n", "\n", "> Running [fastapi](https://github.com/tiangolo/fastapi) under [uvicorn](https://github.com/encode/uvicorn) inside [IPython](https://github.com/ipython/ipython)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import os, asyncio, uvicorn, fastapi, pydantic, IPython, ujson, tornado.ioloop" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Hubba Hubba" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "port = 8000\n", "app_args = {}\n", "uvi_args = dict(port=port)\n", "IN_HUB = \"JUPYTERHUB_SERVICE_PREFIX\" in os.environ \n", "prefix = \"/\"\n", "if IN_HUB:\n", " prefix = f\"\"\"{os.environ[\"JUPYTERHUB_SERVICE_PREFIX\"]}proxy/{port}\"\"\"\n", " app_args.update(openapi_prefix=prefix)\n", " uvi_args.update(root_path=prefix)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Make an App" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "app = fastapi.FastAPI(title=\"IFastAPI\", **app_args)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Make a route" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@app.get(\"/\")\n", "def read_root():\n", " return {\"Hello\": \"World\"}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Serve in-loop with `uvicorn`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "config = uvicorn.Config(app, **uvi_args)\n", "server = uvicorn.Server(config=config)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The actual serving will live in an `asyncio.Task`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if __name__ == \"__main__\":\n", " task = asyncio.ensure_future(server.serve())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Inspecting the route" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def show_route(route, height=\"400px\"):\n", " url = f\"{prefix}{route}\" if IN_HUB else f\"http://localhost:{port}{route}\"\n", " display(IPython.display.Markdown(f\"[`{url}`]({url})\"))\n", " display(IPython.display.IFrame(url, width=\"100%\", height=height))\n", "show_route(\"/\", height=\"40px\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Live: Modelling the domain" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Item(pydantic.BaseModel):\n", " \"\"\"\n", " a lovely model\n", " \"\"\"\n", " name: str\n", " price: float\n", " is_offer: bool = None\n", "IPython.display.JSON(ujson.loads(Item.schema_json()))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "DB = {}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Live: using the domain" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@app.get(\"/items/{item_id}\", response_model=Item)\n", "def read_item(item_id: int, q: str = None) -> Item:\n", " return DB[item_id]\n", "\n", "@app.put(\"/items/{item_id}\")\n", "def create_item(item_id: int, item: Item):\n", " DB[item_id] = item\n", " return {\"item_name\": item.name, \"item_id\": item_id}\n", "\n", "# openapi gets cached, force reloading it\n", "app.openapi_schema = None" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Showing Docs" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "show_route(\"/docs\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Showing (More) Docs" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "show_route(\"/redoc\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## All done" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if __name__ == \"__main__\":\n", " tornado.ioloop.IOLoop.current().add_callback(server.shutdown)" ] } ], "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.7.6" } }, "nbformat": 4, "nbformat_minor": 4 }