From b10b18772ba597e65fd3bcd3e0b7d9451785f2ba Mon Sep 17 00:00:00 2001 From: KhiemTon Date: Fri, 5 Apr 2024 12:51:29 +0700 Subject: [PATCH 1/8] Add warning in low level annontated field --- pydantic/_internal/_fields.py | 11 +++++++++++ tests/test_annotated.py | 13 ++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pydantic/_internal/_fields.py b/pydantic/_internal/_fields.py index 6e5e933061..83b0aa06c2 100644 --- a/pydantic/_internal/_fields.py +++ b/pydantic/_internal/_fields.py @@ -226,6 +226,17 @@ def collect_model_fields( # noqa: C901 # Nothing stops us from just creating a new FieldInfo for this type hint, so we do this. field_info = FieldInfo.from_annotation(ann_type) else: + if hasattr(ann_type, '__args__'): + for anno_arg in ann_type.__args__: + if _typing_extra.is_annotated(anno_arg): + for anno_type_arg in _typing_extra.get_args(anno_arg): + if isinstance(anno_type_arg, FieldInfo) and anno_type_arg.alias is not None: + warnings.warn( + f'Field "{ann_name}" has `alias` should be set at higher level to have affect', + UserWarning, + ) + break + break field_info = FieldInfo.from_annotated_attribute(ann_type, default) # attributes which are fields are removed from the class namespace: # 1. To match the behaviour of annotation-only fields diff --git a/tests/test_annotated.py b/tests/test_annotated.py index 6264454458..edc3706b9e 100644 --- a/tests/test_annotated.py +++ b/tests/test_annotated.py @@ -1,5 +1,5 @@ import sys -from typing import Any, Generic, Iterator, List, Set, TypeVar +from typing import Any, Generic, Iterator, List, Optional, Set, TypeVar import pytest from annotated_types import BaseMetadata, GroupedMetadata, Gt, Lt, Predicate @@ -232,6 +232,17 @@ class _(BaseModel): calls.clear() +def test_annotated_alias_at_low_level() -> None: + with pytest.warns( + UserWarning, match=r'Field "low_level_alias_field" have `alias` should be set at higher level to have affect' + ): + + class Model(BaseModel): + low_level_alias_field: Optional[Annotated[int, Field(alias='field_alias')]] = None + + assert Model(field_alias=1).low_level_alias_field is None + + def test_get_pydantic_core_schema_source_type() -> None: types: Set[Any] = set() From fc394c544eacf484fa263b19520133bfb4beb0a3 Mon Sep 17 00:00:00 2001 From: KhiemTon Date: Fri, 5 Apr 2024 13:39:39 +0700 Subject: [PATCH 2/8] fix test typo --- tests/test_annotated.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_annotated.py b/tests/test_annotated.py index edc3706b9e..7d11fe097a 100644 --- a/tests/test_annotated.py +++ b/tests/test_annotated.py @@ -234,7 +234,7 @@ class _(BaseModel): def test_annotated_alias_at_low_level() -> None: with pytest.warns( - UserWarning, match=r'Field "low_level_alias_field" have `alias` should be set at higher level to have affect' + UserWarning, match=r'Field "low_level_alias_field" has `alias` should be set at higher level to have affect' ): class Model(BaseModel): From 0b99f81df76498b6f00079c5d837af3e6e2ee8d9 Mon Sep 17 00:00:00 2001 From: nix010 Date: Sat, 18 May 2024 17:21:57 +0700 Subject: [PATCH 3/8] refactor warning alias into separated function --- pydantic/_internal/_fields.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/pydantic/_internal/_fields.py b/pydantic/_internal/_fields.py index 83b0aa06c2..613ed3f785 100644 --- a/pydantic/_internal/_fields.py +++ b/pydantic/_internal/_fields.py @@ -226,17 +226,7 @@ def collect_model_fields( # noqa: C901 # Nothing stops us from just creating a new FieldInfo for this type hint, so we do this. field_info = FieldInfo.from_annotation(ann_type) else: - if hasattr(ann_type, '__args__'): - for anno_arg in ann_type.__args__: - if _typing_extra.is_annotated(anno_arg): - for anno_type_arg in _typing_extra.get_args(anno_arg): - if isinstance(anno_type_arg, FieldInfo) and anno_type_arg.alias is not None: - warnings.warn( - f'Field "{ann_name}" has `alias` should be set at higher level to have affect', - UserWarning, - ) - break - break + _warning_if_annotate_have_low_level_alias(ann_type, ann_name) field_info = FieldInfo.from_annotated_attribute(ann_type, default) # attributes which are fields are removed from the class namespace: # 1. To match the behaviour of annotation-only fields @@ -262,6 +252,21 @@ def collect_model_fields( # noqa: C901 return fields, class_vars +def _warning_if_annotate_have_low_level_alias(ann_type: type[Any], ann_name: str): + from ..fields import FieldInfo + + if hasattr(ann_type, '__args__'): + for anno_arg in ann_type.__args__: + if _typing_extra.is_annotated(anno_arg): + for anno_type_arg in _typing_extra.get_args(anno_arg): + if isinstance(anno_type_arg, FieldInfo) and anno_type_arg.alias is not None: + warnings.warn( + f'Field "{ann_name}" has `alias` should be set at higher level to have affect', + UserWarning, + ) + break + + def _is_finalvar_with_default_val(type_: type[Any], val: Any) -> bool: from ..fields import FieldInfo From 369b7185d5bfd66e2359e156fa4b6a3fb4c6b279 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Sun, 19 May 2024 10:22:04 -0500 Subject: [PATCH 4/8] Apply suggestions from code review --- pydantic/_internal/_fields.py | 6 +++--- tests/test_annotated.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pydantic/_internal/_fields.py b/pydantic/_internal/_fields.py index 613ed3f785..fd9ed4bf9e 100644 --- a/pydantic/_internal/_fields.py +++ b/pydantic/_internal/_fields.py @@ -226,7 +226,7 @@ def collect_model_fields( # noqa: C901 # Nothing stops us from just creating a new FieldInfo for this type hint, so we do this. field_info = FieldInfo.from_annotation(ann_type) else: - _warning_if_annotate_have_low_level_alias(ann_type, ann_name) + _warn_on_nested_alias_in_annotation(ann_type, ann_name) field_info = FieldInfo.from_annotated_attribute(ann_type, default) # attributes which are fields are removed from the class namespace: # 1. To match the behaviour of annotation-only fields @@ -252,7 +252,7 @@ def collect_model_fields( # noqa: C901 return fields, class_vars -def _warning_if_annotate_have_low_level_alias(ann_type: type[Any], ann_name: str): +def _warn_on_nested_alias_in_annotation(ann_type: type[Any], ann_name: str): from ..fields import FieldInfo if hasattr(ann_type, '__args__'): @@ -261,7 +261,7 @@ def _warning_if_annotate_have_low_level_alias(ann_type: type[Any], ann_name: str for anno_type_arg in _typing_extra.get_args(anno_arg): if isinstance(anno_type_arg, FieldInfo) and anno_type_arg.alias is not None: warnings.warn( - f'Field "{ann_name}" has `alias` should be set at higher level to have affect', + f'`alias` specification on field "{ann_name}" must be set on outermost annotation to take effect.', UserWarning, ) break diff --git a/tests/test_annotated.py b/tests/test_annotated.py index 7d11fe097a..9df3149546 100644 --- a/tests/test_annotated.py +++ b/tests/test_annotated.py @@ -234,7 +234,7 @@ class _(BaseModel): def test_annotated_alias_at_low_level() -> None: with pytest.warns( - UserWarning, match=r'Field "low_level_alias_field" has `alias` should be set at higher level to have affect' + UserWarning, match=r'`alias` specification on field "low_level_alias_field" must be set on outermost annotation to take effect. ): class Model(BaseModel): From a6bc840b6ce34cfad48cb6914af94c9054db2cc5 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Sun, 19 May 2024 10:23:36 -0500 Subject: [PATCH 5/8] Fix missing quote --- tests/test_annotated.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_annotated.py b/tests/test_annotated.py index 9df3149546..88d9f9d343 100644 --- a/tests/test_annotated.py +++ b/tests/test_annotated.py @@ -234,7 +234,7 @@ class _(BaseModel): def test_annotated_alias_at_low_level() -> None: with pytest.warns( - UserWarning, match=r'`alias` specification on field "low_level_alias_field" must be set on outermost annotation to take effect. + UserWarning, match=r'`alias` specification on field "low_level_alias_field" must be set on outermost annotation to take effect.' ): class Model(BaseModel): From 6ae9058f710da66ce215653a2015c3bd73810924 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Sun, 19 May 2024 10:29:29 -0500 Subject: [PATCH 6/8] Trying to fix linting issue --- tests/test_annotated.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_annotated.py b/tests/test_annotated.py index 88d9f9d343..98da4df39d 100644 --- a/tests/test_annotated.py +++ b/tests/test_annotated.py @@ -234,7 +234,8 @@ class _(BaseModel): def test_annotated_alias_at_low_level() -> None: with pytest.warns( - UserWarning, match=r'`alias` specification on field "low_level_alias_field" must be set on outermost annotation to take effect.' + UserWarning, + match=r'`alias` specification on field "low_level_alias_field" must be set on outermost annotation to take effect.' ): class Model(BaseModel): From b55e683da57689113619cec311345da321acdc82 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Sun, 19 May 2024 10:31:47 -0500 Subject: [PATCH 7/8] Update tests/test_annotated.py --- tests/test_annotated.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_annotated.py b/tests/test_annotated.py index 98da4df39d..4ad49d7370 100644 --- a/tests/test_annotated.py +++ b/tests/test_annotated.py @@ -234,7 +234,7 @@ class _(BaseModel): def test_annotated_alias_at_low_level() -> None: with pytest.warns( - UserWarning, + UserWarning, match=r'`alias` specification on field "low_level_alias_field" must be set on outermost annotation to take effect.' ): From b4ff2c5152b04346d33eb0726efc8b98665c35b9 Mon Sep 17 00:00:00 2001 From: nix010 Date: Sun, 19 May 2024 23:53:08 +0800 Subject: [PATCH 8/8] Resolve lint issues --- tests/test_annotated.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_annotated.py b/tests/test_annotated.py index 4ad49d7370..8d82372d34 100644 --- a/tests/test_annotated.py +++ b/tests/test_annotated.py @@ -235,7 +235,7 @@ class _(BaseModel): def test_annotated_alias_at_low_level() -> None: with pytest.warns( UserWarning, - match=r'`alias` specification on field "low_level_alias_field" must be set on outermost annotation to take effect.' + match=r'`alias` specification on field "low_level_alias_field" must be set on outermost annotation to take effect.', ): class Model(BaseModel):