You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
e.g. Union[IntEnum, int] always gives int, enums with string values in unions with str types always give str, etc.
I tried lots of ways of defining a Union type where an Enum is one option and the Enum values' primitive type is the other option. None of these attempts worked, so I thought I'd ask here in case I'm missing something obvious or not so obvious.
i.e. the union type never uses the enum, even though when you take the option of the primitive int type out of the union it validates to it.
A Literal works instead of an enum, but Enums are documented as if they're valid for representing multiple choices, so I'm surprised that this isn't working. If I have to write a manual field validator it feels like the type annotation machinery that Pydantic uses so well is going to waste.
This also prevents 'splitting' models based on a union annotation: as shown here, Literal would let you choose a different model to handle binary numbers but an Enum will never be used so would effectively produce a single 'un-split' model.
from enum import Enum
from typing import Literal, Union
from pydantic import RootModel, TypeAdapter
class BinaryEnum(Enum):
ZERO = 0
ONE = 1
class EnumRootModel(RootModel):
root: BinaryEnum
class LiteralRootModel(RootModel):
root: Literal[0, 1]
LiteralUnion = Union[LiteralRootModel, int]
EnumUnion = Union[EnumRootModel, int]
print("Using Literal root model:")
print(TypeAdapter(list[LiteralUnion]).validate_python([1, 2]))
print()
print("Using Enum root model:")
print(TypeAdapter(list[EnumUnion]).validate_python([1, 2]))
Using Literal root model:
[LiteralRootModel(root=1), 2]
Using Enum root model:
[1, 2]
Using a RootModel with int-type root produces the same result
I find this difference in behaviour counterintuitive to the point that I'm inclined to think it's a bug.
When I used datamodel-code-generator my memory was that you could choose between Literals and Enums interchangeably there, so this might be impacting models produced there I'm not sure.
v2 does multiple passes on unions. the first without coercion and if none of the options match it does another pass with coercion
Would that description maybe explain this case? Is the first pass trying to match str type, and finding a str in an enum value, so not resorting to the 2nd pass to coerce the str value to Enum?
I’m not familiar with the internals of this routine so don’t know where to look to confirm or not.
Maybe it needs a validator with __get_pydantic_core_schema__ (like some of the other types listed there)?
This bug might also be phrased as “strict coercion is always used for Union of Enum and the Enum value’s type (or subclassed type)” if so.
The “subclassed type” here meaning int for IntEnum, str for StrEnum, but the same effect is seen when using a regular Enum.
Indeed I expect you could have a regular Enum with both int and str values, and a Union with int and str would likewise resolve to the primitive types.
Initial Checks
Description
e.g.
Union[IntEnum, int]
always givesint
, enums with string values in unions withstr
types always givestr
, etc.I tried lots of ways of defining a Union type where an Enum is one option and the Enum values' primitive type is the other option. None of these attempts worked, so I thought I'd ask here in case I'm missing something obvious or not so obvious.
i.e. the union type never uses the enum, even though when you take the option of the primitive
int
type out of the union it validates to it.A
Literal
works instead of an enum, but Enums are documented as if they're valid for representing multiple choices, so I'm surprised that this isn't working. If I have to write a manual field validator it feels like the type annotation machinery that Pydantic uses so well is going to waste.This also prevents 'splitting' models based on a union annotation: as shown here,
Literal
would let you choose a different model to handle binary numbers but an Enum will never be used so would effectively produce a single 'un-split' model.RootModel
withint
-typeroot
produces the same resultI find this difference in behaviour counterintuitive to the point that I'm inclined to think it's a bug.
When I used
datamodel-code-generator
my memory was that you could choose between Literals and Enums interchangeably there, so this might be impacting models produced there I'm not sure.Example Code
Python, Pydantic & OS Version
Selected Assignee: @hramezani
The text was updated successfully, but these errors were encountered: