Compare commits
2 Commits
dd0c896df6
...
a763f0960c
| Author | SHA1 | Date | |
|---|---|---|---|
| a763f0960c | |||
| 6cb01f6204 |
@@ -9,7 +9,7 @@ license = "LGPL-3.0-or-later"
|
|||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"megasniff>=0.2.0",
|
"megasniff>=0.2.0",
|
||||||
"breakshaft>=0.1.0",
|
"breakshaft>=0.1.1",
|
||||||
"case-insensitive-dictionary>=0.2.1",
|
"case-insensitive-dictionary>=0.2.1",
|
||||||
"mypy>=1.17.0",
|
"mypy>=1.17.0",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ import uvicorn
|
|||||||
|
|
||||||
from turbosloth import SlothApp
|
from turbosloth import SlothApp
|
||||||
from turbosloth.interfaces.serialized import SerializedResponse, SerializedRequest
|
from turbosloth.interfaces.serialized import SerializedResponse, SerializedRequest
|
||||||
from turbosloth.internal_types import QTYPE, BTYPE, PTYPE
|
from turbosloth.internal_types import QTYPE, BTYPE, PTYPE, HTYPE
|
||||||
from turbosloth.req_schema import UnwrappedRequest
|
from turbosloth.req_schema import UnwrappedRequest
|
||||||
|
|
||||||
app = SlothApp()
|
app = SlothApp()
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def index(req: UnwrappedRequest[QTYPE, BTYPE, PTYPE]) -> SerializedResponse:
|
async def index(req: UnwrappedRequest[QTYPE, BTYPE, PTYPE, HTYPE]) -> SerializedResponse:
|
||||||
return SerializedResponse(200, {}, 'Hello, ASGI Router!')
|
return SerializedResponse(200, {}, 'Hello, ASGI Router!')
|
||||||
|
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ class UserIdSchema:
|
|||||||
|
|
||||||
|
|
||||||
@app.get("/user/")
|
@app.get("/user/")
|
||||||
async def get_user(req: UnwrappedRequest[UserIdSchema, BTYPE, PTYPE]) -> SerializedResponse:
|
async def get_user(req: UnwrappedRequest[UserIdSchema, BTYPE, PTYPE, HTYPE]) -> SerializedResponse:
|
||||||
print(req)
|
print(req)
|
||||||
resp: dict[str, Any] = {'message': f'Hello, User ы {req.query.user_id}!', 'from': 'server', 'echo': req.body}
|
resp: dict[str, Any] = {'message': f'Hello, User ы {req.query.user_id}!', 'from': 'server', 'echo': req.body}
|
||||||
return SerializedResponse(200, {}, resp)
|
return SerializedResponse(200, {}, resp)
|
||||||
@@ -59,7 +59,7 @@ class PTYPESchema:
|
|||||||
|
|
||||||
|
|
||||||
@app.post("/user/u{user_id}r")
|
@app.post("/user/u{user_id}r")
|
||||||
async def post_user(req: UnwrappedRequest[QTYPE, UserPostSchema, PTYPESchema],
|
async def post_user(req: UnwrappedRequest[QTYPE, UserPostSchema, PTYPESchema, HTYPE],
|
||||||
dat: SomeInternalData) -> SerializedResponse:
|
dat: SomeInternalData) -> SerializedResponse:
|
||||||
print(req)
|
print(req)
|
||||||
print(dat)
|
print(dat)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from .interfaces.serialized.text import TextSerializedResponse
|
|||||||
from .req_schema import UnwrappedRequest
|
from .req_schema import UnwrappedRequest
|
||||||
from .router import Router, Route
|
from .router import Router, Route
|
||||||
from .types import HandlerType, InternalHandlerType, ContentType
|
from .types import HandlerType, InternalHandlerType, ContentType
|
||||||
from .internal_types import Scope, Receive, Send, MethodType, QTYPE, BTYPE, PTYPE
|
from .internal_types import Scope, Receive, Send, MethodType, QTYPE, BTYPE, PTYPE, HTYPE
|
||||||
from breakshaft.convertor import ConvRepo
|
from breakshaft.convertor import ConvRepo
|
||||||
|
|
||||||
from .util import parse_content_type
|
from .util import parse_content_type
|
||||||
@@ -198,6 +198,10 @@ class SlothApp(HTTPApp, WSApp, LifespanApp, MethodRoutersApp):
|
|||||||
def extract_path_matches(req: BasicRequest | SerializedRequest) -> PTYPE:
|
def extract_path_matches(req: BasicRequest | SerializedRequest) -> PTYPE:
|
||||||
return req.path_matches
|
return req.path_matches
|
||||||
|
|
||||||
|
@self.inj_repo.mark_injector()
|
||||||
|
def extract_headers(req: BasicRequest | SerializedRequest) -> HTYPE:
|
||||||
|
return req.headers
|
||||||
|
|
||||||
@self.inj_repo.mark_injector()
|
@self.inj_repo.mark_injector()
|
||||||
def extract_body(req: SerializedRequest) -> BTYPE:
|
def extract_body(req: SerializedRequest) -> BTYPE:
|
||||||
return req.body
|
return req.body
|
||||||
@@ -230,75 +234,47 @@ class SlothApp(HTTPApp, WSApp, LifespanApp, MethodRoutersApp):
|
|||||||
if req_schema is None:
|
if req_schema is None:
|
||||||
raise ValueError(f'Unable to find request schema in handler {fn}')
|
raise ValueError(f'Unable to find request schema in handler {fn}')
|
||||||
|
|
||||||
query_type, body_type, path_type = get_args(req_schema)
|
unwrap_types = get_args(req_schema)
|
||||||
q_inflator = None
|
defaults = (QTYPE, BTYPE, PTYPE, HTYPE)
|
||||||
b_inflator = None
|
|
||||||
p_inflator = None
|
|
||||||
|
|
||||||
fork_with = set()
|
|
||||||
|
|
||||||
def none_generator(*args) -> None:
|
def none_generator(*args) -> None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if path_type not in [PTYPE, None, Any]:
|
def create_convertor(t, def_type):
|
||||||
p_inflator = self.infl_generator.schema_to_inflator(
|
infl = None
|
||||||
path_type,
|
if t not in [def_type, None, Any]:
|
||||||
|
infl = self.infl_generator.schema_to_inflator(
|
||||||
|
t,
|
||||||
strict_mode_override=False,
|
strict_mode_override=False,
|
||||||
from_type_override=QTYPE
|
from_type_override=def_type
|
||||||
)
|
)
|
||||||
fork_with.add(ConversionPoint(p_inflator, path_type, (PTYPE,)))
|
return ConversionPoint(infl, t, (def_type,), (), )
|
||||||
else:
|
else:
|
||||||
fork_with.add(ConversionPoint(none_generator, path_type, (PTYPE,)))
|
return ConversionPoint(none_generator, t, (def_type,), (), )
|
||||||
|
|
||||||
if query_type not in [QTYPE, None, Any]:
|
fork_with = set(map(lambda x: create_convertor(*x), zip(unwrap_types, defaults)))
|
||||||
q_inflator = self.infl_generator.schema_to_inflator(
|
|
||||||
query_type,
|
|
||||||
strict_mode_override=False,
|
|
||||||
from_type_override=QTYPE
|
|
||||||
)
|
|
||||||
fork_with.add(ConversionPoint(q_inflator, query_type, (QTYPE,)))
|
|
||||||
else:
|
|
||||||
fork_with.add(ConversionPoint(none_generator, query_type, (QTYPE,)))
|
|
||||||
|
|
||||||
if body_type != [BTYPE, None, Any]:
|
def construct_unwrap(q: QTYPE, b: BTYPE, p: PTYPE, h: HTYPE) -> UnwrappedRequest:
|
||||||
b_inflator = self.infl_generator.schema_to_inflator(
|
return UnwrappedRequest(q, b, p, h)
|
||||||
body_type,
|
|
||||||
from_type_override=BTYPE
|
|
||||||
)
|
|
||||||
fork_with.add(ConversionPoint(b_inflator, body_type, (BTYPE,)))
|
|
||||||
else:
|
|
||||||
fork_with.add(ConversionPoint(none_generator, body_type, (BTYPE,)))
|
|
||||||
|
|
||||||
def construct_unwrap(q, b, p) -> UnwrappedRequest:
|
|
||||||
return UnwrappedRequest(q, b, p)
|
|
||||||
|
|
||||||
fork_with |= {
|
fork_with |= {
|
||||||
ConversionPoint(
|
ConversionPoint(
|
||||||
construct_unwrap,
|
construct_unwrap,
|
||||||
req_schema,
|
req_schema,
|
||||||
((query_type or QTYPE), (body_type or BTYPE), (path_type or PTYPE))
|
unwrap_types,
|
||||||
|
(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp_repo = self.inj_repo.fork(fork_with)
|
tmp_repo = self.inj_repo.fork(fork_with)
|
||||||
|
|
||||||
conv = tmp_repo.get_conversion(
|
p = tmp_repo.create_pipeline(
|
||||||
(BasicRequest,),
|
(Send, BasicRequest),
|
||||||
fn,
|
[fn, self.send_answer],
|
||||||
force_async=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
out_conv = self.inj_repo.get_conversion(
|
|
||||||
(Send, BasicRequest, handle_hints['return'],),
|
|
||||||
self.send_answer,
|
|
||||||
force_async=True
|
force_async=True
|
||||||
)
|
)
|
||||||
|
|
||||||
async def pipeline(send: Send, req: BasicRequest):
|
self.router.add(method, path_pattern, p)
|
||||||
ret = await conv(req)
|
|
||||||
await out_conv(send, req, ret)
|
|
||||||
|
|
||||||
self.router.add(method, path_pattern, pipeline)
|
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
from typing import Any, Callable, Awaitable, Literal, Annotated
|
from typing import Any, Callable, Awaitable, Literal, Annotated
|
||||||
|
|
||||||
|
from case_insensitive_dict import CaseInsensitiveDict
|
||||||
|
|
||||||
type Scope = dict[str, Any]
|
type Scope = dict[str, Any]
|
||||||
type ASGIMessage = dict[str, Any]
|
type ASGIMessage = dict[str, Any]
|
||||||
type Receive = Callable[[], Awaitable[ASGIMessage]]
|
type Receive = Callable[[], Awaitable[ASGIMessage]]
|
||||||
@@ -20,3 +22,4 @@ type MethodType = (
|
|||||||
type QTYPE = Annotated[dict[str, Any], 'query_params']
|
type QTYPE = Annotated[dict[str, Any], 'query_params']
|
||||||
type BTYPE = Annotated[dict[str, Any] | list[Any] | str | None, 'body']
|
type BTYPE = Annotated[dict[str, Any] | list[Any] | str | None, 'body']
|
||||||
type PTYPE = Annotated[dict[str, str], 'path_matches']
|
type PTYPE = Annotated[dict[str, str], 'path_matches']
|
||||||
|
type HTYPE = Annotated[CaseInsensitiveDict[str, str], 'headers']
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ from typing import Any, TypeVar, Generic
|
|||||||
Q = TypeVar('Q')
|
Q = TypeVar('Q')
|
||||||
B = TypeVar('B')
|
B = TypeVar('B')
|
||||||
P = TypeVar('P')
|
P = TypeVar('P')
|
||||||
|
H = TypeVar('H')
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class UnwrappedRequest(Generic[Q, B, P]):
|
class UnwrappedRequest(Generic[Q, B, P, H]):
|
||||||
query: Q
|
query: Q
|
||||||
body: B
|
body: B
|
||||||
path_matches: P
|
path_matches: P
|
||||||
|
headers: H
|
||||||
|
|||||||
8
uv.lock
generated
8
uv.lock
generated
@@ -4,15 +4,15 @@ requires-python = ">=3.13"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "breakshaft"
|
name = "breakshaft"
|
||||||
version = "0.1.0.post2"
|
version = "0.1.1.post1"
|
||||||
source = { registry = "https://git.nikto-b.ru/api/packages/nikto_b/pypi/simple" }
|
source = { registry = "https://git.nikto-b.ru/api/packages/nikto_b/pypi/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "hatchling" },
|
{ name = "hatchling" },
|
||||||
{ name = "jinja2" },
|
{ name = "jinja2" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://git.nikto-b.ru/api/packages/nikto_b/pypi/files/breakshaft/0.1.0.post2/breakshaft-0.1.0.post2.tar.gz", hash = "sha256:d04f8685336080cddb5111422a9624c8afcc8c47264e1b847339139bfd690570" }
|
sdist = { url = "https://git.nikto-b.ru/api/packages/nikto_b/pypi/files/breakshaft/0.1.1.post1/breakshaft-0.1.1.post1.tar.gz", hash = "sha256:0c880a57eb53122cd1d5c7d2f2520d9f536edd7fac333496da72cc0fa7bf5283" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://git.nikto-b.ru/api/packages/nikto_b/pypi/files/breakshaft/0.1.0.post2/breakshaft-0.1.0.post2-py3-none-any.whl", hash = "sha256:fd4c9b213e2569c2c4d4f86cef2fe04065c08f9bffac3aa46b8ae21459f35074" },
|
{ url = "https://git.nikto-b.ru/api/packages/nikto_b/pypi/files/breakshaft/0.1.1.post1/breakshaft-0.1.1.post1-py3-none-any.whl", hash = "sha256:46895336aee55b3fbd0664a2f2deb73a04f15f1dc3ac382fa17884bad4c0f818" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -380,7 +380,7 @@ xml = [
|
|||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "breakshaft", specifier = ">=0.1.0", index = "https://git.nikto-b.ru/api/packages/nikto_b/pypi/simple" },
|
{ name = "breakshaft", specifier = ">=0.1.1", index = "https://git.nikto-b.ru/api/packages/nikto_b/pypi/simple" },
|
||||||
{ name = "case-insensitive-dictionary", specifier = ">=0.2.1" },
|
{ name = "case-insensitive-dictionary", specifier = ">=0.2.1" },
|
||||||
{ name = "megasniff", specifier = ">=0.2.0", index = "https://git.nikto-b.ru/api/packages/nikto_b/pypi/simple" },
|
{ name = "megasniff", specifier = ">=0.2.0", index = "https://git.nikto-b.ru/api/packages/nikto_b/pypi/simple" },
|
||||||
{ name = "mypy", specifier = ">=1.17.0" },
|
{ name = "mypy", specifier = ">=1.17.0" },
|
||||||
|
|||||||
Reference in New Issue
Block a user