Skip to content

Commit

Permalink
draft: update pydanttic-core
Browse files Browse the repository at this point in the history
  • Loading branch information
adriangb committed Jul 3, 2023
1 parent 6bffa13 commit f30bd91
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 22 deletions.
6 changes: 5 additions & 1 deletion pydantic/_internal/_config.py
Expand Up @@ -131,8 +131,12 @@ def core_config(self, obj: Any) -> core_schema.CoreConfig:
Returns:
A `CoreConfig` object created from config.
"""

def dict_not_none(**kwargs: Any) -> Any:
return {k: v for k, v in kwargs.items() if v is not None}

core_config = core_schema.CoreConfig(
**core_schema.dict_not_none(
**dict_not_none(
title=self.config_dict.get('title') or (obj and obj.__name__),
extra_fields_behavior=self.config_dict.get('extra'),
allow_inf_nan=self.config_dict.get('allow_inf_nan'),
Expand Down
56 changes: 36 additions & 20 deletions pydantic/_internal/_generate_schema.py
Expand Up @@ -137,28 +137,30 @@ def filter_field_decorator_info_by_field(


def apply_each_item_validators(
schema: core_schema.CoreSchema, each_item_validators: list[Decorator[ValidatorDecoratorInfo]]
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

# push down any `each_item=True` 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)
schema['schema'] = apply_each_item_validators(schema['schema'], each_item_validators, field_name)
return schema
elif is_list_like_schema_with_items_schema(schema):
inner_schema = schema.get('items_schema', None)
if inner_schema is None:
inner_schema = core_schema.any_schema()
schema['items_schema'] = apply_validators(inner_schema, each_item_validators)
schema['items_schema'] = apply_validators(inner_schema, each_item_validators, field_name)
elif schema['type'] == 'dict':
# push down any `each_item=True` validators onto dict _values_
# this is super arbitrary but it's the V1 behavior
inner_schema = schema.get('values_schema', None)
if inner_schema is None:
inner_schema = core_schema.any_schema()
schema['values_schema'] = apply_validators(inner_schema, each_item_validators)
schema['values_schema'] = apply_validators(inner_schema, each_item_validators, field_name)
elif each_item_validators:
raise TypeError(
f"`@validator(..., each_item=True)` cannot be applied to fields with a schema of {schema['type']}"
Expand Down Expand Up @@ -356,7 +358,7 @@ def _model_schema(self, cls: type[BaseModel]) -> core_schema.CoreSchema:
finally:
self._config_wrapper_stack.pop()

inner_schema = apply_validators(fields_schema, decorators.root_validators.values())
inner_schema = apply_validators(fields_schema, decorators.root_validators.values(), None)
inner_schema = define_expected_missing_refs(inner_schema, recursively_defined_type_refs())
inner_schema = apply_model_validators(inner_schema, model_validators, 'inner')

Expand Down Expand Up @@ -667,11 +669,11 @@ def set_discriminator(schema: CoreSchema) -> CoreSchema:
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)
schema = apply_each_item_validators(schema, each_item_validators, name)

schema = apply_validators(schema, filter_field_decorator_info_by_field(this_field_validators, name))
schema = apply_validators(schema, filter_field_decorator_info_by_field(this_field_validators, name), name)
schema = apply_validators(
schema, filter_field_decorator_info_by_field(decorators.field_validators.values(), name)
schema, filter_field_decorator_info_by_field(decorators.field_validators.values(), name), name
)

# the default validator needs to go outside of any other validators
Expand Down Expand Up @@ -1053,7 +1055,7 @@ def _dataclass_schema(
if config is not None:
self._config_wrapper_stack.pop()

inner_schema = apply_validators(args_schema, decorators.root_validators.values())
inner_schema = apply_validators(args_schema, decorators.root_validators.values(), None)

model_validators = decorators.model_validators.values()
inner_schema = apply_model_validators(inner_schema, model_validators, 'inner')
Expand Down Expand Up @@ -1457,19 +1459,26 @@ def _apply_model_serializers(

_VALIDATOR_F_MATCH: Mapping[
tuple[FieldValidatorModes, Literal['no-info', 'general', 'field']],
Callable[[Callable[..., Any], core_schema.CoreSchema], core_schema.CoreSchema],
Callable[[Callable[..., Any], None, 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', 'general'): lambda f, _, schema: core_schema.general_before_validator_function(f, schema),
('after', 'general'): lambda f, _, schema: core_schema.general_after_validator_function(f, schema),
('plain', 'general'): lambda f, _1, _2: core_schema.general_plain_validator_function(f),
('wrap', 'general'): lambda f, _, schema: core_schema.general_wrap_validator_function(f, schema),
}


_FIELD_VALIDATOR_F_MATCH: Mapping[
tuple[FieldValidatorModes, Literal['no-info', 'general', 'field']],
Callable[[Callable[..., Any], str, core_schema.CoreSchema], core_schema.CoreSchema],
] = {
('before', 'no-info'): core_schema.no_info_before_validator_function,
('after', 'no-info'): core_schema.no_info_after_validator_function,
('plain', 'no-info'): lambda f, _: core_schema.no_info_plain_validator_function(f),
('wrap', 'no-info'): core_schema.no_info_wrap_validator_function,
('before', 'general'): core_schema.general_before_validator_function,
('after', 'general'): core_schema.general_after_validator_function,
('plain', 'general'): lambda f, _: core_schema.general_plain_validator_function(f),
('wrap', 'general'): core_schema.general_wrap_validator_function,
('before', 'field'): core_schema.field_before_validator_function,
('after', 'field'): core_schema.field_after_validator_function,
('plain', 'field'): lambda f, _: core_schema.field_plain_validator_function(f),
('plain', 'field'): lambda f, field_name, _: core_schema.field_plain_validator_function(f, field_name),
('wrap', 'field'): core_schema.field_wrap_validator_function,
}

Expand All @@ -1479,12 +1488,14 @@ 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.
Args:
schema: The schema to apply validators on.
validators: An iterable of validators.
field_name: The name of the field if validators are being applied to a model field.
Returns:
The updated schema.
Expand All @@ -1494,10 +1505,15 @@ def apply_validators(
if not info_arg:
val_type: Literal['no-info', 'general', 'field'] = 'no-info'
elif isinstance(validator.info, (FieldValidatorDecoratorInfo, ValidatorDecoratorInfo)):
assert field_name is not None, 'field validators must be used within a model field'
val_type = 'field'
else:
val_type = 'general'
schema = _VALIDATOR_F_MATCH[(validator.info.mode, val_type)](validator.func, schema)

if field_name is None or val_type != 'field':
schema = _VALIDATOR_F_MATCH[(validator.info.mode, val_type)](validator.func, None, schema)
else:
schema = _FIELD_VALIDATOR_F_MATCH[(validator.info.mode, val_type)](validator.func, field_name, schema)
return schema


Expand Down
4 changes: 3 additions & 1 deletion tests/test_utils.py
Expand Up @@ -637,7 +637,9 @@ def walk(s, recurse):


def test_handle_function_schema():
schema = core_schema.field_before_validator_function(function=lambda v, _info: v, schema=core_schema.float_schema())
schema = core_schema.field_before_validator_function(
function=lambda v, _info: v, field_name='field_name', schema=core_schema.float_schema()
)

def walk(s, recurse):
# change type to str
Expand Down

0 comments on commit f30bd91

Please sign in to comment.