Browse Source

Refactor `Dependant.is_async_gen_callable` property for improved memory usage

```
Samples: 200000 - current=318.4 MB  peak=318.4 MB
Samples: 100000 - current=159.2 MB  peak=159.2 MB
Samples: 50000 - current=79.6 MB  peak=79.6 MB
Samples: 1000 - current=1.6 MB  peak=1.6 MB
Samples: 500 - current=0.8 MB  peak=0.8 MB
```
pull/15336/head
ipeluffo 2 months ago
parent
commit
dd109d3695
Failed to extract signature
  1. 65
      fastapi/dependencies/models.py

65
fastapi/dependencies/models.py

@ -57,6 +57,7 @@ class Dependant:
_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)
_is_async_gen_callable_cache: bool = field(default=None, init=False, repr=False)
@property
def oauth_scopes(self) -> list[str]:
@ -145,7 +146,6 @@ class Dependant:
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
@ -157,7 +157,6 @@ class Dependant:
) 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
@ -170,34 +169,46 @@ class Dependant:
self._is_gen_callable_cache = True
else:
self._is_gen_callable_cache = False
return self._is_gen_callable_cache
@cached_property
@property
def is_async_gen_callable(self) -> bool:
if self.call is None:
return False # pragma: no cover
if inspect.isasyncgenfunction(
_impartial(self.call)
) or inspect.isasyncgenfunction(_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.isasyncgenfunction(
_impartial(dunder_call)
) or inspect.isasyncgenfunction(_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.isasyncgenfunction(
_impartial(dunder_unwrapped_call)
) or inspect.isasyncgenfunction(_unwrapped_call(dunder_unwrapped_call)):
return True
return False
if self._is_async_gen_callable_cache is None:
if self.call is None:
self._is_async_gen_callable_cache = False # pragma: no cover
elif inspect.isasyncgenfunction(
_impartial(self.call)
) or inspect.isasyncgenfunction(_unwrapped_call(self.call)):
self._is_async_gen_callable_cache = True
elif inspect.isclass(_unwrapped_call(self.call)):
self._is_async_gen_callable_cache = False
if self._is_async_gen_callable_cache is not None:
return self._is_async_gen_callable_cache
dunder_call = getattr(_impartial(self.call), "__call__", None) # noqa: B004
if dunder_call is None:
self._is_async_gen_callable_cache = False # pragma: no cover
elif inspect.isasyncgenfunction(
_impartial(dunder_call)
) or inspect.isasyncgenfunction(_unwrapped_call(dunder_call)):
self._is_async_gen_callable_cache = True
if self._is_async_gen_callable_cache is not None:
return self._is_async_gen_callable_cache
dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None) # noqa: B004
if dunder_unwrapped_call is None:
self._is_async_gen_callable_cache = False # pragma: no cover
elif inspect.isasyncgenfunction(
_impartial(dunder_unwrapped_call)
) or inspect.isasyncgenfunction(_unwrapped_call(dunder_unwrapped_call)):
self._is_async_gen_callable_cache = True
else:
self._is_async_gen_callable_cache = False
return self._is_async_gen_callable_cache
@cached_property
def is_coroutine_callable(self) -> bool:

Loading…
Cancel
Save