8000 Remove guarding check on computed field with field_serializer by nix010 · Pull Request #10390 · pydantic/pydantic · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions pydantic/_internal/_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,9 +548,7 @@ def inspect_validator(validator: Callable[..., Any], mode: FieldValidatorModes)
)


def inspect_field_serializer(
serializer: Callable[..., Any], mode: Literal['plain', 'wrap'], computed_field: bool = False
) -> tuple[bool, bool]:
def inspect_field_serializer(serializer: Callable[..., Any], mode: Literal['plain', 'wrap']) -> tuple[bool, bool]:
"""Look at a field serializer function and determine if it is a field serializer,
and whether it takes an info argument.

Expand All @@ -559,8 +557,6 @@ def inspect_field_serializer(
Args:
serializer: The serializer function to inspect.
mode: The serializer mode, either 'plain' or 'wrap'.
computed_field: When serializer is applied on computed_field. It doesn't require
info signature.

Returns:
Tuple of (is_field_serializer, info_arg).
Expand All @@ -587,13 +583,8 @@ def inspect_field_serializer(
f'Unrecognized field_serializer function signature for {serializer} with `mode={mode}`:{sig}',
code='field-serializer-signature',
)
if info_arg and computed_field:
raise PydanticUserError(
'field_serializer on computed_field does not use info signature', code='field-serializer-signature'
)

else:
return is_field_serializer, info_arg
return is_field_serializer, info_arg


def inspect_annotated_serializer(serializer: Callable[..., Any], mode: Literal['plain', 'wrap']) -> bool:
Expand Down
6 changes: 1 addition & 5 deletions pydantic/_internal/_generate_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -1972,7 +1972,6 @@ def _computed_field_schema(
return_type_schema = self._apply_field_serializers(
return_type_schema,
filter_field_decorator_info_by_field(field_serializers.values(), d.cls_var_name),
computed_field=True,
)

alias_generator = self._config_wrapper.alias_generator
Expand Down Expand Up @@ -2197,7 +2196,6 @@ def _apply_field_serializers(
self,
schema: core_schema.CoreSchema,
serializers: list[Decorator[FieldSerializerDecoratorInfo]],
computed_field: bool = False,
) -> core_schema.CoreSchema:
"""Apply field serializers to a schema."""
if serializers:
Expand All @@ -2214,9 +2212,7 @@ def _apply_field_serializers(

# use the last serializer to make it easy to override a serializer set on a parent model
serializer = serializers[-1]
is_field_serializer, info_arg = inspect_field_serializer(
serializer.func, serializer.info.mode, computed_field=computed_field
)
is_field_serializer, info_arg = inspect_field_serializer(serializer.func, serializer.info.mode)

try:
return_type = _decorators.get_function_return_type(
Expand Down
17 changes: 17 additions & 0 deletions tests/test_computed_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from pydantic import (
BaseModel,
Field,
FieldSerializationInfo,
GetCoreSchemaHandler,
PrivateAttr,
TypeAdapter,
Expand Down Expand Up @@ -798,3 +799,19 @@ def id(self) -> str:

m = Model(bar=42)
assert m.model_dump() == {'bar': 42, 'id': 'id: {"bar":42}'}


def test_computed_field_with_field_serializer():
class MyModel(BaseModel):
other_field: int = 42

@computed_field
@property
def my_field(self) -> str:
return 'foo'

@field_serializer('*')
def my_field_serializer(self, value: Any, info: FieldSerializationInfo) -> Any:
return f'{info.field_name} = {value}'

assert MyModel().model_dump() == {'my_field': 'my_field = foo', 'other_field': 'other_field = 42'}
19 changes: 0 additions & 19 deletions tests/test_serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
BaseModel,
Field,
FieldSerializationInfo,
PydanticUserError,
SerializationInfo,
SerializerFunctionWrapHandler,
TypeAdapter,
Expand Down Expand Up @@ -1073,24 +1072,6 @@ def quadruple_x_plus_one(self) -> Annotated[int, PlainSerializer(lambda v: v + 1
}


def test_computed_field_custom_serializer_bad_signature():
error_msg = 'field_serializer on computed_field does not use info signature'

with pytest.raises(PydanticUserError, match=error_msg):

class Model(BaseModel):
x: int

@computed_field
@property
def two_x(self) -> int:
return self.x * 2

@field_serializer('two_x')
def ser_two_x_bad_signature(self, v, _info):
return f'The double of x is {v}'


@pytest.mark.skipif(
sys.version_info < (3, 9) or sys.version_info >= (3, 13),
reason='@computed_field @classmethod @property only works in 3.9-3.12',
Expand Down
0