
* fully migrate vars into new system * i hate rufffff (no i don't) * fix silly pright issues (except colormode and state) * remove all instances of Var.create * create immutable callable var and get rid of more base vars * implement hash for all functions * get reflex-web to compile * get it to compile reflex-web successfully * fix tests * fix pyi * use override from typing_extension * put plotly inside of a catch * dicts are unusable sadly * fix silly mistake * overload equals to special case immutable var * improve test_cond * solve more CI issues, down to 94 failures * down to 20 errors * down to 13 errors * pass all testcases * fix pyright issues * reorder things * use get origin more * use fixed_type logic * various optimizations * go back to passing test cases * use less boilerplate * remove unnecessary print message * remove weird comment * add test for html issue * add type ignore * fix another silly issue * override get all var data for var operations call * make integration tests pass * fix immutable call var * better logic for finding parent class * use even better logic for finding state wrt computedvar * only choose the ones that are defined in the same module * small dict to large dict * [REF-3591] Remove chakra-related files from immutable vars PR (#3821) * Add comments to html metadata component (#3731) * fix: add verification for path /404 (#3723) Co-authored-by: coolstorm <manas.gupta@fampay.in> * Use the new state name when setting `is_hydrated` to false (#3738) * Use `._is_mutable()` to account for parent state proxy (#3739) When a parent state proxy is set, also allow child StateProxy._self_mutable to override the parent's `_is_mutable()`. * bump to 0.5.9 (#3746) * add message when installing requirements.txt is needed for chosen template during init (#3750) * #3752 bugfix add domain for XAxis (#3764) * fix appharness app_source typing (#3777) * fix import clash between connectionToaster and hooks.useState (#3749) * use different registry when in china, fixes #3700 (#3702) * do not reload compilation if using local app in AppHarness (#3790) * do not reload if using local app * Update reflex/testing.py Co-authored-by: Masen Furer <m_github@0x26.net> --------- Co-authored-by: Masen Furer <m_github@0x26.net> * Bump memory on relevant actions (#3781) Co-authored-by: Alek Petuskey <alekpetuskey@Aleks-MacBook-Pro.local> * [REF-3334] Validate Toast Props (#3793) * [REF-3536][REF-3537][REF-3541] Move chakra components into its repo(reflex-chakra) (#3798) * fix get_uuid_string_var (#3795) * minor State cleanup (#3768) * Fix code wrap in markdown (#3755) --------- Co-authored-by: Alek Petuskey <alek@pynecone.io> Co-authored-by: Manas Gupta <53006261+Manas1820@users.noreply.github.com> Co-authored-by: coolstorm <manas.gupta@fampay.in> Co-authored-by: Thomas Brandého <thomas.brandeho@gmail.com> Co-authored-by: Shubhankar Dimri <dimrishubhi@gmail.com> Co-authored-by: benedikt-bartscher <31854409+benedikt-bartscher@users.noreply.github.com> Co-authored-by: Khaleel Al-Adhami <khaleel.aladhami@gmail.com> Co-authored-by: Alek Petuskey <alekpetuskey@Aleks-MacBook-Pro.local> Co-authored-by: Elijah Ahianyo <elijahahianyo@gmail.com> * pyproject.toml: bump to 0.6.0a1 * pyproject.toml: depend on reflex-chakra>=0.6.0a New Var system support in reflex-chakra 0.6.0a1 * poetry.lock: relock dependencies * integration: bump listening timeout to 1200 seconds * integration: bump listening timeout to 1800 seconds * Use cached_var_no_lock to avoid ImmutableVar deadlocks (#3835) * Use cached_var_no_lock to avoid ImmutableVar deadlocks ImmutableVar subclasses will always return the same value for a _var_name or _get_all_var_data so there is no need to use a per-class lock to protect a cached attribute on an instance, and doing so actually is observed to cause deadlocks when a particular _cached_var_name creates new LiteralVar instances and attempts to serialize them. * remove unused module global --------- Co-authored-by: Masen Furer <m_github@0x26.net> Co-authored-by: Alek Petuskey <alek@pynecone.io> Co-authored-by: Manas Gupta <53006261+Manas1820@users.noreply.github.com> Co-authored-by: coolstorm <manas.gupta@fampay.in> Co-authored-by: Thomas Brandého <thomas.brandeho@gmail.com> Co-authored-by: Shubhankar Dimri <dimrishubhi@gmail.com> Co-authored-by: benedikt-bartscher <31854409+benedikt-bartscher@users.noreply.github.com> Co-authored-by: Alek Petuskey <alekpetuskey@Aleks-MacBook-Pro.local> Co-authored-by: Elijah Ahianyo <elijahahianyo@gmail.com>
274 lines
8.9 KiB
Python
274 lines
8.9 KiB
Python
"""rx.match."""
|
|
|
|
import textwrap
|
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
|
|
from reflex.components.base import Fragment
|
|
from reflex.components.component import BaseComponent, Component, MemoizationLeaf
|
|
from reflex.components.tags import MatchTag, Tag
|
|
from reflex.ivars.base import ImmutableVar, LiteralVar
|
|
from reflex.style import Style
|
|
from reflex.utils import format, types
|
|
from reflex.utils.exceptions import MatchTypeError
|
|
from reflex.utils.imports import ImportDict
|
|
from reflex.vars import ImmutableVarData, Var, VarData
|
|
|
|
|
|
class Match(MemoizationLeaf):
|
|
"""Match cases based on a condition."""
|
|
|
|
# The condition to determine which case to match.
|
|
cond: Var[Any]
|
|
|
|
# The list of match cases to be matched.
|
|
match_cases: List[Any] = []
|
|
|
|
# The catchall case to match.
|
|
default: Any
|
|
|
|
@classmethod
|
|
def create(cls, cond: Any, *cases) -> Union[Component, Var]:
|
|
"""Create a Match Component.
|
|
|
|
Args:
|
|
cond: The condition to determine which case to match.
|
|
cases: This list of cases to match.
|
|
|
|
Returns:
|
|
The match component.
|
|
|
|
Raises:
|
|
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(list(cases))
|
|
match_cases = cls._process_match_cases(cases)
|
|
|
|
cls._validate_return_types(match_cases)
|
|
|
|
if default is None and types._issubclass(type(match_cases[0][-1]), Var):
|
|
raise ValueError(
|
|
"For cases with return types as Vars, a default case must be provided"
|
|
)
|
|
|
|
return cls._create_match_cond_var_or_component(
|
|
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
|
|
def _process_cases(
|
|
cls, cases: List
|
|
) -> Tuple[List, Optional[Union[Var, BaseComponent]]]:
|
|
"""Process the list of match cases and the catchall default case.
|
|
|
|
Args:
|
|
cases: The list of match cases.
|
|
|
|
Returns:
|
|
The default case and the list of match case tuples.
|
|
|
|
Raises:
|
|
ValueError: If there are multiple default cases.
|
|
"""
|
|
default = None
|
|
|
|
if len([case for case in cases if not isinstance(case, tuple)]) > 1:
|
|
raise ValueError("rx.match can only have one default case.")
|
|
|
|
if not cases:
|
|
raise ValueError("rx.match should have at least one case.")
|
|
|
|
# Get the default case which should be the last non-tuple arg
|
|
if not isinstance(cases[-1], tuple):
|
|
default = cases.pop()
|
|
default = (
|
|
cls._create_case_var_with_var_data(default)
|
|
if not isinstance(default, BaseComponent)
|
|
else default
|
|
)
|
|
|
|
return cases, default
|
|
|
|
@classmethod
|
|
def _create_case_var_with_var_data(cls, case_element):
|
|
"""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.
|
|
|
|
Args:
|
|
cases: The match cases.
|
|
|
|
Returns:
|
|
The processed match cases.
|
|
|
|
Raises:
|
|
ValueError: If the default case is not the last case or the tuple elements are less than 2.
|
|
"""
|
|
match_cases = []
|
|
for case in cases:
|
|
if not isinstance(case, tuple):
|
|
raise ValueError(
|
|
"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)
|
|
if len(case) < 2:
|
|
raise ValueError(
|
|
"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
|
|
def _validate_return_types(cls, match_cases: List[List[Var]]) -> None:
|
|
"""Validate that match cases have the same return types.
|
|
|
|
Args:
|
|
match_cases: The match cases.
|
|
|
|
Raises:
|
|
MatchTypeError: If the return types of cases are different.
|
|
"""
|
|
first_case_return = match_cases[0][-1]
|
|
return_type = type(first_case_return)
|
|
|
|
if types._isinstance(first_case_return, BaseComponent):
|
|
return_type = BaseComponent
|
|
elif types._isinstance(first_case_return, Var):
|
|
return_type = Var
|
|
|
|
for index, case in enumerate(match_cases):
|
|
if not types._issubclass(type(case[-1]), return_type):
|
|
raise MatchTypeError(
|
|
f"Match cases should have the same return types. Case {index} with return "
|
|
f"value `{case[-1]._var_name if isinstance(case[-1], Var) else textwrap.shorten(str(case[-1]), width=250)}`"
|
|
f" of type {type(case[-1])!r} is not {return_type}"
|
|
)
|
|
|
|
@classmethod
|
|
def _create_match_cond_var_or_component(
|
|
cls,
|
|
match_cond_var: Var,
|
|
match_cases: List[List[Var]],
|
|
default: Optional[Union[Var, BaseComponent]],
|
|
) -> Union[Component, Var]:
|
|
"""Create and return the match condition var or component.
|
|
|
|
Args:
|
|
match_cond_var: The match condition.
|
|
match_cases: The list of match cases.
|
|
default: The default case.
|
|
|
|
Returns:
|
|
The match component wrapped in a fragment or the match var.
|
|
|
|
Raises:
|
|
ValueError: If the return types are not vars when creating a match var for Var types.
|
|
"""
|
|
if default is None and types._issubclass(
|
|
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 ImmutableVar(
|
|
_var_name=format.format_match(
|
|
cond=match_cond_var._var_name_unwrapped,
|
|
match_cases=match_cases, # type: ignore
|
|
default=default, # type: ignore
|
|
),
|
|
_var_type=default._var_type, # type: ignore
|
|
_var_data=ImmutableVarData.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.
|
|
"""
|
|
return getattr(VarData.merge(self.cond._get_all_var_data()), "imports", {})
|
|
|
|
|
|
match = Match.create
|