router and request changes
* redo route parameters * add `Application.route` method * disable generics on `Application` in `Request` for now
This commit is contained in:
parent
cad946555b
commit
4d5da1ed50
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import traceback
|
||||
|
||||
from blib import HttpError, Signal
|
||||
from blib import HttpError, Signal, Url
|
||||
from collections.abc import Callable
|
||||
from mimetypes import guess_type
|
||||
from os.path import normpath
|
||||
|
@ -36,7 +36,8 @@ class Application(Generic[R, RT, AT]):
|
|||
template_context: TemplateContextCallback | None = None,
|
||||
request_class: type[R] = Request, # type: ignore[assignment]
|
||||
request_state_class: type[RT] = StateProxy, # type: ignore[assignment]
|
||||
app_state_class: type[AT] = StateProxy) -> None: # type: ignore[assignment]
|
||||
app_state_class: type[AT] = StateProxy, # type: ignore[assignment]
|
||||
http_proxy: Url | str | None = None) -> None:
|
||||
"""
|
||||
Create a new ``Application``
|
||||
|
||||
|
@ -76,7 +77,7 @@ class Application(Generic[R, RT, AT]):
|
|||
self.router: Router = get_router(self.name)
|
||||
"Contains all of the routes the application will handle"
|
||||
|
||||
self.client: Client = Client()
|
||||
self.client: Client = Client(name, http_proxy)
|
||||
"Customized httpx client"
|
||||
|
||||
self.template: Template = Template(
|
||||
|
@ -195,7 +196,7 @@ class Application(Generic[R, RT, AT]):
|
|||
...
|
||||
|
||||
|
||||
def add_route(self, method: str, path: str, handler: RouteHandler) -> None:
|
||||
def add_route(self, handler: RouteHandler, method: str, *paths: str) -> None:
|
||||
"""
|
||||
Add a route handler
|
||||
|
||||
|
@ -204,7 +205,7 @@ class Application(Generic[R, RT, AT]):
|
|||
:param handler: Function to call when the route is requested
|
||||
"""
|
||||
|
||||
self.router.bind(handler, path, methods = [method])
|
||||
self.router.bind(handler, method, *paths)
|
||||
|
||||
|
||||
def add_static(self, path: str, location: Path | str, cached: bool = False) -> None:
|
||||
|
@ -219,7 +220,7 @@ class Application(Generic[R, RT, AT]):
|
|||
"""
|
||||
|
||||
handler = StaticHandler(path, location, cached)
|
||||
self.add_route("GET", handler.path, handler)
|
||||
self.add_route(handler, "GET", handler.path)
|
||||
|
||||
|
||||
def print_access_log(self, request: Request, response: Response) -> None:
|
||||
|
@ -246,6 +247,21 @@ class Application(Generic[R, RT, AT]):
|
|||
print(message, flush = True)
|
||||
|
||||
|
||||
def route(self, method: str, *paths: str) -> Callable[[RouteHandler], RouteHandler]:
|
||||
"""
|
||||
Decorator for adding a route from a callable
|
||||
|
||||
:param method: HTTP method the route should handle
|
||||
:param paths: Paths the route should handle
|
||||
"""
|
||||
|
||||
def wrapper(func: RouteHandler) -> RouteHandler:
|
||||
self.router.bind(func, method, *paths)
|
||||
return func
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def _handle_error(self, request: Request, error: Exception) -> Response:
|
||||
try:
|
||||
return self.error_handlers[type(error)](request, error)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import httpx
|
||||
import os
|
||||
|
||||
from typing import TypeVar
|
||||
|
||||
|
@ -24,7 +23,7 @@ class Client:
|
|||
"HTTP client with useful methods for fetching ActivityPub resources"
|
||||
|
||||
|
||||
def __init__(self, useragent: str = f"BarksharkASGI/{__version__}"):
|
||||
def __init__(self, useragent: str = f"BarksharkASGI/{__version__}", proxy: str | None = None):
|
||||
"""
|
||||
Create a new ``Client`` object
|
||||
|
||||
|
@ -35,13 +34,10 @@ class Client:
|
|||
http2 = True,
|
||||
timeout = 5,
|
||||
max_redirects = 3,
|
||||
proxies = proxy,
|
||||
headers = {
|
||||
"User-Agent": useragent
|
||||
},
|
||||
proxies = {
|
||||
"http": os.environ.get("all_proxy", os.environ.get("http_proxy")),
|
||||
"https": os.environ.get("all_proxy", os.environ.get("https_proxy"))
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@ from multipart.multipart import Field, File, create_form_parser
|
|||
from typing import Any, Literal, TypedDict, TypeVar
|
||||
from urllib.parse import parse_qsl
|
||||
|
||||
from .misc import Cookie, Stream
|
||||
from .misc import Cookie, StateProxy, Stream
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from .application import Application, R, RT, AT
|
||||
from .application import Application
|
||||
|
||||
|
||||
T = TypeVar("T", bound = JsonBase)
|
||||
|
@ -45,7 +45,9 @@ class Request:
|
|||
"Represents an incoming client request"
|
||||
|
||||
|
||||
app: Application[R, RT, AT] # type: ignore[valid-type]
|
||||
# I'm not sure how to "forward" generics without needing to specify then on every request,
|
||||
# so it'll just be static for now until I can figure it out
|
||||
app: Application[Request, StateProxy, StateProxy]
|
||||
"Application the request is associated with"
|
||||
|
||||
stream: Stream
|
||||
|
@ -75,7 +77,7 @@ class Request:
|
|||
cookies: dict[str, Cookie]
|
||||
"Cookies sent by the client"
|
||||
|
||||
state: RT # type: ignore[valid-type]
|
||||
state: StateProxy
|
||||
"Data to pass with the request"
|
||||
|
||||
extensions: dict[str, Any]
|
||||
|
@ -92,7 +94,7 @@ class Request:
|
|||
|
||||
|
||||
def __init__(self,
|
||||
app: Application[R, RT, AT],
|
||||
app: Application[Request, StateProxy, StateProxy],
|
||||
scope: ScopeType,
|
||||
stream: Stream) -> None:
|
||||
|
||||
|
@ -101,7 +103,7 @@ class Request:
|
|||
raw_query = parse_qsl(scope["query_string"].decode("utf-8"), keep_blank_values = True)
|
||||
raw_headers = ((k.decode("utf-8").title(), v.decode("utf-8")) for k, v in scope["headers"])
|
||||
|
||||
self.app: Application[R, RT, AT] = app
|
||||
self.app: Application[Request, StateProxy, StateProxy] = app
|
||||
self.stream: Stream = stream
|
||||
self.method: str = scope["method"].upper()
|
||||
self.path: str = scope["path"]
|
||||
|
@ -111,7 +113,7 @@ class Request:
|
|||
self.remote_raw: str | None = (scope.get("client") or (None,))[0]
|
||||
self.local: str | None = (scope.get("server") or (None, ))[0]
|
||||
self.cookies: dict[str, Cookie] = {}
|
||||
self.state: RT = app.request_state_class(scope.get("state") or {})
|
||||
self.state: StateProxy = app.request_state_class(scope.get("state") or {})
|
||||
self.extensions: dict[str, Any] = scope.get("extensions", {}) # type: ignore[assignment]
|
||||
|
||||
# keep?
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import http_router
|
||||
|
||||
from blib import HttpError
|
||||
from collections.abc import Awaitable, Callable, Iterable
|
||||
from collections.abc import Awaitable, Callable
|
||||
from http_router.routes import RouteMatch
|
||||
from typing import Any, Pattern
|
||||
|
||||
|
@ -55,23 +55,25 @@ class Router(http_router.Router):
|
|||
|
||||
def bind(self, # type: ignore[override]
|
||||
target: RouteHandler,
|
||||
*paths: str | Pattern[str],
|
||||
methods: Iterable[str] | str | None = None) -> list[http_router.Route]:
|
||||
method: str,
|
||||
*paths: str | Pattern[str]) -> list[http_router.Route]:
|
||||
"""
|
||||
Add a route handler
|
||||
|
||||
:param target: Function to call on request
|
||||
:param method: HTTP method the route should handle
|
||||
:param paths: List of paths the route should handle
|
||||
:param methods: List of methods the route should handle
|
||||
"""
|
||||
|
||||
if isinstance(methods, list):
|
||||
methods = [method.upper() for method in methods]
|
||||
return http_router.Router.bind(self, target, *paths, methods = [method.upper()])
|
||||
|
||||
elif isinstance(methods, str):
|
||||
methods = [methods.upper()]
|
||||
|
||||
return http_router.Router.bind(self, target, *paths, methods = methods)
|
||||
def route(self, method: str, *paths: str) -> Callable[[RouteHandler], RouteHandler]: # type: ignore[override] # noqa: E501
|
||||
def wrapper(handler: RouteHandler) -> RouteHandler:
|
||||
self.bind(handler, method, *paths)
|
||||
return handler
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
ROUTERS: dict[str, Router] = {
|
||||
|
@ -120,7 +122,7 @@ def route(router: str, method: str, *paths: str) -> Callable[[RouteHandler], Rou
|
|||
"""
|
||||
|
||||
def wrapper(func: RouteHandler) -> RouteHandler:
|
||||
get_router(router).bind(func, *paths, methods = method)
|
||||
get_router(router).bind(func, method, *paths)
|
||||
return func
|
||||
|
||||
return wrapper
|
||||
|
|
|
@ -1,2 +1,22 @@
|
|||
Usage
|
||||
=====
|
||||
|
||||
|
||||
Application
|
||||
-----------
|
||||
|
||||
:class:`basgi.Application` is the base for a web server. Be sure to set a unique name.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import basgi
|
||||
|
||||
@basgi.get("xyz.barkshark.BASGI", "/")
|
||||
async def handle_home(request: basgi.Request) -> basgi.Response:
|
||||
return basgi.Response(200, "Merp!")
|
||||
|
||||
app = basgi.Application("xyz.barkshark.BASGI")
|
||||
|
||||
@app.route("GET", "/about")
|
||||
async def handle_about(request: basgi.Request) -> basgi.Response:
|
||||
return basgi.Response(200, "")
|
||||
|
|
Loading…
Reference in a new issue