Compare commits

..
5 Commits
Author SHA1 Message Date
Sebastián Ramírezandgithub-actions[bot] 051dfeddcc 🔖 Release version 0.140.2 (#16066)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-27 14:14:11 +00:00
github-actions[bot] e9980492f2 📝 Update release notes
[skip ci]
2026-07-27 14:07:09 +00:00
Sebastián RamírezandMarcelo Trylesinski 8069eadf5e ️ Stop retaining flat dependency trees (#16065)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-07-27 16:06:31 +02:00
github-actions[bot] 2b4349d6c2 📝 Update release notes
[skip ci]
2026-07-27 12:40:07 +00:00
Sebastián Ramírez 2f34b90742 👷 Add new memory benchmark (#16064) 2026-07-27 12:39:36 +00:00
4 changed files with 80 additions and 9 deletions

No files matched your search

+10
View File
@@ -7,6 +7,16 @@ hide:
## Latest Changes
## 0.140.2 (2026-07-27)
### Refactors
* ⚡️ Stop retaining flat dependency trees. PR [#16065](https://github.com/fastapi/fastapi/pull/16065) by [@tiangolo](https://github.com/tiangolo).
### Internal
* 👷 Add new memory benchmark. PR [#16064](https://github.com/fastapi/fastapi/pull/16064) by [@tiangolo](https://github.com/tiangolo).
## 0.140.1 (2026-07-27)
### Refactors
+1 -1
View File
@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.140.1"
__version__ = "0.140.2"
from starlette import status as status
+5 -8
View File
@@ -807,7 +807,7 @@ class APIWebSocketRoute(routing.WebSocketRoute):
self.path_regex, self.path_format, self.param_convertors = compile_path(path)
(
self.dependant,
self._flat_dependant,
_,
self._embed_body_fields,
) = _build_dependant_with_parameterless_dependencies(
path=self.path_format,
@@ -944,7 +944,6 @@ class _APIRouteLike(Protocol):
description: str
response_fields: dict[int | str, ModelField]
dependant: Dependant
_flat_dependant: Dependant
_embed_body_fields: bool
body_field: ModelField | None
is_sse_stream: bool
@@ -1091,7 +1090,7 @@ def _populate_api_route_state(
assert callable(endpoint), "An endpoint must be a callable"
(
route.dependant,
route._flat_dependant,
flat_dependant,
route._embed_body_fields,
) = _build_dependant_with_parameterless_dependencies(
path=route.path_format,
@@ -1099,7 +1098,7 @@ def _populate_api_route_state(
dependencies=route.dependencies,
)
route.body_field = get_body_field(
flat_dependant=route._flat_dependant,
flat_dependant=flat_dependant,
name=route.unique_id,
embed_body_fields=route._embed_body_fields,
)
@@ -1145,7 +1144,6 @@ class APIRoute(routing.Route):
description: str
response_fields: dict[int | str, ModelField]
dependant: Dependant
_flat_dependant: Dependant
_embed_body_fields: bool
body_field: ModelField | None
is_sse_stream: bool
@@ -1410,7 +1408,6 @@ class _EffectiveRouteContext:
description: str = ""
response_fields: dict[int | str, ModelField] = field(default_factory=dict)
dependant: Dependant | None = None
_flat_dependant: Dependant | None = None
_embed_body_fields: bool = False
body_field: ModelField | None = None
is_sse_stream: bool = False
@@ -1486,7 +1483,7 @@ class _EffectiveRouteContext:
)
(
context.dependant,
context._flat_dependant,
_,
context._embed_body_fields,
) = _build_dependant_with_parameterless_dependencies(
path="",
@@ -2080,7 +2077,7 @@ class _FrontendRouteGroup(BaseRoute):
self.dependency_overrides_provider = dependency_overrides_provider
(
self.dependant,
self._flat_dependant,
_,
self._embed_body_fields,
) = _build_dependant_with_parameterless_dependencies(
path="",
@@ -0,0 +1,64 @@
import sys
from collections.abc import Callable
from typing import Annotated, Any
import pytest
from fastapi import Depends, FastAPI
from fastapi.routing import APIRoute
if "--codspeed" not in sys.argv:
pytest.skip(
"Benchmark tests are skipped by default; run with --codspeed.",
allow_module_level=True,
)
LAST_DEPENDENCY_INDEX = 100
ROUTE_COUNT = 20
ROUTE_PATH_PREFIX = "/route-"
def _create_app() -> FastAPI:
app = FastAPI()
dependencies: dict[int, Callable[..., Any]] = {}
def create_dependency(index: int) -> Callable[..., Any]:
if index == LAST_DEPENDENCY_INDEX:
def dependency() -> str:
return str(index)
dependency.__name__ = f"dependency_{index}"
return dependency
next_dependency = dependencies[index + 1]
async def dependency(
sub_dependency: Annotated[str, Depends(next_dependency)],
) -> str:
return f"{index} -> {sub_dependency}"
dependency.__name__ = f"dependency_{index}"
return dependency
for index in reversed(range(LAST_DEPENDENCY_INDEX + 1)):
dependencies[index] = create_dependency(index)
async def endpoint(
value: Annotated[str, Depends(dependencies[0])],
) -> dict[str, str]:
return {"value": value}
for index in range(ROUTE_COUNT):
app.add_api_route(f"{ROUTE_PATH_PREFIX}{index}", endpoint)
return app
def test_route_dependency_graph(benchmark) -> None:
app = benchmark(_create_app)
api_routes = [
route
for route in app.routes
if isinstance(route, APIRoute) and route.path.startswith(ROUTE_PATH_PREFIX)
]
assert len(api_routes) == ROUTE_COUNT