diff --git a/reflex/components/component.py b/reflex/components/component.py index 6eeb3d162..f98df026a 100644 --- a/reflex/components/component.py +++ b/reflex/components/component.py @@ -283,7 +283,11 @@ class Component(BaseComponent, ABC): continue # 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. if types._issubclass(field_type, Var): diff --git a/reflex/components/radix/themes/base.py b/reflex/components/radix/themes/base.py index 95f213256..502316a21 100644 --- a/reflex/components/radix/themes/base.py +++ b/reflex/components/radix/themes/base.py @@ -100,13 +100,9 @@ class RadixThemesComponent(Component): """ component = super().create(*children, **props) if component.library is None: -<<<<<<< HEAD - component.library = RadixThemesComponent.model_fields["library"].default -======= component.library = RadixThemesComponent.model_fields[ "library" ].default ->>>>>>> f7035d9b (optionalize some Component props) component.alias = "RadixThemes" + ( component.tag or component.__class__.__name__ ) diff --git a/reflex/components/radix/themes/components/dropdown_menu.py b/reflex/components/radix/themes/components/dropdown_menu.py index ae5a9c160..54d8fb6d0 100644 --- a/reflex/components/radix/themes/components/dropdown_menu.py +++ b/reflex/components/radix/themes/components/dropdown_menu.py @@ -113,7 +113,9 @@ class DropdownMenuContent(RadixThemesComponent): 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. - 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. arrow_padding: Optional[Var[Union[float, int]]] = None @@ -204,7 +206,9 @@ class DropdownMenuSubContent(RadixThemesComponent): 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. - 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. arrow_padding: Optional[Var[Union[float, int]]] = None diff --git a/reflex/components/radix/themes/components/tooltip.py b/reflex/components/radix/themes/components/tooltip.py index 01859ee08..9778b34a8 100644 --- a/reflex/components/radix/themes/components/tooltip.py +++ b/reflex/components/radix/themes/components/tooltip.py @@ -60,7 +60,9 @@ class Tooltip(RadixThemesComponent): 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. - 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. arrow_padding: Optional[Var[Union[float, int]]] = None diff --git a/reflex/state.py b/reflex/state.py index e45c35c1d..26669b98b 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -195,7 +195,7 @@ def _split_substate_key(substate_key: str) -> tuple[str, str]: return token, state_name -class BaseState(Base, ABC, extra=pydantic.Extra.allow): +class BaseState(Base, ABC, extra="allow"): """The state of the app.""" # 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 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() super().__init__(*args, **kwargs) @@ -362,7 +366,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow): ] @classmethod - def __init_subclass__(cls, **kwargs): + def __pydantic_init_subclass__(cls, **kwargs: Any) -> None: """Do some magic for the subclass initialization. Args: @@ -371,7 +375,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow): Raises: ValueError: If a substate class shadows another. """ - super().__init_subclass__(**kwargs) + # super().__init_subclass__(**kwargs) # Event handlers should not shadow builtin state methods. cls._check_overridden_methods() @@ -486,6 +490,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow): cls.event_handlers[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() @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 not super().__getattribute__("__dict__"): return super().__getattribute__(name) - private_attrs = super().__getattribute__("__pydantic_private__") - if private_attrs is None: - return super().__getattribute__(name) inherited_vars = { **super().__getattribute__("inherited_vars"), @@ -1009,6 +1016,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow): if parent_state is not None: 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"] if name in backend_vars: 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. fields = self.get_fields() 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 ( isinstance(field.annotation, type) and issubclass(field.annotation, ClientStorageBase) diff --git a/tests/states/mutation.py b/tests/states/mutation.py index 465514192..4fa26586c 100644 --- a/tests/states/mutation.py +++ b/tests/states/mutation.py @@ -37,7 +37,7 @@ class DictMutationTestState(BaseState): self.details.pop("age") # 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): """Remove the home address from dict in the list.""" @@ -48,7 +48,10 @@ class DictMutationTestState(BaseState): self.address[0]["street"] = "street address" # 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): """Change the friend's name in the nested dict."""