-
-
Notifications
You must be signed in to change notification settings - Fork 118
Check if the data can be structured without actually structuring it #414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
You could consider the following approach: from dataclasses import astuple, dataclass, fields
from cattrs import Converter
c = Converter()
def get_converter() -> Converter:
return c
@dataclass
class MyDataClass:
a: str
b: int
def __post_init__(self):
self.validate_types()
def validate_types(self) -> None:
c.structure(astuple(self), tuple[tuple(f.type for f in fields(self.__class__))])
@classmethod
def from_dict(
cls, data: dict, converter: Converter = get_converter()
) -> "MyDataClass":
return converter.structure(data, cls)
MyDataClass(a="a", b="a") # Will raise since `b` isn't an int The idea is you run the structuring path, but not for your class but for a Note that this approach will be somewhat slow, especially if you use |
i guess this could also work :), tanks! |
Unfortunately this method doesn't work when you have such dataclasses nested one inside the other all with:
I guess my example from before was too simplistic :( |
Your This is getting a little tricky. We can set up a separate converter with from dataclasses import astuple, dataclass, fields
from cattrs import Converter, UnstructureStrategy
c = Converter()
tuple_c = Converter(unstruct_strat=UnstructureStrategy.AS_TUPLE)
def get_converter() -> Converter:
return c
@dataclass
class MyDataClass:
a: str
b: int
def __post_init__(self):
self.validate_types()
def validate_types(self) -> None:
tuple_c.structure(
astuple(self), tuple[tuple(f.type for f in fields(self.__class__))]
)
@classmethod
def from_dict(
cls, data: dict, converter: Converter = get_converter()
) -> "MyDataClass":
return converter.structure(data, cls)
@dataclass
class GenericDataClass:
a: MyDataClass
def __post_init__(self):
self.validate_types()
def validate_types(self) -> None:
tuple_c.structure(
astuple(self), tuple[tuple(f.type for f in fields(self.__class__))]
)
GenericDataClass(MyDataClass("a", 1)) |
ok, sorry for the incompleteness, the full example would be something like this (with whatever works in validate types):
I know that with from_dict it will validate the types twice but for now it's ok but your proposal seems to be working if i change get_converter to:
and call the respective converters in from_dict and validate_types :) however it would be nice to have something directly from cattrs :) |
Hm, what you're really asking for is runtime validation of types in |
i think that what i'd like to have is already built in cattrs since it structures the data and while doing so it checks the typing. what i'd like to have is just the type check
the thing is that the method you proposed i think might fail if the nested dictionaries in data have an order of the keys different from the one of the nested dataclasses in the structure. it doesn't really affect me in this case since the data comes from asdict(self) so the order will be the same |
Description
I have a dataclass that can be instantiated (structured) with a from_dict method, in this way I know all the typings are correct:
It also has a validate_types method that can be run after instantiation
What I Did
I would like to add the validate_types in the post_init:
unfortunately this ends in an infinite loop, because the from_dict generates a new instance of the class and the code goes into the post_init which calls validate_types, which calls from_dict, which creates a new instance of the class which goes into the post_init and on and on.
would it be possible for you to expose a function/method of the converter that only checks if the data can be structured without actually structuring/creating the new instance?
The text was updated successfully, but these errors were encountered: