pydantic-settings new version can't handle scenario
Closed this issue · 2 comments
Hi All:
Recently, we get an error when we define "discriminated union" for one field within BaseSettings, and use _env_file to construct the class which was working fine before.
Enviroment
(.venv_pydantic_settings_2_5_2) user@jupyter:/abehsu/pydantic_testing$ python --version/abehsu/pydantic_testing$ pip list | grep pydantic
Python 3.10.12
(.venv_pydantic_settings_2_5_2) user@jupyter:
pydantic 2.9.2
pydantic_core 2.23.4
pydantic-settings 2.5.2
Error message
(.venv_pydantic_settings_2_5_2) user@abehsu-us-vscode:~/abehsu/pydantic_testing$ python test.py
Traceback (most recent call last):
File "/home/user/abehsu/pydantic_testing/test.py", line 40, in <module>
result = Html(_env_file="/home/user/abehsu/pydantic_testing/test.env")
File "/home/user/abehsu/pydantic_testing/.venv_pydantic_settings_2_5_2/lib/python3.10/site-packages/pydantic_settings/main.py", line 152, in __init__
super().__init__(
File "/home/user/abehsu/pydantic_testing/.venv_pydantic_settings_2_5_2/lib/python3.10/site-packages/pydantic/main.py", line 212, in __init__
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
pydantic_core._pydantic_core.ValidationError: 2 validation errors for Html
contents__el_type
Extra inputs are not permitted [type=extra_forbidden, input_value='input', input_type=str]
For further information visit https://errors.pydantic.dev/2.9/v/extra_forbidden
contents__class_name
Extra inputs are not permitted [type=extra_forbidden, input_value='123', input_type=str]
For further information visit https://errors.pydantic.dev/2.9/v/extra_forbidden
Code
from typing import Any, Literal
from pydantic import BaseModel, Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class DivModel(BaseModel):
el_type: Literal['div'] = 'div'
class_name: str | None = None
children: list[Any] | None = None
class SpanModel(BaseModel):
el_type: Literal['span'] = 'span'
class_name: str | None = None
contents: str | None = None
class ButtonModel(BaseModel):
el_type: Literal['button'] = 'button'
class_name: str | None = None
contents: str | None = None
class InputModel(BaseModel):
el_type: Literal['input'] = 'input'
class_name: str | None = None
value: str | None = None
class Html(BaseSettings):
contents: DivModel | SpanModel | ButtonModel | InputModel = Field(
discriminator='el_type'
)
model_config = SettingsConfigDict(
env_nested_delimiter="__",
)
result = Html(_env_file="/home/user/abehsu/pydantic_testing/test.env")
print(result)
contents__el_type=input
contents__class_name=123
==================================================
The same code can successfully if i use pydantic-settings 2.4.0 version.
(.venv_pydantic_settings_2_4_0) user@abehsu-us-vscode:~/abehsu/pydantic_testing$ python --version
Python 3.10.12
(.venv_pydantic_settings_2_4_0) user@abehsu-us-vscode:~/abehsu/pydantic_testing$ pip list | grep pydantic
pydantic 2.8.2
pydantic_core 2.20.1
pydantic-extra-types 2.9.0
pydantic-settings 2.4.0
(.venv_pydantic_settings_2_4_0) user@abehsu-us-vscode:~/abehsu/pydantic_testing$ python test.py
contents=InputModel(el_type='input', class_name='123', value=None)
After comparison, i find out it is because this line be added from last week's release (https://github.com/pydantic/pydantic-settings/blob/main/pydantic_settings/sources.py#L989) by this PR (287cb22), are there any suggestion?
def lenient_issubclass(cls: Any, class_or_tuple: Any) -> bool: # pragma: no cover
try:
return isinstance(cls, type) and issubclass(cls, class_or_tuple)
except TypeError:
if isinstance(cls, _typing_extra.WithArgsTypes):
return False
raise # pragma: no cover
(Pdb) l
75
76 def lenient_issubclass(cls: Any, class_or_tuple: Any) -> bool: # pragma: no cover
77 try:
78 if cls != {}:
79 breakpoint()
80 -> return isinstance(cls, type) and issubclass(cls, class_or_tuple)
81 except TypeError:
82 if isinstance(cls, _typing_extra.WithArgsTypes):
83 return False
84 raise # pragma: no cover
85
(Pdb) cls
__main__.DivModel | __main__.SpanModel | __main__.ButtonModel | __main__.InputModel
(Pdb) isinstance(cls, type)
False
(Pdb) type(cls)
<class 'types.UnionType'>
(Pdb) issubclass(cls, class_or_tuple)
*** TypeError: issubclass() arg 1 must be a class
Thanks @hsuyuming for reporting this bug.
I think it is a duplicate of #420
I created #423 as a fix. Could you please confirm the fix?