-
-
Notifications
You must be signed in to change notification settings - Fork 113
Closed
Labels
bugSomething isn't workingSomething isn't working
Description
According to Python's Document Type aliases, type aliases can be created through simple assignment or using the type statement. The two ways should be equivalent. However, as the example code below, If a field's annotation is a type aliases defined by using the type statement, the NoDecode won't working.
Example Code
from typing import Any, Annotated
from pydantic import BeforeValidator
from pydantic_settings import NoDecode, BaseSettings
def _parse_comma_sep_str_list(value: Any) -> list[str]:
if not value:
return []
if isinstance(value, str):
return value.split(',')
return value
CommaSepStrListA = Annotated[list[str], NoDecode, BeforeValidator(_parse_comma_sep_str_list)]
type CommaSepStrListB = Annotated[list[str], NoDecode, BeforeValidator(_parse_comma_sep_str_list)]
class MySettingsA(BaseSettings):
enabled_options: CommaSepStrListA = ["A", "B", "C", "D"]
class MySettingsB(BaseSettings):
enabled_options: CommaSepStrListB = ["A", "B", "C", "D"]
if __name__ == "__main__":
import os
os.environ["ENABLED_OPTIONS"] = "A,B,C,D"
my_settings_a = MySettingsA()
print(my_settings_a)
my_settings_b = MySettingsB()
print(my_settings_b)The result
MySettingsA works well, and MySettingsB will try to parse the enabled options as JSON.
enabled_options=['A', 'B', 'C', 'D']
Traceback (most recent call last):
File "E:\pythonProject\.venv\Lib\site-packages\pydantic_settings\sources\base.py", line 510, in __call__
field_value = self.prepare_field_value(field_name, field, field_value, value_is_complex)
File "E:\pythonProject\.venv\Lib\site-packages\pydantic_settings\sources\providers\env.py", line 123, in prepare_field_value
raise e
File "E:\pythonProject\.venv\Lib\site-packages\pydantic_settings\sources\providers\env.py", line 120, in prepare_field_value
value = self.decode_complex_value(field_name, field, value)
File "E:\pythonProject\.venv\Lib\site-packages\pydantic_settings\sources\base.py", line 187, in decode_complex_value
return json.loads(value)
~~~~~~~~~~^^^^^^^
File "C:\Program Files\Python313\Lib\json\__init__.py", line 346, in loads
return _default_decoder.decode(s)
~~~~~~~~~~~~~~~~~~~~~~~^^^
File "C:\Program Files\Python313\Lib\json\decoder.py", line 345, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\Python313\Lib\json\decoder.py", line 363, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "E:\pythonProject\my_settings.py", line 31, in <module>
my_settings_b = MySettingsB()
File "E:\pythonProject\.venv\Lib\site-packages\pydantic_settings\main.py", line 195, in __init__
**__pydantic_self__._settings_build_values(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
values,
^^^^^^^
...<25 lines>...
_secrets_dir=_secrets_dir,
^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "E:\pythonProject\.venv\Lib\site-packages\pydantic_settings\main.py", line 438, in _settings_build_values
source_state = source()
File "E:\pythonProject\.venv\Lib\site-packages\pydantic_settings\sources\base.py", line 512, in __call__
raise SettingsError(
f'error parsing value for field "{field_name}" from source "{self.__class__.__name__}"'
) from e
pydantic_settings.exceptions.SettingsError: error parsing value for field "enabled_options" from source "EnvSettingsSource"
Expected Behavior
enabled_options=['A', 'B', 'C', 'D']
enabled_options=['A', 'B', 'C', 'D']
Additional Info
However, if I instantiate the BaseSettings, like instantiate the BaseModel in Pydantic, instead of setting the env, it works as expected.
if __name__ == "__main__":
my_settings_a = MySettingsA(enabled_options="A,B,C,D")
print(my_settings_a)
my_settings_b = MySettingsB(enabled_options="A,B,C,D")
print(my_settings_b)The result:
enabled_options=['A', 'B', 'C', 'D']
enabled_options=['A', 'B', 'C', 'D']
When I print the FieldInfo of MySettingsA and MySettingsB, I found they have different FieldInfo, it seems metadata was lost in MySettingsB. However, BeforeValidator works well in MySettingsB.
if __name__ == "__main__":
print(MySettingsA.model_fields.items())
print(MySettingsB.model_fields.items())dict_items([('enabled_options', FieldInfo(annotation=list[str], required=False, default=['A', 'B', 'C', 'D'], metadata=[<class 'pydantic_settings.sources.types.NoDecode'>, BeforeValidator(func=<function _parse_comma_sep_str_list at 0x0000024FA30B3B00>, json_schema_input_type=PydanticUndefined)]))])
dict_items([('enabled_options', FieldInfo(annotation=CommaSepStrListB, required=False, default=['A', 'B', 'C', 'D']))])
Python, Pydantic & OS Version
python -c "import pydantic.version; print(pydantic.version.version_info())"
pydantic version: 2.12.4
pydantic-core version: 2.41.5
pydantic-core build: profile=release pgo=false
python version: 3.13.9 (tags/v3.13.9:8183fa5, Oct 14 2025, 14:09:13) [MSC v.1944 64 bit (AMD64)]
platform: Windows-10-10.0.19045-SP0
related packages: pydantic-settings-2.12.0 typing_extensions-4.15.0
commit: unknown
``
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working