`ResponseModel` is not fully defined
I'm running into an issue when returning a class as a return type.
Omitting the return type works great:
from ..config import Config
def get_config():
return Config()
Specifying an explicit return type works great:
from ..config import Config
def get_config() -> Config:
return Config()
Specifying the type under TYPE_CHECKING fails:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from ..config import Config
def get_config() -> "Config":
from ..config import Config
return Config()
It fails with this error:
self = <fast_depends.core.model.CallModel object at 0x1045a8300>
value = Config(...)
def _cast_response(self, /, value: Any) -> Any:
if self.response_model is not None:
> return self.response_model(response=value).response
E pydantic.errors.PydanticUserError: `ResponseModel` is not fully defined; you should define `Config`, then call `ResponseModel.model_rebuild()`.
E
E For further information visit https://errors.pydantic.dev/2.10/u/class-not-fully-defined
.venv/lib/python3.12/site-packages/fast_depends/core/model.py:325: PydanticUserError
Is this use case supported?
For my application, config.py does some heavy lifting that I'm trying to mock in my tests, therefore I'm trying to avoid loading it unless the file is explicitly called, but I want to reference the type.
In Russian, this is called: "пиздец."
If we're speaking more seriously, you simply shouldn't do it this way.
Just like you shouldn't use if TYPE_CHECKING at all unless you need to work around circular imports (despite the opposite recommendation from tools like Ruff, for example).
Use:
from ..config import Config
def get_config() -> Config:
return Config()
if TYPE_CHECKING is sometimes used to hide types from runtime, keeping them available for typecheckers. It leads to a difference between checked behavior and runtime behavior, so it is not recommended as a general practice.
In modern frameworks, such as FastStream, types are essential part of runtime, so such hacks are expected to break you code, which you can observe
Understood. Are there drawbacks to omitting the types in order to leverage dependency injection from FastDepends? i.e.:
@inject
async def foo(
config = Depends(get_config),
):
We have a large existing app so refactoring away from if TYPE_CHECKING is not feasible.
From my perspective, you do not need get_config function as a Depends argument, you need some stub or class itself. In my experience, I did it this way:
# handlers.py
def get_config_stub():
raise NotImplementedError # actual body
async def foo(
config = Depends(get_config_stub),
):
...
# main.py
def get_config() -> Config:
return Config()
app.dependency_overrides[get_config_stub] = get_config
Also I made a special Stub class as a unification of this approach https://gist.github.com/Tishka17/9b2625753c80681e8ba688c84d834bb6