fastapi-deferred-init 0.3.2__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/PKG-INFO +2 -2
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/pyproject.toml +2 -2
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/src/fastapi_deferred_init/patch.py +1 -1
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/src/fastapi_deferred_init/routing.py +70 -46
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/tests/data/template.py +4 -4
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/tests/test_lib.py +8 -7
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/LICENSE +0 -0
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/README.md +0 -0
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/src/fastapi_deferred_init/__init__.py +0 -0
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/src/fastapi_deferred_init/py.typed +0 -0
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/tests/__init__.py +0 -0
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/tests/conftest.py +0 -0
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/tests/data/__init__.py +0 -0
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/tests/data/gen_code_ast.py +0 -0
- {fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/tests/helpers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fastapi-deferred-init
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: Faster FastAPI start-up time for Projects with many nested routers
|
|
5
5
|
Keywords: fastapi,speed,router,startup,optimization
|
|
6
6
|
Author-Email: Jan Vollmer <jan@vllmr.dev>
|
|
@@ -18,7 +18,7 @@ Classifier: Typing :: Typed
|
|
|
18
18
|
Project-URL: Source, https://github.com/jvllmr/fastapi-deferred-init
|
|
19
19
|
Project-URL: Changelog, https://github.com/jvllmr/fastapi-deferred-init/releases
|
|
20
20
|
Requires-Python: >=3.10
|
|
21
|
-
Requires-Dist: fastapi>=0.
|
|
21
|
+
Requires-Dist: fastapi>=0.141.1
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
|
|
24
24
|
# fastapi-deferred-init
|
|
@@ -12,7 +12,7 @@ authors = [
|
|
|
12
12
|
{ name = "Jan Vollmer", email = "jan@vllmr.dev" },
|
|
13
13
|
]
|
|
14
14
|
dependencies = [
|
|
15
|
-
"fastapi>=0.
|
|
15
|
+
"fastapi>=0.141.1",
|
|
16
16
|
]
|
|
17
17
|
requires-python = ">=3.10"
|
|
18
18
|
readme = "README.md"
|
|
@@ -29,7 +29,7 @@ classifiers = [
|
|
|
29
29
|
"Typing :: Typed",
|
|
30
30
|
]
|
|
31
31
|
dynamic = []
|
|
32
|
-
version = "0.3.
|
|
32
|
+
version = "0.3.3"
|
|
33
33
|
|
|
34
34
|
[project.license]
|
|
35
35
|
text = "MIT"
|
{fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/src/fastapi_deferred_init/routing.py
RENAMED
|
@@ -1,29 +1,27 @@
|
|
|
1
|
-
from fastapi.dependencies.models import Dependant
|
|
2
1
|
import inspect
|
|
2
|
+
from collections.abc import Callable, Sequence
|
|
3
3
|
from enum import Enum, IntEnum
|
|
4
4
|
from functools import cached_property
|
|
5
|
-
from typing import Any,
|
|
6
|
-
from collections.abc import Sequence
|
|
7
|
-
|
|
5
|
+
from typing import Any, cast
|
|
8
6
|
|
|
9
|
-
from fastapi.sse import (
|
|
10
|
-
EventSourceResponse,
|
|
11
|
-
ServerSentEvent,
|
|
12
|
-
)
|
|
13
|
-
from fastapi.routing import _is_async_gen_callable, _is_gen_callable
|
|
14
|
-
from fastapi import params, routing # type: ignore[attr-defined]
|
|
15
7
|
from fastapi._compat import ModelField, lenient_issubclass
|
|
16
8
|
from fastapi.datastructures import Default, DefaultPlaceholder
|
|
9
|
+
from fastapi.dependencies.models import Dependant
|
|
17
10
|
from fastapi.dependencies.utils import (
|
|
18
|
-
_should_embed_body_fields,
|
|
19
11
|
_get_body_field,
|
|
12
|
+
_get_flat_body_params,
|
|
13
|
+
_should_embed_body_fields,
|
|
20
14
|
get_dependant,
|
|
21
15
|
get_parameterless_sub_dependant,
|
|
22
|
-
get_typed_return_annotation,
|
|
23
16
|
get_stream_item_type,
|
|
24
|
-
|
|
17
|
+
get_typed_return_annotation,
|
|
25
18
|
)
|
|
26
19
|
from fastapi.responses import JSONResponse, Response
|
|
20
|
+
from fastapi.routing import _is_async_gen_callable, _is_gen_callable
|
|
21
|
+
from fastapi.sse import (
|
|
22
|
+
EventSourceResponse,
|
|
23
|
+
ServerSentEvent,
|
|
24
|
+
)
|
|
27
25
|
from fastapi.types import IncEx
|
|
28
26
|
from fastapi.utils import (
|
|
29
27
|
create_model_field,
|
|
@@ -31,6 +29,8 @@ from fastapi.utils import (
|
|
|
31
29
|
is_body_allowed_for_status_code,
|
|
32
30
|
)
|
|
33
31
|
|
|
32
|
+
from fastapi import params, routing # type: ignore[attr-defined]
|
|
33
|
+
|
|
34
34
|
|
|
35
35
|
def _add_cache_attribute(
|
|
36
36
|
instance: routing._APIRouteLike, name: str, func: Callable[[Any], Any]
|
|
@@ -54,7 +54,7 @@ def _populate_api_route_state(
|
|
|
54
54
|
path: str,
|
|
55
55
|
endpoint: Callable[..., Any],
|
|
56
56
|
*,
|
|
57
|
-
response_model: Any = Default(None),
|
|
57
|
+
response_model: Any = Default(None), # noqa: B008
|
|
58
58
|
status_code: int | None = None,
|
|
59
59
|
tags: list[str | Enum] | None = None,
|
|
60
60
|
dependencies: Sequence[params.Depends] | None = None,
|
|
@@ -73,40 +73,20 @@ def _populate_api_route_state(
|
|
|
73
73
|
response_model_exclude_defaults: bool = False,
|
|
74
74
|
response_model_exclude_none: bool = False,
|
|
75
75
|
include_in_schema: bool = True,
|
|
76
|
-
response_class: type[Response] | DefaultPlaceholder = Default(JSONResponse),
|
|
76
|
+
response_class: type[Response] | DefaultPlaceholder = Default(JSONResponse), # noqa: B008
|
|
77
77
|
dependency_overrides_provider: Any | None = None,
|
|
78
78
|
callbacks: list[routing.BaseRoute] | None = None,
|
|
79
79
|
openapi_extra: dict[str, Any] | None = None,
|
|
80
|
-
generate_unique_id_function: Callable[[Any], str] | DefaultPlaceholder = Default(
|
|
80
|
+
generate_unique_id_function: Callable[[Any], str] | DefaultPlaceholder = Default( # noqa: B008
|
|
81
81
|
generate_unique_id
|
|
82
82
|
),
|
|
83
|
-
strict_content_type: bool | DefaultPlaceholder = Default(True),
|
|
83
|
+
strict_content_type: bool | DefaultPlaceholder = Default(True), # noqa: B008
|
|
84
|
+
stream_item_type: Any | None = None,
|
|
84
85
|
) -> None:
|
|
85
86
|
route.path = path
|
|
86
87
|
route.endpoint = endpoint
|
|
87
|
-
route.stream_item_type =
|
|
88
|
-
|
|
89
|
-
return_annotation = get_typed_return_annotation(endpoint)
|
|
90
|
-
if lenient_issubclass(return_annotation, Response):
|
|
91
|
-
response_model = None
|
|
92
|
-
else:
|
|
93
|
-
stream_item = get_stream_item_type(return_annotation)
|
|
94
|
-
if stream_item is not None:
|
|
95
|
-
# Extract item type for JSONL or SSE streaming when
|
|
96
|
-
# response_class is DefaultPlaceholder (JSONL) or
|
|
97
|
-
# EventSourceResponse (SSE).
|
|
98
|
-
# ServerSentEvent is excluded: it's a transport
|
|
99
|
-
# wrapper, not a data model, so it shouldn't feed
|
|
100
|
-
# into validation or OpenAPI schema generation.
|
|
101
|
-
if (
|
|
102
|
-
isinstance(response_class, DefaultPlaceholder)
|
|
103
|
-
or lenient_issubclass(response_class, EventSourceResponse)
|
|
104
|
-
) and not lenient_issubclass(stream_item, ServerSentEvent):
|
|
105
|
-
route.stream_item_type = stream_item
|
|
106
|
-
response_model = None
|
|
107
|
-
else:
|
|
108
|
-
response_model = return_annotation
|
|
109
|
-
route.response_model = response_model
|
|
88
|
+
route.stream_item_type = stream_item_type
|
|
89
|
+
|
|
110
90
|
route.summary = summary
|
|
111
91
|
route.response_description = response_description
|
|
112
92
|
route.deprecated = deprecated
|
|
@@ -144,13 +124,12 @@ def _populate_api_route_state(
|
|
|
144
124
|
if isinstance(status_code, IntEnum):
|
|
145
125
|
status_code = int(status_code)
|
|
146
126
|
route.status_code = status_code
|
|
147
|
-
if route.response_model:
|
|
148
|
-
assert is_body_allowed_for_status_code(status_code), (
|
|
149
|
-
f"Status code {status_code} must not have a response body"
|
|
150
|
-
)
|
|
151
127
|
|
|
152
128
|
def _response_field(self):
|
|
153
129
|
if self.response_model:
|
|
130
|
+
assert is_body_allowed_for_status_code(status_code), (
|
|
131
|
+
f"Status code {status_code} must not have a response body"
|
|
132
|
+
)
|
|
154
133
|
response_name = "Response_" + self.unique_id
|
|
155
134
|
return create_model_field(
|
|
156
135
|
name=response_name,
|
|
@@ -180,8 +159,8 @@ def _populate_api_route_state(
|
|
|
180
159
|
# truncate description text to the content preceding the first "form feed"
|
|
181
160
|
route.description = route.description.split("\f")[0].strip()
|
|
182
161
|
|
|
183
|
-
def _response_fields(self) -> dict[
|
|
184
|
-
response_fields: dict[
|
|
162
|
+
def _response_fields(self) -> dict[int | str, ModelField]:
|
|
163
|
+
response_fields: dict[int | str, ModelField] = {}
|
|
185
164
|
for additional_status_code, response in self.responses.items():
|
|
186
165
|
assert isinstance(response, dict), "An additional response must be a dict"
|
|
187
166
|
model = response.get("model")
|
|
@@ -254,6 +233,51 @@ def _populate_api_route_state(
|
|
|
254
233
|
|
|
255
234
|
_add_cache_attribute(route, "is_json_stream", _is_json_stream)
|
|
256
235
|
|
|
236
|
+
def _resolved_response_model_stream_item(self):
|
|
237
|
+
nonlocal response_model, stream_item_type
|
|
238
|
+
resolved_model = response_model
|
|
239
|
+
resolved_item_type = stream_item_type
|
|
240
|
+
if isinstance(response_model, DefaultPlaceholder):
|
|
241
|
+
return_annotation = get_typed_return_annotation(endpoint)
|
|
242
|
+
if lenient_issubclass(return_annotation, Response):
|
|
243
|
+
resolved_model = None
|
|
244
|
+
else:
|
|
245
|
+
stream_item = get_stream_item_type(return_annotation)
|
|
246
|
+
if stream_item is not None and self.is_generator:
|
|
247
|
+
# Extract item type for JSONL or SSE streaming for
|
|
248
|
+
# generator endpoints when response_class is
|
|
249
|
+
# DefaultPlaceholder (JSONL) or EventSourceResponse (SSE).
|
|
250
|
+
# ServerSentEvent is excluded: it's a transport
|
|
251
|
+
# wrapper, not a data model, so it shouldn't feed
|
|
252
|
+
# into validation or OpenAPI schema generation.
|
|
253
|
+
if (
|
|
254
|
+
isinstance(response_class, DefaultPlaceholder)
|
|
255
|
+
or lenient_issubclass(response_class, EventSourceResponse)
|
|
256
|
+
) and not lenient_issubclass(stream_item, ServerSentEvent):
|
|
257
|
+
resolved_item_type = stream_item
|
|
258
|
+
resolved_model = None
|
|
259
|
+
else:
|
|
260
|
+
resolved_model = return_annotation
|
|
261
|
+
|
|
262
|
+
return resolved_model, resolved_item_type
|
|
263
|
+
|
|
264
|
+
_add_cache_attribute(
|
|
265
|
+
route,
|
|
266
|
+
"_resolved_response_model_stream_item",
|
|
267
|
+
_resolved_response_model_stream_item,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def _response_model(self):
|
|
271
|
+
|
|
272
|
+
return self._resolved_response_model_stream_item[0]
|
|
273
|
+
|
|
274
|
+
_add_cache_attribute(route, "response_model", _response_model)
|
|
275
|
+
|
|
276
|
+
def _stream_item_type(self):
|
|
277
|
+
return self._resolved_response_model_stream_item[1]
|
|
278
|
+
|
|
279
|
+
_add_cache_attribute(route, "stream_item_type", _stream_item_type)
|
|
280
|
+
|
|
257
281
|
|
|
258
282
|
class DeferringAPIRoute(routing.APIRoute):
|
|
259
283
|
def __init__(
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from fastapi import APIRouter, Depends
|
|
2
2
|
|
|
3
|
-
dependency0 = lambda: 1
|
|
3
|
+
dependency0 = lambda: 1
|
|
4
4
|
|
|
5
|
-
dependency = lambda: 0
|
|
5
|
+
dependency = lambda: 0
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def dependency1(sub_dependant: int = Depends(dependency0)):
|
|
@@ -13,7 +13,7 @@ router1 = APIRouter(prefix="/prefix1")
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
@router1.get("/get1")
|
|
16
|
-
def get1(dependency=Depends(dependency)):
|
|
16
|
+
def get1(dependency=Depends(dependency)): # noqa: B008
|
|
17
17
|
return {"1": 1}
|
|
18
18
|
|
|
19
19
|
|
|
@@ -21,7 +21,7 @@ router2 = APIRouter(prefix="/prefix2")
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
@router2.get("/get2")
|
|
24
|
-
def get2(dependency=Depends(dependency)):
|
|
24
|
+
def get2(dependency=Depends(dependency)): # noqa: B008
|
|
25
25
|
return {"2": 2}
|
|
26
26
|
|
|
27
27
|
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import os
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
+
from fastapi.testclient import TestClient
|
|
4
5
|
from pydantic import BaseModel
|
|
5
6
|
|
|
6
|
-
from fastapi import FastAPI,
|
|
7
|
-
from fastapi import routing
|
|
8
|
-
from fastapi.testclient import TestClient
|
|
7
|
+
from fastapi import APIRouter, FastAPI, routing
|
|
9
8
|
from fastapi_deferred_init.routing import (
|
|
10
|
-
_populate_api_route_state,
|
|
11
9
|
DeferringAPIRoute,
|
|
12
10
|
DeferringAPIRouter,
|
|
11
|
+
_populate_api_route_state,
|
|
13
12
|
)
|
|
14
13
|
|
|
15
14
|
from .data.gen_code_ast import create_code
|
|
@@ -107,10 +106,10 @@ def test_fastapi_openapi_schema(monkeypatch):
|
|
|
107
106
|
"fastapi/tests/test_openapi_examples.py",
|
|
108
107
|
)
|
|
109
108
|
|
|
110
|
-
from fastapi_clone.tests.test_additional_properties import ( #
|
|
109
|
+
from fastapi_clone.tests.test_additional_properties import ( # type: ignore
|
|
111
110
|
test_openapi_schema as fastapi_test_openapi_schema_additional,
|
|
112
111
|
)
|
|
113
|
-
from fastapi_clone.tests.test_openapi_examples import ( #
|
|
112
|
+
from fastapi_clone.tests.test_openapi_examples import ( # type: ignore
|
|
114
113
|
test_openapi_schema as fastapi_test_openapi_schema,
|
|
115
114
|
)
|
|
116
115
|
|
|
@@ -125,9 +124,11 @@ def test_fastapi_sse(monkeypatch):
|
|
|
125
124
|
"fastapi/tests/test_sse.py",
|
|
126
125
|
)
|
|
127
126
|
|
|
127
|
+
from fastapi_clone.tests.test_sse import (
|
|
128
|
+
client_fixture as fastapi_client_fixture,
|
|
129
|
+
)
|
|
128
130
|
from fastapi_clone.tests.test_sse import ( # type: ignore
|
|
129
131
|
test_raw_data_sent_without_json_encoding as fastapi_test_raw_data_sent_without_json_encoding,
|
|
130
|
-
client_fixture as fastapi_client_fixture,
|
|
131
132
|
)
|
|
132
133
|
|
|
133
134
|
for client in fastapi_client_fixture.__wrapped__():
|
|
File without changes
|
|
File without changes
|
{fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/src/fastapi_deferred_init/__init__.py
RENAMED
|
File without changes
|
{fastapi_deferred_init-0.3.2 → fastapi_deferred_init-0.3.3}/src/fastapi_deferred_init/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|