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

Pydantic v2, dataclasses, UUID, and __annotations__ #10007

Open
1 task done
tiangolo opened this issue Aug 4, 2023 · 16 comments
Open
1 task done

Pydantic v2, dataclasses, UUID, and __annotations__ #10007

tiangolo opened this issue Aug 4, 2023 · 16 comments
Labels
bug Something isn't working

Comments

@tiangolo
Copy link
Owner

tiangolo commented Aug 4, 2023

Privileged issue

  • I'm @tiangolo or he asked me directly to create an issue here.

Issue Content

The combination of using:

  • Pydantic v2
  • dataclasses (instead of Pydantic models)
  • UUIDs
  • from future import __annotations__

seems to break in a strange way.

This was original reported in this discussion by @sanzoghenzo: #9709 (comment)

In particular the comment by @raddevon with the minimal example by @fantix:

from __future__ import annotations

import uuid
from dataclasses import dataclass, field
from typing import List, Union

from fastapi import FastAPI


@dataclass
class Item:
    id: uuid.UUID
    name: str
    price: float
    tags: List[str] = field(default_factory=list)
    description: Union[str, None] = None
    tax: Union[float, None] = None


app = FastAPI()


@app.get("/items/next", response_model=Item)
async def read_next_item():
    return {
        "name": "Island In The Moon",
        "price": 12.99,
        "description": "A place to be be playin' and havin' fun",
        "tags": ["breater"],
    }

Starting FastAPI with that breaks with an error of:

log
uvicorn main:app
Traceback (most recent call last):
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/type_adapter.py", line 165, in __init__
    core_schema = _getattr_no_parents(type, '__pydantic_core_schema__')
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/type_adapter.py", line 97, in _getattr_no_parents
    raise AttributeError(attribute)
AttributeError: __pydantic_core_schema__

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 625, in _resolve_forward_ref
    obj = _typing_extra.evaluate_fwd_ref(obj, globalns=self._types_namespace)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_typing_extra.py", line 423, in evaluate_fwd_ref
    return ref._evaluate(globalns=globalns, localns=localns, recursive_guard=frozenset())
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/typing.py", line 694, in _evaluate
    eval(self.__forward_code__, globalns, localns),
  File "<string>", line 1, in <module>
NameError: name 'uuid' is not defined

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/user/code/fastapi/env3.10/bin/uvicorn", line 8, in <module>
    sys.exit(main())
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/uvicorn/main.py", line 410, in main
    run(
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/uvicorn/main.py", line 578, in run
    server.run()
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/uvicorn/server.py", line 61, in run
    return asyncio.run(self.serve(sockets=sockets))
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "uvloop/loop.pyx", line 1517, in uvloop.loop.Loop.run_until_complete
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/uvicorn/server.py", line 68, in serve
    config.load()
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/uvicorn/config.py", line 473, in load
    self.loaded_app = import_from_string(self.app)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/uvicorn/importer.py", line 21, in import_from_string
    module = importlib.import_module(module_str)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/Users/user/code/fastapi/main.py", line 24, in <module>
    async def read_next_item():
  File "/Users/user/code/fastapi/fastapi/routing.py", line 706, in decorator
    self.add_api_route(
  File "/Users/user/code/fastapi/fastapi/routing.py", line 645, in add_api_route
    route = route_class(
  File "/Users/user/code/fastapi/fastapi/routing.py", line 448, in __init__
    self.response_field = create_response_field(
  File "/Users/user/code/fastapi/fastapi/utils.py", line 99, in create_response_field
    return ModelField(**kwargs)  # type: ignore[arg-type]
  File "<string>", line 6, in __init__
  File "/Users/user/code/fastapi/fastapi/_compat.py", line 101, in __post_init__
    self._type_adapter: TypeAdapter[Any] = TypeAdapter(
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/type_adapter.py", line 167, in __init__
    core_schema = _get_schema(type, config_wrapper, parent_depth=_parent_depth + 1)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/type_adapter.py", line 80, in _get_schema
    schema = gen.generate_schema(type_)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 425, in generate_schema
    return self._annotated_schema(obj)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 1464, in _annotated_schema
    schema = self._apply_annotations(source_type, annotations)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 1586, in _apply_annotations
    schema = get_inner_schema(source_type)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_schema_generation_shared.py", line 82, in __call__
    schema = self._handler(__source_type)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 1679, in new_handler
    schema = metadata_get_schema(source, get_inner_schema)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 1675, in <lambda>
    lambda source, handler: handler(source)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_schema_generation_shared.py", line 82, in __call__
    schema = self._handler(__source_type)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 1550, in inner_handler
    schema = self._generate_schema(obj)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 689, in _generate_schema
    return self.match_type(obj)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 763, in match_type
    return self._dataclass_schema(obj, None)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 1317, in _dataclass_schema
    args = sorted(
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 1318, in <genexpr>
    (self._generate_dc_field_schema(k, v, decorators) for k, v in fields.items()),
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 865, in _generate_dc_field_schema
    common_field = self._common_field_schema(name, field_info, decorators)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 900, in _common_field_schema
    schema = self._apply_annotations(
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 1586, in _apply_annotations
    schema = get_inner_schema(source_type)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_schema_generation_shared.py", line 82, in __call__
    schema = self._handler(__source_type)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 1550, in inner_handler
    schema = self._generate_schema(obj)
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 679, in _generate_schema
    return self.generate_schema(self._resolve_forward_ref(obj))
  File "/Users/user/code/fastapi/env3.10/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 627, in _resolve_forward_ref
    raise PydanticUndefinedAnnotation.from_name_error(e) from e
pydantic.errors.PydanticUndefinedAnnotation: name 'uuid' is not defined

For further information visit https://errors.pydantic.dev/2.1.1/u/undefined-annotation

I still don't know if it's about which of these multiple interacting parts, I'm creating this issue to keep track of it.

@tiangolo tiangolo added the bug Something isn't working label Aug 4, 2023
@MaquinaTech
Copy link

Maybe using an id of type str can solve the problem...

class Item:
id: str # Using string instead of uuid.UUID
name: str
price: float
tags: List[str] = field(default_factory=list)
description: Union[str, None] = None
tax: Union[float, None] = None

@xouyang1
Copy link

xouyang1 commented Aug 6, 2023

It looks like forward references are resolved from namespaces built from the previous 2 frames
https://github.com/pydantic/pydantic/blob/adc657a8b9f0b5191c180fc51005c0bf1fe529db/pydantic/type_adapter.py#L76

@harunyasar
Copy link

harunyasar commented Aug 9, 2023

You could use Pydantic's dataclass extension. It doesn't just give you the same data validation features but also fix your issue.

from __future__ import annotations

from typing import List, Union
import uuid

from pydantic.dataclasses import dataclass, Field

@dataclass
class Item:
    id: uuid.UUID
    name: str
    price: float
    tags: List[str] = Field(default_factory=list)
    description: Union[str, None] = None
    tax: Union[float, None] = None

item = Item(
    id=uuid.uuid4(),
    name="John",
    price=666.66,
)

print(item)

# IItem(id=UUID('c399d5c9-c2bf-4e5f-94f9-21a7a5f78610'), name='John', price=666.66, tags=[], description=None, tax=None)

@tiangolo
Copy link
Owner Author

tiangolo commented Aug 14, 2023

This would need to be handled on the Pydantic side (or at least require some input/changes from them). pydantic/pydantic#7111

As @harunyasar mentions, there's a simple workaround, to use Pydantic's flavor of dataclasses instead of the standard library ones.

from pydantic.dataclasses import dataclass

I understand this is one of the things that begs for PEP 649 (Deferred Evaluation Of Annotations Using Descriptors) that will hopefully come to Python 3.13 🎉

@vsoch
Copy link

vsoch commented Aug 19, 2023

I'm not sure if this is related, but I'm hitting:

==================================== ERRORS ====================================
______________________ ERROR collecting tests/test_api.py ______________________
ImportError while importing test module '/__w/flux-restful-api/flux-restful-api/tests/test_api.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.8/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests/test_api.py:6: in <module>
    from fastapi.testclient import TestClient
/usr/local/lib/python3.8/dist-packages/fastapi/__init__.py:7: in <module>
    from .applications import FastAPI as FastAPI
/usr/local/lib/python3.8/dist-packages/fastapi/applications.py:3: in <module>
    from fastapi import routing
/usr/local/lib/python3.8/dist-packages/fastapi/routing.py:20: in <module>
    from fastapi import params
/usr/local/lib/python3.8/dist-packages/fastapi/params.py:4: in <module>
    from pydantic.fields import FieldInfo, Undefined
E   ImportError: cannot import name 'Undefined' from 'pydantic.fields' (/usr/local/lib/python3.8/dist-packages/pydantic/fields.py)

I've installed the lastest fastapi and pydantic, so I suspect this is a new issue! I think probably the only way to fix this now is to fall back to a much older pydantic. Also @tiangolo your use of emojis in commits is pure 🔥 I love looking at your code! 🙌

@vsoch
Copy link

vsoch commented Aug 19, 2023

I actually can't find a pairing that works! I'm going to look in an older container (where it did work) and cross my fingers I can reproduce that! 🤞

@vsoch
Copy link

vsoch commented Aug 19, 2023

Yeah this is strange - when I go to previously working versions I still get:

from pydantic.fields import FieldInfo, Undefined
ImportError: cannot import name 'Undefined' from 'pydantic.fields' (/env/lib/python3.8/site-packages/pydantic/fields.py)

@johnthagen
Copy link
Contributor

johnthagen commented Aug 28, 2023

When I tried to update from:

  • fastapi (0.101.1 -> 0.103.0)
  • pydantic (1.10.12 -> 2.3.0)

A route that uses a stdlib @dataclass as the return type annotation, started throwing an exception.

pydantic.errors.PydanticUndefinedAnnotation: name 'MyDataclass' is not defined

MyDataclass is defined in another module.

from other.path import MyDataclass

@router.post("/route/")
def route() -> MyDataclass:
    ...

In my case, it appears that if any of the nested @dataclasses referenced by the top level MyDataclass are defined in a module with from __future__ import annotations, then this error will be thrown by one of them.

The reason we are using from __future__ import annotations is that many of these models use forward references themselves, so this avoids the (IMO ugiler) manually stringified syntax. We try to avoid forward references as much as possible (using Self on @classmethods whenever possible, for example) but there are some instances where it can't be avoided (e.g., a normal method that returns a new instance of itself).

There are also weird things that can happen with manually stringified type annotations:

@AhsanSheraz
Copy link
Contributor

AhsanSheraz commented Sep 14, 2023

You can use create your own data model like below:

from __future__ import annotations

import uuid
from dataclasses import dataclass, field
from typing import List, Union

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
    id: uuid.UUID
    name: str
    price: float
    tags: List[str] = field(default_factory=list)
    description: Union[str, None] = None
    tax: Union[float, None] = None


app = FastAPI()


@app.get("/items/next", response_model=Item)
async def read_next_item():
    return {
        "name": "Island In The Moon",
        "price": 12.99,
        "description": "A place to be be playin' and havin' fun",
        "tags": ["breater"],
    }

@anonymoussss
Copy link

Yeah this is strange - when I go to previously working versions I still get:

from pydantic.fields import FieldInfo, Undefined
ImportError: cannot import name 'Undefined' from 'pydantic.fields' (/env/lib/python3.8/site-packages/pydantic/fields.py)

Hi~have you found a solution?

@Kludex
Copy link
Sponsor Collaborator

Kludex commented Sep 15, 2023

Yeah this is strange - when I go to previously working versions I still get:

from pydantic.fields import FieldInfo, Undefined
ImportError: cannot import name 'Undefined' from 'pydantic.fields' (/env/lib/python3.8/site-packages/pydantic/fields.py)

Hi~have you found a solution?

I think a bump should solve the issue - either FastAPI or Pydantic.

@anonymoussss
Copy link

Yeah this is strange - when I go to previously working versions I still get:

from pydantic.fields import FieldInfo, Undefined
ImportError: cannot import name 'Undefined' from 'pydantic.fields' (/env/lib/python3.8/site-packages/pydantic/fields.py)

Hi~have you found a solution?

I think a bump should solve the issue - either FastAPI or Pydantic.

Right. I have solved this issue by "pip install pydantic==1.9.0"

@Kludex
Copy link
Sponsor Collaborator

Kludex commented Sep 15, 2023

I said bump, not downgrade. 😅

@dineshbvadhia
Copy link

This might also be related.

Python 3.11x plus latest FastApi and Pydantic V2.

Replaced BaseModel with TypedDict as don't want Pydantic models or validation. Using typing_extensions.TypedDict as Python < 3.12. Yet, get error in endpoint:

@router.get("/getthis", response_class=JSONResponse, response_model = Union[Done, Problem])

Traceback (most recent call last):
  File "...\Python311\Lib\site-packages\pydantic\type_adapter.py", line 239, in __init__
    core_schema = _getattr_no_parents(type, '__pydantic_core_schema__')
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Python311\Lib\site-packages\pydantic\type_adapter.py", line 173, in _getattr_no_parents
    raise AttributeError(attribute)
AttributeError: __pydantic_core_schema__

@MichaelAReed
Copy link

I'm just chiming into this conversation to share an issue I had with this recently and what I did to solve it in case it helps with someone else chasing this down.

I have separated my models, controllers and routers into separate modules - I was trying to create a dependable to extract a provided auth token, pull the appropriate user from my db and then return it as a pydantic User object. This meant that I wanted to reference the dependable in my router module as a method of the controller class held as an instance variable.

So something like this:

class UserRouter:
    def __init__(self, db: DynamoDBServiceResource) -> None:
        self.__controller = UserController(db)

    @property
    def router(self) -> APIRouter:
        api_router = APIRouter(prefix="/user", tags=["user"])

        @api_router.get("/my-details")
        async def get_my_details(
            requesting_user: Annotated[User, Depends(self.__controller.get_requesting_user)]
        ) -> User:
            logger.info(f"Retrieving user details for user: {requesting_user}")
            return requesting_user

This resulted in an error as below:

Error log
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/pydantic/type_adapter.py", line 207, in __init__
    core_schema = _getattr_no_parents(type, '__pydantic_core_schema__')
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/type_adapter.py", line 98, in _getattr_no_parents
    raise AttributeError(attribute)
AttributeError: __pydantic_core_schema__

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_generate_schema.py", line 677, in _resolve_forward_ref
    obj = _typing_extra.eval_type_backport(obj, globalns=self._types_namespace)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_typing_extra.py", line 240, in eval_type_backport
    return typing._eval_type(  # type: ignore
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/typing.py", line 400, in _eval_type
    return t._evaluate(globalns, localns, recursive_guard)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/typing.py", line 907, in _evaluate
    eval(self.__forward_code__, globalns, localns),
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 1, in <module>
NameError: name 'User' is not defined

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.12/multiprocessing/process.py", line 314, in _bootstrap
    self.run()
  File "/usr/local/lib/python3.12/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/home/dev_user/.local/lib/python3.12/site-packages/uvicorn/_subprocess.py", line 78, in subprocess_started
    target(sockets=sockets)
  File "/home/dev_user/.local/lib/python3.12/site-packages/uvicorn/server.py", line 62, in run
    return asyncio.run(self.serve(sockets=sockets))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/runners.py", line 194, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/base_events.py", line 685, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/home/dev_user/.local/lib/python3.12/site-packages/uvicorn/server.py", line 69, in serve
    config.load()
  File "/home/dev_user/.local/lib/python3.12/site-packages/uvicorn/config.py", line 433, in load
    self.loaded_app = import_from_string(self.app)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dev_user/.local/lib/python3.12/site-packages/uvicorn/importer.py", line 19, in import_from_string
    module = importlib.import_module(module_str)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/importlib/__init__.py", line 90, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 995, in exec_module
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "/workspaces/drip_backend/src/main.py", line 81, in <module>
    app.include_router(user_router.router)
                       ^^^^^^^^^^^^^^^^^^
  File "/workspaces/drip_backend/src/user/user_router.py", line 69, in router
    @api_router.get("/my-details")
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dev_user/.local/lib/python3.12/site-packages/fastapi/routing.py", line 944, in decorator
    self.add_api_route(
  File "/home/dev_user/.local/lib/python3.12/site-packages/fastapi/routing.py", line 883, in add_api_route
    route = route_class(
            ^^^^^^^^^^^^
  File "/home/dev_user/.local/lib/python3.12/site-packages/fastapi/routing.py", line 513, in __init__
    self.dependant = get_dependant(path=self.path_format, call=self.endpoint)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dev_user/.local/lib/python3.12/site-packages/fastapi/dependencies/utils.py", line 261, in get_dependant
    type_annotation, depends, param_field = analyze_param(
                                            ^^^^^^^^^^^^^^
  File "/home/dev_user/.local/lib/python3.12/site-packages/fastapi/dependencies/utils.py", line 442, in analyze_param
    field = create_response_field(
            ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dev_user/.local/lib/python3.12/site-packages/fastapi/utils.py", line 99, in create_response_field
    return ModelField(**kwargs)  # type: ignore[arg-type]
           ^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 6, in __init__
  File "/home/dev_user/.local/lib/python3.12/site-packages/fastapi/_compat.py", line 107, in __post_init__
    self._type_adapter: TypeAdapter[Any] = TypeAdapter(
                                           ^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/type_adapter.py", line 209, in __init__
    core_schema = _get_schema(type, config_wrapper, parent_depth=_parent_depth + 1)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/type_adapter.py", line 81, in _get_schema
    schema = gen.generate_schema(type_)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_generate_schema.py", line 499, in generate_schema
    schema = self._generate_schema(obj)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_generate_schema.py", line 737, in _generate_schema
    schema = self._post_process_generated_schema(self._generate_schema_inner(obj))
                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_generate_schema.py", line 744, in _generate_schema_inner
    return self._annotated_schema(obj)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_generate_schema.py", line 1675, in _annotated_schema
    source_type, *annotations = self._get_args_resolving_forward_refs(
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_generate_schema.py", line 701, in _get_args_resolving_forward_refs
    args = tuple([self._resolve_forward_ref(a) if isinstance(a, ForwardRef) else a for a in args])
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pydantic/_internal/_generate_schema.py", line 679, in _resolve_forward_ref
    raise PydanticUndefinedAnnotation.from_name_error(e) from e
pydantic.errors.PydanticUndefinedAnnotation: name 'User' is not defined

So I figured there was an issue with how the type forward refs were being resolved and I quoted the User references. This led to a very similar error but with pydantic.errors.PydanticUndefinedAnnotation: name 'Depends' is not defined.

So, lastly I tried removing the Annotated type and set Depends directly as the default value:

        @api_router.get("/my-details")
        async def get_my_details(
            requesting_user: User = Depends(self.__controller.get_requesting_user),
        ) -> User:
            # requesting_user = self.__controller.get_item({"id": requesting_user_id})
            logger.info(f"Retrieving user details for user: {requesting_user}")
            return requesting_user

This now seems to work fine.

I presume the issue is something to do with when Annotated gets resolved as opposed to defining it as a standard default value. Certainly did have me floundering for a while, I would loved to have seen this mentioned somewhere in the otherwise flawless FastAPI docs (please point me to it if it's already there).

@PaleNeutron
Copy link

I think my problem is related to this issue, all code comes from document but I can not resolve it.

#11423

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
Development

No branches or pull requests