parameterized routes working

This commit is contained in:
2018-10-11 10:29:15 -04:00
parent 1fdda366dd
commit 83fa6d6897
7 changed files with 126 additions and 26 deletions
+1
View File
@@ -5,6 +5,7 @@ name = "pypi"
[packages]
responder = {editable = true, path = "."}
uvicorn = "*"
[dev-packages]
pytest = "*"
Generated
+57 -4
View File
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "5843d79d019341544a1c9456b537125203079f127721132c8111421095660524"
"sha256": "04a8f69fdffabcc25d69228af2c581260027423940c5ff741c42ba752ad2e35d"
},
"pipfile-spec": 6,
"requires": {
@@ -37,6 +37,13 @@
],
"version": "==3.0.4"
},
"click": {
"hashes": [
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
],
"version": "==7.0"
},
"graphene": {
"hashes": [
"sha256:b8ec446d17fa68721636eaad3d6adc1a378cb6323e219814c8f98c9928fc9642",
@@ -63,6 +70,13 @@
],
"version": "==1.1.1"
},
"h11": {
"hashes": [
"sha256:acca6a44cb52a32ab442b1779adf0875c443c689e9e028f8d831a3769f9c5208",
"sha256:f2b1ca39bfed357d1f19ac732913d5f9faa54a5062eca7d2ec3a916cfb7ae4c7"
],
"version": "==0.8.1"
},
"idna": {
"hashes": [
"sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
@@ -83,6 +97,12 @@
],
"version": "==1.0"
},
"parse": {
"hashes": [
"sha256:9dd6048ea212cd032a342f9f6aa2b7bc222f7407c7e37bdc2777fecd36897437"
],
"version": "==1.9.0"
},
"promise": {
"hashes": [
"sha256:2ebbfc10b7abf6354403ed785fe4f04b9dfd421eb1a474ac8d187022228332af",
@@ -138,6 +158,13 @@
],
"version": "==1.23"
},
"uvicorn": {
"hashes": [
"sha256:8de03999a936d8704f07cc3b1d3a3edb6922a068b64d84b4f5e49604c8b70a11"
],
"index": "pypi",
"version": "==0.3.12"
},
"waitress": {
"hashes": [
"sha256:40b0f297a7f3af61fbfbdc67e59090c70dc150a1601c39ecc9f5f1d283fb931b",
@@ -145,6 +172,32 @@
],
"version": "==1.1.0"
},
"websockets": {
"hashes": [
"sha256:0e2f7d6567838369af074f0ef4d0b802d19fa1fee135d864acc656ceefa33136",
"sha256:2a16dac282b2fdae75178d0ed3d5b9bc3258dabfae50196cbb30578d84b6f6a6",
"sha256:5a1fa6072405648cb5b3688e9ed3b94be683ce4a4e5723e6f5d34859dee495c1",
"sha256:5c1f55a1274df9d6a37553fef8cff2958515438c58920897675c9bc70f5a0538",
"sha256:669d1e46f165e0ad152ed8197f7edead22854a6c90419f544e0f234cc9dac6c4",
"sha256:695e34c4dbea18d09ab2c258994a8bf6a09564e762655408241f6a14592d2908",
"sha256:6b2e03d69afa8d20253455e67b64de1a82ff8612db105113cccec35d3f8429f0",
"sha256:79ca7cdda7ad4e3663ea3c43bfa8637fc5d5604c7737f19a8964781abbd1148d",
"sha256:7fd2dd9a856f72e6ed06f82facfce01d119b88457cd4b47b7ae501e8e11eba9c",
"sha256:82c0354ac39379d836719a77ee360ef865377aa6fdead87909d50248d0f05f4d",
"sha256:8f3b956d11c5b301206382726210dc1d3bee1a9ccf7aadf895aaf31f71c3716c",
"sha256:91ec98640220ae05b34b79ee88abf27f97ef7c61cf525eec57ea8fcea9f7dddb",
"sha256:952be9540d83dba815569d5cb5f31708801e0bbfc3a8c5aef1890b57ed7e58bf",
"sha256:99ac266af38ba1b1fe13975aea01ac0e14bb5f3a3200d2c69f05385768b8568e",
"sha256:9fa122e7adb24232247f8a89f2d9070bf64b7869daf93ac5e19546b409e47e96",
"sha256:a0873eadc4b8ca93e2e848d490809e0123eea154aa44ecd0109c4d0171869584",
"sha256:cb998bd4d93af46b8b49ecf5a72c0a98e5cc6d57fdca6527ba78ad89d6606484",
"sha256:e02e57346f6a68523e3c43bbdf35dde5c440318d1f827208ae455f6a2ace446d",
"sha256:e79a5a896bcee7fff24a788d72e5c69f13e61369d055f28113e71945a7eb1559",
"sha256:ee55eb6bcf23ecc975e6b47c127c201b913598f38b6a300075f84eeef2d3baff",
"sha256:f1414e6cbcea8d22843e7eafdfdfae3dd1aba41d1945f6ca66e4806c07c4f454"
],
"version": "==6.0"
},
"werkzeug": {
"hashes": [
"sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
@@ -290,11 +343,11 @@
},
"colorama": {
"hashes": [
"sha256:463f8483208e921368c9f306094eb6f725c6ca42b0f97e313cb5d5512459feda",
"sha256:48eb22f4f8461b1df5734a074b57042430fb06e1d61bd1e11b078c0fe6d7a1f1"
"sha256:a3d89af5db9e9806a779a50296b5fdb466e281147c2c235e8225ecc6dbf7bbf3",
"sha256:c9b54bebe91a6a803e0772c8561d53f2926bfeb17cd141fbabcb08424086595c"
],
"markers": "sys_platform == 'win32'",
"version": "==0.3.9"
"version": "==0.4.0"
},
"docutils": {
"hashes": [
+1 -1
View File
@@ -49,7 +49,7 @@ print(
# headers={"Accept": "application/x-yaml"},
# data="hello",
)
.headers
.text
)
# print(
+16 -14
View File
@@ -14,8 +14,9 @@ from graphql_server import encode_execution_results, json_encode, default_format
from . import models
from .status_codes import HTTP_404
from .routes import Route
# TODO: consider moving status codes here
class API:
def __init__(self, static_dir="static", templates_dir="templates"):
self.static_dir = Path(os.path.abspath(static_dir))
@@ -81,8 +82,8 @@ class API:
return self.wsgi_app(environ, start_response)
def path_matches_route(self, url):
for (route, view) in self.routes.items():
if url == route:
for (route, route_object) in self.routes.items():
if route_object.does_match(url):
return route
def _dispatch_request(self, req):
@@ -91,13 +92,14 @@ class API:
if route:
try:
self.routes[route](req, resp)
params = self.routes[route].incoming_matches(req.path)
self.routes[route].endpoint(req, resp, **params)
# The request is using class-based views.
except TypeError:
try:
view = self.routes[route]()
view = self.routes[route].endpoint(**params)
except TypeError:
view = self.routes[route]
view = self.routes[route].endpoint
try:
# GraphQL Schema.
assert hasattr(view, "execute")
@@ -119,7 +121,7 @@ class API:
pass
# Then on_get.
method = req.method.lower()
method = req.method
try:
getattr(view, f"on_{method}")(req, resp)
@@ -135,7 +137,7 @@ class API:
assert route not in self.routes
# TODO: Support grpahiql.
self.routes[route] = view
self.routes[route] = Route(route, view)
def default_response(self, req, resp):
resp.status_code = HTTP_404
@@ -193,14 +195,13 @@ class API:
return self._session
def url_for(self, view, absolute_url=False, **params):
for (route, _view) in self.routes.items():
if view == _view:
# TODO: Lots of cleanup here.
return route
for (route, route_object) in self.routes.items():
if route_object.endpoint == _view:
return route_object.url(**params)
raise ValueError
def url(self):
# Current URL, somehow.
# TODO: Current URL, somehow.
pass
def template(self, name, auto_escape=True, **values):
@@ -254,5 +255,6 @@ class API:
port = 0
bind_to = f"{address}:{port}"
import uvicorn
waitress.serve(app=self, listen=bind_to, **kwargs)
uvicorn.serve(app=self, listen=bind_to, **kwargs)
+42
View File
@@ -0,0 +1,42 @@
from parse import parse, search
class Route:
def __init__(self, route, endpoint):
self.route = route
self.endpoint = endpoint
def __repr__(self):
return f"<Route {self.route!r}={self.endpoint!r}>"
def __eq__(self, other):
if hasattr(other, "route"):
# Being compared to other routes.
return self.route == other.route
else:
# Strings.
return self.does_match(other)
@property
def has_parameters(self):
return all([("{" in self.route), ("}" in self.route)])
def does_match(self, s):
if s == self.route:
return True
named = self.incoming_matches(s)
return bool(len(named))
def incoming_matches(self, s):
results = parse(self.route, s)
return results.named if results else {}
def url(self, **params):
return self.route.format(**params)
# def is_graphql, is_wsgi
r = Route(route="/2/{name}", endpoint=print)
print(r.does_match("/2/hello"))
+1
View File
@@ -31,6 +31,7 @@ required = [
"graphql-server-core>=1.1",
"whitenoise",
"jinja2",
"parse",
]
+8 -7
View File
@@ -33,10 +33,6 @@ def schema():
return graphene.Schema(query=Query)
def test_api_fixture(api):
assert api
def test_api_basic_route(api):
@api.route("/")
def home(req, resp):
@@ -153,6 +149,11 @@ def test_graphql_schema_query_querying(api, schema):
assert r.json() == {"data": {"hello": None}}
# GRAPHQL
def test_assert():
assert True
def test_argumented_routing(api):
@api.route("/{name}")
def hello(req, resp, *, name):
print("yay")
resp.text = f"Hello, {name}."
r = api.session().get("http://app/sean")
assert r.text == "Hello, sean."