http_server_async: add blueprints (sub-apps)
This commit is contained in:
parent
7023f4fa40
commit
0dd58fc06e
|
@ -20,3 +20,7 @@ class MethodNotHandledException(Exception):
|
|||
def __init__(self, method):
|
||||
super().__init__(f'HTTP method not handled by handler: {method}')
|
||||
self.method = method
|
||||
|
||||
|
||||
class NoBlueprintForPath(Exception):
|
||||
'raise when no blueprint is found for a specific path'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
http_methods = ['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE']
|
||||
|
||||
|
||||
from .application import Application
|
||||
from .application import Application, Blueprint
|
||||
from .misc import Cookies, Headers
|
||||
from .request import Request
|
||||
from .response import Response
|
||||
|
|
|
@ -12,7 +12,7 @@ from .view import Static, Manifest, Robots, Style
|
|||
|
||||
from .. import logging
|
||||
from ..dotdict import DotDict
|
||||
from ..exceptions import MethodNotHandledException
|
||||
from ..exceptions import MethodNotHandledException, NoBlueprintForPath
|
||||
from ..misc import signal_handler
|
||||
from ..path import Path
|
||||
from ..template import Template
|
||||
|
@ -21,26 +21,12 @@ from ..template import Template
|
|||
frontend = Path(__file__).resolve().parent.parent.join('http_frontend')
|
||||
|
||||
|
||||
class Application:
|
||||
class ApplicationBase:
|
||||
ctx = DotDict()
|
||||
|
||||
def __init__(self, loop=None, views=[], middleware=[], **kwargs):
|
||||
if loop:
|
||||
self.loop = loop
|
||||
|
||||
else:
|
||||
try:
|
||||
self.loop = asyncio.get_running_loop()
|
||||
|
||||
except RuntimeError:
|
||||
self.loop = asyncio.new_event_loop()
|
||||
|
||||
self._server = None
|
||||
self._tasks = []
|
||||
|
||||
def __init__(self, views=[], middleware=[], **kwargs):
|
||||
self.cfg = Config(**kwargs)
|
||||
self.router = Router(trim_last_slash=True)
|
||||
#self.router = Router()
|
||||
self.middleware = DotDict({'request': [], 'response': []})
|
||||
|
||||
for view in views:
|
||||
|
@ -49,27 +35,6 @@ class Application:
|
|||
for mw in middleware:
|
||||
self.add_middleware(mw)
|
||||
|
||||
if self.cfg.tpl_default:
|
||||
self.template = Template(
|
||||
self.cfg.tpl_search,
|
||||
self.cfg.tpl_globals,
|
||||
self.cfg.tpl_context,
|
||||
self.cfg.tpl_autoescape
|
||||
)
|
||||
|
||||
self.template.add_env('app', self)
|
||||
self.template.add_env('cfg', self.cfg)
|
||||
self.template.add_env('len', len)
|
||||
|
||||
self.template.add_search_path(frontend)
|
||||
self.add_view(Manifest)
|
||||
#self.add_view(Robots)
|
||||
self.add_view(Style)
|
||||
self.add_static('/framework/static/', frontend.join('static'))
|
||||
|
||||
else:
|
||||
self.template = None
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.ctx[key]
|
||||
|
@ -137,6 +102,99 @@ class Application:
|
|||
mwlist.append(handler)
|
||||
|
||||
|
||||
async def handle_request(self, request, response, path=None):
|
||||
handler = self.get_route(path or request.path, request.method)
|
||||
|
||||
await self.handle_middleware(request)
|
||||
|
||||
if handler.params:
|
||||
handler_response = await handler.target(request, response, **handler.params)
|
||||
|
||||
else:
|
||||
handler_response = await handler.target(request, response)
|
||||
|
||||
if isinstance(handler_response, dict):
|
||||
response = self.cfg.response_class(**handler_response)
|
||||
|
||||
elif isinstance(handler_response, Response):
|
||||
response = handler_response
|
||||
|
||||
elif not handler_response:
|
||||
pass
|
||||
|
||||
else:
|
||||
raise error.InternalServerError()
|
||||
|
||||
await self.handle_middleware(request, response)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
async def handle_middleware(self, request, response=None):
|
||||
for middleware in self.middleware['response' if response else 'request']:
|
||||
if response:
|
||||
await middleware(request, response)
|
||||
|
||||
else:
|
||||
await middleware(request)
|
||||
|
||||
|
||||
class Application(ApplicationBase):
|
||||
ctx = DotDict()
|
||||
|
||||
def __init__(self, loop=None, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
if loop:
|
||||
self.loop = loop
|
||||
|
||||
else:
|
||||
try:
|
||||
self.loop = asyncio.get_running_loop()
|
||||
|
||||
except RuntimeError:
|
||||
self.loop = asyncio.new_event_loop()
|
||||
|
||||
self._blueprints = {}
|
||||
self._server = None
|
||||
self._tasks = []
|
||||
|
||||
if self.cfg.tpl_default:
|
||||
self.template = Template(
|
||||
self.cfg.tpl_search,
|
||||
self.cfg.tpl_globals,
|
||||
self.cfg.tpl_context,
|
||||
self.cfg.tpl_autoescape
|
||||
)
|
||||
|
||||
self.template.add_env('app', self)
|
||||
self.template.add_env('cfg', self.cfg)
|
||||
self.template.add_env('len', len)
|
||||
|
||||
self.template.add_search_path(frontend)
|
||||
self.add_view(Manifest)
|
||||
#self.add_view(Robots)
|
||||
self.add_view(Style)
|
||||
self.add_static('/framework/static/', frontend.join('static'))
|
||||
|
||||
else:
|
||||
self.template = None
|
||||
|
||||
|
||||
def add_blueprint(self, bp):
|
||||
assert bp.prefix not in self._blueprints.values()
|
||||
|
||||
self._blueprints[bp.prefix] = bp
|
||||
|
||||
|
||||
def get_blueprint_for_path(self, path):
|
||||
for bppath, bp in self._blueprints.items():
|
||||
if path.startswith(bppath):
|
||||
return bp
|
||||
|
||||
raise NoBlueprintForPath(path)
|
||||
|
||||
|
||||
def render(self, *args, **kwargs):
|
||||
return self.template.render(*args, **kwargs)
|
||||
|
||||
|
@ -207,29 +265,16 @@ class Application:
|
|||
response = self.cfg.response_class(request=request)
|
||||
await request.parse_headers()
|
||||
|
||||
handler = self.get_route(request.path, request.method)
|
||||
try:
|
||||
# this doesn't work all the time for some reason
|
||||
blueprint = self.get_blueprint_for_path(request.path)
|
||||
response = await blueprint.handle_request(request, response, blueprint.prefix)
|
||||
|
||||
await self.handle_middleware(request)
|
||||
except NoBlueprintForPath:
|
||||
response = await self.handle_request(request, response)
|
||||
|
||||
if handler.params:
|
||||
handler_response = await handler.target(request, response, **handler.params)
|
||||
|
||||
else:
|
||||
handler_response = await handler.target(request, response)
|
||||
|
||||
if isinstance(handler_response, dict):
|
||||
response = self.cfg.response_class(**handler_response)
|
||||
|
||||
elif isinstance(handler_response, Response):
|
||||
response = handler_response
|
||||
|
||||
elif not handler_response:
|
||||
pass
|
||||
|
||||
else:
|
||||
raise error.InternalServerError()
|
||||
|
||||
await self.handle_middleware(request, response)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
|
||||
except NotFound:
|
||||
response = self.cfg.response_class(request=request).set_error('Not Found', 404)
|
||||
|
@ -255,7 +300,7 @@ class Application:
|
|||
writer.write(response.compile())
|
||||
await writer.drain()
|
||||
|
||||
if request:
|
||||
if request and not request.path.startswith('/framework'):
|
||||
logging.info(f'{request.remote} {request.method} {request.path} {response.status} {len(response.body)} {request.agent}')
|
||||
|
||||
except:
|
||||
|
@ -265,10 +310,8 @@ class Application:
|
|||
await writer.wait_closed()
|
||||
|
||||
|
||||
async def handle_middleware(self, request, response=None):
|
||||
for middleware in self.middleware['response' if response else 'request']:
|
||||
if response:
|
||||
await middleware(request, response)
|
||||
class Blueprint(ApplicationBase):
|
||||
def __init__(self, prefix, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
else:
|
||||
await middleware(request)
|
||||
self.prefix = prefix
|
||||
|
|
|
@ -18,6 +18,11 @@ cookie_params = {
|
|||
'HttpOnly': 'httponly'
|
||||
}
|
||||
|
||||
request_methods = [
|
||||
'GET', 'HEAD', 'POST', 'PUT', 'DELETE',
|
||||
'CONNECT', 'OPTIONS', 'TRACE', 'PATCH'
|
||||
]
|
||||
|
||||
|
||||
class Cookies(DotDict):
|
||||
def __setitem__(self, key, value):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from sqlalchemy import text
|
||||
from sqlalchemy import text, or_
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.orm.session import Session as sqlalchemy_session
|
||||
|
||||
|
@ -80,7 +80,10 @@ class Session(sqlalchemy_session):
|
|||
|
||||
|
||||
def count(self, table_name, **kwargs):
|
||||
return self.query(self.table[table_name]).filter_by(**kwargs).count()
|
||||
if kwargs:
|
||||
return self.query(self.table[table_name]).filter_by(**kwargs).count()
|
||||
|
||||
return self.query(self.table[table_name]).count()
|
||||
|
||||
|
||||
def fetch(self, table, single=True, orderby=None, orderdir='asc', **kwargs):
|
||||
|
@ -112,6 +115,12 @@ class Session(sqlalchemy_session):
|
|||
return self.fetch(*args, single=False, **kwargs)
|
||||
|
||||
|
||||
# not finished
|
||||
def like(self, table, orderby=None, orderdir='asc', **kwargs):
|
||||
assert len(kwargs)
|
||||
query = self.query(self.table[table]).filter(or_(self.table[table].filename.like(f'')))
|
||||
|
||||
|
||||
def insert(self, table, return_row=False, **kwargs):
|
||||
row = self.fetch(table, **kwargs)
|
||||
|
||||
|
|
Loading…
Reference in a new issue