Skip to content

Commit b331a2b

Browse files
committed
Provide field metadata utulity function to properly handle Annotated types.
1 parent 3e66430 commit b331a2b

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

pydantic_settings/sources/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from .utils import (
2626
_annotation_is_complex,
2727
_get_alias_names,
28+
_get_field_metadata,
2829
_get_model_fields,
2930
_strip_annotated,
3031
_union_is_complex,
@@ -180,7 +181,7 @@ def decode_complex_value(self, field_name: str, field: FieldInfo, value: Any) ->
180181
The decoded value for further preparation
181182
"""
182183
if field and (
183-
NoDecode in field.metadata
184+
NoDecode in _get_field_metadata(field)
184185
or (self.config.get('enable_decoding') is False and ForceDecode not in field.metadata)
185186
):
186187
return value

pydantic_settings/sources/utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@ def _annotation_is_complex(annotation: Any, metadata: list[Any]) -> bool:
7373
)
7474

7575

76+
def _get_field_metadata(field: Any) -> list[Any]:
77+
annotation = field.annotation
78+
metadata = field.metadata
79+
if typing_objects.is_typealiastype(annotation) or typing_objects.is_typealiastype(get_origin(annotation)):
80+
annotation = annotation.__value__
81+
origin = get_origin(annotation)
82+
if typing_objects.is_annotated(origin):
83+
_, *meta = get_args(annotation)
84+
metadata += meta
85+
return metadata
86+
87+
7688
def _annotation_is_complex_inner(annotation: type[Any] | None) -> bool:
7789
if _lenient_issubclass(annotation, (str, bytes)):
7890
return False

tests/test_settings.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,24 @@ class AnnotatedComplexSettings(BaseSettings):
498498
assert s.apples == ['russet', 'granny smith']
499499

500500

501+
def test_annotated_with_type_no_decode(env):
502+
A = TypeAliasType('A', Annotated[list[str], NoDecode])
503+
504+
class Settings(BaseSettings):
505+
a: A
506+
507+
# decode the value here. the field value won't be decoded because of NoDecode
508+
@field_validator('a', mode='before')
509+
@classmethod
510+
def decode_a(cls, v: str) -> list[str]:
511+
return json.loads(v)
512+
513+
env.set('a', '["one", "two"]')
514+
515+
s = Settings()
516+
assert s.model_dump() == {'a': ['one', 'two']}
517+
518+
501519
def test_set_dict_model(env):
502520
env.set('bananas', '[1, 2, 3, 3]')
503521
env.set('CARROTS', '{"a": null, "b": 4}')

0 commit comments

Comments
 (0)