diff options
Diffstat (limited to 'src_py')
| -rw-r--r-- | src_py/hatter/backend.py | 9 | ||||
| -rw-r--r-- | src_py/hatter/main.py | 96 | ||||
| -rw-r--r-- | src_py/hatter/server.py | 46 |
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 |
