{
 "metadata": {
  "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.9.5"
  },
  "orig_nbformat": 2,
  "kernelspec": {
   "name": "python395jvsc74a57bd0916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1",
   "display_name": "Python 3.9.5 64-bit"
  },
  "metadata": {
   "interpreter": {
    "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2,
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import websockets\n",
    "import asyncio\n",
    "import json\n",
    "import logging"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class JsonWebsocketConnection(object):\n",
    "\n",
    "    def __init__(self,\n",
    "                 websocket: websockets.WebSocketServerProtocol):\n",
    "        \n",
    "        logging.debug(\"incoming connection\")\n",
    "        self._websocket = websocket\n",
    "        self._is_closed = False;\n",
    "    \n",
    "    def is_closed(self):\n",
    "        return self.is_closed\n",
    "    \n",
    "    async def close(self):\n",
    "        logging.debug(\"closing connection\")\n",
    "        try:\n",
    "            if self._is_closed:\n",
    "                return\n",
    "            self._websocket.close()\n",
    "            self._is_closed = True\n",
    "        except Exception as e:\n",
    "            logging.warning(\"error closing connection: %s\", str(e))\n",
    "    \n",
    "    async def send(self, message: dict):\n",
    "        string_message = json.dumps(message)\n",
    "        logging.debug(\"sending message: %s\", string_message)\n",
    "        try:\n",
    "            await self._websocket.send(string_message)\n",
    "        except Exception as e:\n",
    "            logging.warning(\"error sending message: %s\", str(e))\n",
    "    \n",
    "    async def handle_message(self, message: dict):\n",
    "        pass # override this function\n",
    "\n",
    "    async def run(self):\n",
    "        try:\n",
    "            async for message in self._websocket:\n",
    "                try:\n",
    "                    json_message = json.loads(message)\n",
    "                except ValueError as e:\n",
    "                    logging.warning(\"received unprocessable message %s\", str(e))\n",
    "                await self.handle_message(message) \n",
    "        \n",
    "        except Exception as e:\n",
    "                logging.warning(\"error in websocket connection: %s\", str(e))\n",
    "\n",
    "\n",
    "\n",
    "class JsonWebsocketServer(object):\n",
    "    def __init__(self, handler_class: JsonWebsocketConnection, host:str = 'localhost', port:int = 8765):\n",
    "        self._host = host\n",
    "        self._port = port\n",
    "        self._handler_class = handler_class\n",
    "\n",
    "    def run(self):\n",
    "        async def main(websocket: websockets.WebSocketServerProtocol,\n",
    "                       path: str):\n",
    "\n",
    "            connection = self._handler_class(websocket)\n",
    "\n",
    "            connection.run()\n",
    "        \n",
    "        start_server = websockets.serve(main, self._host, self._port)\n",
    "\n",
    "        asyncio.get_event_loop().run_until_complete(start_server)\n",
    "        asyncio.get_event_loop().run_forever()\n",
    "        \n",
    "\n",
    "def CrosswordConnection(JsonWebsocketConnection): \n",
    "    async def handle_message(self, message: dict):\n",
    "        await self.send({\"message\": \"hello\"})\n",
    "    \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "output_type": "error",
     "ename": "RuntimeError",
     "evalue": "This event loop is already running",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-3-ecab008e957f>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mserver\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mJsonWebsocketServer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mCrosswordConnection\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mserver\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-2-3c372b72dd90>\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m     62\u001b[0m         \u001b[0mstart_server\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwebsockets\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mserve\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmain\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_host\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_port\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     63\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 64\u001b[0;31m         \u001b[0masyncio\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_event_loop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_until_complete\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstart_server\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     65\u001b[0m         \u001b[0masyncio\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_event_loop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_forever\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     66\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/usr/lib/python3.9/asyncio/base_events.py\u001b[0m in \u001b[0;36mrun_until_complete\u001b[0;34m(self, future)\u001b[0m\n\u001b[1;32m    616\u001b[0m         \"\"\"\n\u001b[1;32m    617\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_check_closed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 618\u001b[0;31m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_check_running\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    619\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    620\u001b[0m         \u001b[0mnew_task\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mfutures\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0misfuture\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfuture\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/usr/lib/python3.9/asyncio/base_events.py\u001b[0m in \u001b[0;36m_check_running\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    576\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0m_check_running\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    577\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_running\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 578\u001b[0;31m             \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'This event loop is already running'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    579\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mevents\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_running_loop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    580\u001b[0m             raise RuntimeError(\n",
      "\u001b[0;31mRuntimeError\u001b[0m: This event loop is already running"
     ]
    }
   ],
   "source": [
    "server = JsonWebsocketServer(CrosswordConnection)\n",
    "server.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ]
}