Add Router, Path, PathPrefix

This commit is contained in:
Tom Christie 2018-06-25 22:23:40 +01:00
parent 5ab86e1522
commit 903e1b2fc5
3 changed files with 108 additions and 0 deletions

View File

@ -1,6 +1,7 @@
from starlette.decorators import asgi_application from starlette.decorators import asgi_application
from starlette.response import HTMLResponse, JSONResponse, Response, StreamingResponse from starlette.response import HTMLResponse, JSONResponse, Response, StreamingResponse
from starlette.request import Request from starlette.request import Request
from starlette.routing import Path, PathPrefix, Router
from starlette.testclient import TestClient from starlette.testclient import TestClient

64
starlette/routing.py Normal file
View File

@ -0,0 +1,64 @@
from starlette import Response
import re
class Path:
def __init__(self, path, app):
self.path = path
self.app = app
regex = '^' + path + '$'
regex = re.sub('{([a-zA-Z_][a-zA-Z0-9_]*)}', r'(?P<\1>[^/]*)', regex)
self.path_regex = re.compile(regex)
def matches(self, scope):
match = self.path_regex.match(scope['path'])
if match:
kwargs = dict(scope.get('kwargs', {}))
kwargs.update(match.groupdict())
child_scope = scope.copy()
child_scope['kwargs'] = kwargs
return True, child_scope
return False, {}
def __call__(self, scope):
return self.app(scope)
class PathPrefix:
def __init__(self, path, app):
self.path = path
self.app = app
regex = '^' + path
regex = re.sub('{([a-zA-Z_][a-zA-Z0-9_]*)}', r'(?P<\1>[^/]*)', regex)
self.path_regex = re.compile(regex)
def matches(self, scope):
match = self.path_regex.match(scope['path'])
if match:
kwargs = dict(scope.get('kwargs', {}))
kwargs.update(match.groupdict())
child_scope = scope.copy()
child_scope['kwargs'] = kwargs
child_scope['root_path'] = scope.get('root_path', '') + match.string
child_scope['path'] = scope['path'][match.span()[1]:]
return True, child_scope
return False, {}
def __call__(self, scope):
return self.app(scope)
class Router:
def __init__(self, routes, default=None):
self.routes = routes
self.default = self.not_found if default is None else default
def __call__(self, scope):
for route in self.routes:
matched, child_scope = route.matches(scope)
if matched:
return route(child_scope)
return self.not_found(scope)
def not_found(self, scope):
return Response('Not found', 404, media_type='text/plain')

43
tests/test_routing.py Normal file
View File

@ -0,0 +1,43 @@
from starlette import Response, Path, PathPrefix, Router, TestClient
def homepage(scope):
return Response('Hello, world', media_type='text/plain')
def users(scope):
return Response('All users', media_type='text/plain')
def user(scope):
content = 'User ' + scope['kwargs']['username']
return Response(content, media_type='text/plain')
app = Router([
Path('/', app=homepage),
PathPrefix('/users', app=Router([
Path('', app=users),
Path('/{username}', app=user),
]))
])
def test_router():
client = TestClient(app)
response = client.get('/')
assert response.status_code == 200
assert response.text == 'Hello, world'
response = client.get('/foo')
assert response.status_code == 404
assert response.text == 'Not found'
response = client.get('/users')
assert response.status_code == 200
assert response.text == 'All users'
response = client.get('/users/tomchristie')
assert response.status_code == 200
assert response.text == 'User tomchristie'