-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Validate component children #1647
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,6 +66,10 @@ class Component(Base, ABC): | |
|
||
# components that cannot be children | ||
invalid_children: List[str] = [] | ||
|
||
# components that are only allowed as children | ||
valid_children: List[str] = [] | ||
|
||
# custom attribute | ||
custom_attrs: Dict[str, str] = {} | ||
|
||
|
@@ -103,9 +107,10 @@ def __init__(self, *args, **kwargs): | |
TypeError: If an invalid prop is passed. | ||
""" | ||
# Set the id and children initially. | ||
children = kwargs.get("children", []) | ||
initial_kwargs = { | ||
"id": kwargs.get("id"), | ||
"children": kwargs.get("children", []), | ||
"children": children, | ||
**{ | ||
prop: Var.create(kwargs[prop]) | ||
for prop in self.get_initial_props() | ||
|
@@ -114,6 +119,8 @@ def __init__(self, *args, **kwargs): | |
} | ||
super().__init__(**initial_kwargs) | ||
|
||
self._validate_component_children(children) | ||
|
||
# Get the component fields, triggers, and props. | ||
fields = self.get_fields() | ||
triggers = self.get_triggers() | ||
|
@@ -381,6 +388,7 @@ def create(cls, *children, **props) -> Component: | |
else Bare.create(contents=Var.create(child, is_string=True)) | ||
for child in children | ||
] | ||
|
||
return cls(children=children, **props) | ||
|
||
def _add_style(self, style): | ||
|
@@ -435,30 +443,44 @@ def render(self) -> Dict: | |
), | ||
autofocus=self.autofocus, | ||
) | ||
self._validate_component_children( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we keep the check here? Only reason is because if they somehow added children dynamically after creating their component, then we wouldn't catch it. Though I don't think I've seen people really do that... |
||
rendered_dict["name"], rendered_dict["children"] | ||
) | ||
return rendered_dict | ||
|
||
def _validate_component_children(self, comp_name: str, children: List[Dict]): | ||
def _validate_component_children(self, children: List[Component]): | ||
"""Validate the children components. | ||
|
||
Args: | ||
comp_name: name of the component. | ||
children: list of children components. | ||
children: The children of the component. | ||
|
||
Raises: | ||
ValueError: when an unsupported component is matched. | ||
""" | ||
if not self.invalid_children: | ||
if not self.invalid_children and not self.valid_children: | ||
return | ||
for child in children: | ||
name = child["name"] | ||
if name in self.invalid_children: | ||
|
||
comp_name = type(self).__name__ | ||
|
||
def validate_invalid_child(child_name): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: we could inline these functions ore define them outside of the function. I've noticed before in Python that defining functions within tight loops is often a performance issue, and these checks will be called many times per compile. |
||
if child_name in self.invalid_children: | ||
raise ValueError( | ||
f"The component `{comp_name.lower()}` cannot have `{name.lower()}` as a child component" | ||
f"The component `{comp_name}` cannot have `{child_name}` as a child component" | ||
) | ||
|
||
def validate_valid_child(child_name): | ||
if child_name not in self.valid_children: | ||
valid_child_list = ", ".join( | ||
[f"`{v_child}`" for v_child in self.valid_children] | ||
) | ||
raise ValueError( | ||
f"The component `{comp_name}` only allows the components: {valid_child_list} as children. Got `{child_name}` instead." | ||
) | ||
|
||
for child in children: | ||
name = type(child).__name__ | ||
|
||
if self.invalid_children: | ||
validate_invalid_child(name) | ||
|
||
if self.valid_children: | ||
validate_valid_child(name) | ||
|
||
def _get_custom_code(self) -> Optional[str]: | ||
"""Get custom code for the component. | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also thinking in a follow up if these should maybe be
List[Type[Component]]
rather thanstr