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

Wrong JSON Schema generation with sequence like enums #7045

Closed
1 task done
Viicos opened this issue Aug 8, 2023 · 2 comments · Fixed by #7056
Closed
1 task done

Wrong JSON Schema generation with sequence like enums #7045

Viicos opened this issue Aug 8, 2023 · 2 comments · Fixed by #7056
Assignees
Labels
bug V2 Bug related to Pydantic V2 unconfirmed Bug not yet confirmed as valid/applicable

Comments

@Viicos
Copy link
Contributor

Viicos commented Aug 8, 2023

Initial Checks

  • I confirm that I'm using Pydantic V2

Description

When using Enums with complex values, the generated JSON Schema is incorrect (e.g. this validator raises an error).

By the way, I'm trying to find a good way of serializing such enums (by their name), similar to #6466, but imho it it a bit cumbersome for a use case that could be pretty common. Do you think a helper could be integrated into Pydantic?

A (weird) alternative implementation (has the benefit of not having to subclass from a custom Enum class, as suggested in #6466):

from typing import Annotated

from pydantic import BaseModel, PlainSerializer, WrapValidator

def serialize_with_name(EnumClass: type[Enum]) -> type[Enum]:
    return Annotated[
        EnumClass,
        WrapValidator(lambda v, handler: EnumClass[v] if isinstance(v, str) else handler(v)),
        PlainSerializer(lambda el: el.name, return_type=Literal[EnumClass._member_names_], when_used="json"),   # The Literal is still valid Python
    ]

class Model(BaseModel):
    direction: serialize_with_name(Direction)

Related issue: #3174

Example Code

from enum import Enum

from pydantic import BaseModel

class Direction(Enum):
    NORTH      = (0, 1)
    NORTH_EAST = (1, 1)
    EAST       = (1, 0)
    SOUTH_EAST = (1, -1)
    SOUTH      = (0, -1)
    SOUTH_WEST = (-1, -1)
    WEST       = (-1, 0)
    NORTH_WEST = (-1, 1)

    def __init__(self, x: int, y: int) -> None:
        self.x = x
        self.y = y


class Model(BaseModel):
    direction: Direction

Model.model_json_schema()
#> {'$defs': {'Direction': {'enum': [(0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0), (-1, 1)], 'title': 'Direction'}}, 'properties': {'direction': {'$ref': '#/$defs/Direction'}}, 'required': ['direction'], 'title': 'Model', 'type': 'object'}

Python, Pydantic & OS Version

pydantic version: 2.1.1
pydantic-core version: 2.4.0
pydantic-core build: profile=release pgo=true mimalloc=true
python version: 3.10.9 (main, Jan 13 2023, 19:08:27) [GCC 11.3.0]

Selected Assignee: @hramezani

@Viicos Viicos added bug V2 Bug related to Pydantic V2 unconfirmed Bug not yet confirmed as valid/applicable labels Aug 8, 2023
@dmontagu
Copy link
Contributor

I don't in principle have any objection to providing a way to serialize/validate enums by their name, but I think whether it would be accepted would depend a bit on the implementation. I suspect it could be done without too much hassle through a custom __get_pydantic_core_schema__ but I'm not sure.

@Viicos
Copy link
Contributor Author

Viicos commented Aug 17, 2023

I don't in principle have any objection to providing a way to serialize/validate enums by their name, but I think whether it would be accepted would depend a bit on the implementation. I suspect it could be done without too much hassle through a custom __get_pydantic_core_schema__ but I'm not sure.

Ok, I'll see if I can come up with something

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug V2 Bug related to Pydantic V2 unconfirmed Bug not yet confirmed as valid/applicable
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants