-
-
Notifications
You must be signed in to change notification settings - Fork 930
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Routing decorator #17
Conversation
Nice work. Tho I do want to think about this a bit more. This routes to an ASGI application, so it's not quite the view style that lots of devs would expect: @app.route(...)
async def do_something(request):
... I think that's potentially confusing. Related: Thinking on it, I also dislike our existing
(Still not sure about any of the names there) |
I understand what you mean, I think fleshing out a more traditional view pattern in a pure-ASGI context would make things quite a bit more clear. Two question I have at this point:
Because how I am understanding the current routing components seems consistent with how the decorator is being used here, and I would think any confusion using the decorator would be experienced when using the current router functionality also. I could continue implementing this outside of Starlette and work the routing components into a more appropriate view pattern (eventually), however, I think the convenience of this routing style could be desirable to enough people to include here somehow. |
Thinking about this a bit more, perhaps the |
Somewhat related to this: I started on a new project using Starlette, and I'm currently trying to figure out a good way of handling the routing/templating/views. Below is my first attempt to come up with a CBV pattern using the functionality import asyncio
from starlette import Request, Response
from starlette.routing import Router, Path
from starlette.types import Receive, Send, Scope
class View:
def __init__(self, scope: Scope):
self.scope = scope
async def __call__(self, receive: Receive, send: Send) -> None:
request = Request(self.scope, receive)
request_method = request.method if request.method != "HEAD" else "GET"
func = getattr(self, request_method.lower(), None)
if func is None:
raise Exception(
f"Method {request_method} is not implemented for this view handler."
)
if asyncio.iscoroutinefunction(func):
response = await func(request)
else:
response = func(request)
await response(receive, send)
class HomepageView(View):
# async def get(self, request):
# response = Response("Hello async world!", media_type="text/plain")
# return response
def get(self, request):
response = Response("Hello sync world!", media_type="text/plain")
return response
app = Router([Path("/", app=HomepageView, methods=["GET"])]) It would need to be finessed a bit and the Would something like this be in the scope of Starlette? If it is too much to include here, I could see about doing a PR for just the |
Exactly what I was thinking, yes. Perhaps @asgi_application could be renamed to ‘@view’ too. |
One other thing here: we could run sync views inside a threadpool, always. For static views that just return some templates data or a fixed response, that’s just unnecessary overhead, but for most teal view cases it’d be what you want. That’d allow you to use either standard thread-concurrency code at the view level, or asyncio-concurrency code instead if needed. |
What are your thoughts on creating a distinction between application and view routing? Also about including class-based views? The answers in #3 made things generally clear, but there are still a few grey areas in regards to the scope of Starlette that are relevant here. |
I think we should only provide routing to ASGI applications. For class based views that works seemlessly, since View is an ASGI app. For function based views, we’ve got a request/response interface tho. Either you can wrap that function up in a decorator to make it an ASGI app (That’s what we currently have documented, tho I don’t like that it changes the signature, makes the interface less directly testable) or instead wrap the adapter around the function within the routing, something like ‘Path(‘/blah’, app=asgi_application(my_view))’ |
In other words: I think it’d make sense to start by adding CBVs, with our existing routing. I like decorator routing, but still not sure how to best apply it. |
Thanks for clarifying. I'll close this PR now and see about introducing CBVs first with the existing routing then approach the decorator routing later. |
Refs #12
Implements a method on the
Router
class to allow a simple means of routing ASGI applications toPath
objects via a decorator.Example: