Merge remote-tracking branch 'origin/main' into reflex-0.4.0

This commit is contained in:
Masen Furer 2024-02-09 19:38:35 -08:00
commit 58b5e2f5bd
No known key found for this signature in database
GPG Key ID: B0008AD22B3B3A95
12 changed files with 630 additions and 561 deletions

View File

@ -204,6 +204,20 @@ async def test_client_side_state(
set_sub_state_button = driver.find_element(By.ID, "set_sub_state")
set_sub_sub_state_button = driver.find_element(By.ID, "set_sub_sub_state")
def set_sub(var: str, value: str):
AppHarness._poll_for(lambda: state_var_input.get_attribute("value") == "")
AppHarness._poll_for(lambda: input_value_input.get_attribute("value") == "")
state_var_input.send_keys(var)
input_value_input.send_keys(value)
set_sub_state_button.click()
def set_sub_sub(var: str, value: str):
AppHarness._poll_for(lambda: state_var_input.get_attribute("value") == "")
AppHarness._poll_for(lambda: input_value_input.get_attribute("value") == "")
state_var_input.send_keys(var)
input_value_input.send_keys(value)
set_sub_sub_state_button.click()
# get a reference to all cookie and local storage elements
c1 = driver.find_element(By.ID, "c1")
c2 = driver.find_element(By.ID, "c2")
@ -241,45 +255,19 @@ async def test_client_side_state(
assert not local_storage_items
# set some cookies and local storage values
state_var_input.send_keys("c1")
input_value_input.send_keys("c1 value")
set_sub_state_button.click()
state_var_input.send_keys("c2")
input_value_input.send_keys("c2 value")
set_sub_state_button.click()
state_var_input.send_keys("c4")
input_value_input.send_keys("c4 value")
set_sub_state_button.click()
state_var_input.send_keys("c5")
input_value_input.send_keys("c5 value")
set_sub_state_button.click()
state_var_input.send_keys("c6")
input_value_input.send_keys("c6 throwaway value")
set_sub_state_button.click()
state_var_input.send_keys("c6")
input_value_input.send_keys("c6 value")
set_sub_state_button.click()
state_var_input.send_keys("c7")
input_value_input.send_keys("c7 value")
set_sub_state_button.click()
state_var_input.send_keys("l1")
input_value_input.send_keys("l1 value")
set_sub_state_button.click()
state_var_input.send_keys("l2")
input_value_input.send_keys("l2 value")
set_sub_state_button.click()
state_var_input.send_keys("l3")
input_value_input.send_keys("l3 value")
set_sub_state_button.click()
state_var_input.send_keys("l4")
input_value_input.send_keys("l4 value")
set_sub_state_button.click()
state_var_input.send_keys("c1s")
input_value_input.send_keys("c1s value")
set_sub_sub_state_button.click()
state_var_input.send_keys("l1s")
input_value_input.send_keys("l1s value")
set_sub_sub_state_button.click()
set_sub("c1", "c1 value")
set_sub("c2", "c2 value")
set_sub("c4", "c4 value")
set_sub("c5", "c5 value")
set_sub("c6", "c6 throwaway value")
set_sub("c6", "c6 value")
set_sub("c7", "c7 value")
set_sub("l1", "l1 value")
set_sub("l2", "l2 value")
set_sub("l3", "l3 value")
set_sub("l4", "l4 value")
set_sub_sub("c1s", "c1s value")
set_sub_sub("l1s", "l1s value")
exp_cookies = {
"state.client_side_state.client_side_sub_state.c1": {
@ -347,9 +335,7 @@ async def test_client_side_state(
assert not cookies
# Test cookie with expiry by itself to avoid timing flakiness
state_var_input.send_keys("c3")
input_value_input.send_keys("c3 value")
set_sub_state_button.click()
set_sub("c3", "c3 value")
AppHarness._poll_for(
lambda: "state.client_side_state.client_side_sub_state.c3"
in cookie_info_map(driver)

1023
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -40,10 +40,7 @@ redis = "^4.3.5"
rich = "^13.0.0"
sqlmodel = "^0.0.14"
typer = ">=0.4.2,<1"
uvicorn = [
{version = "^0.24.0", python = ">=3.12"},
{version = "^0.20.0", python = "<3.12"},
]
uvicorn = "^0.27.0"
watchdog = "^2.3.1"
watchfiles = "^0.19.0"
starlette-admin = "^0.9.0"

View File

@ -1,13 +1,21 @@
"""Shims the real reflex app module for running backend server (uvicorn or gunicorn).
Only the app attribute is explicitly exposed.
"""
from concurrent.futures import ThreadPoolExecutor
from reflex import constants
from reflex.utils.prerequisites import get_compiled_app
from reflex.utils.prerequisites import get_app, get_compiled_app
if "app" != constants.CompileVars.APP:
raise AssertionError("unexpected variable name for 'app'")
app = getattr(get_compiled_app(), constants.CompileVars.APP)
app_module = get_app(reload=False)
app = getattr(app_module, constants.CompileVars.APP)
ThreadPoolExecutor(max_workers=1).submit(app.compile_)
# ensure only "app" is exposed.
del app_module
del get_app
del get_compiled_app
del constants
del ThreadPoolExecutor

View File

@ -2,11 +2,10 @@
from reflex.constants.colors import Color, ColorType, ShadeType
from reflex.utils.types import validate_parameter_literals
from reflex.vars import Var
@validate_parameter_literals
def color(color: ColorType, shade: ShadeType = 7, alpha: bool = False) -> Var:
def color(color: ColorType, shade: ShadeType = 7, alpha: bool = False) -> Color:
"""Create a color object.
Args:
@ -17,4 +16,4 @@ def color(color: ColorType, shade: ShadeType = 7, alpha: bool = False) -> Var:
Returns:
The color object.
"""
return Var.create(Color(color, shade, alpha))._replace(_var_is_string=True) # type: ignore
return Color(color, shade, alpha)

View File

@ -7,6 +7,7 @@ from reflex.components.base.fragment import Fragment
from reflex.components.component import BaseComponent, Component, MemoizationLeaf
from reflex.components.tags import CondTag, Tag
from reflex.constants import Dirs
from reflex.constants.colors import Color
from reflex.style import LIGHT_COLOR_MODE, color_mode
from reflex.utils import format, imports
from reflex.vars import BaseVar, Var, VarData
@ -168,6 +169,17 @@ def cond(condition: Any, c1: Any, c2: Any = None):
if isinstance(c2, Var):
var_datas.append(c2._var_data)
def create_var(cond_part):
return Var.create_safe(
cond_part,
_var_is_string=type(cond_part) is str or isinstance(cond_part, Color),
)
# convert the truth and false cond parts into vars so the _var_data can be obtained.
c1 = create_var(c1)
c2 = create_var(c2)
var_datas.extend([c1._var_data, c2._var_data])
# Create the conditional var.
return cond_var._replace(
_var_name=format.format_cond(

View File

@ -4,6 +4,7 @@ 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.core.colors import Color
from reflex.components.tags import MatchTag, Tag
from reflex.style import Style
from reflex.utils import format, imports, types
@ -116,7 +117,8 @@ class Match(MemoizationLeaf):
"""
_var_data = case_element._var_data if isinstance(case_element, Style) else None # type: ignore
case_element = Var.create(
case_element, _var_is_string=type(case_element) is str
case_element,
_var_is_string=type(case_element) is str or isinstance(case_element, Color),
)
if _var_data is not None:
case_element._var_data = VarData.merge(case_element._var_data, _var_data) # type: ignore

View File

@ -6,6 +6,8 @@ from .base import (
LOCAL_STORAGE,
POLLING_MAX_HTTP_BUFFER_SIZE,
PYTEST_CURRENT_TEST,
REFLEX_VAR_CLOSING_TAG,
REFLEX_VAR_OPENING_TAG,
RELOAD_CONFIG,
SKIP_COMPILE_ENV_VAR,
ColorMode,
@ -73,6 +75,8 @@ __ALL__ = [
Expiration,
Ext,
Fnm,
REFLEX_VAR_CLOSING_TAG,
REFLEX_VAR_OPENING_TAG,
GitIgnore,
Hooks,
Imports,

View File

@ -187,3 +187,6 @@ SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE"
# Testing os env set by pytest when running a test case.
PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"
RELOAD_CONFIG = "__REFLEX_RELOAD_CONFIG"
REFLEX_VAR_OPENING_TAG = "<reflex.Var>"
REFLEX_VAR_CLOSING_TAG = "</reflex.Var>"

View File

@ -25,6 +25,7 @@ from pydantic.fields import ModelField
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import DeclarativeBase, Mapped, QueryableAttribute, Relationship
from reflex import constants
from reflex.base import Base
from reflex.utils import serializers
@ -332,6 +333,18 @@ def check_prop_in_allowed_types(prop: Any, allowed_types: Iterable) -> bool:
return type_ in allowed_types
def is_encoded_fstring(value) -> bool:
"""Check if a value is an encoded Var f-string.
Args:
value: The value string to check.
Returns:
Whether the value is an f-string
"""
return isinstance(value, str) and constants.REFLEX_VAR_OPENING_TAG in value
def validate_literal(key: str, value: Any, expected_type: Type, comp_name: str):
"""Check that a value is a valid literal.
@ -349,6 +362,7 @@ def validate_literal(key: str, value: Any, expected_type: Type, comp_name: str):
if (
is_literal(expected_type)
and not isinstance(value, Var) # validating vars is not supported yet.
and not is_encoded_fstring(value) # f-strings are not supported.
and value not in expected_type.__args__
):
allowed_values = expected_type.__args__

View File

@ -202,7 +202,10 @@ def _encode_var(value: Var) -> str:
The encoded var.
"""
if value._var_data:
return f"<reflex.Var>{value._var_data.json()}</reflex.Var>" + str(value)
return (
f"{constants.REFLEX_VAR_OPENING_TAG}{value._var_data.json()}{constants.REFLEX_VAR_CLOSING_TAG}"
+ str(value)
)
return str(value)
@ -219,7 +222,7 @@ def _decode_var(value: str) -> tuple[VarData | None, str]:
if isinstance(value, str):
# Extract the state name from a formatted var
while m := re.match(
pattern=r"(.*)<reflex.Var>(.*)</reflex.Var>(.*)",
pattern=rf"(.*){constants.REFLEX_VAR_OPENING_TAG}(.*){constants.REFLEX_VAR_CLOSING_TAG}(.*)",
string=value,
flags=re.DOTALL, # Ensure . matches newline characters.
):

View File

@ -1,24 +1,46 @@
import pytest
import reflex as rx
from reflex.vars import Var
class ColorState(rx.State):
"""Test color state."""
color: str = "mint"
color_part: str = "tom"
shade: int = 4
def create_color_var(color):
return Var.create(color)
@pytest.mark.parametrize(
"color, expected",
[
(rx.color("mint"), "{`var(--mint-7)`}"),
(rx.color("mint", 3), "{`var(--mint-3)`}"),
(rx.color("mint", 3, True), "{`var(--mint-a3)`}"),
(create_color_var(rx.color("mint")), "var(--mint-7)"),
(create_color_var(rx.color("mint", 3)), "var(--mint-3)"),
(create_color_var(rx.color("mint", 3, True)), "var(--mint-a3)"),
(
rx.color(ColorState.color, ColorState.shade), # type: ignore
"{`var(--${state__color_state.color}-${state__color_state.shade})`}",
create_color_var(rx.color(ColorState.color, ColorState.shade)), # type: ignore
"var(--${state__color_state.color}-${state__color_state.shade})",
),
(
create_color_var(rx.color(f"{ColorState.color}", f"{ColorState.shade}")), # type: ignore
"var(--${state__color_state.color}-${state__color_state.shade})",
),
(
create_color_var(rx.color(f"{ColorState.color_part}ato", f"{ColorState.shade}")), # type: ignore
"var(--${state__color_state.color_part}ato-${state__color_state.shade})",
),
(
create_color_var(f'{rx.color(ColorState.color, f"{ColorState.shade}")}'), # type: ignore
"var(--${state__color_state.color}-${state__color_state.shade})",
),
(
create_color_var(f'{rx.color(f"{ColorState.color}", f"{ColorState.shade}")}'), # type: ignore
"var(--${state__color_state.color}-${state__color_state.shade})",
),
],
)