diff --git a/pydantic_settings/main.py b/pydantic_settings/main.py index 2aab9b95..d28975ae 100644 --- a/pydantic_settings/main.py +++ b/pydantic_settings/main.py @@ -85,6 +85,7 @@ def _settings_build_values( ), env_prefix_len=len(self.model_config.get('env_prefix', '')), ) + # self.__env_settings__ = env_settings dotenv_settings = DotEnvSettingsSource( self.__class__, env_file=(_env_file if _env_file != env_file_sentinel else self.model_config.get('env_file')), diff --git a/pydantic_settings/sources.py b/pydantic_settings/sources.py index 5dd9db32..612394bd 100644 --- a/pydantic_settings/sources.py +++ b/pydantic_settings/sources.py @@ -4,15 +4,16 @@ import os import warnings from abc import ABC, abstractmethod +from collections import deque from dataclasses import is_dataclass from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Tuple, Type, Union +from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Sequence, Tuple, Type, Union from pydantic import BaseModel from pydantic._internal._typing_extra import origin_is_union from pydantic._internal._utils import deep_update, lenient_issubclass from pydantic.fields import FieldInfo -from typing_extensions import get_origin +from typing_extensions import get_args, get_origin from pydantic_settings.utils import path_type_label @@ -53,10 +54,18 @@ def get_field_value(self, field: FieldInfo, field_name: str) -> Tuple[Any, str, pass def field_is_complex(self, field: FieldInfo) -> bool: - def _annotation_is_complex(annotation: type[Any] | None) -> bool: - return lenient_issubclass(annotation, (BaseModel, list, set, frozenset, dict)) or is_dataclass(annotation) + """ + Checks whether a field is complex, in which case it will attempt to be parsed as JSON. + + Args: + field (FieldInfo): The field. - return _annotation_is_complex(field.annotation) or _annotation_is_complex(get_origin(field.annotation)) + Returns: + bool: Whether the field is complex. + """ + result = _annotation_is_complex(field.annotation) + print(result, field.annotation) + return result def prepare_field_value(self, field_name: str, field: FieldInfo, value: Any, value_is_complex: bool) -> Any: """ @@ -433,3 +442,22 @@ def find_case_path(dir_path: Path, file_name: str, case_sensitive: bool) -> Opti elif not case_sensitive and f.name.lower() == file_name.lower(): return f return None + + +def _annotation_is_complex(annotation: type[Any] | None) -> bool: + origin = get_origin(annotation) + return ( + _annotation_is_complex_inner(annotation) + or _annotation_is_complex_inner(origin) + or hasattr(origin, '__pydantic_core_schema__') + or hasattr(origin, '__get_pydantic_core_schema__') + ) + + +def _annotation_is_complex_inner(annotation: type[Any] | None) -> bool: + if lenient_issubclass(annotation, str): + return False + + return lenient_issubclass(annotation, (BaseModel, Mapping, Sequence, tuple, set, frozenset, deque)) or is_dataclass( + annotation + ) diff --git a/tests/test_settings.py b/tests/test_settings.py index f431616b..1c1b38d5 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -622,6 +622,10 @@ class B(BaseSettings): class Settings(BaseSettings): content: Union[A, B, datetime] + env.set('content', '{"a": "test"}') + s = Settings() + assert s.content == A(a='test') + env.set('content', '2020-07-05T00:00:00Z') s = Settings() assert s.content == datetime(2020, 7, 5, 0, 0, tzinfo=timezone.utc)