diff --git a/fastapi/dependencies/models.py b/fastapi/dependencies/models.py index aeda28ac15..c2737df23a 100644 --- a/fastapi/dependencies/models.py +++ b/fastapi/dependencies/models.py @@ -56,6 +56,7 @@ class Dependant: _is_security_scheme_cache: bool = field(default=None, init=False, repr=False) _security_scheme_cache: SecurityBase = field(default=None, init=False, repr=False) _security_dependencies_cache: list["Dependant"] = field(default=None, init=False, repr=False) + _is_gen_callable_cache: bool = field(default=None, init=False, repr=False) @property def oauth_scopes(self) -> list[str]: @@ -132,31 +133,45 @@ class Dependant: return self._security_dependencies_cache - @cached_property + @property def is_gen_callable(self) -> bool: - if self.call is None: - return False # pragma: no cover - if inspect.isgeneratorfunction( - _impartial(self.call) - ) or inspect.isgeneratorfunction(_unwrapped_call(self.call)): - return True - if inspect.isclass(_unwrapped_call(self.call)): - return False - dunder_call = getattr(_impartial(self.call), "__call__", None) # noqa: B004 - if dunder_call is None: - return False # pragma: no cover - if inspect.isgeneratorfunction( - _impartial(dunder_call) - ) or inspect.isgeneratorfunction(_unwrapped_call(dunder_call)): - return True - dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None) # noqa: B004 - if dunder_unwrapped_call is None: - return False # pragma: no cover - if inspect.isgeneratorfunction( - _impartial(dunder_unwrapped_call) - ) or inspect.isgeneratorfunction(_unwrapped_call(dunder_unwrapped_call)): - return True - return False + if self._is_gen_callable_cache is None: + if self.call is None: + self._is_gen_callable_cache = False # pragma: no cover + elif inspect.isgeneratorfunction( + _impartial(self.call) + ) or inspect.isgeneratorfunction(_unwrapped_call(self.call)): + self._is_gen_callable_cache = True + elif inspect.isclass(_unwrapped_call(self.call)): + self._is_gen_callable_cache = False + + # optimization to exit early + if self._is_gen_callable_cache is not None: + return self._is_gen_callable_cache + + dunder_call = getattr(_impartial(self.call), "__call__", None) # noqa: B004 + if dunder_call is None: + self._is_gen_callable_cache = False # pragma: no cover + elif inspect.isgeneratorfunction( + _impartial(dunder_call) + ) or inspect.isgeneratorfunction(_unwrapped_call(dunder_call)): + self._is_gen_callable_cache = True + + # optimization to exit early + if self._is_gen_callable_cache is not None: + return self._is_gen_callable_cache + + dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None) # noqa: B004 + if dunder_unwrapped_call is None: + self._is_gen_callable_cache = False # pragma: no cover + if inspect.isgeneratorfunction( + _impartial(dunder_unwrapped_call) + ) or inspect.isgeneratorfunction(_unwrapped_call(dunder_unwrapped_call)): + self._is_gen_callable_cache = True + else: + self._is_gen_callable_cache = False + + return self._is_gen_callable_cache @cached_property def is_async_gen_callable(self) -> bool: