Skip to content

Commit

Permalink
Properly rebuild the FieldInfo when a forward ref gets evaluated (#7698)
Browse files Browse the repository at this point in the history
  • Loading branch information
dmontagu committed Sep 29, 2023
1 parent a03ac5b commit 0c54c35
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 1 deletion.
12 changes: 11 additions & 1 deletion pydantic/_internal/_generate_schema.py
Expand Up @@ -897,7 +897,9 @@ def _generate_dc_field_schema(
metadata=common_field['metadata'],
)

def _common_field_schema(self, name: str, field_info: FieldInfo, decorators: DecoratorInfos) -> _CommonField:
def _common_field_schema( # noqa C901
self, name: str, field_info: FieldInfo, decorators: DecoratorInfos
) -> _CommonField:
# Update FieldInfo annotation if appropriate:
if has_instance_in_type(field_info.annotation, (ForwardRef, str)):
types_namespace = self._types_namespace
Expand All @@ -910,6 +912,14 @@ def _common_field_schema(self, name: str, field_info: FieldInfo, decorators: Dec
if evaluated is not field_info.annotation and not has_instance_in_type(evaluated, PydanticRecursiveRef):
field_info.annotation = evaluated

# Handle any field info attributes that may have been obtained from now-resolved annotations
new_field_info = FieldInfo.from_annotation(evaluated)
for k, v in new_field_info._attributes_set.items():
# If an attribute is already set, it means it was set by assigning to a call to Field (or just a
# default value), and that should take the highest priority. So don't overwrite existing attributes.
if k not in field_info._attributes_set:
setattr(field_info, k, v)

source_type, annotations = field_info.annotation, field_info.metadata

def set_discriminator(schema: CoreSchema) -> CoreSchema:
Expand Down
27 changes: 27 additions & 0 deletions tests/test_annotated.py
Expand Up @@ -373,3 +373,30 @@ def test_predicate_error_python() -> None:
'input': -1,
}
]


def test_annotated_field_info_not_lost_from_forwardref():
from pydantic import BaseModel

class ForwardRefAnnotatedFieldModel(BaseModel):
foo: 'Annotated[Integer, Field(alias="bar", default=1)]' = 2
foo2: 'Annotated[Integer, Field(alias="bar2", default=1)]' = Field(default=2, alias='baz')

Integer = int

ForwardRefAnnotatedFieldModel.model_rebuild()

assert ForwardRefAnnotatedFieldModel(bar=3).foo == 3
assert ForwardRefAnnotatedFieldModel(baz=3).foo2 == 3

with pytest.raises(ValidationError) as exc_info:
ForwardRefAnnotatedFieldModel(bar='bar')
assert exc_info.value.errors() == [
{
'input': 'bar',
'loc': ('bar',),
'msg': 'Input should be a valid integer, unable to parse string as an integer',
'type': 'int_parsing',
'url': 'https://errors.pydantic.dev/2.4/v/int_parsing',
}
]

0 comments on commit 0c54c35

Please sign in to comment.