Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conflict with "from __future__ import annotations" #369

Closed
ivanovmg opened this issue Feb 17, 2025 · 2 comments · Fixed by #373
Closed

Conflict with "from __future__ import annotations" #369

ivanovmg opened this issue Feb 17, 2025 · 2 comments · Fixed by #373
Labels
bug Something isn't working

Comments

@ivanovmg
Copy link
Contributor

Hello! Thank you for this useful library!

I found an issue (maybe I am doing something wrong, though) when using Dishka with FastAPI if the code in the module with the routes contains from __future__ import annotations.

Here is a minimal working example, consisting of main.py, use_case.py and entity_router.py.

main.py
from __future__ import annotations

from dishka import (
    Provider,
    Scope,
    make_async_container,
)
from dishka.integrations.fastapi import setup_dishka
from fastapi import FastAPI

from .entity_router import router
from .use_case import RegisterNewEntityUseCase


def create_app():
    app = FastAPI()
    app.include_router(router, prefix='/entities')
    return app


service_provider = Provider(scope=Scope.REQUEST)
service_provider.provide(RegisterNewEntityUseCase)
container = make_async_container(service_provider)

app = create_app()
setup_dishka(container, app)
use_case.py
from __future__ import annotations

from dataclasses import dataclass

@dataclass(frozen=True)
class RegisterNewEntityInputDto:
    title: str
    content: bytes


@dataclass
class RegisterNewEntityUseCase:
    def execute(self, input_dto: RegisterNewEntityInputDto) -> None:
        self.presented_content = {
            'title': input_dto.title,
            'content': input_dto.content.decode(),
        }
entity_router.py
# from __future__ import annotations
# When previous line is uncommented, then the application fails

from typing import Annotated

from dishka.integrations.fastapi import (
    FromDishka,
    inject,
)
from fastapi import (
    APIRouter,
    Form,
    UploadFile,
)
from pydantic import BaseModel

from .use_case import (
    RegisterNewEntityInputDto,
    RegisterNewEntityUseCase,
)

router = APIRouter()

class RegisterNewEntityOutputDto(BaseModel):
    title: str
    content: str


@router.post('/')
@inject
async def register_new_entity(
    title: Annotated[str, Form()],
    file: UploadFile,
    use_case: FromDishka[RegisterNewEntityUseCase],
) -> RegisterNewEntityOutputDto:
    file_bytes = await file.read()
    use_case.execute(
        RegisterNewEntityInputDto(
            title=title,
            content=file_bytes,
        ),
    )
    return RegisterNewEntityOutputDto(**use_case.presented_content)

If entity_router.py contains from __future__ import annotations, then the application fails on startup.

$ fastapi run main.py
...
FastAPIError: Invalid args for response field! Hint: check that ForwardRef('UploadFile') is a valid Pydantic field type. If you are using a return type annotation that is not a valid Pydantic field (e.g. Union[Response, dict, None]) you can disable generating the response model from the type annotation with the path operation decorator parameter response_model=None. Read more: https://fastapi.tiangolo.com/tutorial/response-model/

System info:

└➜ uname -r
5.15.153.1-microsoft-standard-WSL2
└➜ python --version
Python 3.12.3
└➜ pip list
Package           Version
----------------- ---------
annotated-types   0.7.0
anyio             4.8.0
certifi           2025.1.31
click             8.1.8
dishka            1.4.2
dnspython         2.7.0
email_validator   2.2.0
fastapi           0.115.8
fastapi-cli       0.0.7
h11               0.14.0
httpcore          1.0.7
httptools         0.6.4
httpx             0.28.1
idna              3.10
Jinja2            3.1.5
markdown-it-py    3.0.0
MarkupSafe        3.0.2
mdurl             0.1.2
pip               24.0
pydantic          2.10.6
pydantic_core     2.27.2
Pygments          2.19.1
python-dotenv     1.0.1
python-multipart  0.0.20
PyYAML            6.0.2
rich              13.9.4
rich-toolkit      0.13.2
shellingham       1.5.4
sniffio           1.3.1
starlette         0.45.3
typer             0.15.1
typing_extensions 4.12.2
uvicorn           0.34.0
uvloop            0.21.0
watchfiles        1.0.4
websockets        15.0
@Tishka17 Tishka17 added the bug Something isn't working label Feb 20, 2025
@Tishka17
Copy link
Member

More traceback

  File "/home/tishka17/src/dishka/tmp/q1/entity_router.py", line 29, in <module>
    @router.post('/')
     ^^^^^^^^^^^^^^^^
  File "/home/tishka17/src/dishka/venv/lib/python3.11/site-packages/fastapi/routing.py", line 956, in decorator
    self.add_api_route(
  File "/home/tishka17/src/dishka/venv/lib/python3.11/site-packages/fastapi/routing.py", line 895, in add_api_route
    route = route_class(
            ^^^^^^^^^^^^
  File "/home/tishka17/src/dishka/venv/lib/python3.11/site-packages/fastapi/routing.py", line 525, in __init__
    self.dependant = get_dependant(path=self.path_format, call=self.endpoint)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/tishka17/src/dishka/venv/lib/python3.11/site-packages/fastapi/dependencies/utils.py", line 261, in get_dependant
    type_annotation, depends, param_field = analyze_param(
                                            ^^^^^^^^^^^^^^
  File "/home/tishka17/src/dishka/venv/lib/python3.11/site-packages/fastapi/dependencies/utils.py", line 442, in analyze_param
    field = create_response_field(
            ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/tishka17/src/dishka/venv/lib/python3.11/site-packages/fastapi/utils.py", line 101, in create_response_field
    raise fastapi.exceptions.FastAPIError(
fastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that ForwardRef('UploadFile') is a valid Pydantic field type. If you are using a return type annotation that is not a valid Pydantic field (e.g. Union[Response, dict, None]) you can disable generating the response model from the type annotation with the path operation decorator parameter response_model=None. Read more: https://fastapi.tiangolo.com/tutorial/response-model/

@Tishka17
Copy link
Member

Looks like a bug in fastapi

Here it should be using typing.get_type_hints() instead of his custom logic
https://github.com/fastapi/fastapi/blob/master/fastapi/dependencies/utils.py#L239

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
2 participants