fastapi-deferred-init 0.2.10__tar.gz → 0.2.12__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.2.10 → fastapi_deferred_init-0.2.12}/PKG-INFO +3 -3
- {fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/pyproject.toml +7 -7
- {fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/src/fastapi_deferred_init/routing.py +72 -23
- {fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/tests/test_lib.py +17 -0
- {fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/LICENSE +0 -0
- {fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/README.md +0 -0
- {fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/src/fastapi_deferred_init/__init__.py +0 -0
- {fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/src/fastapi_deferred_init/py.typed +0 -0
- {fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/tests/__init__.py +0 -0
- {fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/tests/conftest.py +0 -0
- {fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/tests/data/__init__.py +0 -0
- {fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/tests/data/gen_code_ast.py +0 -0
- {fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/tests/data/template.py +0 -0
- {fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/tests/helpers.py +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fastapi-deferred-init
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.12
|
|
4
4
|
Summary: Faster FastAPI start-up time for Projects with many nested routers
|
|
5
5
|
Keywords: fastapi,speed,router
|
|
6
6
|
Author-Email: Jan Vollmer <jan@vllmr.dev>
|
|
7
7
|
License: MIT
|
|
8
8
|
Classifier: Framework :: FastAPI
|
|
9
9
|
Project-URL: Source, https://github.com/jvllmr/fastapi-deferred-init
|
|
10
|
-
Requires-Python: >=3.
|
|
11
|
-
Requires-Dist: fastapi>=0.
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Requires-Dist: fastapi>=0.132.0
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
|
|
14
14
|
# fastapi-deferred-init
|
|
@@ -10,15 +10,15 @@ authors = [
|
|
|
10
10
|
{ name = "Jan Vollmer", email = "jan@vllmr.dev" },
|
|
11
11
|
]
|
|
12
12
|
dependencies = [
|
|
13
|
-
"fastapi>=0.
|
|
13
|
+
"fastapi>=0.132.0",
|
|
14
14
|
]
|
|
15
|
-
requires-python = ">=3.
|
|
15
|
+
requires-python = ">=3.10"
|
|
16
16
|
readme = "README.md"
|
|
17
17
|
classifiers = [
|
|
18
18
|
"Framework :: FastAPI",
|
|
19
19
|
]
|
|
20
20
|
dynamic = []
|
|
21
|
-
version = "0.2.
|
|
21
|
+
version = "0.2.12"
|
|
22
22
|
|
|
23
23
|
[project.license]
|
|
24
24
|
text = "MIT"
|
|
@@ -74,12 +74,12 @@ all = true
|
|
|
74
74
|
[dependency-groups]
|
|
75
75
|
dev = [
|
|
76
76
|
"pytest>=8.4.2",
|
|
77
|
-
"ruff>=0.
|
|
77
|
+
"ruff>=0.15.2",
|
|
78
78
|
"pre-commit>=4.3.0",
|
|
79
|
-
"mypy>=1.19.
|
|
79
|
+
"mypy>=1.19.1",
|
|
80
80
|
"tuna>=0.5.11",
|
|
81
81
|
"httpx>=0.28.1",
|
|
82
82
|
"pytest-benchmark>=5.2.3",
|
|
83
|
-
"pip>=
|
|
84
|
-
"inline-snapshot>=0.
|
|
83
|
+
"pip>=26.0.1",
|
|
84
|
+
"inline-snapshot>=0.32.2",
|
|
85
85
|
]
|
{fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/src/fastapi_deferred_init/routing.py
RENAMED
|
@@ -15,7 +15,10 @@ from starlette.routing import Mount as Mount # noqa
|
|
|
15
15
|
from starlette.routing import compile_path, get_name
|
|
16
16
|
from starlette.types import ASGIApp, Lifespan
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
from fastapi.sse import (
|
|
19
|
+
EventSourceResponse,
|
|
20
|
+
ServerSentEvent,
|
|
21
|
+
)
|
|
19
22
|
from fastapi import params, routing # type:ignore
|
|
20
23
|
from fastapi._compat import ModelField, lenient_issubclass
|
|
21
24
|
from fastapi.datastructures import Default, DefaultPlaceholder
|
|
@@ -26,6 +29,7 @@ from fastapi.dependencies.utils import (
|
|
|
26
29
|
get_flat_dependant,
|
|
27
30
|
get_parameterless_sub_dependant,
|
|
28
31
|
get_typed_return_annotation,
|
|
32
|
+
get_stream_item_type,
|
|
29
33
|
)
|
|
30
34
|
from fastapi.responses import JSONResponse, Response
|
|
31
35
|
from fastapi.types import IncEx
|
|
@@ -45,42 +49,56 @@ class DeferringAPIRoute(routing.APIRoute):
|
|
|
45
49
|
endpoint: Callable[..., Any],
|
|
46
50
|
*,
|
|
47
51
|
response_model: Any = Default(None),
|
|
48
|
-
status_code:
|
|
49
|
-
tags:
|
|
50
|
-
dependencies:
|
|
51
|
-
summary:
|
|
52
|
-
description:
|
|
52
|
+
status_code: int | None = None,
|
|
53
|
+
tags: list[str | Enum] | None = None,
|
|
54
|
+
dependencies: Sequence[params.Depends] | None = None,
|
|
55
|
+
summary: str | None = None,
|
|
56
|
+
description: str | None = None,
|
|
53
57
|
response_description: str = "Successful Response",
|
|
54
|
-
responses:
|
|
55
|
-
deprecated:
|
|
56
|
-
name:
|
|
57
|
-
methods:
|
|
58
|
-
operation_id:
|
|
59
|
-
response_model_include:
|
|
60
|
-
response_model_exclude:
|
|
58
|
+
responses: dict[int | str, dict[str, Any]] | None = None,
|
|
59
|
+
deprecated: bool | None = None,
|
|
60
|
+
name: str | None = None,
|
|
61
|
+
methods: set[str] | list[str] | None = None,
|
|
62
|
+
operation_id: str | None = None,
|
|
63
|
+
response_model_include: IncEx | None = None,
|
|
64
|
+
response_model_exclude: IncEx | None = None,
|
|
61
65
|
response_model_by_alias: bool = True,
|
|
62
66
|
response_model_exclude_unset: bool = False,
|
|
63
67
|
response_model_exclude_defaults: bool = False,
|
|
64
68
|
response_model_exclude_none: bool = False,
|
|
65
69
|
include_in_schema: bool = True,
|
|
66
|
-
response_class:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Callable[["routing.APIRoute"], str], DefaultPlaceholder
|
|
74
|
-
] = Default(generate_unique_id),
|
|
70
|
+
response_class: type[Response] | DefaultPlaceholder = Default(JSONResponse),
|
|
71
|
+
dependency_overrides_provider: Any | None = None,
|
|
72
|
+
callbacks: list[BaseRoute] | None = None,
|
|
73
|
+
openapi_extra: dict[str, Any] | None = None,
|
|
74
|
+
generate_unique_id_function: Callable[[routing.APIRoute], str]
|
|
75
|
+
| DefaultPlaceholder = Default(generate_unique_id),
|
|
76
|
+
strict_content_type: bool | DefaultPlaceholder = Default(True),
|
|
75
77
|
) -> None:
|
|
76
78
|
self.path = path
|
|
77
79
|
self.endpoint = endpoint
|
|
80
|
+
self.stream_item_type: Any | None = None
|
|
78
81
|
if isinstance(response_model, DefaultPlaceholder):
|
|
79
82
|
return_annotation = get_typed_return_annotation(endpoint)
|
|
80
83
|
if lenient_issubclass(return_annotation, Response):
|
|
81
84
|
response_model = None
|
|
82
85
|
else:
|
|
83
|
-
|
|
86
|
+
stream_item = get_stream_item_type(return_annotation)
|
|
87
|
+
if stream_item is not None:
|
|
88
|
+
# Extract item type for JSONL or SSE streaming when
|
|
89
|
+
# response_class is DefaultPlaceholder (JSONL) or
|
|
90
|
+
# EventSourceResponse (SSE).
|
|
91
|
+
# ServerSentEvent is excluded: it's a transport
|
|
92
|
+
# wrapper, not a data model, so it shouldn't feed
|
|
93
|
+
# into validation or OpenAPI schema generation.
|
|
94
|
+
if (
|
|
95
|
+
isinstance(response_class, DefaultPlaceholder)
|
|
96
|
+
or lenient_issubclass(response_class, EventSourceResponse)
|
|
97
|
+
) and not lenient_issubclass(stream_item, ServerSentEvent):
|
|
98
|
+
self.stream_item_type = stream_item
|
|
99
|
+
response_model = None
|
|
100
|
+
else:
|
|
101
|
+
response_model = return_annotation
|
|
84
102
|
self.response_model = response_model
|
|
85
103
|
self.summary = summary
|
|
86
104
|
self.response_description = response_description
|
|
@@ -98,6 +116,7 @@ class DeferringAPIRoute(routing.APIRoute):
|
|
|
98
116
|
self.callbacks = callbacks
|
|
99
117
|
self.openapi_extra = openapi_extra
|
|
100
118
|
self.generate_unique_id_function = generate_unique_id_function
|
|
119
|
+
self.strict_content_type = strict_content_type
|
|
101
120
|
self.tags = tags or []
|
|
102
121
|
self.responses = responses or {}
|
|
103
122
|
self.name = get_name(endpoint) if name is None else name
|
|
@@ -120,8 +139,11 @@ class DeferringAPIRoute(routing.APIRoute):
|
|
|
120
139
|
assert is_body_allowed_for_status_code(status_code), (
|
|
121
140
|
f"Status code {status_code} must not have a response body"
|
|
122
141
|
)
|
|
142
|
+
|
|
123
143
|
self.dependencies = list(dependencies or [])
|
|
124
144
|
self.description = description or inspect.cleandoc(self.endpoint.__doc__ or "")
|
|
145
|
+
# if a "form feed" character (page break) is found in the description text,
|
|
146
|
+
# truncate description text to the content preceding the first "form feed"
|
|
125
147
|
self.description = self.description.split("\f")[0].strip()
|
|
126
148
|
|
|
127
149
|
assert callable(endpoint), "An endpoint must be a callable"
|
|
@@ -139,6 +161,20 @@ class DeferringAPIRoute(routing.APIRoute):
|
|
|
139
161
|
)
|
|
140
162
|
return dependant
|
|
141
163
|
|
|
164
|
+
@property
|
|
165
|
+
def is_generator(self) -> bool:
|
|
166
|
+
return self.dependant.is_async_gen_callable or self.dependant.is_gen_callable
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def is_sse_stream(self) -> bool:
|
|
170
|
+
return self.is_generator and lenient_issubclass(
|
|
171
|
+
self.response_class, EventSourceResponse
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def is_json_stream(self) -> bool:
|
|
176
|
+
return self.is_generator and isinstance(self.response_class, DefaultPlaceholder)
|
|
177
|
+
|
|
142
178
|
@cached_property
|
|
143
179
|
def _flat_dependant(self):
|
|
144
180
|
return get_flat_dependant(self.dependant)
|
|
@@ -159,6 +195,17 @@ class DeferringAPIRoute(routing.APIRoute):
|
|
|
159
195
|
else:
|
|
160
196
|
return None
|
|
161
197
|
|
|
198
|
+
@cached_property
|
|
199
|
+
def stream_item_field(self) -> ModelField | None:
|
|
200
|
+
if not self.stream_item_type:
|
|
201
|
+
return None
|
|
202
|
+
stream_item_name = "StreamItem_" + self.unique_id
|
|
203
|
+
return create_model_field(
|
|
204
|
+
name=stream_item_name,
|
|
205
|
+
type_=self.stream_item_type,
|
|
206
|
+
mode="serialization",
|
|
207
|
+
)
|
|
208
|
+
|
|
162
209
|
@cached_property
|
|
163
210
|
def response_fields(self) -> dict[Union[int, str], ModelField]:
|
|
164
211
|
response_fields: dict[Union[int, str], ModelField] = {}
|
|
@@ -212,6 +259,7 @@ class DeferringAPIRouter(routing.APIRouter):
|
|
|
212
259
|
generate_unique_id_function: Callable[[routing.APIRoute], str] = Default(
|
|
213
260
|
generate_unique_id
|
|
214
261
|
),
|
|
262
|
+
strict_content_type: bool = Default(True),
|
|
215
263
|
) -> None:
|
|
216
264
|
super().__init__(
|
|
217
265
|
prefix=prefix,
|
|
@@ -231,4 +279,5 @@ class DeferringAPIRouter(routing.APIRouter):
|
|
|
231
279
|
deprecated=deprecated,
|
|
232
280
|
include_in_schema=include_in_schema,
|
|
233
281
|
generate_unique_id_function=generate_unique_id_function,
|
|
282
|
+
strict_content_type=strict_content_type,
|
|
234
283
|
)
|
|
@@ -89,3 +89,20 @@ def test_fastapi_openapi_schema(monkeypatch):
|
|
|
89
89
|
|
|
90
90
|
fastapi_test_openapi_schema()
|
|
91
91
|
fastapi_test_openapi_schema_additional()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_fastapi_sse(monkeypatch):
|
|
95
|
+
monkeypatch.setattr("fastapi.routing.APIRoute", DeferringAPIRoute)
|
|
96
|
+
monkeypatch.setattr("fastapi.routing.APIRouter", DeferringAPIRouter)
|
|
97
|
+
import_via_file_path(
|
|
98
|
+
"fastapi_clone.tests.test_sse",
|
|
99
|
+
"fastapi/tests/test_sse.py",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
from fastapi_clone.tests.test_sse import ( # type: ignore
|
|
103
|
+
test_raw_data_sent_without_json_encoding as fastapi_test_raw_data_sent_without_json_encoding,
|
|
104
|
+
client_fixture as fastapi_client_fixture,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
for client in fastapi_client_fixture.__wrapped__():
|
|
108
|
+
fastapi_test_raw_data_sent_without_json_encoding(client)
|
|
File without changes
|
|
File without changes
|
{fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/src/fastapi_deferred_init/__init__.py
RENAMED
|
File without changes
|
{fastapi_deferred_init-0.2.10 → fastapi_deferred_init-0.2.12}/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
|
|
File without changes
|