From 47eebe0ea5b3e4cd92e3e816e4e4aae813ceb5a6 Mon Sep 17 00:00:00 2001 From: Nikhil Rao Date: Mon, 13 Feb 2023 15:27:03 -0800 Subject: [PATCH] Clean up cond vars (#536) --- pynecone/__init__.py | 2 +- pynecone/components/__init__.py | 27 +++++++++--- pynecone/components/forms/input.py | 1 - pynecone/components/forms/slider.py | 3 ++ pynecone/components/forms/textarea.py | 1 - pynecone/components/tags/tag.py | 59 +-------------------------- pynecone/route.py | 10 ++--- pynecone/utils.py | 14 +++---- pynecone/var.py | 8 ++-- tests/components/layout/test_cond.py | 11 ++--- tests/components/test_tag.py | 9 ---- tests/conftest.py | 4 +- tests/test_propcond.py | 34 --------------- 13 files changed, 51 insertions(+), 132 deletions(-) delete mode 100644 tests/test_propcond.py diff --git a/pynecone/__init__.py b/pynecone/__init__.py index 56e66b33f..9027df00b 100644 --- a/pynecone/__init__.py +++ b/pynecone/__init__.py @@ -10,7 +10,7 @@ from .components.component import custom_component as component from .components.graphing.victory import data from .config import Config from .constants import Env, Transports -from .event import EventChain, console_log, redirect, window_alert +from .event import EVENT_ARG, EventChain, console_log, redirect, window_alert from .middleware import Middleware from .model import Model, session from .route import route diff --git a/pynecone/components/__init__.py b/pynecone/components/__init__.py index 3a6fac1e8..eb72b009f 100644 --- a/pynecone/components/__init__.py +++ b/pynecone/components/__init__.py @@ -106,11 +106,12 @@ def cond(condition: Any, c1: Any, c2: Any = None): Returns: The conditional component. + + Raises: + ValueError: If the arguments are invalid. """ # Import here to avoid circular imports. - from pynecone.var import Var - - from .tags.tag import PropCond + from pynecone.var import BaseVar, Var # Convert the condition to a Var. cond_var = Var.create(condition) @@ -123,6 +124,20 @@ def cond(condition: Any, c1: Any, c2: Any = None): ), "Both arguments must be components." return Cond.create(cond_var, c1, c2) - # Otherwise, create a PropCond. - assert not isinstance(c2, Component), "Both arguments must be props." - return PropCond.create(cond_var, c1, c2) + # Otherwise, create a conditionl Var. + # Check that the second argument is valid. + if isinstance(c2, Component): + raise ValueError("Both arguments must be props.") + if c2 is None: + raise ValueError("For conditional vars, the second argument must be set.") + + # Create the conditional var. + return BaseVar( + name=utils.format_cond( + cond=cond_var.full_name, + true_value=c1, + false_value=c2, + is_prop=True, + ), + type_=c1.type_ if isinstance(c1, BaseVar) else type(c1), + ) diff --git a/pynecone/components/forms/input.py b/pynecone/components/forms/input.py index dd97c463d..77c043a46 100644 --- a/pynecone/components/forms/input.py +++ b/pynecone/components/forms/input.py @@ -60,7 +60,6 @@ class Input(ChakraComponent): "on_focus": EVENT_ARG.target.value, "on_blur": EVENT_ARG.target.value, "on_key_down": EVENT_ARG.key, - "on_key_press": EVENT_ARG.key, "on_key_up": EVENT_ARG.key, } diff --git a/pynecone/components/forms/slider.py b/pynecone/components/forms/slider.py index 9cd45a1af..4f9af579e 100644 --- a/pynecone/components/forms/slider.py +++ b/pynecone/components/forms/slider.py @@ -16,6 +16,9 @@ class Slider(ChakraComponent): # State var to bind the the input. value: Var[int] + # The color scheme. + color_scheme: Var[str] + # The placeholder text. default_value: Var[int] diff --git a/pynecone/components/forms/textarea.py b/pynecone/components/forms/textarea.py index a47ea3dc3..ae9ee38bb 100644 --- a/pynecone/components/forms/textarea.py +++ b/pynecone/components/forms/textarea.py @@ -54,6 +54,5 @@ class TextArea(ChakraComponent): "on_focus": EVENT_ARG.target.value, "on_blur": EVENT_ARG.target.value, "on_key_down": EVENT_ARG.key, - "on_key_press": EVENT_ARG.key, "on_key_up": EVENT_ARG.key, } diff --git a/pynecone/components/tags/tag.py b/pynecone/components/tags/tag.py index c9f807e64..87f2550fc 100644 --- a/pynecone/components/tags/tag.py +++ b/pynecone/components/tags/tag.py @@ -47,7 +47,7 @@ class Tag(Base): @staticmethod def format_prop( - prop: Union[Var, EventChain, ComponentStyle, PropCond, str], + prop: Union[Var, EventChain, ComponentStyle, str], ) -> Union[int, float, str]: """Format a prop. @@ -71,10 +71,6 @@ class Tag(Base): events = ",".join([utils.format_event(event) for event in prop.events]) prop = f"({local_args}) => Event([{events}])" - # Handle conditional props. - elif isinstance(prop, PropCond): - return str(prop) - # Handle other types. elif isinstance(prop, str): if utils.is_wrapped(prop, "{"): @@ -89,7 +85,7 @@ class Tag(Base): if isinstance(prop, dict): # Convert any var keys to strings. prop = { - key: str(val) if isinstance(val, (Var, PropCond)) else val + key: str(val) if isinstance(val, Var) else val for key, val in prop.items() } @@ -188,54 +184,3 @@ class Tag(Base): Whether the prop is valid. """ return prop is not None and not (isinstance(prop, dict) and len(prop) == 0) - - -class PropCond(Base): - """A conditional prop.""" - - # The condition to determine which prop to render. - cond: Var[Any] - - # The prop to render if the condition is true. - prop1: Any - - # The prop to render if the condition is false. - prop2: Any - - @classmethod - def create(cls, cond: Var, prop1: Any, prop2: Any): - """Create a conditional Prop. - - Args: - cond: The cond to determine which prop to render. - prop1: The prop value to render if the cond is true. - prop2: The prop value to render if the cond is false. - - Returns: - The conditional Prop. - - Raises: - ValueError: If the condition or prop values are not set. - """ - if cond is None: - raise ValueError("The condition must be set.") - if prop1 is None or prop2 is None: - raise ValueError("Both prop values must be set.") - return cls( - cond=cond, - prop1=prop1, - prop2=prop2, - ) - - def __str__(self) -> str: - """Render the prop as a React string. - - Returns: - The React code to render the prop. - """ - return utils.format_cond( - cond=self.cond.full_name, - true_value=self.prop1, - false_value=self.prop2, - is_prop=True, - ) diff --git a/pynecone/route.py b/pynecone/route.py index b50a0a2e5..f6e43b934 100644 --- a/pynecone/route.py +++ b/pynecone/route.py @@ -24,11 +24,11 @@ def route( Note: the decorated functions still need to be imported. Args: - route: The route to reach the page. Defaults to None. - title: The title of the page. Defaults to None. - image: The favicon of the page. Defaults to None. - description: The description of the page. Defaults to None. - on_load: The event handler called when the page load. Defaults to None. + route: The route to reach the page. + title: The title of the page. + image: The favicon of the page. + description: The description of the page + on_load: The event handler called when the page load. Returns: The decorated function. diff --git a/pynecone/utils.py b/pynecone/utils.py index cac739d15..8bd0c302b 100644 --- a/pynecone/utils.py +++ b/pynecone/utils.py @@ -1060,14 +1060,14 @@ def format_cond( Returns: The formatted conditional expression. """ + # Import here to avoid circular imports. + from pynecone.var import Var + if is_prop: - if isinstance(true_value, str): - true_value = wrap(true_value, "'") - if isinstance(false_value, str): - false_value = wrap(false_value, "'") - expr = f"{cond} ? {true_value} : {false_value}".replace("{", "").replace( - "}", "" - ) + prop1 = Var.create(true_value, is_string=type(true_value) == str) + prop2 = Var.create(false_value, is_string=type(false_value) == str) + assert prop1 is not None and prop2 is not None, "Invalid prop values" + expr = f"{cond} ? {prop1} : {prop2}".replace("{", "").replace("}", "") else: expr = f"{cond} ? {true_value} : {false_value}" diff --git a/pynecone/var.py b/pynecone/var.py index 2272c0aca..3b1f41254 100644 --- a/pynecone/var.py +++ b/pynecone/var.py @@ -876,7 +876,7 @@ class PCDict(dict): self._reassign_field() def setdefault(self, *args, **kwargs): - """set default. + """Return value of key if or set default. Args: args: The args passed. @@ -901,7 +901,7 @@ class PCDict(dict): self._reassign_field() def update(self, *args, **kwargs): - """update dict. + """Update the dict with another dict. Args: args: The args passed. @@ -911,7 +911,7 @@ class PCDict(dict): self._reassign_field() def __setitem__(self, *args, **kwargs): - """set item. + """Set an item in the dict. Args: args: The args passed. @@ -921,7 +921,7 @@ class PCDict(dict): self._reassign_field() if hasattr(self, "_reassign_field") else None def __delitem__(self, *args, **kwargs): - """delete item. + """Delete an item in the dict. Args: args: The args passed. diff --git a/tests/components/layout/test_cond.py b/tests/components/layout/test_cond.py index 6e76a81c8..fe30c372d 100644 --- a/tests/components/layout/test_cond.py +++ b/tests/components/layout/test_cond.py @@ -1,3 +1,4 @@ +import json from typing import Any import pytest @@ -6,8 +7,8 @@ import pynecone as pc from pynecone.components import cond from pynecone.components.layout.cond import Cond from pynecone.components.layout.fragment import Fragment -from pynecone.components.tags.tag import PropCond from pynecone.components.typography.text import Text +from pynecone.var import Var @pytest.fixture @@ -68,10 +69,10 @@ def test_prop_cond(c1: Any, c2: Any): c2, ) - assert isinstance(prop_cond, PropCond) - assert prop_cond.prop1 == c1 - assert prop_cond.prop2 == c2 - assert prop_cond.cond == True # noqa + assert isinstance(prop_cond, Var) + c1 = json.dumps(c1).replace('"', "`") + c2 = json.dumps(c2).replace('"', "`") + assert str(prop_cond) == f"{{true ? {c1} : {c2}}}" def test_cond_no_else(): diff --git a/tests/components/test_tag.py b/tests/components/test_tag.py index a350938ce..349a4fceb 100644 --- a/tests/components/test_tag.py +++ b/tests/components/test_tag.py @@ -3,7 +3,6 @@ from typing import Dict import pytest from pynecone.components.tags import CondTag, Tag -from pynecone.components.tags.tag import PropCond from pynecone.event import EventChain, EventHandler, EventSpec from pynecone.var import BaseVar, Var @@ -40,14 +39,6 @@ def mock_event(arg): ), '{(e) => Event([E("mock_event", {arg:e.target.value})])}', ), - ( - PropCond.create( - cond=BaseVar(name="random_var", type_=str), - prop1="true_value", - prop2="false_value", - ), - "{random_var ? 'true_value' : 'false_value'}", - ), ], ) def test_format_value(prop: Var, formatted: str): diff --git a/tests/conftest.py b/tests/conftest.py index 08a3585b3..58a688031 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,7 +19,7 @@ def windows_platform() -> Generator: @pytest.fixture def list_mutation_state(): - """A fixture to create a state with list mutation features. + """Create a state with list mutation features. Returns: A state with list mutation features. @@ -82,7 +82,7 @@ def list_mutation_state(): @pytest.fixture def dict_mutation_state(): - """A fixture to create a state with dict mutation features. + """Create a state with dict mutation features. Returns: A state with dict mutation features. diff --git a/tests/test_propcond.py b/tests/test_propcond.py deleted file mode 100644 index 326ec5223..000000000 --- a/tests/test_propcond.py +++ /dev/null @@ -1,34 +0,0 @@ -from typing import Any - -import pytest - -from pynecone.components.tags.tag import PropCond -from pynecone.utils import wrap -from pynecone.var import BaseVar - - -@pytest.mark.parametrize( - "prop1,prop2", - [ - (1, 3), - (1, "text"), - ("text1", "text2"), - ], -) -def test_validate_propcond(prop1: Any, prop2: Any): - """Test the creation of conditional props. - - Args: - prop1: truth condition value - prop2: false condition value - """ - prop_cond = PropCond.create( - cond=BaseVar(name="cond_state.value", type_=str), prop1=prop1, prop2=prop2 - ) - - expected_prop1 = wrap(prop1, "'") if isinstance(prop1, str) else prop1 - expected_prop2 = wrap(prop2, "'") if isinstance(prop2, str) else prop2 - - assert str(prop_cond) == ( - "{cond_state.value ? " f"{expected_prop1} : " f"{expected_prop2}" "}" - )