From cd0d37c4c18f24b5624ae86cfe5288cd82edf2c1 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 16 Apr 2025 18:01:58 +0000 Subject: [PATCH 1/4] Stop using deprecated field_name argument on validation function schemas --- docs/concepts/types.md | 2 +- pydantic/_internal/_generate_schema.py | 45 ++++++++++---------------- pydantic/functional_validators.py | 5 +-- tests/test_validators.py | 2 +- 4 files changed, 20 insertions(+), 34 deletions(-) Index: pydantic-2.11.7/docs/concepts/types.md =================================================================== --- pydantic-2.11.7.orig/docs/concepts/types.md +++ pydantic-2.11.7/docs/concepts/types.md @@ -979,7 +979,7 @@ class CustomType: cls, source_type: Any, handler: GetCoreSchemaHandler ) -> core_schema.CoreSchema: return core_schema.with_info_after_validator_function( - cls.validate, handler(int), field_name=handler.field_name + cls.validate, handler(int) ) Index: pydantic-2.11.7/pydantic/_internal/_generate_schema.py =================================================================== --- pydantic-2.11.7.orig/pydantic/_internal/_generate_schema.py +++ pydantic-2.11.7/pydantic/_internal/_generate_schema.py @@ -222,7 +222,6 @@ def filter_field_decorator_info_by_field def apply_each_item_validators( schema: core_schema.CoreSchema, each_item_validators: list[Decorator[ValidatorDecoratorInfo]], - field_name: str | None, ) -> core_schema.CoreSchema: # This V1 compatibility shim should eventually be removed @@ -234,21 +233,20 @@ def apply_each_item_validators( # note that this won't work for any Annotated types that get wrapped by a function validator # but that's okay because that didn't exist in V1 if schema['type'] == 'nullable': - schema['schema'] = apply_each_item_validators(schema['schema'], each_item_validators, field_name) + schema['schema'] = apply_each_item_validators(schema['schema'], each_item_validators) return schema elif schema['type'] == 'tuple': if (variadic_item_index := schema.get('variadic_item_index')) is not None: schema['items_schema'][variadic_item_index] = apply_validators( schema['items_schema'][variadic_item_index], each_item_validators, - field_name, ) elif is_list_like_schema_with_items_schema(schema): inner_schema = schema.get('items_schema', core_schema.any_schema()) - schema['items_schema'] = apply_validators(inner_schema, each_item_validators, field_name) + schema['items_schema'] = apply_validators(inner_schema, each_item_validators) elif schema['type'] == 'dict': inner_schema = schema.get('values_schema', core_schema.any_schema()) - schema['values_schema'] = apply_validators(inner_schema, each_item_validators, field_name) + schema['values_schema'] = apply_validators(inner_schema, each_item_validators) else: raise TypeError( f'`@validator(..., each_item=True)` cannot be applied to fields with a schema of {schema["type"]}' @@ -840,7 +838,7 @@ class GenerateSchema: extras_keys_schema=extras_keys_schema, model_name=cls.__name__, ) - inner_schema = apply_validators(fields_schema, decorators.root_validators.values(), None) + inner_schema = apply_validators(fields_schema, decorators.root_validators.values()) inner_schema = apply_model_validators(inner_schema, model_validators, 'inner') model_schema = core_schema.model_schema( @@ -1380,9 +1378,9 @@ class GenerateSchema: field_info.validate_default = True each_item_validators = [v for v in this_field_validators if v.info.each_item is True] this_field_validators = [v for v in this_field_validators if v not in each_item_validators] - schema = apply_each_item_validators(schema, each_item_validators, name) + schema = apply_each_item_validators(schema, each_item_validators) - schema = apply_validators(schema, this_field_validators, name) + schema = apply_validators(schema, this_field_validators) # the default validator needs to go outside of any other validators # so that it is the topmost validator for the field validator @@ -1972,7 +1970,7 @@ class GenerateSchema: collect_init_only=has_post_init, ) - inner_schema = apply_validators(args_schema, decorators.root_validators.values(), None) + inner_schema = apply_validators(args_schema, decorators.root_validators.values()) model_validators = decorators.model_validators.values() inner_schema = apply_model_validators(inner_schema, model_validators, 'inner') @@ -2484,24 +2482,16 @@ class GenerateSchema: _VALIDATOR_F_MATCH: Mapping[ tuple[FieldValidatorModes, Literal['no-info', 'with-info']], - Callable[[Callable[..., Any], core_schema.CoreSchema, str | None], core_schema.CoreSchema], + Callable[[Callable[..., Any], core_schema.CoreSchema], core_schema.CoreSchema], ] = { - ('before', 'no-info'): lambda f, schema, _: core_schema.no_info_before_validator_function(f, schema), - ('after', 'no-info'): lambda f, schema, _: core_schema.no_info_after_validator_function(f, schema), - ('plain', 'no-info'): lambda f, _1, _2: core_schema.no_info_plain_validator_function(f), - ('wrap', 'no-info'): lambda f, schema, _: core_schema.no_info_wrap_validator_function(f, schema), - ('before', 'with-info'): lambda f, schema, field_name: core_schema.with_info_before_validator_function( - f, schema, field_name=field_name - ), - ('after', 'with-info'): lambda f, schema, field_name: core_schema.with_info_after_validator_function( - f, schema, field_name=field_name - ), - ('plain', 'with-info'): lambda f, _, field_name: core_schema.with_info_plain_validator_function( - f, field_name=field_name - ), - ('wrap', 'with-info'): lambda f, schema, field_name: core_schema.with_info_wrap_validator_function( - f, schema, field_name=field_name - ), + ('before', 'no-info'): lambda f, schema: core_schema.no_info_before_validator_function(f, schema), + ('after', 'no-info'): lambda f, schema: core_schema.no_info_after_validator_function(f, schema), + ('plain', 'no-info'): lambda f, _: core_schema.no_info_plain_validator_function(f), + ('wrap', 'no-info'): lambda f, schema: core_schema.no_info_wrap_validator_function(f, schema), + ('before', 'with-info'): lambda f, schema: core_schema.with_info_before_validator_function(f, schema), + ('after', 'with-info'): lambda f, schema: core_schema.with_info_after_validator_function(f, schema), + ('plain', 'with-info'): lambda f, _: core_schema.with_info_plain_validator_function(f), + ('wrap', 'with-info'): lambda f, schema: core_schema.with_info_wrap_validator_function(f, schema), } @@ -2512,7 +2502,6 @@ def apply_validators( validators: Iterable[Decorator[RootValidatorDecoratorInfo]] | Iterable[Decorator[ValidatorDecoratorInfo]] | Iterable[Decorator[FieldValidatorDecoratorInfo]], - field_name: str | None, ) -> core_schema.CoreSchema: """Apply validators to a schema. @@ -2528,7 +2517,7 @@ def apply_validators( info_arg = inspect_validator(validator.func, validator.info.mode) val_type = 'with-info' if info_arg else 'no-info' - schema = _VALIDATOR_F_MATCH[(validator.info.mode, val_type)](validator.func, schema, field_name) + schema = _VALIDATOR_F_MATCH[(validator.info.mode, val_type)](validator.func, schema) return schema Index: pydantic-2.11.7/pydantic/functional_validators.py =================================================================== --- pydantic-2.11.7.orig/pydantic/functional_validators.py +++ pydantic-2.11.7/pydantic/functional_validators.py @@ -75,7 +75,7 @@ class AfterValidator: info_arg = _inspect_validator(self.func, 'after') if info_arg: func = cast(core_schema.WithInfoValidatorFunction, self.func) - return core_schema.with_info_after_validator_function(func, schema=schema, field_name=handler.field_name) + return core_schema.with_info_after_validator_function(func, schema=schema) else: func = cast(core_schema.NoInfoValidatorFunction, self.func) return core_schema.no_info_after_validator_function(func, schema=schema) @@ -136,7 +136,6 @@ class BeforeValidator: return core_schema.with_info_before_validator_function( func, schema=schema, - field_name=handler.field_name, json_schema_input_schema=input_schema, ) else: @@ -230,7 +229,6 @@ class PlainValidator: func = cast(core_schema.WithInfoValidatorFunction, self.func) return core_schema.with_info_plain_validator_function( func, - field_name=handler.field_name, serialization=serialization, # pyright: ignore[reportArgumentType] json_schema_input_schema=input_schema, ) @@ -307,7 +305,6 @@ class WrapValidator: return core_schema.with_info_wrap_validator_function( func, schema=schema, - field_name=handler.field_name, json_schema_input_schema=input_schema, ) else: Index: pydantic-2.11.7/tests/test_validators.py =================================================================== --- pydantic-2.11.7.orig/tests/test_validators.py +++ pydantic-2.11.7/tests/test_validators.py @@ -21,7 +21,7 @@ from unittest.mock import MagicMock import pytest from dirty_equals import HasRepr, IsInstance from pydantic_core import core_schema -from typing_extensions import TypedDict +from typing_extensions import TypeAliasType, TypedDict from pydantic import ( BaseModel, @@ -2684,7 +2684,7 @@ def foobar_validate(value: Any, info: co class Foobar: @classmethod def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: - return core_schema.with_info_plain_validator_function(foobar_validate, field_name=handler.field_name) + return core_schema.with_info_plain_validator_function(foobar_validate) def test_custom_type_field_name_model(): @@ -2779,6 +2779,29 @@ def test_plain_validator_field_name(): assert m.foobar == {'value': '1', 'field_name': 'foobar', 'data': {'x': 123}} +def test_validator_field_name_with_reused_type_alias(): + calls = [] + + def validate_my_field(value: str, info: ValidationInfo): + calls.append((info.field_name, value)) + return value + + MyField = TypeAliasType('MyField', Annotated[str, AfterValidator(validate_my_field)]) + + class MyModel(BaseModel): + field1: MyField + field2: MyField + + MyModel.model_validate( + { + 'field1': 'value1', + 'field2': 'value2', + } + ) + + assert calls == [('field1', 'value1'), ('field2', 'value2')] + + def validate_wrap(value: Any, handler: core_schema.ValidatorFunctionWrapHandler, info: core_schema.ValidationInfo): data = info.data if isinstance(data, dict):