0

I'm using a field wrap validator in Pydantic with the Annotated pattern, and I want to access the expected/annotated type of a field from within the validator function. Here's an example of what I want:

from typing import Annotated, Any

from pydantic import (
    BaseModel, ValidationError, ValidationInfo,
    ValidatorFunctionWrapHandler, WrapValidator
)


def wrapper(value: Any,
            handler: ValidatorFunctionWrapHandler,
            info: ValidationInfo) -> Any:
    try:
        return handler(value)
    except ValidationError as e:
        # Custom error handling where I want to know the expected type.
        # I'm looking for something like this:
        if info.annotation == str:
            # Do something
        elif info.annotation == int | bool:
            # Do something else
        else:
            raise


class MyModel(BaseModel):
    foo: Annotated[str, WrapValidator(wrapper)]


class AnotherModel(BaseModel):
    bar: Annotated[int | bool, WrapValidator(wrapper)]

I would have expected ValidationInfo to include the expected data type of value, but it doesn't seem to.

Another option is to go through the model fields. Something like this: MyModel.model_fields[info.field_name].annotation.

However, this wrapper is applied to multiple models, so I can't just reference MyModel explicitly like that. And again ValidationInfo doesn't seem to include any reference back to the model.

I noticed that info.config = {"title": "MyModel"}. Is there a way to get the MyModel class from the "MyModel" string, and is this a reliable means of finding the model?


This is not a duplicate of this question, which concerns the decorator pattern for validators, not the annotated pattern, and also uses old syntax from v1.

1 Answer 1

0

I think your best bet (if you are set on this pattern) is to wrap your wrapper, providing the type hint again:

from typing import Annotated, Any

from pydantic import (
    BaseModel, ValidationError, ValidationInfo,
    ValidatorFunctionWrapHandler, WrapValidator
)


def wrap_with_type(type_):
    def wrapper(value: Any,
                handler: ValidatorFunctionWrapHandler,
                info: ValidationInfo) -> Any:
        try:
            return handler(value)
        except ValidationError as e:
            # Custom error handling where I want to know the expected type.
            # I'm looking for something like this:
            if type_ == str:
                # Do something
            elif type_ == int | bool:
                # Do something else
            else:
                raise

    return WrapValidator(wrapper)


class MyModel(BaseModel):
    foo: Annotated[str, wrap_with_type(str)]


class AnotherModel(BaseModel):
    bar: Annotated[int | bool, wrap_with_type(int | bool)]

That will allow you to do what you want, but it comes with costs: your code is less readable, and there's now redundant information in your class definition. It might be worth rethinking your design to separate your validator into one for each of the expected types. Without a RME, it is hard to offer another solution. I'd be happy to help work something out though.

If it's any consolation, I am surprised this isn't something you could grab during validation. Consider delving into the project's source code to see if there's a contribution to be made!

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, I did end up finding a different pattern, but this does work. It could also be (arguably) a bit more readable with something like functools.partial (or a lambda): the wrapper accepts a fourth argument, annotation, and then it's called with WrapValidator(partial(wrapper, annotation=str)).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.