Skip to content

Commit 8fb9abb

Browse files
user1584hramezani
andauthored
Determine RootModel complexity from root type (#344)
Co-authored-by: Hasan Ramezani <[email protected]>
1 parent 5c3a817 commit 8fb9abb

File tree

2 files changed

+49
-1
lines changed

2 files changed

+49
-1
lines changed

pydantic_settings/sources.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
import typing_extensions
3737
from dotenv import dotenv_values
38-
from pydantic import AliasChoices, AliasPath, BaseModel, Json
38+
from pydantic import AliasChoices, AliasPath, BaseModel, Json, RootModel
3939
from pydantic._internal._repr import Representation
4040
from pydantic._internal._typing_extra import WithArgsTypes, origin_is_union, typing_base
4141
from pydantic._internal._utils import deep_update, is_model_class, lenient_issubclass
@@ -1904,6 +1904,16 @@ def read_env_file(
19041904

19051905

19061906
def _annotation_is_complex(annotation: type[Any] | None, metadata: list[Any]) -> bool:
1907+
# If the model is a root model, the root annotation should be used to
1908+
# evaluate the complexity.
1909+
if isinstance(annotation, type) and issubclass(annotation, RootModel):
1910+
# In some rare cases (see test_root_model_as_field),
1911+
# the root attribute is not available. For these cases, python 3.8 and 3.9
1912+
# return 'RootModelRootType'.
1913+
root_annotation = annotation.__annotations__.get('root', None)
1914+
if root_annotation is not None and root_annotation != 'RootModelRootType':
1915+
annotation = root_annotation
1916+
19071917
if any(isinstance(md, Json) for md in metadata): # type: ignore[misc]
19081918
return False
19091919
# Check if annotation is of the form Annotated[type, metadata].

tests/test_settings.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import dataclasses
33
import json
44
import os
5+
import pathlib
56
import re
67
import sys
78
import typing
@@ -2185,6 +2186,43 @@ class Settings(BaseSettings):
21852186
assert s.model_dump() == {'z': [{'x': 1, 'y': {'foo': 1}}, {'x': 2, 'y': {'foo': 2}}]}
21862187

21872188

2189+
def test_str_based_root_model(env):
2190+
"""Testing to pass string directly to root model."""
2191+
2192+
class Foo(RootModel[str]):
2193+
root: str
2194+
2195+
class Settings(BaseSettings):
2196+
foo: Foo
2197+
plain: str
2198+
2199+
TEST_STR = 'hello world'
2200+
env.set('foo', TEST_STR)
2201+
env.set('plain', TEST_STR)
2202+
s = Settings()
2203+
assert s.model_dump() == {'foo': TEST_STR, 'plain': TEST_STR}
2204+
2205+
2206+
def test_path_based_root_model(env):
2207+
"""Testing to pass path directly to root model."""
2208+
2209+
class Foo(RootModel[pathlib.PurePosixPath]):
2210+
root: pathlib.PurePosixPath
2211+
2212+
class Settings(BaseSettings):
2213+
foo: Foo
2214+
plain: pathlib.PurePosixPath
2215+
2216+
TEST_PATH: str = '/hello/world'
2217+
env.set('foo', TEST_PATH)
2218+
env.set('plain', TEST_PATH)
2219+
s = Settings()
2220+
assert s.model_dump() == {
2221+
'foo': pathlib.PurePosixPath(TEST_PATH),
2222+
'plain': pathlib.PurePosixPath(TEST_PATH),
2223+
}
2224+
2225+
21882226
def test_optional_field_from_env(env):
21892227
class Settings(BaseSettings):
21902228
x: Optional[str] = None

0 commit comments

Comments
 (0)