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

This commit is contained in:
Thomas Brandého 2025-01-20 13:55:53 -08:00 committed by GitHub
parent 268effe62e
commit 9c019a65d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 34 additions and 16 deletions

View File

@ -740,22 +740,21 @@ class Component(BaseComponent, ABC):
# Import here to avoid circular imports.
from reflex.components.base.bare import Bare
from reflex.components.base.fragment import Fragment
from reflex.utils.exceptions import ComponentTypeError
from reflex.utils.exceptions import ChildrenTypeError
# Filter out None props
props = {key: value for key, value in props.items() if value is not None}
def validate_children(children):
for child in children:
if isinstance(child, tuple):
if isinstance(child, (tuple, list)):
validate_children(child)
# 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_children(children)

View File

@ -1,6 +1,6 @@
"""Custom Exceptions."""
from typing import NoReturn
from typing import Any, NoReturn
class ReflexError(Exception):
@ -31,6 +31,22 @@ class ComponentTypeError(ReflexError, TypeError):
"""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):
"""Custom TypeError for event handler related errors."""

View File

@ -27,7 +27,7 @@ from reflex.event import (
from reflex.state import BaseState
from reflex.style import Style
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.vars import VarData
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"'
@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):
with pytest.raises(TypeError) as err:
with pytest.raises(ChildrenTypeError):
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(