Skip to content

Pydantic model fails to generate with annotated list of models using discriminated union and typing-extensions==4.12.0 #9503

@thomascellerier

Description

@thomascellerier

Initial Checks

  • I confirm that I'm using Pydantic V2

Description

The recently released typing extensions 4.12.0 causes issues with models with a field containing an annotated list of discriminated unions.

With typing extensions less than 4.12 it works fine:

$ pip list | grep typing_extensions
typing_extensions          4.11.0
$ python test.py
items=[MyItemA(item_type='a', things=[1, 2, 3]), MyItemB(item_type='b', thing=4)]

With 4.12.0 it fails:

$ pip list | grep typing_extensions
typing_extensions          4.12.0
$ python test.py
Traceback (most recent call last):
  File "/build/test.py", line 16, in <module>
    class MyModel(BaseModel):
  File "/build/test.py", line 17, in MyModel
    items: Annotated[
           ^^^^^^^^^^
  File "/usr/local/lib/python3.12/typing.py", line 2090, in __class_getitem__
    return cls._class_getitem_inner(cls, *params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/typing.py", line 398, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/typing.py", line 2104, in _class_getitem_inner
    return _AnnotatedAlias(origin, metadata)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/typing.py", line 1997, in __init__
    super().__init__(origin, origin, name='Annotated')
  File "/usr/local/lib/python3.12/typing.py", line 1248, in __init__
    self.__parameters__ = _collect_parameters(args)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/typing_extensions.py", line 3028, in _collect_parameters
    enforce_default_ordering = _has_generic_or_protocol_as_origin()
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/typing_extensions.py", line 2955, in _has_generic_or_protocol_as_origin
    return frame.f_locals.get("origin") in {
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/typing.py", line 2023, in __hash__
    return hash((self.__origin__, self.__metadata__))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 3, in __hash__
TypeError: unhashable type: 'dict'

Note that if I remove either the inner Discriminator annotation or the outer Len annotation it works.

Example Code

from typing import Annotated, Literal
from annotated_types import Len
from pydantic import BaseModel, Field, Discriminator


class MyItemA(BaseModel):
    item_type: Literal["a"] = "a"
    things: list[int]


class MyItemB(BaseModel):
    item_type: Literal["b"] = "b"
    thing: int


class MyModel(BaseModel):
    items: Annotated[
        list[
            Annotated[
                MyItemA | MyItemB,
                Discriminator(
                    discriminator="item_type",
                    custom_error_type="invalid_union_member",
                    custom_error_message="Invalid union member",
                    custom_error_context={"discriminator": "item_type"},
                ),
            ]
        ],
        Len(min_length=1),
    ]


print(
    MyModel.model_validate(
        {
            "items": [
                {"item_type": "a", "things": [1, 2, 3]},
                {"item_type": "b", "thing": 4},
            ]
        }
    )
)

Python, Pydantic & OS Version

pydantic version: 2.7.1
        pydantic-core version: 2.18.2
          pydantic-core build: profile=release pgo=true
                 install path: /usr/local/lib/python3.12/site-packages/pydantic
               python version: 3.12.3 (main, May 14 2024, 07:23:41) [GCC 12.2.0]
                     platform: Linux-6.5.0-35-generic-x86_64-with-glibc2.36
             related packages: typing_extensions-4.12.0 mypy-1.10.0 pydantic-settings-2.2.1
                       commit: unknown

Metadata

Metadata

Assignees

Labels

bug V2Bug related to Pydantic V2

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions