Skip to content

Commit

Permalink
Add Pydantic Json field support (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
hramezani committed Jun 23, 2023
1 parent a6f6d60 commit 0de9feb
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 3 deletions.
8 changes: 5 additions & 3 deletions pydantic_settings/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pathlib import Path
from typing import TYPE_CHECKING, Any, List, Mapping, Sequence, Tuple, Union, cast

from pydantic import AliasChoices, AliasPath, BaseModel
from pydantic import AliasChoices, AliasPath, BaseModel, Json
from pydantic._internal._typing_extra import origin_is_union
from pydantic._internal._utils import deep_update, lenient_issubclass
from pydantic.fields import FieldInfo
Expand Down Expand Up @@ -68,7 +68,7 @@ def field_is_complex(self, field: FieldInfo) -> bool:
Returns:
Whether the field is complex.
"""
return _annotation_is_complex(field.annotation)
return _annotation_is_complex(field.annotation, field.metadata)

def prepare_field_value(self, field_name: str, field: FieldInfo, value: Any, value_is_complex: bool) -> Any:
"""
Expand Down Expand Up @@ -611,7 +611,9 @@ def read_env_file(
return file_vars


def _annotation_is_complex(annotation: type[Any] | None) -> bool:
def _annotation_is_complex(annotation: type[Any] | None, metadata: list[Any]) -> bool:
if any(isinstance(md, Json) for md in metadata): # type: ignore[misc]
return False
origin = get_origin(annotation)
return (
_annotation_is_complex_inner(annotation)
Expand Down
46 changes: 46 additions & 0 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
ConfigDict,
Field,
HttpUrl,
Json,
SecretStr,
ValidationError,
)
Expand Down Expand Up @@ -1652,3 +1653,48 @@ class Settings(BaseSettings):
env.set('foobar_apple', 'has_prefix')
s = Settings(_env_prefix='foobar_')
assert s.apple == 'has_prefix'


def test_env_json_field(env):
class Settings(BaseSettings):
x: Json

env.set('x', '{"foo": "bar"}')

s = Settings()
assert s.x == {'foo': 'bar'}

env.set('x', 'test')
with pytest.raises(ValidationError) as exc_info:
Settings()
assert exc_info.value.errors(include_url=False) == [
{
'type': 'json_invalid',
'loc': ('x',),
'msg': 'Invalid JSON: expected ident at line 1 column 2',
'input': 'test',
'ctx': {'error': 'expected ident at line 1 column 2'},
}
]


def test_env_json_field_dict(env):
class Settings(BaseSettings):
x: Json[Dict[str, int]]

env.set('x', '{"foo": 1}')

s = Settings()
assert s.x == {'foo': 1}

env.set('x', '{"foo": "bar"}')
with pytest.raises(ValidationError) as exc_info:
Settings()
assert exc_info.value.errors(include_url=False) == [
{
'type': 'int_parsing',
'loc': ('x', 'foo'),
'msg': 'Input should be a valid integer, unable to parse string as an integer',
'input': 'bar',
}
]

0 comments on commit 0de9feb

Please sign in to comment.