aboutsummaryrefslogtreecommitdiff
path: root/src_py
diff options
context:
space:
mode:
Diffstat (limited to 'src_py')
-rw-r--r--src_py/hatter/backend.py9
-rw-r--r--src_py/hatter/main.py96
-rw-r--r--src_py/hatter/server.py46
3 files changed, 151 insertions, 0 deletions
diff --git a/src_py/hatter/backend.py b/src_py/hatter/backend.py
new file mode 100644
index 0000000..b2f97e4
--- /dev/null
+++ b/src_py/hatter/backend.py
@@ -0,0 +1,9 @@
+
+
+class Backend:
+
+ def __init__(self, db_path):
+ pass
+
+ async def async_close(self):
+ pass
diff --git a/src_py/hatter/main.py b/src_py/hatter/main.py
new file mode 100644
index 0000000..a110a4d
--- /dev/null
+++ b/src_py/hatter/main.py
@@ -0,0 +1,96 @@
+import sys
+import asyncio
+import argparse
+import pdb
+import contextlib
+import yaml
+import logging.config
+import atexit
+import pkg_resources
+
+import hatter.json_validator
+from hatter.backend import Backend
+from hatter.server import create_web_server
+
+
+def main():
+ args = _create_parser().parse_args()
+
+ with open(args.conf, encoding='utf-8') as conf_file:
+ conf = yaml.safe_load(conf_file)
+ hatter.json_validator.validate(conf, 'hatter://server.yaml#')
+
+ if conf['log']:
+ logging.config.dictConfig(conf['log'])
+
+ if args.web_path:
+ web_path = args.web_path
+ else:
+ atexit.register(pkg_resources.cleanup_resources)
+ web_path = pkg_resources.resource_filename('hatter', 'web')
+
+ _run_until_complete_without_interrupt(async_main(conf, web_path))
+
+
+async def async_main(conf, web_path):
+ backend = None
+ web_server = None
+ try:
+ backend = Backend(conf.get('db_path', 'hatter.db'))
+ web_server = await create_web_server(
+ backend, conf.get('host', '0.0.0.0'), conf.get('port', 24000),
+ conf.get('webhook_path', '/webhook'), web_path)
+ await asyncio.Future()
+ except asyncio.CancelledError:
+ pass
+ except Exception as e:
+ pdb.set_trace()
+ raise
+ finally:
+ if web_server:
+ await web_server.async_close()
+ if backend:
+ await backend.async_close()
+ await asyncio.sleep(0.5)
+
+
+def _create_parser():
+ parser = argparse.ArgumentParser(prog='hatter')
+ parser.add_argument(
+ '--web-path', default=None, metavar='path', dest='web_path',
+ help="web ui directory path")
+
+ named_arguments = parser.add_argument_group('required named arguments')
+ named_arguments.add_argument(
+ '--conf', required=True, metavar='path', dest='conf_path',
+ help='configuration path')
+
+ return parser
+
+
+def _run_until_complete_without_interrupt(future):
+ async def ping_loop():
+ with contextlib.suppress(asyncio.CancelledError):
+ while True:
+ await asyncio.sleep(1)
+
+ task = asyncio.ensure_future(future)
+ if sys.platform == 'win32':
+ ping_loop_task = asyncio.ensure_future(ping_loop())
+ with contextlib.suppress(KeyboardInterrupt):
+ asyncio.get_event_loop().run_until_complete(task)
+ asyncio.get_event_loop().call_soon(task.cancel)
+ if sys.platform == 'win32':
+ asyncio.get_event_loop().call_soon(ping_loop_task.cancel)
+ while not task.done():
+ with contextlib.suppress(KeyboardInterrupt):
+ asyncio.get_event_loop().run_until_complete(task)
+ if sys.platform == 'win32':
+ while not ping_loop_task.done():
+ with contextlib.suppress(KeyboardInterrupt):
+ asyncio.get_event_loop().run_until_complete(ping_loop_task)
+ return task.result()
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/src_py/hatter/server.py b/src_py/hatter/server.py
new file mode 100644
index 0000000..894d101
--- /dev/null
+++ b/src_py/hatter/server.py
@@ -0,0 +1,46 @@
+import asyncio
+import aiohttp.web
+
+
+async def create_web_server(backend, host, port, webhook_path, web_path):
+ srv = WebServer()
+ srv._backend = backend
+ srv._app = aiohttp.web.Application()
+ srv._app.router.add_route(
+ 'GET', '/', lambda req: aiohttp.web.HTTPFound('/index.html'))
+ srv._app.router.add_route('*', '/ws', srv._ws_handler)
+ srv._app.router.add_route('POST', webhook_path, srv._webhook_handler)
+ srv._app.router.add_static('/', web_path)
+ srv._app_handler = srv._app.make_handler()
+ srv._srv = await asyncio.get_event_loop().create_server(
+ srv._app_handler, host=host, port=port)
+ return srv
+
+
+class WebServer:
+
+ async def async_close(self):
+ self._srv.close()
+ await self._srv.wait_closed()
+ await self._app.shutdown()
+ await self._app_handler.finish_connections(0)
+ await self._app.cleanup()
+
+ async def _ws_handler(self, request):
+ ws = aiohttp.web.WebSocketResponse()
+ await ws.prepare(request)
+ client = Client(self._backend, ws)
+ await client.run()
+ return ws
+
+ async def _webhook_handler(self, request):
+ pass
+
+
+class Client:
+
+ def __init__(self, backend, ws):
+ pass
+
+ async def run(self):
+ pass