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

ODMantic schema generation fails with discriminated unions #509

Open
MickVermeulen opened this issue Dec 5, 2024 · 0 comments
Open

ODMantic schema generation fails with discriminated unions #509

MickVermeulen opened this issue Dec 5, 2024 · 0 comments
Labels
bug Something isn't working

Comments

@MickVermeulen
Copy link

MickVermeulen commented Dec 5, 2024

Bug

When using discriminated unions in a ODMantic model, it raises a schema generation error where vanilla pydantic does not.

Current Behavior

from odmantic import Model, SyncEngine
from pydantic import Field, BaseModel
from typing import Literal, TypeAlias, Annotated


class Cat(BaseModel):
    mice_eaten: int
    pet_type: Literal["cat"] = "cat"


class Dog(BaseModel):
    balls_caught: int
    pet_type: Literal["dog"] = "dog"


Pet: TypeAlias = Annotated[
    Cat | Dog,
    Field(discriminator="pet_type"),
]


class HouseholdPydantic(BaseModel):
    pets: list[Pet]


# Uncomment this and it will crash
# class HouseholdODMantic(Model):
#     pets: list[Pet]


if __name__ == "__main__":
    instance = HouseholdPydantic(pets=[Cat(mice_eaten=5), Dog(balls_caught=3)])
    print(instance.model_json_schema())

    # Uncomment this and it will crash
    # engine = SyncEngine()
    # instance = HouseholdODMantic(pets=[Cat(mice_eaten=5), Dog(balls_caught=3)])
    # print(instance.model_json_schema())
    # engine.save(instance)

If you uncomment the commented out lines, this will raise:

Traceback (most recent call last):
  File "/Users/user/myproject/odmantic_test.py", line 26, in <module>
    class HouseholdODMantic(Model):
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/odmantic/model.py", line 487, in __new__
    return super().__new__(mcs, name, bases, namespace, **kwargs)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/odmantic/model.py", line 405, in __new__
    cls = super().__new__(mcs, name, bases, namespace, **kwargs)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_model_construction.py", line 205, in __new__
    complete_model_class(
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_model_construction.py", line 534, in complete_model_class
    schema = cls.__get_pydantic_core_schema__(cls, handler)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/main.py", line 643, in __get_pydantic_core_schema__
    return handler(source)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_schema_generation_shared.py", line 83, in __call__
    schema = self._handler(source_type)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 512, in generate_schema
    schema = self._generate_schema_inner(obj)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 784, in _generate_schema_inner
    return self._model_schema(obj)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 591, in _model_schema
    {k: self._generate_md_field_schema(k, v, decorators) for k, v in fields.items()},
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 591, in <dictcomp>
    {k: self._generate_md_field_schema(k, v, decorators) for k, v in fields.items()},
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 947, in _generate_md_field_schema
    common_field = self._common_field_schema(name, field_info, decorators)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 1134, in _common_field_schema
    schema = self._apply_annotations(
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 1890, in _apply_annotations
    schema = get_inner_schema(source_type)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_schema_generation_shared.py", line 83, in __call__
    schema = self._handler(source_type)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 1972, in new_handler
    schema = metadata_get_schema(source, get_inner_schema)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_std_types_schema.py", line 316, in __get_pydantic_core_schema__
    items_schema = handler.generate_schema(self.item_source_type)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_schema_generation_shared.py", line 97, in generate_schema
    return self._generate_schema.generate_schema(source_type)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 512, in generate_schema
    schema = self._generate_schema_inner(obj)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 789, in _generate_schema_inner
    return self.match_type(obj)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 871, in match_type
    return self._match_generic_type(obj, origin)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 919, in _match_generic_type
    return self._unknown_type_schema(obj)
  File "/Users/user/.pyenv/versions/miniconda3-latest/envs/myenv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py", line 415, in _unknown_type_schema
    raise PydanticSchemaGenerationError(
pydantic.errors.PydanticSchemaGenerationError: Unable to generate pydantic-core schema for typing.Annotated[types.UnionType[__main__.Cat, __main__.Dog], FieldInfo(annotation=NoneType, required=True, discriminator='pet_type')]. Set `arbitrary_types_allowed=True` in the model_config to ignore this error or implement `__get_pydantic_core_schema__` on your type to fully support it.

If you got this error by calling handler(<some type>) within `__get_pydantic_core_schema__` then you likely need to call `handler.generate_schema(<some type>)` since we do not call `__get_pydantic_core_schema__` on `<some type>` otherwise to avoid infinite recursion.

For further information visit https://errors.pydantic.dev/2.8/u/schema-for-unknown-type

If you keep the lines commented out, the schema prints fine.

Environment

  • Python version: 3.10
  • ODMantic version: 1.0.2
  • Pydantic version: 2.8.2, pydantic-core version: 2.20.1
@MickVermeulen MickVermeulen added the bug Something isn't working label Dec 5, 2024
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

1 participant