Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pydantic - problem with declared types vs response (returned) type

I will show you some minimal example:

from pydantic import BaseModel

class Foo(BaseModel):
    value: PositiveInt | None = None
    
    def some_function(self) -> PositiveInt:
        return self.value

In above example there is problem with type of the returned value of "some_function". The "self.value" is not "PositiveInt" - its "Optional[PositiveInt, None]" - cause I want it during initialization as None - then doing some "_post_init_" (what is not supported right now) and return just "PositiveInt".

What do you think, how should I do it?

For example I can add "assert self.value" - in this case I will have assertion error when self.value is None. Or maybe just do "return PositiveInt(self.value)" - to prevent some mypy errors?

Or maybe there is some internal build-in method for changing the type of returning value? (something like response_type in FastApi - when you need or want to return some data that is not exactly what the type declares)

Any ideas?

BR!

Edit: More details: What if you want to have some checking if value1 or value2 exists and based on it calculate the second one?

from pydantic import BaseModel

class Foo(BaseModel):
    value1: PositiveInt | None = None
    value2: PositiveInt | None = None
    
    def post_init_function(self) -> None:
        if self.value1:
           self.value2 = 100
        elif self.value2:
           self.value1 = 100

    @property
    def value1(self) -> PositiveInt:
        return self.PositiveInt

foo1 = Foo(value1=10)
foo1.post_init_function()
# value1 = 10, value2 = 100

foo2 = Foo(value2=5)
foo2.post_init_function()
# value1 = 100, value2 = 5
like image 675
Bony Avatar asked Jan 18 '26 00:01

Bony


1 Answers

Using dataclasses from the default library, I would separate the __init__ parameter that could be None from the field which cannot be None.

from dataclasses import dataclass, field


@dataclass
class Foo:
    value1: int = field(init=False)
    value2: int = field(init=False)
    v1: InitVar[int | None] = None
    v2: InitVar[int | None] = None

    def __post_init__(self, v1, v2):
        if v1 is None and v2 is None:
            raise ValueError("v1 and v2 cannot both be None")
        elif v1 is None:
            self.value1 = 100
            self.value2 = v2
        else:
            self.value1 = v1
            self.value2 = 100

I am not sure what the equivalent idiom for a Pydantic model would be, though I think this should work with few or no changes for a Pydantic dataclass.

like image 181
chepner Avatar answered Jan 20 '26 13:01

chepner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!