Ver código fonte

check for dict passed as children for component (#4656)

Thomas Brandého 4 meses atrás
pai
commit
9c019a65d5

+ 7 - 8
reflex/components/component.py

@@ -740,22 +740,21 @@ class Component(BaseComponent, ABC):
         # Import here to avoid circular imports.
         # Import here to avoid circular imports.
         from reflex.components.base.bare import Bare
         from reflex.components.base.bare import Bare
         from reflex.components.base.fragment import Fragment
         from reflex.components.base.fragment import Fragment
-        from reflex.utils.exceptions import ComponentTypeError
+        from reflex.utils.exceptions import ChildrenTypeError
 
 
         # Filter out None props
         # Filter out None props
         props = {key: value for key, value in props.items() if value is not None}
         props = {key: value for key, value in props.items() if value is not None}
 
 
         def validate_children(children):
         def validate_children(children):
             for child in children:
             for child in children:
-                if isinstance(child, tuple):
+                if isinstance(child, (tuple, list)):
                     validate_children(child)
                     validate_children(child)
+
                 # Make sure the child is a valid type.
                 # Make sure the child is a valid type.
-                if not types._isinstance(child, ComponentChild):
-                    raise ComponentTypeError(
-                        "Children of Reflex components must be other components, "
-                        "state vars, or primitive Python types. "
-                        f"Got child {child} of type {type(child)}.",
-                    )
+                if isinstance(child, dict) or not types._isinstance(
+                    child, ComponentChild
+                ):
+                    raise ChildrenTypeError(component=cls.__name__, child=child)
 
 
         # Validate all the children.
         # Validate all the children.
         validate_children(children)
         validate_children(children)

+ 17 - 1
reflex/utils/exceptions.py

@@ -1,6 +1,6 @@
 """Custom Exceptions."""
 """Custom Exceptions."""
 
 
-from typing import NoReturn
+from typing import Any, NoReturn
 
 
 
 
 class ReflexError(Exception):
 class ReflexError(Exception):
@@ -31,6 +31,22 @@ class ComponentTypeError(ReflexError, TypeError):
     """Custom TypeError for component related errors."""
     """Custom TypeError for component related errors."""
 
 
 
 
+class ChildrenTypeError(ComponentTypeError):
+    """Raised when the children prop of a component is not a valid type."""
+
+    def __init__(self, component: str, child: Any):
+        """Initialize the exception.
+
+        Args:
+            component: The name of the component.
+            child: The child that caused the error.
+        """
+        super().__init__(
+            f"Component {component} received child {child} of type {type(child)}. "
+            "Accepted types are other components, state vars, or primitive Python types (dict excluded)."
+        )
+
+
 class EventHandlerTypeError(ReflexError, TypeError):
 class EventHandlerTypeError(ReflexError, TypeError):
     """Custom TypeError for event handler related errors."""
     """Custom TypeError for event handler related errors."""
 
 

+ 10 - 7
tests/units/components/test_component.py

@@ -27,7 +27,7 @@ from reflex.event import (
 from reflex.state import BaseState
 from reflex.state import BaseState
 from reflex.style import Style
 from reflex.style import Style
 from reflex.utils import imports
 from reflex.utils import imports
-from reflex.utils.exceptions import EventFnArgMismatch
+from reflex.utils.exceptions import ChildrenTypeError, EventFnArgMismatch
 from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
 from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
 from reflex.vars import VarData
 from reflex.vars import VarData
 from reflex.vars.base import LiteralVar, Var
 from reflex.vars.base import LiteralVar, Var
@@ -645,14 +645,17 @@ def test_create_filters_none_props(test_component):
     assert str(component.style["text-align"]) == '"center"'
     assert str(component.style["text-align"]) == '"center"'
 
 
 
 
-@pytest.mark.parametrize("children", [((None,),), ("foo", ("bar", (None,)))])
+@pytest.mark.parametrize(
+    "children",
+    [
+        ((None,),),
+        ("foo", ("bar", (None,))),
+        ({"foo": "bar"},),
+    ],
+)
 def test_component_create_unallowed_types(children, test_component):
 def test_component_create_unallowed_types(children, test_component):
-    with pytest.raises(TypeError) as err:
+    with pytest.raises(ChildrenTypeError):
         test_component.create(*children)
         test_component.create(*children)
-    assert (
-        err.value.args[0]
-        == "Children of Reflex components must be other components, state vars, or primitive Python types. Got child None of type <class 'NoneType'>."
-    )
 
 
 
 
 @pytest.mark.parametrize(
 @pytest.mark.parametrize(