From 28446d95471e74de364b53c3f24d6182fddac0e0 Mon Sep 17 00:00:00 2001 From: "bozo.kopic" Date: Wed, 16 Aug 2017 15:11:21 +0200 Subject: backend --- src_py/hatter/backend.py | 9 +++++ src_py/hatter/main.py | 96 ++++++++++++++++++++++++++++++++++++++++++++++++ src_py/hatter/server.py | 46 +++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 src_py/hatter/backend.py create mode 100644 src_py/hatter/main.py create mode 100644 src_py/hatter/server.py (limited to 'src_py') 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 -- cgit v1.2.3-70-g09d2