FastDepends icon indicating copy to clipboard operation
FastDepends copied to clipboard

`ResponseModel` is not fully defined

Open keviddles opened this issue 9 months ago • 5 comments

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.

keviddles avatar Mar 13 '25 21:03 keviddles

In Russian, this is called: "пиздец."

deus-developer avatar Mar 13 '25 21:03 deus-developer

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()

deus-developer avatar Mar 13 '25 21:03 deus-developer

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

Tishka17 avatar Mar 13 '25 21:03 Tishka17

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.

keviddles avatar Mar 14 '25 15:03 keviddles

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

Tishka17 avatar Mar 14 '25 16:03 Tishka17