Clean up cond vars (#536)

This commit is contained in:
Nikhil Rao 2023-02-13 15:27:03 -08:00 committed by GitHub
parent adf5b7fb1b
commit 47eebe0ea5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 51 additions and 132 deletions

View File

@ -10,7 +10,7 @@ from .components.component import custom_component as component
from .components.graphing.victory import data from .components.graphing.victory import data
from .config import Config from .config import Config
from .constants import Env, Transports 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 .middleware import Middleware
from .model import Model, session from .model import Model, session
from .route import route from .route import route

View File

@ -106,11 +106,12 @@ def cond(condition: Any, c1: Any, c2: Any = None):
Returns: Returns:
The conditional component. The conditional component.
Raises:
ValueError: If the arguments are invalid.
""" """
# Import here to avoid circular imports. # Import here to avoid circular imports.
from pynecone.var import Var from pynecone.var import BaseVar, Var
from .tags.tag import PropCond
# Convert the condition to a Var. # Convert the condition to a Var.
cond_var = Var.create(condition) cond_var = Var.create(condition)
@ -123,6 +124,20 @@ def cond(condition: Any, c1: Any, c2: Any = None):
), "Both arguments must be components." ), "Both arguments must be components."
return Cond.create(cond_var, c1, c2) return Cond.create(cond_var, c1, c2)
# Otherwise, create a PropCond. # Otherwise, create a conditionl Var.
assert not isinstance(c2, Component), "Both arguments must be props." # Check that the second argument is valid.
return PropCond.create(cond_var, c1, c2) 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),
)

View File

@ -60,7 +60,6 @@ class Input(ChakraComponent):
"on_focus": EVENT_ARG.target.value, "on_focus": EVENT_ARG.target.value,
"on_blur": EVENT_ARG.target.value, "on_blur": EVENT_ARG.target.value,
"on_key_down": EVENT_ARG.key, "on_key_down": EVENT_ARG.key,
"on_key_press": EVENT_ARG.key,
"on_key_up": EVENT_ARG.key, "on_key_up": EVENT_ARG.key,
} }

View File

@ -16,6 +16,9 @@ class Slider(ChakraComponent):
# State var to bind the the input. # State var to bind the the input.
value: Var[int] value: Var[int]
# The color scheme.
color_scheme: Var[str]
# The placeholder text. # The placeholder text.
default_value: Var[int] default_value: Var[int]

View File

@ -54,6 +54,5 @@ class TextArea(ChakraComponent):
"on_focus": EVENT_ARG.target.value, "on_focus": EVENT_ARG.target.value,
"on_blur": EVENT_ARG.target.value, "on_blur": EVENT_ARG.target.value,
"on_key_down": EVENT_ARG.key, "on_key_down": EVENT_ARG.key,
"on_key_press": EVENT_ARG.key,
"on_key_up": EVENT_ARG.key, "on_key_up": EVENT_ARG.key,
} }

View File

@ -47,7 +47,7 @@ class Tag(Base):
@staticmethod @staticmethod
def format_prop( def format_prop(
prop: Union[Var, EventChain, ComponentStyle, PropCond, str], prop: Union[Var, EventChain, ComponentStyle, str],
) -> Union[int, float, str]: ) -> Union[int, float, str]:
"""Format a prop. """Format a prop.
@ -71,10 +71,6 @@ class Tag(Base):
events = ",".join([utils.format_event(event) for event in prop.events]) events = ",".join([utils.format_event(event) for event in prop.events])
prop = f"({local_args}) => Event([{events}])" prop = f"({local_args}) => Event([{events}])"
# Handle conditional props.
elif isinstance(prop, PropCond):
return str(prop)
# Handle other types. # Handle other types.
elif isinstance(prop, str): elif isinstance(prop, str):
if utils.is_wrapped(prop, "{"): if utils.is_wrapped(prop, "{"):
@ -89,7 +85,7 @@ class Tag(Base):
if isinstance(prop, dict): if isinstance(prop, dict):
# Convert any var keys to strings. # Convert any var keys to strings.
prop = { 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() for key, val in prop.items()
} }
@ -188,54 +184,3 @@ class Tag(Base):
Whether the prop is valid. Whether the prop is valid.
""" """
return prop is not None and not (isinstance(prop, dict) and len(prop) == 0) 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,
)

View File

@ -24,11 +24,11 @@ def route(
Note: the decorated functions still need to be imported. Note: the decorated functions still need to be imported.
Args: Args:
route: The route to reach the page. Defaults to None. route: The route to reach the page.
title: The title of the page. Defaults to None. title: The title of the page.
image: The favicon of the page. Defaults to None. image: The favicon of the page.
description: The description of the page. Defaults to None. description: The description of the page
on_load: The event handler called when the page load. Defaults to None. on_load: The event handler called when the page load.
Returns: Returns:
The decorated function. The decorated function.

View File

@ -1060,14 +1060,14 @@ def format_cond(
Returns: Returns:
The formatted conditional expression. The formatted conditional expression.
""" """
# Import here to avoid circular imports.
from pynecone.var import Var
if is_prop: if is_prop:
if isinstance(true_value, str): prop1 = Var.create(true_value, is_string=type(true_value) == str)
true_value = wrap(true_value, "'") prop2 = Var.create(false_value, is_string=type(false_value) == str)
if isinstance(false_value, str): assert prop1 is not None and prop2 is not None, "Invalid prop values"
false_value = wrap(false_value, "'") expr = f"{cond} ? {prop1} : {prop2}".replace("{", "").replace("}", "")
expr = f"{cond} ? {true_value} : {false_value}".replace("{", "").replace(
"}", ""
)
else: else:
expr = f"{cond} ? {true_value} : {false_value}" expr = f"{cond} ? {true_value} : {false_value}"

View File

@ -876,7 +876,7 @@ class PCDict(dict):
self._reassign_field() self._reassign_field()
def setdefault(self, *args, **kwargs): def setdefault(self, *args, **kwargs):
"""set default. """Return value of key if or set default.
Args: Args:
args: The args passed. args: The args passed.
@ -901,7 +901,7 @@ class PCDict(dict):
self._reassign_field() self._reassign_field()
def update(self, *args, **kwargs): def update(self, *args, **kwargs):
"""update dict. """Update the dict with another dict.
Args: Args:
args: The args passed. args: The args passed.
@ -911,7 +911,7 @@ class PCDict(dict):
self._reassign_field() self._reassign_field()
def __setitem__(self, *args, **kwargs): def __setitem__(self, *args, **kwargs):
"""set item. """Set an item in the dict.
Args: Args:
args: The args passed. args: The args passed.
@ -921,7 +921,7 @@ class PCDict(dict):
self._reassign_field() if hasattr(self, "_reassign_field") else None self._reassign_field() if hasattr(self, "_reassign_field") else None
def __delitem__(self, *args, **kwargs): def __delitem__(self, *args, **kwargs):
"""delete item. """Delete an item in the dict.
Args: Args:
args: The args passed. args: The args passed.

View File

@ -1,3 +1,4 @@
import json
from typing import Any from typing import Any
import pytest import pytest
@ -6,8 +7,8 @@ import pynecone as pc
from pynecone.components import cond from pynecone.components import cond
from pynecone.components.layout.cond import Cond from pynecone.components.layout.cond import Cond
from pynecone.components.layout.fragment import Fragment from pynecone.components.layout.fragment import Fragment
from pynecone.components.tags.tag import PropCond
from pynecone.components.typography.text import Text from pynecone.components.typography.text import Text
from pynecone.var import Var
@pytest.fixture @pytest.fixture
@ -68,10 +69,10 @@ def test_prop_cond(c1: Any, c2: Any):
c2, c2,
) )
assert isinstance(prop_cond, PropCond) assert isinstance(prop_cond, Var)
assert prop_cond.prop1 == c1 c1 = json.dumps(c1).replace('"', "`")
assert prop_cond.prop2 == c2 c2 = json.dumps(c2).replace('"', "`")
assert prop_cond.cond == True # noqa assert str(prop_cond) == f"{{true ? {c1} : {c2}}}"
def test_cond_no_else(): def test_cond_no_else():

View File

@ -3,7 +3,6 @@ from typing import Dict
import pytest import pytest
from pynecone.components.tags import CondTag, Tag from pynecone.components.tags import CondTag, Tag
from pynecone.components.tags.tag import PropCond
from pynecone.event import EventChain, EventHandler, EventSpec from pynecone.event import EventChain, EventHandler, EventSpec
from pynecone.var import BaseVar, Var from pynecone.var import BaseVar, Var
@ -40,14 +39,6 @@ def mock_event(arg):
), ),
'{(e) => Event([E("mock_event", {arg:e.target.value})])}', '{(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): def test_format_value(prop: Var, formatted: str):

View File

@ -19,7 +19,7 @@ def windows_platform() -> Generator:
@pytest.fixture @pytest.fixture
def list_mutation_state(): def list_mutation_state():
"""A fixture to create a state with list mutation features. """Create a state with list mutation features.
Returns: Returns:
A state with list mutation features. A state with list mutation features.
@ -82,7 +82,7 @@ def list_mutation_state():
@pytest.fixture @pytest.fixture
def dict_mutation_state(): def dict_mutation_state():
"""A fixture to create a state with dict mutation features. """Create a state with dict mutation features.
Returns: Returns:
A state with dict mutation features. A state with dict mutation features.

View File

@ -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}" "}"
)