Skip to content

Commit 6ef8be5

Browse files
committed
Draft refactoring. Optimization resolver and aiohttp router and extractor.
1 parent fdda22c commit 6ef8be5

File tree

5 files changed

+39
-16
lines changed

5 files changed

+39
-16
lines changed

fastopenapi/resolution/resolver.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,12 @@ class ParameterResolver:
4848
_signature_cache: dict[Callable, MappingProxyType] = {}
4949

5050
@classmethod
51-
def _get_signature(cls, endpoint) -> typing.ItemsView:
51+
def _get_signature(cls, endpoint) -> MappingProxyType[str, inspect.Parameter]:
5252
"""Get cached signature parameters for endpoint"""
53-
cached_params = cls._signature_cache.get(endpoint)
54-
if cached_params:
55-
return cached_params.items()
56-
else:
53+
if endpoint not in cls._signature_cache:
5754
sig = inspect.signature(endpoint)
5855
cls._signature_cache[endpoint] = sig.parameters
59-
return cls._signature_cache[endpoint].items()
56+
return cls._signature_cache[endpoint]
6057

6158
@classmethod
6259
def resolve(cls, endpoint: Callable, request_data: RequestData) -> dict[str, Any]:
@@ -95,18 +92,34 @@ def _resolve_dependencies(
9592

9693
@classmethod
9794
def _process_parameters(
98-
cls, params: typing.ItemsView, request_data: RequestData
95+
cls,
96+
params: MappingProxyType[str, inspect.Parameter],
97+
request_data: RequestData,
9998
) -> tuple[dict[str, Any], dict[str, tuple], dict[str, Any]]:
10099
"""Process all endpoint parameters"""
101100
regular_kwargs = {}
102101
model_fields = {}
103102
model_values = {}
104103

105-
for name, param in params:
104+
for name, param in params.items():
106105
# Skip dependency parameters - already resolved
107106
if isinstance(param.default, (Depends, Security)):
108107
continue
109108

109+
# Handle Pydantic models separately (direct validation without wrapper)
110+
if cls._is_pydantic_model(param.annotation):
111+
source = cls._determine_source(name, param, request_data.path_params)
112+
data = (
113+
request_data.body
114+
if source == ParameterSource.BODY
115+
else request_data.query_params
116+
)
117+
resolved_model = cls._resolve_pydantic_model(
118+
param.annotation, data, name
119+
)
120+
regular_kwargs[name] = resolved_model
121+
continue # Don't add to model_fields
122+
110123
processed_param = cls._process_single_parameter(name, param, request_data)
111124

112125
if processed_param.needs_validation:

fastopenapi/routers/aiohttp/async_router.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from collections.abc import Callable
33

44
from aiohttp import web
5+
from pydantic_core import to_json
56

67
from fastopenapi.core.types import Response
78
from fastopenapi.openapi.ui import render_redoc_ui, render_swagger_ui
@@ -62,8 +63,14 @@ def build_framework_response(self, response: Response) -> web.Response:
6263
)
6364

6465
# JSON content (dict, list, None)
65-
return web.json_response(
66-
response.content, status=response.status_code, headers=response.headers
66+
body_bytes = to_json(response.content)
67+
return web.Response(
68+
body=body_bytes,
69+
status=response.status_code,
70+
headers={
71+
**response.headers,
72+
"Content-Type": content_type or "application/json",
73+
},
6774
)
6875

6976
def is_framework_response(self, response: Response | web.Response) -> bool:

fastopenapi/routers/aiohttp/extractors.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from typing import Any
22

3+
from pydantic_core import from_json
4+
35
from fastopenapi.core.types import FileUpload
46
from fastopenapi.routers.extractors import BaseAsyncRequestDataExtractor
57

@@ -31,13 +33,15 @@ def _get_cookies(cls, request: Any) -> dict:
3133

3234
@classmethod
3335
async def _get_body(cls, request: Any) -> dict | list | None:
36+
"""Extract JSON body"""
3437
try:
3538
body_bytes = await request.read()
3639
if body_bytes:
37-
return await request.json()
40+
return from_json(body_bytes)
41+
else:
42+
return {}
3843
except Exception:
39-
pass
40-
return {}
44+
return {}
4145

4246
@classmethod
4347
async def _get_form_data(cls, request: Any) -> dict:

tests/resolution/test_parameter_resolver.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def dependency() -> str:
135135
}
136136

137137
regular, model_fields, model_values = ParameterResolver._process_parameters(
138-
params_dict.items(), request_data
138+
params_dict, request_data
139139
)
140140

141141
assert "dep" not in regular
@@ -153,7 +153,7 @@ def test_process_parameters_with_security(self, request_data: RequestData) -> No
153153
}
154154

155155
regular, model_fields, model_values = ParameterResolver._process_parameters(
156-
params_dict.items(), request_data
156+
params_dict, request_data
157157
)
158158

159159
assert "token" not in regular

tests/routers/aiohttp/test_aiohttp_extractor.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ async def test_get_body_json(self):
114114

115115
assert result == {"key": "value"}
116116
request.read.assert_called_once()
117-
request.json.assert_called_once()
118117

119118
@pytest.mark.asyncio
120119
async def test_get_body_empty(self):

0 commit comments

Comments
 (0)