0

I have a project using fastapi and pydantic. I want to define certain subtypes of standard library types, eg:

def check_negative(v: int) -> int:
    assert v >= 0, f"{v} is negative"
    return v

PositiveInt = Annotated[int, AfterValidator(check_negative)]

class DemoModel(BaseModel):
    numbers: list[PositiveInt]

ISSUE:

The following code runs the evaluation only when the list of numbers is passed on instantiation of the DemoModel. Meaning numbers contains false values until it is actually used in a BaseModel. This is unconvenient since i want to evaluate the value, for example, directly in the route of a defined fastapi endpoint and not wait until the value is passed down to the model.

numbers = [PositiveInt(2), PositiveInt(-3)] <--- should throw evaluation error here
print(numbers)
demo = DemoModel(numbers=numbers) <--- throws evaluation error here

QUESTION:

Is there a way to run the validations like AfterValidator directly on the instantiation of the type PositiveInt and not just when it is used for the instantiation of a BaseModel class?

So that in the given example, the validation error would be thrown in the first line numbers = [PositiveInt(2), PositiveInt(-3)] and not at the end at demo = DemoModel(numbers=numbers).

Thanks for your help!

2 Answers 2

2

You could use TypeAdapter like this:

from pydantic import TypeAdapter

TypeAdapter(list[PositiveInt]).validate_python([2, -3])

Python docs on type adapter: https://docs.pydantic.dev/latest/api/type_adapter/

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

Comments

1

Not sure if this is the best approach, but this is how i got it working now, after reading this part of the pydantic docs:

class PositiveInt(int):
    def __new__(cls, number: int):
        check_negative(number)
        return super().__new__(cls, number)

    @classmethod
    def __get_pydantic_core_schema__(
        cls, source: Type[Any], handler: GetCoreSchemaHandler
    ) -> core_schema.CoreSchema:
        return core_schema.general_after_validator_function(
            cls.validate,
            core_schema.int_schema(),
            serialization=core_schema.int_schema(),
        )

    @classmethod
    def validate(cls, v: int, _info):
        return check_negative(v)


class DemoModel(BaseModel):
    numbers: list[PositiveInt]

This gives me assertion errors right on the instantiation of PositiveInt AND i can use it in DemoModel as well.

if __name__ == "__main__":
    a = PositiveInt(1)
    b = PositiveInt(-3)

    c = DemoModel(numbers=[PositiveInt(1), PositiveInt(-3)])

Comments

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.