I am creating a API to return pydantic backed object, with optional / "fetchable" fields that might not be fetched based on the API parameter passed in.
when accessing these optional data fields in the object, i want to add an assertion that the field is actually fetched.
A "ok" way is adding getter and setter for each field, but i think it is very ugly, hard to add additional support like serialization etc, and there are lots of such fields in the object:
_field_a: int | NotFetched = NOT_FETCHED
@property
def field_a(self) -> int:
return assert_fetched(self._field_a)
@field_a.setter
def field_a(self, value):
self._field_a = value
A "better" way is using descriptor class:
class FetchableField(Generic[T]):
def __set_name__(self, owner: type, name: str) -> None:
self.public_name = name
self.private_name = f"_{name}"
def __get__(self, instance: Any, owner: type | None = None) -> T:
if instance is None:
return self # pyright: ignore[reportReturnType]
return assert_fetched(
getattr(instance, self.private_name, NOT_FETCHED),
self.public_name,
)
def __set__(self, instance: Any, value: T) -> None:
setattr(instance, self.private_name, value)
...
_field_a: int | NotFetched = NOT_FETCHED # optional,
field_a: int = FetchableField()
...
However, it seems like Pydantic is not working with descriptor, and then:
obj = Object()
obj.a # will equal to a <FetchableField object> without calling the getter function in FetchableField