fix init_subclass for pydantic v2, add some missing type hints, format black

This commit is contained in:
Benedikt Bartscher 2024-03-01 14:55:34 +01:00
parent 80f02753ba
commit 37c360e786
No known key found for this signature in database
6 changed files with 39 additions and 18 deletions

View File

@ -283,7 +283,11 @@ class Component(BaseComponent, ABC):
continue continue
# unwrap Optional from field_type # unwrap Optional from field_type
field_type = typing.get_args(field_type)[0] if types.is_optional(field_type) else field_type field_type = (
typing.get_args(field_type)[0]
if types.is_optional(field_type)
else field_type
)
# Check whether the key is a component prop. # Check whether the key is a component prop.
if types._issubclass(field_type, Var): if types._issubclass(field_type, Var):

View File

@ -100,13 +100,9 @@ class RadixThemesComponent(Component):
""" """
component = super().create(*children, **props) component = super().create(*children, **props)
if component.library is None: if component.library is None:
<<<<<<< HEAD
component.library = RadixThemesComponent.model_fields["library"].default
=======
component.library = RadixThemesComponent.model_fields[ component.library = RadixThemesComponent.model_fields[
"library" "library"
].default ].default
>>>>>>> f7035d9b (optionalize some Component props)
component.alias = "RadixThemes" + ( component.alias = "RadixThemes" + (
component.tag or component.__class__.__name__ component.tag or component.__class__.__name__
) )

View File

@ -113,7 +113,9 @@ class DropdownMenuContent(RadixThemesComponent):
avoid_collisions: Optional[Var[bool]] = None avoid_collisions: Optional[Var[bool]] = None
# The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. # The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0.
collision_padding: Optional[Var[Union[float, int, Dict[str, Union[float, int]]]]] = None collision_padding: Optional[
Var[Union[float, int, Dict[str, Union[float, int]]]]
] = None
# The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. Defaults to 0. # The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. Defaults to 0.
arrow_padding: Optional[Var[Union[float, int]]] = None arrow_padding: Optional[Var[Union[float, int]]] = None
@ -204,7 +206,9 @@ class DropdownMenuSubContent(RadixThemesComponent):
avoid_collisions: Optional[Var[bool]] = None avoid_collisions: Optional[Var[bool]] = None
# The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. # The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0.
collision_padding: Optional[Var[Union[float, int, Dict[str, Union[float, int]]]]] = None collision_padding: Optional[
Var[Union[float, int, Dict[str, Union[float, int]]]]
] = None
# The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. Defaults to 0. # The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. Defaults to 0.
arrow_padding: Optional[Var[Union[float, int]]] = None arrow_padding: Optional[Var[Union[float, int]]] = None

View File

@ -60,7 +60,9 @@ class Tooltip(RadixThemesComponent):
avoid_collisions: Optional[Var[bool]] = None avoid_collisions: Optional[Var[bool]] = None
# The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. # The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0.
collision_padding: Optional[Var[Union[float, int, Dict[str, Union[float, int]]]]] = None collision_padding: Optional[
Var[Union[float, int, Dict[str, Union[float, int]]]]
] = None
# The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. Defaults to 0. # The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. Defaults to 0.
arrow_padding: Optional[Var[Union[float, int]]] = None arrow_padding: Optional[Var[Union[float, int]]] = None

View File

@ -195,7 +195,7 @@ def _split_substate_key(substate_key: str) -> tuple[str, str]:
return token, state_name return token, state_name
class BaseState(Base, ABC, extra=pydantic.Extra.allow): class BaseState(Base, ABC, extra="allow"):
"""The state of the app.""" """The state of the app."""
# A map from the var name to the var. # A map from the var name to the var.
@ -288,7 +288,11 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
kwargs["parent_state"] = parent_state kwargs["parent_state"] = parent_state
for prop_name, prop in self.base_vars.items(): for prop_name, prop in self.base_vars.items():
if prop_name not in kwargs and self.model_fields[prop_name].is_required(): if (
prop_name not in kwargs
and prop_name in self.model_fields
and self.model_fields[prop_name].is_required()
):
kwargs[prop_name] = prop.get_default_value() kwargs[prop_name] = prop.get_default_value()
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -362,7 +366,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
] ]
@classmethod @classmethod
def __init_subclass__(cls, **kwargs): def __pydantic_init_subclass__(cls, **kwargs: Any) -> None:
"""Do some magic for the subclass initialization. """Do some magic for the subclass initialization.
Args: Args:
@ -371,7 +375,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
Raises: Raises:
ValueError: If a substate class shadows another. ValueError: If a substate class shadows another.
""" """
super().__init_subclass__(**kwargs) # super().__init_subclass__(**kwargs)
# Event handlers should not shadow builtin state methods. # Event handlers should not shadow builtin state methods.
cls._check_overridden_methods() cls._check_overridden_methods()
@ -486,6 +490,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
cls.event_handlers[name] = handler cls.event_handlers[name] = handler
setattr(cls, name, handler) setattr(cls, name, handler)
# Inherited vars are handled in __getattribute__, not by pydantic
for prop in cls.model_fields.copy():
if prop in cls.inherited_vars or prop in cls.inherited_backend_vars:
del cls.model_fields[prop]
cls.model_rebuild(force=True)
cls._init_var_dependency_dicts() cls._init_var_dependency_dicts()
@staticmethod @staticmethod
@ -994,9 +1004,6 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
# If the state hasn't been initialized yet, return the default value. # If the state hasn't been initialized yet, return the default value.
if not super().__getattribute__("__dict__"): if not super().__getattribute__("__dict__"):
return super().__getattribute__(name) return super().__getattribute__(name)
private_attrs = super().__getattribute__("__pydantic_private__")
if private_attrs is None:
return super().__getattribute__(name)
inherited_vars = { inherited_vars = {
**super().__getattribute__("inherited_vars"), **super().__getattribute__("inherited_vars"),
@ -1009,6 +1016,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
if parent_state is not None: if parent_state is not None:
return getattr(parent_state, name) return getattr(parent_state, name)
private_attrs = super().__getattribute__("__pydantic_private__")
if private_attrs is None:
return super().__getattribute__(name)
backend_vars = private_attrs["_backend_vars"] backend_vars = private_attrs["_backend_vars"]
if name in backend_vars: if name in backend_vars:
value = backend_vars[name] value = backend_vars[name]
@ -1088,7 +1098,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
# on the browser also resets the values on the backend. # on the browser also resets the values on the backend.
fields = self.get_fields() fields = self.get_fields()
for prop_name in self.base_vars: for prop_name in self.base_vars:
field = fields[prop_name] if not (field := fields.get(prop_name)):
# inherited values are reset in the parent state
continue
if isinstance(field.default, ClientStorageBase) or ( if isinstance(field.default, ClientStorageBase) or (
isinstance(field.annotation, type) isinstance(field.annotation, type)
and issubclass(field.annotation, ClientStorageBase) and issubclass(field.annotation, ClientStorageBase)

View File

@ -37,7 +37,7 @@ class DictMutationTestState(BaseState):
self.details.pop("age") self.details.pop("age")
# dict in list # dict in list
address: List[Dict[str,str]] = [{"home": "home address"}, {"work": "work address"}] address: List[Dict[str, str]] = [{"home": "home address"}, {"work": "work address"}]
def remove_home_address(self): def remove_home_address(self):
"""Remove the home address from dict in the list.""" """Remove the home address from dict in the list."""
@ -48,7 +48,10 @@ class DictMutationTestState(BaseState):
self.address[0]["street"] = "street address" self.address[0]["street"] = "street address"
# nested dict # nested dict
friend_in_nested_dict: Dict[str, Union[str, Dict[str,str]]] = {"name": "Nikhil", "friend": {"name": "Alek"}} friend_in_nested_dict: Dict[str, Union[str, Dict[str, str]]] = {
"name": "Nikhil",
"friend": {"name": "Alek"},
}
def change_friend_name(self): def change_friend_name(self):
"""Change the friend's name in the nested dict.""" """Change the friend's name in the nested dict."""