what if we simply didn't have match
This commit is contained in:
parent
f9d45d5562
commit
94b4443afc
@ -175,7 +175,7 @@ class ConnectionBanner(Component):
|
|||||||
Returns:
|
Returns:
|
||||||
The connection banner component.
|
The connection banner component.
|
||||||
"""
|
"""
|
||||||
from reflex.components.base.bare import Bare
|
from reflex.components.base.fragment import Fragment
|
||||||
|
|
||||||
if not comp:
|
if not comp:
|
||||||
comp = Flex.create(
|
comp = Flex.create(
|
||||||
@ -191,7 +191,7 @@ class ConnectionBanner(Component):
|
|||||||
position="fixed",
|
position="fixed",
|
||||||
)
|
)
|
||||||
|
|
||||||
return Bare.create(cond(has_connection_errors, comp))
|
return Fragment.create(cond(has_connection_errors, comp))
|
||||||
|
|
||||||
|
|
||||||
class ConnectionModal(Component):
|
class ConnectionModal(Component):
|
||||||
@ -207,11 +207,11 @@ class ConnectionModal(Component):
|
|||||||
Returns:
|
Returns:
|
||||||
The connection banner component.
|
The connection banner component.
|
||||||
"""
|
"""
|
||||||
from reflex.components.base.bare import Bare
|
from reflex.components.base.fragment import Fragment
|
||||||
|
|
||||||
if not comp:
|
if not comp:
|
||||||
comp = Text.create(*default_connection_error())
|
comp = Text.create(*default_connection_error())
|
||||||
return Bare.create(
|
return Fragment.create(
|
||||||
cond(
|
cond(
|
||||||
has_too_many_connection_errors,
|
has_too_many_connection_errors,
|
||||||
DialogRoot.create(
|
DialogRoot.create(
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
"""rx.match."""
|
"""rx.match."""
|
||||||
|
|
||||||
import textwrap
|
import textwrap
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
from typing import Any, List, Optional, Sequence, Tuple, Union
|
||||||
|
|
||||||
from reflex.components.base import Fragment
|
from reflex.components.base import Fragment
|
||||||
from reflex.components.component import BaseComponent, Component, MemoizationLeaf
|
from reflex.components.component import BaseComponent, Component, MemoizationLeaf
|
||||||
from reflex.components.tags import MatchTag, Tag
|
from reflex.utils import types
|
||||||
from reflex.style import Style
|
|
||||||
from reflex.utils import format, types
|
|
||||||
from reflex.utils.exceptions import MatchTypeError
|
from reflex.utils.exceptions import MatchTypeError
|
||||||
from reflex.utils.imports import ImportDict
|
from reflex.vars.base import Var
|
||||||
from reflex.vars import VarData
|
from reflex.vars.number import MatchOperation
|
||||||
from reflex.vars.base import LiteralVar, Var
|
|
||||||
|
|
||||||
|
|
||||||
class Match(MemoizationLeaf):
|
class Match(MemoizationLeaf):
|
||||||
@ -40,43 +37,32 @@ class Match(MemoizationLeaf):
|
|||||||
Raises:
|
Raises:
|
||||||
ValueError: When a default case is not provided for cases with Var return types.
|
ValueError: When a default case is not provided for cases with Var return types.
|
||||||
"""
|
"""
|
||||||
match_cond_var = cls._create_condition_var(cond)
|
cases, default = cls._process_cases(cases)
|
||||||
cases, default = cls._process_cases(list(cases))
|
cls._process_match_cases(cases)
|
||||||
match_cases = cls._process_match_cases(cases)
|
|
||||||
|
|
||||||
cls._validate_return_types(match_cases)
|
cls._validate_return_types(cases)
|
||||||
|
|
||||||
if default is None and types._issubclass(type(match_cases[0][-1]), Var):
|
if default is None and any(
|
||||||
|
not (
|
||||||
|
isinstance((return_type := case[-1]), Component)
|
||||||
|
or (
|
||||||
|
isinstance(return_type, Var)
|
||||||
|
and types.typehint_issubclass(return_type._var_type, Component)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for case in cases
|
||||||
|
):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"For cases with return types as Vars, a default case must be provided"
|
"For cases with return types as Vars, a default case must be provided"
|
||||||
)
|
)
|
||||||
|
elif default is None:
|
||||||
|
default = Fragment.create()
|
||||||
|
|
||||||
return cls._create_match_cond_var_or_component(
|
return cls._create_match_cond_var_or_component(cond, cases, default)
|
||||||
match_cond_var, match_cases, default
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _create_condition_var(cls, cond: Any) -> Var:
|
|
||||||
"""Convert the condition to a Var.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
cond: The condition.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The condition as a base var
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: If the condition is not provided.
|
|
||||||
"""
|
|
||||||
match_cond_var = LiteralVar.create(cond)
|
|
||||||
|
|
||||||
if match_cond_var is None:
|
|
||||||
raise ValueError("The condition must be set")
|
|
||||||
return match_cond_var
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _process_cases(
|
def _process_cases(
|
||||||
cls, cases: List
|
cls, cases: Sequence
|
||||||
) -> Tuple[List, Optional[Union[Var, BaseComponent]]]:
|
) -> Tuple[List, Optional[Union[Var, BaseComponent]]]:
|
||||||
"""Process the list of match cases and the catchall default case.
|
"""Process the list of match cases and the catchall default case.
|
||||||
|
|
||||||
@ -99,33 +85,13 @@ class Match(MemoizationLeaf):
|
|||||||
|
|
||||||
# Get the default case which should be the last non-tuple arg
|
# Get the default case which should be the last non-tuple arg
|
||||||
if not isinstance(cases[-1], tuple):
|
if not isinstance(cases[-1], tuple):
|
||||||
default = cases.pop()
|
default = cases[-1]
|
||||||
default = (
|
cases = cases[:-1]
|
||||||
cls._create_case_var_with_var_data(default)
|
|
||||||
if not isinstance(default, BaseComponent)
|
|
||||||
else default
|
|
||||||
)
|
|
||||||
|
|
||||||
return cases, default
|
return list(cases), default
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _create_case_var_with_var_data(cls, case_element):
|
def _process_match_cases(cls, cases: Sequence):
|
||||||
"""Convert a case element into a Var.If the case
|
|
||||||
is a Style type, we extract the var data and merge it with the
|
|
||||||
newly created Var.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
case_element: The case element.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The case element Var.
|
|
||||||
"""
|
|
||||||
_var_data = case_element._var_data if isinstance(case_element, Style) else None
|
|
||||||
case_element = LiteralVar.create(case_element, _var_data=_var_data)
|
|
||||||
return case_element
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _process_match_cases(cls, cases: List) -> List[List[Var]]:
|
|
||||||
"""Process the individual match cases.
|
"""Process the individual match cases.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -137,34 +103,18 @@ class Match(MemoizationLeaf):
|
|||||||
Raises:
|
Raises:
|
||||||
ValueError: If the default case is not the last case or the tuple elements are less than 2.
|
ValueError: If the default case is not the last case or the tuple elements are less than 2.
|
||||||
"""
|
"""
|
||||||
match_cases = []
|
|
||||||
for case in cases:
|
for case in cases:
|
||||||
if not isinstance(case, tuple):
|
if not isinstance(case, tuple):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"rx.match should have tuples of cases and a default case as the last argument."
|
"rx.match should have tuples of cases and a default case as the last argument."
|
||||||
)
|
)
|
||||||
|
|
||||||
# There should be at least two elements in a case tuple(a condition and return value)
|
# There should be at least two elements in a case tuple(a condition and return value)
|
||||||
if len(case) < 2:
|
if len(case) < 2:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"A case tuple should have at least a match case element and a return value."
|
"A case tuple should have at least a match case element and a return value."
|
||||||
)
|
)
|
||||||
|
|
||||||
case_list = []
|
|
||||||
for element in case:
|
|
||||||
# convert all non component element to vars.
|
|
||||||
el = (
|
|
||||||
cls._create_case_var_with_var_data(element)
|
|
||||||
if not isinstance(element, BaseComponent)
|
|
||||||
else element
|
|
||||||
)
|
|
||||||
if not isinstance(el, (Var, BaseComponent)):
|
|
||||||
raise ValueError("Case element must be a var or component")
|
|
||||||
case_list.append(el)
|
|
||||||
|
|
||||||
match_cases.append(case_list)
|
|
||||||
|
|
||||||
return match_cases
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _validate_return_types(cls, match_cases: List[List[Var]]) -> None:
|
def _validate_return_types(cls, match_cases: List[List[Var]]) -> None:
|
||||||
"""Validate that match cases have the same return types.
|
"""Validate that match cases have the same return types.
|
||||||
@ -202,7 +152,7 @@ class Match(MemoizationLeaf):
|
|||||||
cls,
|
cls,
|
||||||
match_cond_var: Var,
|
match_cond_var: Var,
|
||||||
match_cases: List[List[Var]],
|
match_cases: List[List[Var]],
|
||||||
default: Optional[Union[Var, BaseComponent]],
|
default: Union[Var, BaseComponent],
|
||||||
) -> Union[Component, Var]:
|
) -> Union[Component, Var]:
|
||||||
"""Create and return the match condition var or component.
|
"""Create and return the match condition var or component.
|
||||||
|
|
||||||
@ -217,64 +167,7 @@ class Match(MemoizationLeaf):
|
|||||||
Raises:
|
Raises:
|
||||||
ValueError: If the return types are not vars when creating a match var for Var types.
|
ValueError: If the return types are not vars when creating a match var for Var types.
|
||||||
"""
|
"""
|
||||||
if default is None and types._issubclass(
|
return MatchOperation.create(match_cond_var, match_cases, default)
|
||||||
type(match_cases[0][-1]), BaseComponent
|
|
||||||
):
|
|
||||||
default = Fragment.create()
|
|
||||||
|
|
||||||
if types._issubclass(type(match_cases[0][-1]), BaseComponent):
|
|
||||||
return Fragment.create(
|
|
||||||
cls(
|
|
||||||
cond=match_cond_var,
|
|
||||||
match_cases=match_cases,
|
|
||||||
default=default,
|
|
||||||
children=[case[-1] for case in match_cases] + [default], # type: ignore
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Validate the match cases (as well as the default case) to have Var return types.
|
|
||||||
if any(
|
|
||||||
case for case in match_cases if not types._isinstance(case[-1], Var)
|
|
||||||
) or not types._isinstance(default, Var):
|
|
||||||
raise ValueError("Return types of match cases should be Vars.")
|
|
||||||
|
|
||||||
return Var(
|
|
||||||
_js_expr=format.format_match(
|
|
||||||
cond=str(match_cond_var),
|
|
||||||
match_cases=match_cases,
|
|
||||||
default=default, # type: ignore
|
|
||||||
),
|
|
||||||
_var_type=default._var_type, # type: ignore
|
|
||||||
_var_data=VarData.merge(
|
|
||||||
match_cond_var._get_all_var_data(),
|
|
||||||
*[el._get_all_var_data() for case in match_cases for el in case],
|
|
||||||
default._get_all_var_data(), # type: ignore
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def _render(self) -> Tag:
|
|
||||||
return MatchTag(
|
|
||||||
cond=self.cond, match_cases=self.match_cases, default=self.default
|
|
||||||
)
|
|
||||||
|
|
||||||
def render(self) -> Dict:
|
|
||||||
"""Render the component.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The dictionary for template of component.
|
|
||||||
"""
|
|
||||||
tag = self._render()
|
|
||||||
tag.name = "match"
|
|
||||||
return dict(tag)
|
|
||||||
|
|
||||||
def add_imports(self) -> ImportDict:
|
|
||||||
"""Add imports for the Match component.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The import dict.
|
|
||||||
"""
|
|
||||||
var_data = VarData.merge(self.cond._get_all_var_data())
|
|
||||||
return var_data.old_school_imports() if var_data else {}
|
|
||||||
|
|
||||||
|
|
||||||
match = Match.create
|
match = Match.create
|
||||||
|
@ -7,18 +7,30 @@ import functools
|
|||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
import sys
|
import sys
|
||||||
from typing import TYPE_CHECKING, Any, Callable, NoReturn, TypeVar, Union, overload
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
NoReturn,
|
||||||
|
Sequence,
|
||||||
|
TypeVar,
|
||||||
|
Union,
|
||||||
|
overload,
|
||||||
|
)
|
||||||
|
|
||||||
from reflex.constants.base import Dirs
|
from reflex.constants.base import Dirs
|
||||||
from reflex.utils.exceptions import PrimitiveUnserializableToJSON, VarTypeError
|
from reflex.utils.exceptions import PrimitiveUnserializableToJSON, VarTypeError
|
||||||
from reflex.utils.imports import ImportDict, ImportVar
|
from reflex.utils.imports import ImportDict, ImportVar
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
|
VAR_TYPE,
|
||||||
|
CachedVarOperation,
|
||||||
CustomVarOperationReturn,
|
CustomVarOperationReturn,
|
||||||
LiteralVar,
|
LiteralVar,
|
||||||
ReflexCallable,
|
ReflexCallable,
|
||||||
Var,
|
Var,
|
||||||
VarData,
|
VarData,
|
||||||
|
cached_property_no_lock,
|
||||||
nary_type_computer,
|
nary_type_computer,
|
||||||
passthrough_unary_type_computer,
|
passthrough_unary_type_computer,
|
||||||
unionize,
|
unionize,
|
||||||
@ -1056,4 +1068,116 @@ def ternary_operation(
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
TUPLE_ENDS_IN_VAR = (
|
||||||
|
tuple[Var[VAR_TYPE]]
|
||||||
|
| tuple[Var, Var[VAR_TYPE]]
|
||||||
|
| tuple[Var, Var, Var[VAR_TYPE]]
|
||||||
|
| tuple[Var, Var, Var, Var[VAR_TYPE]]
|
||||||
|
| tuple[Var, Var, Var, Var, Var[VAR_TYPE]]
|
||||||
|
| tuple[Var, Var, Var, Var, Var, Var[VAR_TYPE]]
|
||||||
|
| tuple[Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]]
|
||||||
|
| tuple[Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]]
|
||||||
|
| tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]]
|
||||||
|
| tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]]
|
||||||
|
| tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]]
|
||||||
|
| tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]]
|
||||||
|
| tuple[Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]]
|
||||||
|
| tuple[
|
||||||
|
Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var, Var[VAR_TYPE]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(
|
||||||
|
eq=False,
|
||||||
|
frozen=True,
|
||||||
|
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
||||||
|
)
|
||||||
|
class MatchOperation(CachedVarOperation, Var[VAR_TYPE]):
|
||||||
|
"""Base class for immutable match operations."""
|
||||||
|
|
||||||
|
_cond: Var[bool] = dataclasses.field(
|
||||||
|
default_factory=lambda: LiteralBooleanVar.create(True)
|
||||||
|
)
|
||||||
|
_cases: tuple[TUPLE_ENDS_IN_VAR[VAR_TYPE], ...] = dataclasses.field(
|
||||||
|
default_factory=tuple
|
||||||
|
)
|
||||||
|
_default: Var[VAR_TYPE] = dataclasses.field(
|
||||||
|
default_factory=lambda: Var.create(None)
|
||||||
|
)
|
||||||
|
|
||||||
|
@cached_property_no_lock
|
||||||
|
def _cached_var_name(self) -> str:
|
||||||
|
"""Get the name of the var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The name of the var.
|
||||||
|
"""
|
||||||
|
switch_code = f"(() => {{ switch (JSON.stringify({self._cond!s})) {{"
|
||||||
|
|
||||||
|
for case in self._cases:
|
||||||
|
conditions = case[:-1]
|
||||||
|
return_value = case[-1]
|
||||||
|
|
||||||
|
case_conditions = " ".join(
|
||||||
|
[f"case JSON.stringify({condition!s}):" for condition in conditions]
|
||||||
|
)
|
||||||
|
case_code = f"{case_conditions} return ({return_value!s}); break;"
|
||||||
|
switch_code += case_code
|
||||||
|
|
||||||
|
switch_code += f"default: return ({self._default!s}); break;"
|
||||||
|
switch_code += "};})()"
|
||||||
|
|
||||||
|
return switch_code
|
||||||
|
|
||||||
|
@cached_property_no_lock
|
||||||
|
def _cached_get_all_var_data(self) -> VarData | None:
|
||||||
|
"""Get the VarData for the var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The VarData for the var.
|
||||||
|
"""
|
||||||
|
return VarData.merge(
|
||||||
|
self._cond._get_all_var_data(),
|
||||||
|
*(
|
||||||
|
case._get_all_var_data()
|
||||||
|
for cond_or_return in self._cases
|
||||||
|
for case in cond_or_return
|
||||||
|
),
|
||||||
|
self._default._get_all_var_data(),
|
||||||
|
self._var_data,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(
|
||||||
|
cls,
|
||||||
|
cond: Any,
|
||||||
|
cases: Sequence[Sequence[Any | Var[VAR_TYPE]]],
|
||||||
|
default: Var[VAR_TYPE] | VAR_TYPE,
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
_var_type: type[VAR_TYPE] | None = None,
|
||||||
|
):
|
||||||
|
"""Create the match operation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cond: The condition.
|
||||||
|
cases: The cases.
|
||||||
|
default: The default case.
|
||||||
|
_var_data: Additional hooks and imports associated with the Var.
|
||||||
|
_var_type: The type of the Var.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The match operation.
|
||||||
|
"""
|
||||||
|
cases = tuple(tuple(Var.create(c) for c in case) for case in cases)
|
||||||
|
return cls(
|
||||||
|
_js_expr="",
|
||||||
|
_var_data=_var_data,
|
||||||
|
_var_type=_var_type,
|
||||||
|
_cond=Var.create(cond),
|
||||||
|
_cases=cases,
|
||||||
|
_default=Var.create(default),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
NUMBER_TYPES = (int, float, NumberVar)
|
NUMBER_TYPES = (int, float, NumberVar)
|
||||||
|
@ -39,7 +39,7 @@ def create_color_var(color):
|
|||||||
create_color_var(
|
create_color_var(
|
||||||
rx.color(ColorState.color, ColorState.shade, ColorState.alpha) # type: ignore
|
rx.color(ColorState.color, ColorState.shade, ColorState.alpha) # type: ignore
|
||||||
),
|
),
|
||||||
f'("var(--"+{color_state_name!s}.color+"-"+({color_state_name!s}.alpha ? "a" : "")+(((__to_string) => __to_string.toString())({color_state_name!s}.shade))+")")',
|
f'("var(--"+{color_state_name!s}.color+"-"+(((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))({color_state_name!s}.alpha, "a", ""))+(((__to_string) => __to_string.toString())({color_state_name!s}.shade))+")")',
|
||||||
Color,
|
Color,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@ -78,11 +78,11 @@ def test_color(color, expected, expected_type: Union[Type[str], Type[Color]]):
|
|||||||
[
|
[
|
||||||
(
|
(
|
||||||
rx.cond(True, rx.color("mint"), rx.color("tomato", 5)),
|
rx.cond(True, rx.color("mint"), rx.color("tomato", 5)),
|
||||||
'(true ? "var(--mint-7)" : "var(--tomato-5)")',
|
'((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => "var(--mint-7)"), (() => "var(--tomato-5)")))())',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
rx.cond(True, rx.color(ColorState.color), rx.color(ColorState.color, 5)), # type: ignore
|
rx.cond(True, rx.color(ColorState.color), rx.color(ColorState.color, 5)), # type: ignore
|
||||||
f'(true ? ("var(--"+{color_state_name!s}.color+"-7)") : ("var(--"+{color_state_name!s}.color+"-5)"))',
|
f'((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))(true, (() => ("var(--"+{color_state_name!s}.color+"-7)")), (() => ("var(--"+{color_state_name!s}.color+"-5)"))))())',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
rx.match(
|
rx.match(
|
||||||
|
@ -128,7 +128,7 @@ def display_colors_set(color):
|
|||||||
def display_nested_list_element(
|
def display_nested_list_element(
|
||||||
element: ArrayVar[Sequence[str]], index: NumberVar[int]
|
element: ArrayVar[Sequence[str]], index: NumberVar[int]
|
||||||
):
|
):
|
||||||
assert element._var_type == Sequence[str]
|
assert element._var_type == List[str]
|
||||||
assert index._var_type is int
|
assert index._var_type is int
|
||||||
return box(text(element[index]))
|
return box(text(element[index]))
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import Dict, List, Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -17,73 +17,6 @@ class MatchState(BaseState):
|
|||||||
string: str = "random string"
|
string: str = "random string"
|
||||||
|
|
||||||
|
|
||||||
def test_match_components():
|
|
||||||
"""Test matching cases with return values as components."""
|
|
||||||
match_case_tuples = (
|
|
||||||
(1, rx.text("first value")),
|
|
||||||
(2, 3, rx.text("second value")),
|
|
||||||
([1, 2], rx.text("third value")),
|
|
||||||
("random", rx.text("fourth value")),
|
|
||||||
({"foo": "bar"}, rx.text("fifth value")),
|
|
||||||
(MatchState.num + 1, rx.text("sixth value")),
|
|
||||||
rx.text("default value"),
|
|
||||||
)
|
|
||||||
match_comp = Match.create(MatchState.value, *match_case_tuples)
|
|
||||||
match_dict = match_comp.render() # type: ignore
|
|
||||||
assert match_dict["name"] == "Fragment"
|
|
||||||
|
|
||||||
[match_child] = match_dict["children"]
|
|
||||||
|
|
||||||
assert match_child["name"] == "match"
|
|
||||||
assert str(match_child["cond"]) == f"{MatchState.get_name()}.value"
|
|
||||||
|
|
||||||
match_cases = match_child["match_cases"]
|
|
||||||
assert len(match_cases) == 6
|
|
||||||
|
|
||||||
assert match_cases[0][0]._js_expr == "1"
|
|
||||||
assert match_cases[0][0]._var_type is int
|
|
||||||
first_return_value_render = match_cases[0][1].render()
|
|
||||||
assert first_return_value_render["name"] == "RadixThemesText"
|
|
||||||
assert first_return_value_render["children"][0]["contents"] == '{"first value"}'
|
|
||||||
|
|
||||||
assert match_cases[1][0]._js_expr == "2"
|
|
||||||
assert match_cases[1][0]._var_type is int
|
|
||||||
assert match_cases[1][1]._js_expr == "3"
|
|
||||||
assert match_cases[1][1]._var_type is int
|
|
||||||
second_return_value_render = match_cases[1][2].render()
|
|
||||||
assert second_return_value_render["name"] == "RadixThemesText"
|
|
||||||
assert second_return_value_render["children"][0]["contents"] == '{"second value"}'
|
|
||||||
|
|
||||||
assert match_cases[2][0]._js_expr == "[1, 2]"
|
|
||||||
assert match_cases[2][0]._var_type == List[int]
|
|
||||||
third_return_value_render = match_cases[2][1].render()
|
|
||||||
assert third_return_value_render["name"] == "RadixThemesText"
|
|
||||||
assert third_return_value_render["children"][0]["contents"] == '{"third value"}'
|
|
||||||
|
|
||||||
assert match_cases[3][0]._js_expr == '"random"'
|
|
||||||
assert match_cases[3][0]._var_type is str
|
|
||||||
fourth_return_value_render = match_cases[3][1].render()
|
|
||||||
assert fourth_return_value_render["name"] == "RadixThemesText"
|
|
||||||
assert fourth_return_value_render["children"][0]["contents"] == '{"fourth value"}'
|
|
||||||
|
|
||||||
assert match_cases[4][0]._js_expr == '({ ["foo"] : "bar" })'
|
|
||||||
assert match_cases[4][0]._var_type == Dict[str, str]
|
|
||||||
fifth_return_value_render = match_cases[4][1].render()
|
|
||||||
assert fifth_return_value_render["name"] == "RadixThemesText"
|
|
||||||
assert fifth_return_value_render["children"][0]["contents"] == '{"fifth value"}'
|
|
||||||
|
|
||||||
assert match_cases[5][0]._js_expr == f"({MatchState.get_name()}.num + 1)"
|
|
||||||
assert match_cases[5][0]._var_type is int
|
|
||||||
fifth_return_value_render = match_cases[5][1].render()
|
|
||||||
assert fifth_return_value_render["name"] == "RadixThemesText"
|
|
||||||
assert fifth_return_value_render["children"][0]["contents"] == '{"sixth value"}'
|
|
||||||
|
|
||||||
default = match_child["default"].render()
|
|
||||||
|
|
||||||
assert default["name"] == "RadixThemesText"
|
|
||||||
assert default["children"][0]["contents"] == '{"default value"}'
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"cases, expected",
|
"cases, expected",
|
||||||
[
|
[
|
||||||
@ -102,7 +35,7 @@ def test_match_components():
|
|||||||
f'(() => {{ switch (JSON.stringify({MatchState.get_name()}.value)) {{case JSON.stringify(1): return ("first"); break;case JSON.stringify(2): case JSON.stringify(3): return '
|
f'(() => {{ switch (JSON.stringify({MatchState.get_name()}.value)) {{case JSON.stringify(1): return ("first"); break;case JSON.stringify(2): case JSON.stringify(3): return '
|
||||||
'("second value"); break;case JSON.stringify([1, 2]): return ("third-value"); break;case JSON.stringify("random"): '
|
'("second value"); break;case JSON.stringify([1, 2]): return ("third-value"); break;case JSON.stringify("random"): '
|
||||||
'return ("fourth_value"); break;case JSON.stringify(({ ["foo"] : "bar" })): return ("fifth value"); '
|
'return ("fourth_value"); break;case JSON.stringify(({ ["foo"] : "bar" })): return ("fifth value"); '
|
||||||
f'break;case JSON.stringify(({MatchState.get_name()}.num + 1)): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): '
|
f'break;case JSON.stringify((((_lhs, _rhs) => (_lhs + _rhs))({MatchState.get_name()}.num, 1))): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): '
|
||||||
f'return ({MatchState.get_name()}.string); break;case JSON.stringify({MatchState.get_name()}.string): return (({MatchState.get_name()}.value+" - string")); break;default: '
|
f'return ({MatchState.get_name()}.string); break;case JSON.stringify({MatchState.get_name()}.string): return (({MatchState.get_name()}.value+" - string")); break;default: '
|
||||||
'return ("default value"); break;};})()',
|
'return ("default value"); break;};})()',
|
||||||
),
|
),
|
||||||
@ -121,7 +54,7 @@ def test_match_components():
|
|||||||
f'(() => {{ switch (JSON.stringify({MatchState.get_name()}.value)) {{case JSON.stringify(1): return ("first"); break;case JSON.stringify(2): case JSON.stringify(3): return '
|
f'(() => {{ switch (JSON.stringify({MatchState.get_name()}.value)) {{case JSON.stringify(1): return ("first"); break;case JSON.stringify(2): case JSON.stringify(3): return '
|
||||||
'("second value"); break;case JSON.stringify([1, 2]): return ("third-value"); break;case JSON.stringify("random"): '
|
'("second value"); break;case JSON.stringify([1, 2]): return ("third-value"); break;case JSON.stringify("random"): '
|
||||||
'return ("fourth_value"); break;case JSON.stringify(({ ["foo"] : "bar" })): return ("fifth value"); '
|
'return ("fourth_value"); break;case JSON.stringify(({ ["foo"] : "bar" })): return ("fifth value"); '
|
||||||
f'break;case JSON.stringify(({MatchState.get_name()}.num + 1)): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): '
|
f'break;case JSON.stringify((((_lhs, _rhs) => (_lhs + _rhs))({MatchState.get_name()}.num, 1))): return ("sixth value"); break;case JSON.stringify(({MatchState.get_name()}.value+" - string")): '
|
||||||
f'return ({MatchState.get_name()}.string); break;case JSON.stringify({MatchState.get_name()}.string): return (({MatchState.get_name()}.value+" - string")); break;default: '
|
f'return ({MatchState.get_name()}.string); break;case JSON.stringify({MatchState.get_name()}.string): return (({MatchState.get_name()}.value+" - string")); break;default: '
|
||||||
f"return ({MatchState.get_name()}.string); break;}};}})()",
|
f"return ({MatchState.get_name()}.string); break;}};}})()",
|
||||||
),
|
),
|
||||||
@ -143,17 +76,14 @@ def test_match_on_component_without_default():
|
|||||||
"""Test that matching cases with return values as components returns a Fragment
|
"""Test that matching cases with return values as components returns a Fragment
|
||||||
as the default case if not provided.
|
as the default case if not provided.
|
||||||
"""
|
"""
|
||||||
from reflex.components.base.fragment import Fragment
|
|
||||||
|
|
||||||
match_case_tuples = (
|
match_case_tuples = (
|
||||||
(1, rx.text("first value")),
|
(1, rx.text("first value")),
|
||||||
(2, 3, rx.text("second value")),
|
(2, 3, rx.text("second value")),
|
||||||
)
|
)
|
||||||
|
|
||||||
match_comp = Match.create(MatchState.value, *match_case_tuples)
|
match_comp = Match.create(MatchState.value, *match_case_tuples)
|
||||||
default = match_comp.render()["children"][0]["default"] # type: ignore
|
|
||||||
|
|
||||||
assert isinstance(default, Fragment)
|
assert isinstance(match_comp, Var)
|
||||||
|
|
||||||
|
|
||||||
def test_match_on_var_no_default():
|
def test_match_on_var_no_default():
|
||||||
@ -247,8 +177,8 @@ def test_match_case_tuple_elements(match_case):
|
|||||||
(MatchState.num + 1, "black"),
|
(MatchState.num + 1, "black"),
|
||||||
rx.text("default value"),
|
rx.text("default value"),
|
||||||
),
|
),
|
||||||
'Match cases should have the same return types. Case 3 with return value `"red"` of type '
|
"Match cases should have the same return types. Case 3 with return value `red` of type "
|
||||||
"<class 'reflex.vars.sequence.LiteralStringVar'> is not <class 'reflex.components.component.BaseComponent'>",
|
"<class 'str'> is not <class 'reflex.components.component.BaseComponent'>",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
@ -261,7 +191,7 @@ def test_match_case_tuple_elements(match_case):
|
|||||||
rx.text("default value"),
|
rx.text("default value"),
|
||||||
),
|
),
|
||||||
'Match cases should have the same return types. Case 3 with return value `<RadixThemesText as={"p"}> {"first value"} </RadixThemesText>` '
|
'Match cases should have the same return types. Case 3 with return value `<RadixThemesText as={"p"}> {"first value"} </RadixThemesText>` '
|
||||||
"of type <class 'reflex.components.radix.themes.typography.text.Text'> is not <class 'reflex.vars.base.Var'>",
|
"of type <class 'reflex.components.radix.themes.typography.text.Text'> is not <class 'str'>",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -148,7 +148,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe
|
|||||||
(
|
(
|
||||||
"code",
|
"code",
|
||||||
{},
|
{},
|
||||||
"""(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?<lang>.*)/); const _language = match ? match[1] : ''; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( <RadixThemesCode {...props}>{children}</RadixThemesCode> ) : ( <SyntaxHighlighter children={((Array.isArray(children)) ? children.join("\\n") : children)} css={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} customStyle={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} language={_language} style={((resolvedColorMode === "light") ? oneLight : oneDark)} wrapLongLines={true} {...props}/> ); })""",
|
'(({node, inline, className, children, ...props}) => { const match = (className || \'\').match(/language-(?<lang>.*)/); const _language = match ? match[1] : \'\'; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( <RadixThemesCode {...props}>{children}</RadixThemesCode> ) : ( <SyntaxHighlighter children={(((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))((Array.isArray(children)), (((...args) => (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\\n")), children))} css={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} customStyle={({ ["marginTop"] : "1em", ["marginBottom"] : "1em" })} language={_language} style={((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))((((_lhs, _rhs) => (_lhs === _rhs))(resolvedColorMode, "light")), (() => oneLight), (() => oneDark)))())} wrapLongLines={true} {...props}/> ); })',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"code",
|
"code",
|
||||||
@ -157,7 +157,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe
|
|||||||
value, **props
|
value, **props
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
"""(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?<lang>.*)/); const _language = match ? match[1] : ''; ; return inline ? ( <RadixThemesCode {...props}>{children}</RadixThemesCode> ) : ( <RadixThemesBox css={({ ["pre"] : ({ ["margin"] : "0", ["padding"] : "24px", ["background"] : "transparent", ["overflow-x"] : "auto", ["border-radius"] : "6px" }) })} {...props}><ShikiCode code={((Array.isArray(children)) ? children.join("\\n") : children)} decorations={[]} language={_language} theme={((resolvedColorMode === "light") ? "one-light" : "one-dark-pro")} transformers={[]}/></RadixThemesBox> ); })""",
|
'(({node, inline, className, children, ...props}) => { const match = (className || \'\').match(/language-(?<lang>.*)/); const _language = match ? match[1] : \'\'; ; return inline ? ( <RadixThemesCode {...props}>{children}</RadixThemesCode> ) : ( <RadixThemesBox css={({ ["pre"] : ({ ["margin"] : "0", ["padding"] : "24px", ["background"] : "transparent", ["overflow-x"] : "auto", ["border-radius"] : "6px" }) })} {...props}><ShikiCode code={(((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))((Array.isArray(children)), (((...args) => (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\\n")), children))} decorations={[]} language={_language} theme={((((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))((((_lhs, _rhs) => (_lhs === _rhs))(resolvedColorMode, "light")), (() => "one-light"), (() => "one-dark-pro")))())} transformers={[]}/></RadixThemesBox> ); })',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"h1",
|
"h1",
|
||||||
@ -171,7 +171,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe
|
|||||||
(
|
(
|
||||||
"code",
|
"code",
|
||||||
{"codeblock": syntax_highlighter_memoized_component(CodeBlock)},
|
{"codeblock": syntax_highlighter_memoized_component(CodeBlock)},
|
||||||
"""(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?<lang>.*)/); const _language = match ? match[1] : ''; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( <RadixThemesCode {...props}>{children}</RadixThemesCode> ) : ( <CodeBlock code={((Array.isArray(children)) ? children.join("\\n") : children)} language={_language} {...props}/> ); })""",
|
"(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?<lang>.*)/); const _language = match ? match[1] : ''; if (_language) { (async () => { try { const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${_language}`); SyntaxHighlighter.registerLanguage(_language, module.default); } catch (error) { console.error(`Error importing language module for ${_language}:`, error); } })(); } ; return inline ? ( <RadixThemesCode {...props}>{children}</RadixThemesCode> ) : ( <CodeBlock code={(((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))((Array.isArray(children)), (((...args) => (((_array, _sep = \"\") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))(\"\\n\")), children))} language={_language} {...props}/> ); })",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"code",
|
"code",
|
||||||
@ -180,7 +180,7 @@ def test_create_map_fn_var_subclass(cls, fn_body, fn_args, explicit_return, expe
|
|||||||
ShikiHighLevelCodeBlock
|
ShikiHighLevelCodeBlock
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
"""(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?<lang>.*)/); const _language = match ? match[1] : ''; ; return inline ? ( <RadixThemesCode {...props}>{children}</RadixThemesCode> ) : ( <CodeBlock code={((Array.isArray(children)) ? children.join("\\n") : children)} language={_language} {...props}/> ); })""",
|
"""(({node, inline, className, children, ...props}) => { const match = (className || '').match(/language-(?<lang>.*)/); const _language = match ? match[1] : ''; ; return inline ? ( <RadixThemesCode {...props}>{children}</RadixThemesCode> ) : ( <CodeBlock code={(((_condition, _if_true, _if_false) => (_condition ? _if_true : _if_false))((Array.isArray(children)), (((...args) => (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))(children, ...args)))("\\n")), children))} language={_language} {...props}/> ); })""",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user