234 lines
11 KiB
Diff
234 lines
11 KiB
Diff
From cd0d37c4c18f24b5624ae86cfe5288cd82edf2c1 Mon Sep 17 00:00:00 2001
|
|
From: Douwe Maan <hi@douwe.me>
|
|
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):
|