Style props with Callable Values (#1751)

This commit is contained in:
Elijah Ahianyo 2023-09-12 18:26:53 +00:00 committed by GitHub
parent 77edb01e74
commit 06a110a07d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 5 deletions

View File

@ -0,0 +1,7 @@
"""Custom Exceptions."""
class InvalidStylePropError(TypeError):
"""Custom Type Error when style props have invalid values."""
pass

View File

@ -9,14 +9,15 @@ import os
import os.path as op
import re
import sys
from typing import TYPE_CHECKING, Any, Type, Union
import types as builtin_types
from typing import TYPE_CHECKING, Any, Callable, Type, Union
import plotly.graph_objects as go
from plotly.graph_objects import Figure
from plotly.io import to_json
from reflex import constants
from reflex.utils import types
from reflex.utils import exceptions, types
from reflex.vars import Var
if TYPE_CHECKING:
@ -288,7 +289,8 @@ def format_prop(
The formatted prop to display within a tag.
Raises:
TypeError: If the prop is not a valid type.
exceptions.InvalidStylePropError: If the style prop value is not a valid type.
TypeError: If the prop is not valid.
"""
# import here to avoid circular import.
from reflex.event import EVENT_ARG, EventChain
@ -324,6 +326,8 @@ def format_prop(
else:
# Dump the prop as JSON.
prop = json_dumps(prop)
except exceptions.InvalidStylePropError:
raise
except TypeError as e:
raise TypeError(f"Could not format prop: {prop} of type {type(prop)}") from e
@ -584,15 +588,28 @@ def format_dict(prop: ComponentStyle) -> str:
Returns:
The formatted dict.
Raises:
InvalidStylePropError: If a style prop has a callable value
"""
# Import here to avoid circular imports.
from reflex.event import EventHandler
from reflex.vars import Var
prop_dict = {}
# Convert any var keys to strings.
prop = {key: str(val) if isinstance(val, Var) else val for key, val in prop.items()}
for key, value in prop.items():
if issubclass(type(value), Callable):
raise exceptions.InvalidStylePropError(
f"The style prop `{to_snake_case(key)}` cannot have " # type: ignore
f"`{value.fn.__qualname__ if isinstance(value, EventHandler) else value.__qualname__ if isinstance(value, builtin_types.FunctionType) else value}`, "
f"an event handler or callable as its value"
)
prop_dict[key] = str(value) if isinstance(value, Var) else value
# Dump the dict to a string.
fprop = json_dumps(prop)
fprop = json_dumps(prop_dict)
def unescape_double_quotes_in_var(m: re.Match) -> str:
# Since the outer quotes are removed, the inner escaped quotes must be unescaped.

View File

@ -11,6 +11,7 @@ from reflex import constants
from reflex.base import Base
from reflex.components.tags import Tag
from reflex.event import EVENT_ARG, EventChain, EventHandler, EventSpec
from reflex.state import State
from reflex.style import Style
from reflex.utils import (
build,
@ -45,6 +46,18 @@ V056 = version.parse("0.5.6")
VMAXPLUS1 = version.parse(get_above_max_version())
class ExampleTestState(State):
"""Test state class."""
def test_event_handler(self):
"""Test event handler."""
pass
def test_func():
pass
@pytest.mark.parametrize(
"input,output",
[
@ -744,3 +757,24 @@ def test_output_system_info(mocker):
"""
mocker.patch("reflex.utils.console.LOG_LEVEL", constants.LogLevel.DEBUG)
utils_exec.output_system_info()
@pytest.mark.parametrize(
"callable", [ExampleTestState.test_event_handler, test_func, lambda x: x]
)
def test_style_prop_with_event_handler_value(callable):
"""Test that a type error is thrown when a style prop has a
callable as value.
Args:
callable: The callable function or event handler.
"""
style = {
"color": EventHandler(fn=callable)
if type(callable) != EventHandler
else callable
}
with pytest.raises(TypeError):
format.format_dict(style) # type: ignore