Skip to content
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

Path params component #173

Merged
merged 2 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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