Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion pydantic_settings/sources/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from pydantic._internal._utils import is_model_class
from pydantic.fields import FieldInfo
from typing_extensions import get_args
from typing_inspection import typing_objects
from typing_inspection.introspection import is_union_origin

from ..exceptions import SettingsError
Expand All @@ -25,6 +26,7 @@
_annotation_is_complex,
_get_alias_names,
_get_model_fields,
_strip_annotated,
_union_is_complex,
)

Expand Down Expand Up @@ -353,7 +355,12 @@ def _extract_field_info(self, field: FieldInfo, field_name: str) -> list[tuple[s
field_info.append((v_alias, self._apply_case_sensitive(v_alias), False))

if not v_alias or self.config.get('populate_by_name', False):
if is_union_origin(get_origin(field.annotation)) and _union_is_complex(field.annotation, field.metadata):
annotation = field.annotation
if annotation and (
typing_objects.is_typealiastype(annotation) or typing_objects.is_typealiastype(get_origin(annotation))
):
annotation = _strip_annotated(annotation.__value__)
if is_union_origin(get_origin(annotation)) and _union_is_complex(annotation, field.metadata):
field_info.append((field_name, self._apply_case_sensitive(self.env_prefix + field_name), True))
else:
field_info.append((field_name, self._apply_case_sensitive(self.env_prefix + field_name), False))
Expand Down
4 changes: 3 additions & 1 deletion pydantic_settings/sources/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ def parse_env_vars(
}


def _annotation_is_complex(annotation: type[Any] | None, metadata: list[Any]) -> bool:
def _annotation_is_complex(annotation: Any, metadata: list[Any]) -> bool:
# If the model is a root model, the root annotation should be used to
# evaluate the complexity.
if typing_objects.is_typealiastype(annotation) or typing_objects.is_typealiastype(get_origin(annotation)):
annotation = annotation.__value__
if annotation is not None and _lenient_issubclass(annotation, RootModel) and annotation is not RootModel:
annotation = cast('type[RootModel[Any]]', annotation)
root_annotation = annotation.model_fields['root'].annotation
Expand Down
26 changes: 25 additions & 1 deletion tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
dataclasses as pydantic_dataclasses,
)
from pydantic.fields import FieldInfo
from typing_extensions import override
from typing_extensions import TypeAliasType, override

from pydantic_settings import (
BaseSettings,
Expand Down Expand Up @@ -474,6 +474,30 @@ class AnnotatedComplexSettings(BaseSettings):
]


def test_annotated_with_type(env):
"""https://github.com/pydantic/pydantic-settings/issues/536.

PEP 695 type aliases need to be analyzed when determining if an annotation is complex.
"""
MinLenList = TypeAliasType('MinLenList', Annotated[Union[list[str], list[int]], MinLen(2)])

class AnnotatedComplexSettings(BaseSettings):
apples: MinLenList

env.set('apples', '["russet", "granny smith"]')
s = AnnotatedComplexSettings()
assert s.apples == ['russet', 'granny smith']

T = TypeVar('T')
MinLenList = TypeAliasType('MinLenList', Annotated[Union[list[T], tuple[T]], MinLen(2)], type_params=(T,))

class AnnotatedComplexSettings(BaseSettings):
apples: MinLenList[str]

s = AnnotatedComplexSettings()
assert s.apples == ['russet', 'granny smith']


def test_set_dict_model(env):
env.set('bananas', '[1, 2, 3, 3]')
env.set('CARROTS', '{"a": null, "b": 4}')
Expand Down