Refactored out AsgiRouter, refs #870

pull/868/head
Simon Willison 2020-06-28 13:45:17 -07:00
rodzic 0991ea75cc
commit a8bcafc177
3 zmienionych plików z 28 dodań i 59 usunięć

Wyświetl plik

@ -25,7 +25,7 @@ from jinja2.environment import Template
from jinja2.exceptions import TemplateNotFound
import uvicorn
from .views.base import DatasetteError, ureg, AsgiRouter
from .views.base import DatasetteError, ureg
from .views.database import DatabaseDownload, DatabaseView
from .views.index import IndexView
from .views.special import (
@ -902,10 +902,23 @@ class Datasette:
return asgi
class DatasetteRouter(AsgiRouter):
class DatasetteRouter:
def __init__(self, datasette, routes):
self.ds = datasette
super().__init__(routes)
routes = routes or []
self.routes = [
# Compile any strings to regular expressions
((re.compile(pattern) if isinstance(pattern, str) else pattern), view)
for pattern, view in routes
]
async def __call__(self, scope, receive, send):
# Because we care about "foo/bar" v.s. "foo%2Fbar" we decode raw_path ourselves
path = scope["path"]
raw_path = scope.get("raw_path")
if raw_path:
path = raw_path.decode("ascii")
return await self.route_path(scope, receive, send, path)
async def route_path(self, scope, receive, send, path):
# Strip off base_url if present before routing
@ -933,9 +946,18 @@ class DatasetteRouter(AsgiRouter):
if actor:
break
scope_modifications["actor"] = actor or default_actor
return await super().route_path(
dict(scope, **scope_modifications), receive, send, path
)
scope = dict(scope, **scope_modifications)
for regex, view in self.routes:
match = regex.match(path)
if match is not None:
new_scope = dict(scope, url_route={"kwargs": match.groupdict()})
try:
return await view(new_scope, receive, send)
except NotFound as exception:
return await self.handle_404(scope, receive, send, exception)
except Exception as exception:
return await self.handle_500(scope, receive, send, exception)
return await self.handle_404(scope, receive, send)
async def handle_404(self, scope, receive, send, exception=None):
# If URL has a trailing slash, redirect to URL without it

Wyświetl plik

@ -118,58 +118,6 @@ class Request:
return cls(scope, None)
class AsgiRouter:
def __init__(self, routes=None):
routes = routes or []
self.routes = [
# Compile any strings to regular expressions
((re.compile(pattern) if isinstance(pattern, str) else pattern), view)
for pattern, view in routes
]
async def __call__(self, scope, receive, send):
# Because we care about "foo/bar" v.s. "foo%2Fbar" we decode raw_path ourselves
path = scope["path"]
raw_path = scope.get("raw_path")
if raw_path:
path = raw_path.decode("ascii")
return await self.route_path(scope, receive, send, path)
async def route_path(self, scope, receive, send, path):
for regex, view in self.routes:
match = regex.match(path)
if match is not None:
new_scope = dict(scope, url_route={"kwargs": match.groupdict()})
try:
return await view(new_scope, receive, send)
except NotFound as exception:
return await self.handle_404(scope, receive, send, exception)
except Exception as exception:
return await self.handle_500(scope, receive, send, exception)
return await self.handle_404(scope, receive, send)
async def handle_404(self, scope, receive, send, exception=None):
await send(
{
"type": "http.response.start",
"status": 404,
"headers": [[b"content-type", b"text/html; charset=utf-8"]],
}
)
await send({"type": "http.response.body", "body": b"<h1>404</h1>"})
async def handle_500(self, scope, receive, send, exception):
await send(
{
"type": "http.response.start",
"status": 404,
"headers": [[b"content-type", b"text/html; charset=utf-8"]],
}
)
html = "<h1>500</h1><pre{}></pre>".format(escape(repr(exception)))
await send({"type": "http.response.body", "body": html.encode("utf-8")})
class AsgiLifespan:
def __init__(self, app, on_startup=None, on_shutdown=None):
self.app = app

Wyświetl plik

@ -27,7 +27,6 @@ from datasette.utils import (
from datasette.utils.asgi import (
AsgiStream,
AsgiWriter,
AsgiRouter,
AsgiView,
Forbidden,
NotFound,