Skip to content

Commit

Permalink
♻️ Decouple routing module
Browse files Browse the repository at this point in the history
  • Loading branch information
perdy committed Feb 10, 2025
1 parent b71ba98 commit 4b11842
Show file tree
Hide file tree
Showing 42 changed files with 3,633 additions and 2,735 deletions.
6 changes: 2 additions & 4 deletions flama/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from flama.applications import * # noqa
from flama.background import * # noqa
from flama.cli import * # noqa
from flama.endpoints import * # noqa
from flama.injection.components import Component # noqa
from flama.modules import Module # noqa
from flama.routing import * # noqa
from flama.injection.components import * # noqa
from flama.modules import * # noqa
from flama.serialize import * # noqa
36 changes: 17 additions & 19 deletions flama/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
import threading
import typing as t

from flama import asgi, exceptions, http, injection, types, url, validation, websockets
from flama import asgi, exceptions, http, injection, routing, types, url, validation, websockets
from flama.ddd.components import WorkerComponent
from flama.events import Events
from flama.middleware import MiddlewareStack
from flama.models.modules import ModelsModule
from flama.modules import Modules
from flama.pagination import paginator
from flama.resources import ResourcesModule
from flama.routing import BaseRoute, Router
from flama.schemas.modules import SchemaModule

try:
Expand All @@ -23,7 +22,6 @@
from flama.middleware import Middleware
from flama.modules import Module
from flama.pagination.types import PaginationType
from flama.routing import Mount, Route, WebSocketRoute

__all__ = ["Flama"]

Expand All @@ -33,7 +31,7 @@
class Flama:
def __init__(
self,
routes: t.Optional[t.Sequence[t.Union["BaseRoute", "Mount"]]] = None,
routes: t.Optional[t.Sequence["routing.BaseRoute"]] = None,
components: t.Optional[t.Union[t.Sequence[injection.Component], set[injection.Component]]] = None,
modules: t.Optional[t.Union[t.Sequence["Module"], set["Module"]]] = None,
middleware: t.Optional[t.Sequence["Middleware"]] = None,
Expand Down Expand Up @@ -75,8 +73,7 @@ def __init__(
"send": types.Send,
"exc": Exception,
"app": Flama,
"path_params": types.PathParams,
"route": BaseRoute,
"route": routing.BaseRoute,
"request": http.Request,
"response": http.Response,
"websocket": websockets.WebSocket,
Expand All @@ -102,7 +99,7 @@ def __init__(
self.modules = Modules(app=self, modules={*default_modules, *(modules or [])})

# Initialize router
self.app = self.router = Router(
self.app = self.router = routing.Router(
routes=routes, components=[*default_components, *(components or [])], lifespan=lifespan
)

Expand Down Expand Up @@ -144,11 +141,12 @@ async def __call__(self, scope: types.Scope, receive: types.Receive, send: types
:param receive: ASGI receive event.
:param send: ASGI send event.
"""
if scope["type"] != "lifespan" and self.status in (types.AppStatus.NOT_STARTED, types.AppStatus.STARTING):
raise exceptions.ApplicationError("Application is not ready to process requests yet.")
if scope["type"] != "lifespan":
if self.status in (types.AppStatus.NOT_STARTED, types.AppStatus.STARTING):
raise exceptions.ApplicationError("Application is not ready to process requests yet.")

if scope["type"] != "lifespan" and self.status in (types.AppStatus.SHUT_DOWN, types.AppStatus.SHUTTING_DOWN):
raise exceptions.ApplicationError("Application is already shut down.")
elif self.status in (types.AppStatus.SHUT_DOWN, types.AppStatus.SHUTTING_DOWN):
raise exceptions.ApplicationError("Application is already shut down.")

scope["app"] = self
scope.setdefault("root_app", self)
Expand Down Expand Up @@ -182,7 +180,7 @@ def add_component(self, component: injection.Component):
self.router.build(self)

@property
def routes(self) -> list["BaseRoute"]:
def routes(self) -> list["routing.BaseRoute"]:
"""List of registered routes.
:return: Routes.
Expand All @@ -196,10 +194,10 @@ def add_route(
methods: t.Optional[list[str]] = None,
name: t.Optional[str] = None,
include_in_schema: bool = True,
route: t.Optional["Route"] = None,
route: t.Optional["routing.Route"] = None,
pagination: t.Optional[t.Union[str, "PaginationType"]] = None,
tags: t.Optional[dict[str, t.Any]] = None,
) -> "Route":
) -> "routing.Route":
"""Register a new HTTP route or endpoint under given path.
:param path: URL path.
Expand Down Expand Up @@ -257,10 +255,10 @@ def add_websocket_route(
path: t.Optional[str] = None,
endpoint: t.Optional[types.WebSocketHandler] = None,
name: t.Optional[str] = None,
route: t.Optional["WebSocketRoute"] = None,
route: t.Optional["routing.WebSocketRoute"] = None,
pagination: t.Optional[t.Union[str, "PaginationType"]] = None,
tags: t.Optional[dict[str, t.Any]] = None,
) -> "WebSocketRoute":
) -> "routing.WebSocketRoute":
"""Register a new websocket route or endpoint under given path.
:param path: URL path.
Expand Down Expand Up @@ -296,9 +294,9 @@ def mount(
path: t.Optional[str] = None,
app: t.Optional[types.App] = None,
name: t.Optional[str] = None,
mount: t.Optional["Mount"] = None,
mount: t.Optional["routing.Mount"] = None,
tags: t.Optional[dict[str, t.Any]] = None,
) -> "Mount":
) -> "routing.Mount":
"""Register a new mount point containing an ASGI app in this router under given path.
:param path: URL path.
Expand Down Expand Up @@ -366,7 +364,7 @@ def resolve_url(self, name: str, **path_params: t.Any) -> url.URL:
"""
return self.router.resolve_url(name, **path_params)

def resolve_route(self, scope: types.Scope) -> tuple[BaseRoute, types.Scope]:
def resolve_route(self, scope: types.Scope) -> tuple[routing.BaseRoute, types.Scope]:
"""Look for a route that matches given ASGI scope.
:param scope: ASGI scope.
Expand Down
16 changes: 11 additions & 5 deletions flama/asgi.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from http.cookies import SimpleCookie
from urllib.parse import parse_qsl

from flama import http, types
from flama import http, routing, types
from flama.injection.components import Component, Components

__all__ = [
Expand All @@ -11,6 +11,7 @@
"HostComponent",
"PortComponent",
"PathComponent",
"PathParamsComponent",
"QueryStringComponent",
"QueryParamsComponent",
"HeadersComponent",
Expand Down Expand Up @@ -60,7 +61,12 @@ def resolve(self, scope: types.Scope) -> types.Port:

class PathComponent(Component):
def resolve(self, scope: types.Scope) -> types.Path:
return types.Path(scope.get("root_path", "") + scope["path"])
return types.Path(scope.get("root_path", "")) / types.Path(scope["path"])


class PathParamsComponent(Component):
def resolve(self, scope: types.Scope, route: routing.BaseRoute) -> types.PathParams:
return types.PathParams(route.path.match(scope["path"]).parameters or {})


class QueryStringComponent(Component):
Expand All @@ -69,9 +75,8 @@ def resolve(self, scope: types.Scope) -> types.QueryString:


class QueryParamsComponent(Component):
def resolve(self, scope: types.Scope) -> types.QueryParams:
query_string = scope["query_string"].decode()
return types.QueryParams(parse_qsl(query_string))
def resolve(self, query: types.QueryString) -> types.QueryParams:
return types.QueryParams(parse_qsl(query))


class HeadersComponent(Component):
Expand Down Expand Up @@ -113,6 +118,7 @@ async def resolve(self, receive: types.Receive) -> types.Body:
HostComponent(),
PortComponent(),
PathComponent(),
PathParamsComponent(),
QueryStringComponent(),
QueryParamsComponent(),
HeadersComponent(),
Expand Down
172 changes: 0 additions & 172 deletions flama/endpoints.py

This file was deleted.

3 changes: 3 additions & 0 deletions flama/endpoints/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from flama.endpoints.base import * # noqa
from flama.endpoints.http import * # noqa
from flama.endpoints.websocket import * # noqa
32 changes: 32 additions & 0 deletions flama/endpoints/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import typing as t

from flama import types

__all__ = ["BaseEndpoint"]


class BaseEndpoint(types.EndpointProtocol):
def __init__(self, scope: "types.Scope", receive: "types.Receive", send: "types.Send") -> None:
"""An endpoint.
:param scope: ASGI scope.
:param receive: ASGI receive function.
:param send: ASGI send function.
"""
app = scope["app"]
scope["path"] = scope.get("root_path", "").rstrip("/") + scope["path"]
scope["root_path"] = ""
route, route_scope = app.router.resolve_route(scope)
self.state = {
"scope": route_scope,
"receive": receive,
"send": send,
"exc": None,
"app": app,
"root_app": scope["root_app"],
"path_params": route_scope.get("path_params", {}),
"route": route,
}

def __await__(self) -> t.Generator:
return self.dispatch().__await__()
Loading

0 comments on commit 4b11842

Please sign in to comment.