Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change the behavior of field_is_complex to be more similar to pydantic v1 #31

Merged
merged 2 commits into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
34 changes: 30 additions & 4 deletions pydantic_settings/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
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
Expand Down Expand Up @@ -53,10 +54,16 @@ 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.
"""
return _annotation_is_complex(field.annotation)

def prepare_field_value(self, field_name: str, field: FieldInfo, value: Any, value_is_complex: bool) -> Any:
"""
Expand Down Expand Up @@ -433,3 +440,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
)
4 changes: 4 additions & 0 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down