From: Sebastián Ramírez Date: Mon, 27 Jul 2026 11:18:26 +0000 (+0200) Subject: ♻️ Update the lru_cache limit for dependencies to account for large apps (#16062) X-Git-Tag: 0.140.1~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=65ef53ae6ac110ae7d56f81a7723e58ab319f339;p=thirdparty%2Ffastapi%2Ffastapi.git ♻️ Update the lru_cache limit for dependencies to account for large apps (#16062) --- diff --git a/fastapi/dependencies/models.py b/fastapi/dependencies/models.py index 3ae682cb39..58eae0eafb 100644 --- a/fastapi/dependencies/models.py +++ b/fastapi/dependencies/models.py @@ -52,6 +52,7 @@ class Dependant: _UsesScopesCache = dict[int, tuple[Dependant, bool]] +_CALLABLE_CLASSIFICATION_CACHE_SIZE = 4096 class _CallIdentity: @@ -137,7 +138,7 @@ def _get_security_dependencies(*, dependant: Dependant) -> list[Dependant]: return [dep for dep in dependant.dependencies if _is_security_scheme(dependant=dep)] -@lru_cache(maxsize=1024) +@lru_cache(maxsize=_CALLABLE_CLASSIFICATION_CACHE_SIZE) def _is_gen_callable_cached(call_identity: _CallIdentity) -> bool: call = call_identity.call if inspect.isgeneratorfunction(_impartial(call)) or inspect.isgeneratorfunction( @@ -167,7 +168,7 @@ def _is_gen_callable(call: Callable[..., Any] | None) -> bool: return _is_gen_callable_cached(_CallIdentity(call)) -@lru_cache(maxsize=1024) +@lru_cache(maxsize=_CALLABLE_CLASSIFICATION_CACHE_SIZE) def _is_async_gen_callable_cached(call_identity: _CallIdentity) -> bool: call = call_identity.call if inspect.isasyncgenfunction(_impartial(call)) or inspect.isasyncgenfunction( @@ -197,7 +198,7 @@ def _is_async_gen_callable(call: Callable[..., Any] | None) -> bool: return _is_async_gen_callable_cached(_CallIdentity(call)) -@lru_cache(maxsize=1024) +@lru_cache(maxsize=_CALLABLE_CLASSIFICATION_CACHE_SIZE) def _is_coroutine_callable_cached(call_identity: _CallIdentity) -> bool: call = call_identity.call if inspect.isroutine(_impartial(call)) and iscoroutinefunction(_impartial(call)): diff --git a/tests/test_dependency_models.py b/tests/test_dependency_models.py index a2a36eaff4..9ba6009442 100644 --- a/tests/test_dependency_models.py +++ b/tests/test_dependency_models.py @@ -1,4 +1,4 @@ -from collections.abc import AsyncGenerator, Generator +from collections.abc import AsyncGenerator, Callable, Generator from typing import Any from fastapi.dependencies.models import ( @@ -93,7 +93,27 @@ def test_callable_classification_is_shared_by_call() -> None: cache_info = cached_function.cache_info() assert cache_info.hits == 1 assert cache_info.misses == 1 - assert cache_info.maxsize == 1024 + assert cache_info.maxsize == 4096 + + +def test_callable_classification_cache_supports_large_apps() -> None: + callables: list[Callable[[], None]] = [lambda: None for _ in range(3000)] + + for classifier, cached_classifier in ( + (_is_gen_callable, _is_gen_callable_cached), + (_is_async_gen_callable, _is_async_gen_callable_cached), + (_is_coroutine_callable, _is_coroutine_callable_cached), + ): + cached_classifier.cache_clear() + + for _ in range(2): + assert all(not classifier(call) for call in callables) + + cache_info = cached_classifier.cache_info() + assert cache_info.hits == len(callables) + assert cache_info.misses == len(callables) + assert cache_info.maxsize == 4096 + cached_classifier.cache_clear() def test_unhashable_callable_classification() -> None: