fix conflicts

Merge remote-tracking branch 'upstream/main' into guess-type-fallback
This commit is contained in:
Benedikt Bartscher 2024-09-13 23:24:40 +02:00
commit 9138f23923
No known key found for this signature in database
58 changed files with 681 additions and 774 deletions

View File

@ -130,7 +130,6 @@ def render_multiple_pages(app, num: int):
def AppWithOnePage():
"""A reflex app with one page."""
import reflex_chakra as rc
from rxconfig import config # type: ignore
import reflex as rx
@ -145,7 +144,7 @@ def AppWithOnePage():
def index() -> rx.Component:
return rx.center(
rc.input(
rx.input(
id="token", value=State.router.session.client_token, is_read_only=True
),
rx.vstack(

View File

@ -13,7 +13,6 @@ def BackgroundTask():
import asyncio
import pytest
import reflex_chakra as rc
import reflex as rx
from reflex.state import ImmutableStateError
@ -116,11 +115,11 @@ def BackgroundTask():
def index() -> rx.Component:
return rx.vstack(
rc.input(
rx.input(
id="token", value=State.router.session.client_token, is_read_only=True
),
rx.heading(State.counter, id="counter"),
rc.input(
rx.input(
id="iterations",
placeholder="Iterations",
value=State.iterations.to_string(), # type: ignore

View File

@ -17,8 +17,6 @@ from . import utils
def ClientSide():
"""App for testing client-side state."""
import reflex_chakra as rc
import reflex as rx
class ClientSideState(rx.State):
@ -72,18 +70,18 @@ def ClientSide():
def index():
return rx.fragment(
rc.input(
rx.input(
value=ClientSideState.router.session.client_token,
is_read_only=True,
id="token",
),
rc.input(
rx.input(
placeholder="state var",
value=ClientSideState.state_var,
on_change=ClientSideState.set_state_var, # type: ignore
id="state_var",
),
rc.input(
rx.input(
placeholder="input value",
value=ClientSideState.input_value,
on_change=ClientSideState.set_input_value, # type: ignore
@ -313,7 +311,6 @@ async def test_client_side_state(
# no cookies should be set yet!
assert not driver.get_cookies()
local_storage_items = local_storage.items()
local_storage_items.pop("chakra-ui-color-mode", None)
local_storage_items.pop("last_compiled_time", None)
assert not local_storage_items
@ -429,7 +426,6 @@ async def test_client_side_state(
assert f"{sub_state_name}.c3" not in cookie_info_map(driver)
local_storage_items = local_storage.items()
local_storage_items.pop("chakra-ui-color-mode", None)
local_storage_items.pop("last_compiled_time", None)
assert local_storage_items.pop(f"{sub_state_name}.l1") == "l1 value"
assert local_storage_items.pop(f"{sub_state_name}.l2") == "l2 value"

View File

@ -17,8 +17,6 @@ def DynamicRoute():
"""App for testing dynamic routes."""
from typing import List
import reflex_chakra as rc
import reflex as rx
class DynamicState(rx.State):
@ -41,13 +39,13 @@ def DynamicRoute():
def index():
return rx.fragment(
rc.input(
rx.input(
value=DynamicState.router.session.client_token,
is_read_only=True,
id="token",
),
rc.input(value=rx.State.page_id, is_read_only=True, id="page_id"), # type: ignore
rc.input(
rx.input(value=rx.State.page_id, is_read_only=True, id="page_id"), # type: ignore
rx.input(
value=DynamicState.router.page.raw_path,
is_read_only=True,
id="raw_path",
@ -60,10 +58,10 @@ def DynamicRoute():
id="link_page_next", # type: ignore
),
rx.link("missing", href="/missing", id="link_missing"),
rc.list(
rx.list( # type: ignore
rx.foreach(
DynamicState.order, # type: ignore
lambda i: rc.list_item(rx.text(i)),
lambda i: rx.list_item(rx.text(i)),
),
),
)

View File

@ -16,8 +16,6 @@ def TestEventAction():
"""App for testing event_actions."""
from typing import List, Optional
import reflex_chakra as rc
import reflex as rx
class EventActionState(rx.State):
@ -55,7 +53,7 @@ def TestEventAction():
def index():
return rx.vstack(
rc.input(
rx.input(
value=EventActionState.router.session.client_token,
is_read_only=True,
id="token",
@ -148,10 +146,10 @@ def TestEventAction():
200
).stop_propagation,
),
rc.list(
rx.list( # type: ignore
rx.foreach(
EventActionState.order, # type: ignore
rc.list_item,
rx.list_item,
),
),
on_click=EventActionState.on_click("outer"), # type: ignore

View File

@ -18,8 +18,6 @@ def EventChain():
import time
from typing import List
import reflex_chakra as rc
import reflex as rx
# repeated here since the outer global isn't exported into the App module
@ -129,7 +127,7 @@ def EventChain():
app = rx.App(state=rx.State)
token_input = rc.input(
token_input = rx.input(
value=State.router.session.client_token, is_read_only=True, id="token"
)
@ -137,7 +135,7 @@ def EventChain():
def index():
return rx.fragment(
token_input,
rc.input(value=State.interim_value, is_read_only=True, id="interim_value"),
rx.input(value=State.interim_value, is_read_only=True, id="interim_value"),
rx.button(
"Return Event",
id="return_event",

View File

@ -20,8 +20,6 @@ def FormSubmit(form_component):
"""
from typing import Dict, List
import reflex_chakra as rc
import reflex as rx
class FormState(rx.State):
@ -37,28 +35,29 @@ def FormSubmit(form_component):
@app.add_page
def index():
return rx.vstack(
rc.input(
rx.input(
value=FormState.router.session.client_token,
is_read_only=True,
id="token",
),
eval(form_component)(
rx.vstack(
rc.input(id="name_input"),
rx.hstack(rc.pin_input(length=4, id="pin_input")),
rc.number_input(id="number_input"),
rx.input(id="name_input"),
rx.checkbox(id="bool_input"),
rx.switch(id="bool_input2"),
rx.checkbox(id="bool_input3"),
rx.switch(id="bool_input4"),
rx.slider(id="slider_input", default_value=[50], width="100%"),
rc.range_slider(id="range_input"),
rx.radio(["option1", "option2"], id="radio_input"),
rx.radio(FormState.var_options, id="radio_input_var"),
rc.select(["option1", "option2"], id="select_input"),
rc.select(FormState.var_options, id="select_input_var"),
rx.select(
["option1", "option2"],
name="select_input",
default_value="option1",
),
rx.select(FormState.var_options, id="select_input_var"),
rx.text_area(id="text_area_input"),
rc.input(
rx.input(
id="debounce_input",
debounce_timeout=0,
on_change=rx.console_log,
@ -81,8 +80,6 @@ def FormSubmitName(form_component):
"""
from typing import Dict, List
import reflex_chakra as rc
import reflex as rx
class FormState(rx.State):
@ -98,22 +95,19 @@ def FormSubmitName(form_component):
@app.add_page
def index():
return rx.vstack(
rc.input(
rx.input(
value=FormState.router.session.client_token,
is_read_only=True,
id="token",
),
eval(form_component)(
rx.vstack(
rc.input(name="name_input"),
rx.hstack(rc.pin_input(length=4, name="pin_input")),
rc.number_input(name="number_input"),
rx.input(name="name_input"),
rx.checkbox(name="bool_input"),
rx.switch(name="bool_input2"),
rx.checkbox(name="bool_input3"),
rx.switch(name="bool_input4"),
rx.slider(name="slider_input", default_value=[50], width="100%"),
rc.range_slider(name="range_input"),
rx.radio(FormState.options, name="radio_input"),
rx.select(
FormState.options,
@ -121,21 +115,13 @@ def FormSubmitName(form_component):
default_value=FormState.options[0],
),
rx.text_area(name="text_area_input"),
rc.input_group(
rc.input_left_element(rx.icon(tag="chevron_right")),
rc.input(
rx.input(
name="debounce_input",
debounce_timeout=0,
on_change=rx.console_log,
),
rc.input_right_element(rx.icon(tag="chevron_left")),
),
rc.button_group(
rx.button("Submit", type_="submit"),
rx.icon_button(FormState.val, icon=rx.icon(tag="plus")),
variant="outline",
is_attached=True,
),
),
on_submit=FormState.form_submit,
custom_attrs={"action": "/invalid"},
@ -152,16 +138,12 @@ def FormSubmitName(form_component):
functools.partial(FormSubmitName, form_component="rx.form.root"),
functools.partial(FormSubmit, form_component="rx.el.form"),
functools.partial(FormSubmitName, form_component="rx.el.form"),
functools.partial(FormSubmit, form_component="rc.form"),
functools.partial(FormSubmitName, form_component="rc.form"),
],
ids=[
"id-radix",
"name-radix",
"id-html",
"name-html",
"id-chakra",
"name-chakra",
],
)
def form_submit(request, tmp_path_factory) -> Generator[AppHarness, None, None]:
@ -224,16 +206,6 @@ async def test_submit(driver, form_submit: AppHarness):
name_input = driver.find_element(by, "name_input")
name_input.send_keys("foo")
pin_inputs = driver.find_elements(By.CLASS_NAME, "chakra-pin-input")
pin_values = ["8", "1", "6", "4"]
for i, pin_input in enumerate(pin_inputs):
pin_input.send_keys(pin_values[i])
number_input = driver.find_element(By.CLASS_NAME, "chakra-numberinput")
buttons = number_input.find_elements(By.XPATH, "//div[@role='button']")
for _ in range(3):
buttons[1].click()
checkbox_input = driver.find_element(By.XPATH, "//button[@role='checkbox']")
checkbox_input.click()
@ -275,15 +247,12 @@ async def test_submit(driver, form_submit: AppHarness):
print(form_data)
assert form_data["name_input"] == "foo"
assert form_data["pin_input"] == pin_values
assert form_data["number_input"] == "-3"
assert form_data["bool_input"]
assert form_data["bool_input2"]
assert not form_data.get("bool_input3", False)
assert not form_data.get("bool_input4", False)
assert form_data["slider_input"] == "50"
assert form_data["range_input"] == ["25", "75"]
assert form_data["radio_input"] == "option2"
assert form_data["select_input"] == "option1"
assert form_data["text_area_input"] == "Some\nText"

View File

@ -11,8 +11,6 @@ from reflex.testing import AppHarness
def ServerSideEvent():
"""App with inputs set via event handlers and set_value."""
import reflex_chakra as rc
import reflex as rx
class SSState(rx.State):
@ -41,12 +39,12 @@ def ServerSideEvent():
@app.add_page
def index():
return rx.fragment(
rc.input(
rx.input(
id="token", value=SSState.router.session.client_token, is_read_only=True
),
rc.input(default_value="a", id="a"),
rc.input(default_value="b", id="b"),
rc.input(default_value="c", id="c"),
rx.input(default_value="a", id="a"),
rx.input(default_value="b", id="b"),
rx.input(default_value="c", id="c"),
rx.button(
"Clear Immediate",
id="clear_immediate",

View File

@ -10,89 +10,45 @@ from reflex.testing import AppHarness
def Table():
"""App using table component."""
from typing import List
import reflex_chakra as rc
import reflex as rx
class TableState(rx.State):
rows: List[List[str]] = [
["John", "30", "New York"],
["Jane", "31", "San Fransisco"],
["Joe", "32", "Los Angeles"],
]
headers: List[str] = ["Name", "Age", "Location"]
footers: List[str] = ["footer1", "footer2", "footer3"]
caption: str = "random caption"
app = rx.App(state=rx.State)
@app.add_page
def index():
return rx.center(
rc.input(
rx.input(
id="token",
value=TableState.router.session.client_token,
value=rx.State.router.session.client_token,
is_read_only=True,
),
rc.table_container(
rc.table(
headers=TableState.headers,
rows=TableState.rows,
footers=TableState.footers,
caption=TableState.caption,
variant="striped",
color_scheme="blue",
rx.table.root(
rx.table.header(
rx.table.row(
rx.table.column_header_cell("Name"),
rx.table.column_header_cell("Age"),
rx.table.column_header_cell("Location"),
),
),
rx.table.body(
rx.table.row(
rx.table.row_header_cell("John"),
rx.table.cell(30),
rx.table.cell("New York"),
),
rx.table.row(
rx.table.row_header_cell("Jane"),
rx.table.cell(31),
rx.table.cell("San Fransisco"),
),
rx.table.row(
rx.table.row_header_cell("Joe"),
rx.table.cell(32),
rx.table.cell("Los Angeles"),
),
),
width="100%",
),
),
)
@app.add_page
def another():
return rx.center(
rc.table_container(
rc.table( # type: ignore
rc.thead( # type: ignore
rc.tr( # type: ignore
rc.th("Name"),
rc.th("Age"),
rc.th("Location"),
)
),
rc.tbody( # type: ignore
rc.tr( # type: ignore
rc.td("John"),
rc.td(30),
rc.td("New York"),
),
rc.tr( # type: ignore
rc.td("Jane"),
rc.td(31),
rc.td("San Francisco"),
),
rc.tr( # type: ignore
rc.td("Joe"),
rc.td(32),
rc.td("Los Angeles"),
),
),
rc.tfoot( # type: ignore
rc.tr(
rc.td("footer1"),
rc.td("footer2"),
rc.td("footer3"),
) # type: ignore
),
rc.table_caption("random caption"),
variant="striped",
color_scheme="teal",
)
)
)
@ -138,23 +94,20 @@ def driver(table: AppHarness):
driver.quit()
@pytest.mark.parametrize("route", ["", "/another"])
def test_table(driver, table: AppHarness, route):
def test_table(driver, table: AppHarness):
"""Test that a table component is rendered properly.
Args:
driver: Selenium WebDriver open to the app
table: Harness for Table app
route: Page route or path.
"""
driver.get(f"{table.frontend_url}/{route}")
assert table.app_instance is not None, "app is not running"
thead = driver.find_element(By.TAG_NAME, "thead")
# poll till page is fully loaded.
table.poll_for_content(element=thead)
# check headers
assert thead.find_element(By.TAG_NAME, "tr").text == "NAME AGE LOCATION"
assert thead.find_element(By.TAG_NAME, "tr").text == "Name Age Location"
# check first row value
assert (
driver.find_element(By.TAG_NAME, "tbody")
@ -162,12 +115,3 @@ def test_table(driver, table: AppHarness, route):
.text
== "John 30 New York"
)
# check footer
assert (
driver.find_element(By.TAG_NAME, "tfoot")
.find_element(By.TAG_NAME, "tr")
.text.lower()
== "footer1 footer2 footer3"
)
# check caption
assert driver.find_element(By.TAG_NAME, "caption").text == "random caption"

View File

@ -27,8 +27,6 @@ def TailwindApp(
"""
from pathlib import Path
import reflex_chakra as rc
import reflex as rx
class UnusedState(rx.State):
@ -36,7 +34,7 @@ def TailwindApp(
def index():
return rx.el.div(
rc.text(paragraph_text, class_name=paragraph_class_name),
rx.text(paragraph_text, class_name=paragraph_class_name),
rx.el.p(paragraph_text, class_name=paragraph_class_name),
rx.text(paragraph_text, as_="p", class_name=paragraph_class_name),
rx.el.div("Test external stylesheet", class_name="external"),

View File

@ -16,8 +16,6 @@ def UploadFile():
"""App for testing dynamic routes."""
from typing import Dict, List
import reflex_chakra as rc
import reflex as rx
class UploadState(rx.State):
@ -46,7 +44,7 @@ def UploadFile():
def index():
return rx.vstack(
rc.input(
rx.input(
value=UploadState.router.session.client_token,
is_read_only=True,
id="token",

View File

@ -14,8 +14,6 @@ def VarOperations():
"""App with var operations."""
from typing import Dict, List
import reflex_chakra as rc
import reflex as rx
from reflex.ivars.base import LiteralVar
from reflex.ivars.sequence import ArrayVar
@ -552,10 +550,7 @@ def VarOperations():
VarOperationState.html_str,
id="html_str",
),
rc.highlight(
"second",
query=[VarOperationState.str_var2],
),
rx.el.mark("second"),
rx.text(ArrayVar.range(2, 5).join(","), id="list_join_range1"),
rx.text(ArrayVar.range(2, 10, 2).join(","), id="list_join_range2"),
rx.text(ArrayVar.range(5, 0, -1).join(","), id="list_join_range3"),

153
poetry.lock generated
View File

@ -1,9 +1,10 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
[[package]]
name = "alembic"
version = "1.13.2"
description = "A database migration tool for SQLAlchemy."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -25,6 +26,7 @@ tz = ["backports.zoneinfo"]
name = "annotated-types"
version = "0.7.0"
description = "Reusable constraint types to use with typing.Annotated"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -39,6 +41,7 @@ typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""}
name = "anyio"
version = "4.4.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -61,6 +64,7 @@ trio = ["trio (>=0.23)"]
name = "async-timeout"
version = "4.0.3"
description = "Timeout context manager for asyncio programs"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -72,6 +76,7 @@ files = [
name = "asynctest"
version = "0.13.0"
description = "Enhance the standard unittest package with features for testing asyncio libraries"
category = "dev"
optional = false
python-versions = ">=3.5"
files = [
@ -83,6 +88,7 @@ files = [
name = "attrs"
version = "24.2.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -102,6 +108,7 @@ tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
name = "backports-tarfile"
version = "1.2.0"
description = "Backport of CPython tarfile module"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -117,6 +124,7 @@ testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-ch
name = "bidict"
version = "0.23.1"
description = "The bidirectional mapping library for Python."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -128,6 +136,7 @@ files = [
name = "build"
version = "1.2.2"
description = "A simple, correct Python build frontend"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -153,6 +162,7 @@ virtualenv = ["virtualenv (>=20.0.35)"]
name = "certifi"
version = "2024.8.30"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -164,6 +174,7 @@ files = [
name = "cffi"
version = "1.17.1"
description = "Foreign Function Interface for Python calling C code."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -243,6 +254,7 @@ pycparser = "*"
name = "cfgv"
version = "3.4.0"
description = "Validate configuration and produce human readable error messages."
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -254,6 +266,7 @@ files = [
name = "charset-normalizer"
version = "3.3.2"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
python-versions = ">=3.7.0"
files = [
@ -353,6 +366,7 @@ files = [
name = "click"
version = "8.1.7"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -367,6 +381,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""}
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
@ -378,6 +393,7 @@ files = [
name = "coverage"
version = "7.6.1"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -465,6 +481,7 @@ toml = ["tomli"]
name = "cryptography"
version = "43.0.1"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -514,6 +531,7 @@ test-randomorder = ["pytest-randomly"]
name = "darglint"
version = "1.8.1"
description = "A utility for ensuring Google-style docstrings stay up to date with the source code."
category = "dev"
optional = false
python-versions = ">=3.6,<4.0"
files = [
@ -525,6 +543,7 @@ files = [
name = "dill"
version = "0.3.8"
description = "serialize all of Python"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -540,6 +559,7 @@ profile = ["gprof2dot (>=2022.7.29)"]
name = "distlib"
version = "0.3.8"
description = "Distribution utilities"
category = "dev"
optional = false
python-versions = "*"
files = [
@ -551,6 +571,7 @@ files = [
name = "distro"
version = "1.9.0"
description = "Distro - an OS platform information API"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -562,14 +583,19 @@ files = [
name = "docutils"
version = "0.20.1"
description = "Docutils -- Python Documentation Utilities"
category = "main"
optional = false
python-versions = "*"
files = []
python-versions = ">=3.7"
files = [
{file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"},
{file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"},
]
[[package]]
name = "exceptiongroup"
version = "1.2.2"
description = "Backport of PEP 654 (exception groups)"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -584,6 +610,7 @@ test = ["pytest (>=6)"]
name = "fastapi"
version = "0.114.0"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -604,6 +631,7 @@ standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "htt
name = "filelock"
version = "3.15.4"
description = "A platform independent file lock."
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -620,6 +648,7 @@ typing = ["typing-extensions (>=4.8)"]
name = "greenlet"
version = "3.0.3"
description = "Lightweight in-process concurrent programming"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -691,6 +720,7 @@ test = ["objgraph", "psutil"]
name = "gunicorn"
version = "23.0.0"
description = "WSGI HTTP Server for UNIX"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -712,6 +742,7 @@ tornado = ["tornado (>=0.2)"]
name = "h11"
version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -723,6 +754,7 @@ files = [
name = "httpcore"
version = "1.0.5"
description = "A minimal low-level HTTP client."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -737,13 +769,14 @@ h11 = ">=0.13,<0.15"
[package.extras]
asyncio = ["anyio (>=4.0,<5.0)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
socks = ["socksio (>=1.0.0,<2.0.0)"]
trio = ["trio (>=0.22.0,<0.26.0)"]
[[package]]
name = "httpx"
version = "0.27.2"
description = "The next generation HTTP client."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -754,21 +787,22 @@ files = [
[package.dependencies]
anyio = "*"
certifi = "*"
httpcore = "==1.*"
httpcore = ">=1.0.0,<2.0.0"
idna = "*"
sniffio = "*"
[package.extras]
brotli = ["brotli", "brotlicffi"]
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
socks = ["socksio (>=1.0.0,<2.0.0)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "identify"
version = "2.6.0"
description = "File identification library for Python"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -783,6 +817,7 @@ license = ["ukkonen"]
name = "idna"
version = "3.8"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -794,6 +829,7 @@ files = [
name = "importlib-metadata"
version = "8.4.0"
description = "Read metadata from Python packages"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -813,6 +849,7 @@ test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "p
name = "importlib-resources"
version = "6.4.4"
description = "Read resources from Python packages"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -835,6 +872,7 @@ type = ["pytest-mypy"]
name = "iniconfig"
version = "2.0.0"
description = "brain-dead simple config-ini parsing"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -846,6 +884,7 @@ files = [
name = "jaraco-classes"
version = "3.4.0"
description = "Utility functions for Python class constructs"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -864,6 +903,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-ena
name = "jaraco-context"
version = "6.0.1"
description = "Useful decorators and context managers"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -882,6 +922,7 @@ test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-c
name = "jaraco-functools"
version = "4.0.2"
description = "Functools like those found in stdlib"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -900,6 +941,7 @@ test = ["jaraco.classes", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "p
name = "jeepney"
version = "0.8.0"
description = "Low-level, pure Python DBus protocol wrapper."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -915,6 +957,7 @@ trio = ["async_generator", "trio"]
name = "jinja2"
version = "3.1.4"
description = "A very fast and expressive template engine."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -932,6 +975,7 @@ i18n = ["Babel (>=2.7)"]
name = "keyring"
version = "25.3.0"
description = "Store and access your passwords safely."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -958,6 +1002,7 @@ test = ["pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-
name = "lazy-loader"
version = "0.4"
description = "Makes it easy to load subpackages and functions on demand."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -977,6 +1022,7 @@ test = ["pytest (>=7.4)", "pytest-cov (>=4.1)"]
name = "mako"
version = "1.3.5"
description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -996,6 +1042,7 @@ testing = ["pytest"]
name = "markdown-it-py"
version = "3.0.0"
description = "Python port of markdown-it. Markdown parsing, done right!"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -1020,6 +1067,7 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
name = "markupsafe"
version = "2.1.5"
description = "Safely add untrusted strings to HTML/XML markup."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -1089,6 +1137,7 @@ files = [
name = "mdurl"
version = "0.1.2"
description = "Markdown URL utilities"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -1100,6 +1149,7 @@ files = [
name = "more-itertools"
version = "10.5.0"
description = "More routines for operating on iterables, beyond itertools"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -1111,6 +1161,7 @@ files = [
name = "nh3"
version = "0.2.18"
description = "Python bindings to the ammonia HTML sanitization library."
category = "main"
optional = false
python-versions = "*"
files = [
@ -1136,6 +1187,7 @@ files = [
name = "nodeenv"
version = "1.9.1"
description = "Node.js virtual environment builder"
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
@ -1147,6 +1199,7 @@ files = [
name = "numpy"
version = "1.24.4"
description = "Fundamental package for array computing in Python"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -1184,6 +1237,7 @@ files = [
name = "numpy"
version = "2.0.2"
description = "Fundamental package for array computing in Python"
category = "dev"
optional = false
python-versions = ">=3.9"
files = [
@ -1238,6 +1292,7 @@ files = [
name = "numpy"
version = "2.1.1"
description = "Fundamental package for array computing in Python"
category = "dev"
optional = false
python-versions = ">=3.10"
files = [
@ -1300,6 +1355,7 @@ files = [
name = "outcome"
version = "1.3.0.post0"
description = "Capture the outcome of Python function calls."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -1314,6 +1370,7 @@ attrs = ">=19.2.0"
name = "packaging"
version = "24.1"
description = "Core utilities for Python packages"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -1325,6 +1382,7 @@ files = [
name = "pandas"
version = "1.5.3"
description = "Powerful data structures for data analysis, time series, and statistics"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -1369,6 +1427,7 @@ test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"]
name = "pandas"
version = "2.2.2"
description = "Powerful data structures for data analysis, time series, and statistics"
category = "dev"
optional = false
python-versions = ">=3.9"
files = [
@ -1442,6 +1501,7 @@ xml = ["lxml (>=4.9.2)"]
name = "pillow"
version = "10.4.0"
description = "Python Imaging Library (Fork)"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -1539,6 +1599,7 @@ xmp = ["defusedxml"]
name = "pip"
version = "24.2"
description = "The PyPA recommended tool for installing Python packages."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -1550,6 +1611,7 @@ files = [
name = "pipdeptree"
version = "2.16.2"
description = "Command line utility to show dependency tree of packages."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -1568,6 +1630,7 @@ test = ["covdefaults (>=2.3)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pyte
name = "pkginfo"
version = "1.10.0"
description = "Query metadata from sdists / bdists / installed packages."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -1582,6 +1645,7 @@ testing = ["pytest", "pytest-cov", "wheel"]
name = "platformdirs"
version = "4.2.2"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -1598,6 +1662,7 @@ type = ["mypy (>=1.8)"]
name = "plotly"
version = "5.24.0"
description = "An open-source, interactive data visualization library for Python"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -1613,6 +1678,7 @@ tenacity = ">=6.2.0"
name = "pluggy"
version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -1628,6 +1694,7 @@ testing = ["pytest", "pytest-benchmark"]
name = "pre-commit"
version = "3.5.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -1646,6 +1713,7 @@ virtualenv = ">=20.10.0"
name = "psutil"
version = "6.0.0"
description = "Cross-platform lib for process and system monitoring in Python."
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
files = [
@ -1675,6 +1743,7 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
name = "py-cpuinfo"
version = "9.0.0"
description = "Get CPU info with pure Python"
category = "dev"
optional = false
python-versions = "*"
files = [
@ -1686,6 +1755,7 @@ files = [
name = "pycparser"
version = "2.22"
description = "C parser in Python"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -1697,6 +1767,7 @@ files = [
name = "pydantic"
version = "2.9.0"
description = "Data validation using Python type hints"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -1720,6 +1791,7 @@ email = ["email-validator (>=2.0.0)"]
name = "pydantic-core"
version = "2.23.2"
description = "Core functionality for Pydantic validation and serialization"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -1821,6 +1893,7 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
name = "pygments"
version = "2.18.0"
description = "Pygments is a syntax highlighting package written in Python."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -1835,6 +1908,7 @@ windows-terminal = ["colorama (>=0.4.6)"]
name = "pyproject-hooks"
version = "1.1.0"
description = "Wrappers to call pyproject.toml-based build backend hooks."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -1846,6 +1920,7 @@ files = [
name = "pyright"
version = "1.1.334"
description = "Command line wrapper for pyright"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -1864,6 +1939,7 @@ dev = ["twine (>=3.4.1)"]
name = "pysocks"
version = "1.7.1"
description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
@ -1876,6 +1952,7 @@ files = [
name = "pytest"
version = "7.4.4"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -1898,6 +1975,7 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no
name = "pytest-asyncio"
version = "0.21.2"
description = "Pytest support for asyncio"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -1916,6 +1994,7 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy
name = "pytest-benchmark"
version = "4.0.0"
description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -1936,6 +2015,7 @@ histogram = ["pygal", "pygaljs"]
name = "pytest-cov"
version = "4.1.0"
description = "Pytest plugin for measuring coverage."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -1954,6 +2034,7 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale
name = "pytest-mock"
version = "3.14.0"
description = "Thin-wrapper around the mock package for easier use with pytest"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -1971,6 +2052,7 @@ dev = ["pre-commit", "pytest-asyncio", "tox"]
name = "python-dateutil"
version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
@ -1985,6 +2067,7 @@ six = ">=1.5"
name = "python-engineio"
version = "4.9.1"
description = "Engine.IO server and client for Python"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -2004,6 +2087,7 @@ docs = ["sphinx"]
name = "python-multipart"
version = "0.0.9"
description = "A streaming multipart parser for Python"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2018,6 +2102,7 @@ dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatc
name = "python-socketio"
version = "5.11.4"
description = "Socket.IO server and client for Python"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2038,6 +2123,7 @@ docs = ["sphinx"]
name = "pytz"
version = "2024.1"
description = "World timezone definitions, modern and historical"
category = "dev"
optional = false
python-versions = "*"
files = [
@ -2049,6 +2135,7 @@ files = [
name = "pywin32-ctypes"
version = "0.2.3"
description = "A (partial) reimplementation of pywin32 using ctypes/cffi"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -2060,6 +2147,7 @@ files = [
name = "pyyaml"
version = "6.0.2"
description = "YAML parser and emitter for Python"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -2122,6 +2210,7 @@ files = [
name = "readme-renderer"
version = "43.0"
description = "readme_renderer is a library for rendering readme descriptions for Warehouse"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2141,6 +2230,7 @@ md = ["cmarkgfm (>=0.8.0)"]
name = "redis"
version = "5.0.8"
description = "Python client for Redis database and key-value store"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -2157,13 +2247,14 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"
[[package]]
name = "reflex-chakra"
version = "0.6.0a5"
version = "0.6.0a6"
description = "reflex using chakra components"
category = "main"
optional = false
python-versions = "<4.0,>=3.8"
files = [
{file = "reflex_chakra-0.6.0a5-py3-none-any.whl", hash = "sha256:9d502ddf3bd606baef4f0ff6453dcefb5e19dab4cfaf7c23df069fab70687a63"},
{file = "reflex_chakra-0.6.0a5.tar.gz", hash = "sha256:b44e73478462052f09c9677d3fd7726891b4af87711b4b6a2171f010049308c9"},
{file = "reflex_chakra-0.6.0a6-py3-none-any.whl", hash = "sha256:a526f5db8a457c68c24317d007172e77b323a34abf03f05eeb8d92c014563bfb"},
{file = "reflex_chakra-0.6.0a6.tar.gz", hash = "sha256:124a780ea96d36d31f46b4c97bb0c0f111f2fb6e0d66a142e1577dad740f6e35"},
]
[package.dependencies]
@ -2173,6 +2264,7 @@ reflex = ">=0.6.0a"
name = "reflex-hosting-cli"
version = "0.1.13"
description = "Reflex Hosting CLI"
category = "main"
optional = false
python-versions = "<4.0,>=3.8"
files = [
@ -2196,6 +2288,7 @@ websockets = ">=10.4"
name = "requests"
version = "2.32.3"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2217,6 +2310,7 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
name = "requests-toolbelt"
version = "1.0.0"
description = "A utility belt for advanced users of python-requests"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
@ -2231,6 +2325,7 @@ requests = ">=2.0.1,<3.0.0"
name = "rfc3986"
version = "2.0.0"
description = "Validating URI References per RFC 3986"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -2245,6 +2340,7 @@ idna2008 = ["idna"]
name = "rich"
version = "13.8.0"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
category = "main"
optional = false
python-versions = ">=3.7.0"
files = [
@ -2264,6 +2360,7 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
name = "ruff"
version = "0.4.10"
description = "An extremely fast Python linter and code formatter, written in Rust."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -2290,6 +2387,7 @@ files = [
name = "secretstorage"
version = "3.3.3"
description = "Python bindings to FreeDesktop.org Secret Service API"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -2305,6 +2403,7 @@ jeepney = ">=0.6"
name = "selenium"
version = "4.24.0"
description = "Official Python bindings for Selenium WebDriver"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -2324,6 +2423,7 @@ websocket-client = ">=1.8,<2.0"
name = "setuptools"
version = "70.1.1"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2339,6 +2439,7 @@ testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metad
name = "shellingham"
version = "1.5.4"
description = "Tool to Detect Surrounding Shell"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -2350,6 +2451,7 @@ files = [
name = "simple-websocket"
version = "1.0.0"
description = "Simple WebSocket server and client for Python"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -2367,6 +2469,7 @@ docs = ["sphinx"]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
@ -2378,6 +2481,7 @@ files = [
name = "sniffio"
version = "1.3.1"
description = "Sniff out which async library your code is running under"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -2389,6 +2493,7 @@ files = [
name = "sortedcontainers"
version = "2.4.0"
description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
category = "dev"
optional = false
python-versions = "*"
files = [
@ -2400,6 +2505,7 @@ files = [
name = "sqlalchemy"
version = "2.0.34"
description = "Database Abstraction Library"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -2455,7 +2561,7 @@ files = [
]
[package.dependencies]
greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and platform_machine == \"aarch64\" or python_version < \"3.13\" and platform_machine == \"ppc64le\" or python_version < \"3.13\" and platform_machine == \"x86_64\" or python_version < \"3.13\" and platform_machine == \"amd64\" or python_version < \"3.13\" and platform_machine == \"AMD64\" or python_version < \"3.13\" and platform_machine == \"win32\" or python_version < \"3.13\" and platform_machine == \"WIN32\""}
typing-extensions = ">=4.6.0"
[package.extras]
@ -2487,6 +2593,7 @@ sqlcipher = ["sqlcipher3_binary"]
name = "sqlmodel"
version = "0.0.22"
description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -2502,6 +2609,7 @@ SQLAlchemy = ">=2.0.14,<2.1.0"
name = "starlette"
version = "0.38.4"
description = "The little ASGI library that shines."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2520,6 +2628,7 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7
name = "starlette-admin"
version = "0.14.1"
description = "Fast, beautiful and extensible administrative interface framework for Starlette/FastApi applications"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2543,6 +2652,7 @@ test = ["aiomysql (>=0.1.1,<0.3.0)", "aiosqlite (>=0.17.0,<0.21.0)", "arrow (>=1
name = "tabulate"
version = "0.9.0"
description = "Pretty-print tabular data"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -2557,6 +2667,7 @@ widechars = ["wcwidth"]
name = "tenacity"
version = "9.0.0"
description = "Retry code until it succeeds"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -2572,6 +2683,7 @@ test = ["pytest", "tornado (>=4.5)", "typeguard"]
name = "toml"
version = "0.10.2"
description = "Python Library for Tom's Obvious, Minimal Language"
category = "dev"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
@ -2583,6 +2695,7 @@ files = [
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -2594,6 +2707,7 @@ files = [
name = "tomlkit"
version = "0.13.2"
description = "Style preserving TOML library"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2605,6 +2719,7 @@ files = [
name = "trio"
version = "0.26.2"
description = "A friendly Python library for async concurrency and I/O"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -2625,6 +2740,7 @@ sortedcontainers = "*"
name = "trio-websocket"
version = "0.11.1"
description = "WebSocket library for Trio"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -2641,6 +2757,7 @@ wsproto = ">=0.14"
name = "twine"
version = "5.1.1"
description = "Collection of utilities for publishing packages on PyPI"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2663,6 +2780,7 @@ urllib3 = ">=1.26.0"
name = "typer"
version = "0.12.5"
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -2680,6 +2798,7 @@ typing-extensions = ">=3.7.4.3"
name = "typing-extensions"
version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2691,6 +2810,7 @@ files = [
name = "tzdata"
version = "2024.1"
description = "Provider of IANA time zone data"
category = "main"
optional = false
python-versions = ">=2"
files = [
@ -2702,6 +2822,7 @@ files = [
name = "urllib3"
version = "2.2.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2722,6 +2843,7 @@ zstd = ["zstandard (>=0.18.0)"]
name = "uvicorn"
version = "0.30.6"
description = "The lightning-fast ASGI server."
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2741,6 +2863,7 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)",
name = "virtualenv"
version = "20.26.3"
description = "Virtual Python Environment builder"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -2761,6 +2884,7 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess
name = "websocket-client"
version = "1.8.0"
description = "WebSocket client for Python with low level API options"
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -2777,6 +2901,7 @@ test = ["websockets"]
name = "websockets"
version = "13.0.1"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2872,6 +2997,7 @@ files = [
name = "wheel"
version = "0.44.0"
description = "A built-package format for Python"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2886,6 +3012,7 @@ test = ["pytest (>=6.0.0)", "setuptools (>=65)"]
name = "wrapt"
version = "1.16.0"
description = "Module for decorators, wrappers and monkey patching."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -2965,6 +3092,7 @@ files = [
name = "wsproto"
version = "1.2.0"
description = "WebSockets state-machine based protocol implementation"
category = "main"
optional = false
python-versions = ">=3.7.0"
files = [
@ -2979,6 +3107,7 @@ h11 = ">=0.9.0,<1"
name = "zipp"
version = "3.20.1"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -2997,4 +3126,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "0bd98b6baa48d41ec50bfa0903d89005129211f6e429d7b62504ee880f8ba523"
content-hash = "7a8990e432a404802c3ace9a81c3c8c33cdd60596f26cdc38e2de424cb1126dd"

View File

@ -59,7 +59,7 @@ httpx = ">=0.25.1,<1.0"
twine = ">=4.0.0,<6.0"
tomlkit = ">=0.12.4,<1.0"
lazy_loader = ">=0.4"
reflex-chakra = ">=0.6.0a"
reflex-chakra = ">=0.6.0a6"
[tool.poetry.group.dev.dependencies]
pytest = ">=7.1.2,<8.0"

View File

@ -27,7 +27,7 @@ function AppWrap({children}) {
export default function MyApp({ Component, pageProps }) {
return (
<ThemeProvider defaultTheme={ defaultColorMode } storageKey="chakra-ui-color-mode" attribute="class">
<ThemeProvider defaultTheme={ defaultColorMode } attribute="class">
<AppWrap>
<StateProvider>
<EventLoopProvider>

View File

@ -1,36 +0,0 @@
import { useColorMode as chakraUseColorMode } from "@chakra-ui/react";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
import { ColorModeContext, defaultColorMode } from "/utils/context.js";
export default function ChakraColorModeProvider({ children }) {
const { theme, resolvedTheme, setTheme } = useTheme();
const { colorMode, toggleColorMode } = chakraUseColorMode();
const [resolvedColorMode, setResolvedColorMode] = useState(colorMode);
useEffect(() => {
if (colorMode != resolvedTheme) {
toggleColorMode();
}
setResolvedColorMode(resolvedTheme);
}, [theme, resolvedTheme]);
const rawColorMode = colorMode;
const setColorMode = (mode) => {
const allowedModes = ["light", "dark", "system"];
if (!allowedModes.includes(mode)) {
console.error(
`Invalid color mode "${mode}". Defaulting to "${defaultColorMode}".`
);
mode = defaultColorMode;
}
setTheme(mode);
};
return (
<ColorModeContext.Provider
value={{ rawColorMode, resolvedColorMode, toggleColorMode, setColorMode }}
>
{children}
</ColorModeContext.Provider>
);
}

View File

@ -132,6 +132,7 @@ from .components.radix.themes.layout.container import container as container
from .components.radix.themes.layout.flex import flex as flex
from .components.radix.themes.layout.grid import grid as grid
from .components.radix.themes.layout.list import list_item as list_item
from .components.radix.themes.layout.list import list_ns as list # noqa
from .components.radix.themes.layout.list import ordered_list as ordered_list
from .components.radix.themes.layout.list import unordered_list as unordered_list
from .components.radix.themes.layout.section import section as section

View File

@ -9,6 +9,7 @@ import copy
import functools
import inspect
import io
import json
import multiprocessing
import os
import platform
@ -1096,6 +1097,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
if delta:
# When the state is modified reset dirty status and emit the delta to the frontend.
state._clean()
print(dir(state.router))
await self.event_namespace.emit_update(
update=StateUpdate(delta=delta),
sid=state.router.session.session_id,
@ -1531,8 +1533,9 @@ class EventNamespace(AsyncNamespace):
sid: The Socket.IO session id.
data: The event data.
"""
fields = json.loads(data)
# Get the event.
event = Event.parse_raw(data)
event = Event(**{k: v for k, v in fields.items() if k != "handler"})
self.token_to_sid[event.token] = sid
self.sid_to_token[sid] = event.token

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import copy
import typing
import warnings
from abc import ABC, abstractmethod
from functools import lru_cache, wraps
from hashlib import md5
@ -169,6 +170,8 @@ ComponentStyle = Dict[
]
ComponentChild = Union[types.PrimitiveType, Var, BaseComponent]
warnings.filterwarnings("ignore", message="fields may not start with an underscore")
class Component(BaseComponent, ABC):
"""A component with style, event trigger and other props."""
@ -195,7 +198,7 @@ class Component(BaseComponent, ABC):
class_name: Any = None
# Special component props.
special_props: Set[ImmutableVar] = set()
special_props: List[ImmutableVar] = []
# Whether the component should take the focus once the page is loaded
autofocus: bool = False
@ -655,7 +658,7 @@ class Component(BaseComponent, ABC):
"""
# Create the base tag.
tag = Tag(
name=self.tag if not self.alias else self.alias,
name=(self.tag if not self.alias else self.alias) or "",
special_props=self.special_props,
)
@ -2244,7 +2247,7 @@ class StatefulComponent(BaseComponent):
Returns:
The tag to render.
"""
return dict(Tag(name=self.tag))
return dict(Tag(name=self.tag or ""))
def __str__(self) -> str:
"""Represent the component in React.

View File

@ -247,9 +247,9 @@ class Upload(MemoizationLeaf):
}
# The file input to use.
upload = Input.create(type="file")
upload.special_props = {
upload.special_props = [
ImmutableVar(_var_name="{...getInputProps()}", _var_type=None)
}
]
# The dropzone to use.
zone = Box.create(
@ -257,9 +257,9 @@ class Upload(MemoizationLeaf):
*children,
**{k: v for k, v in props.items() if k not in supported_props},
)
zone.special_props = {
zone.special_props = [
ImmutableVar(_var_name="{...getRootProps()}", _var_type=None)
}
]
# Create the component.
upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID)

View File

@ -1,6 +1,6 @@
"""Element classes. This is an auto-generated file. Do not edit. See ../generate.py."""
from typing import Set, Union
from typing import List, Union
from reflex.components.el.element import Element
from reflex.ivars.base import ImmutableVar
@ -90,9 +90,9 @@ class StyleEl(Element): # noqa: E742
media: Var[Union[str, int, bool]]
special_props: Set[ImmutableVar] = {
special_props: List[ImmutableVar] = [
ImmutableVar.create_safe("suppressHydrationWarning")
}
]
base = Base.create

View File

@ -195,17 +195,17 @@ class Markdown(Component):
if tag not in self.component_map:
raise ValueError(f"No markdown component found for tag: {tag}.")
special_props = {_PROPS_IN_TAG}
special_props = [_PROPS_IN_TAG]
children = [_CHILDREN]
# For certain tags, the props from the markdown renderer are not actually valid for the component.
if tag in NO_PROPS_TAGS:
special_props = set()
special_props = []
# If the children are set as a prop, don't pass them as children.
children_prop = props.pop("children", None)
if children_prop is not None:
special_props.add(
special_props.append(
ImmutableVar.create_safe(f"children={{{str(children_prop)}}}")
)
children = []

View File

@ -1,26 +1,27 @@
"""Moment component for humanized date rendering."""
import dataclasses
from typing import List, Optional
from reflex.base import Base
from reflex.components.component import Component, NoSSRComponent
from reflex.event import EventHandler
from reflex.utils.imports import ImportDict
from reflex.vars import Var
class MomentDelta(Base):
@dataclasses.dataclass(frozen=True)
class MomentDelta:
"""A delta used for add/subtract prop in Moment."""
years: Optional[int]
quarters: Optional[int]
months: Optional[int]
weeks: Optional[int]
days: Optional[int]
hours: Optional[int]
minutess: Optional[int]
seconds: Optional[int]
milliseconds: Optional[int]
years: Optional[int] = dataclasses.field(default=None)
quarters: Optional[int] = dataclasses.field(default=None)
months: Optional[int] = dataclasses.field(default=None)
weeks: Optional[int] = dataclasses.field(default=None)
days: Optional[int] = dataclasses.field(default=None)
hours: Optional[int] = dataclasses.field(default=None)
minutess: Optional[int] = dataclasses.field(default=None)
seconds: Optional[int] = dataclasses.field(default=None)
milliseconds: Optional[int] = dataclasses.field(default=None)
class Moment(NoSSRComponent):

View File

@ -3,9 +3,9 @@
# ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------
import dataclasses
from typing import Any, Callable, Dict, Optional, Union, overload
from reflex.base import Base
from reflex.components.component import NoSSRComponent
from reflex.event import EventHandler, EventSpec
from reflex.ivars.base import ImmutableVar
@ -13,7 +13,8 @@ from reflex.style import Style
from reflex.utils.imports import ImportDict
from reflex.vars import Var
class MomentDelta(Base):
@dataclasses.dataclass(frozen=True)
class MomentDelta:
years: Optional[int]
quarters: Optional[int]
months: Optional[int]

View File

@ -267,7 +267,7 @@ const extractPoints = (points) => {
template_dict = LiteralVar.create({"layout": {"template": self.template}})
merge_dicts.append(template_dict.without_data())
if merge_dicts:
tag.special_props.add(
tag.special_props.append(
# Merge all dictionaries and spread the result over props.
ImmutableVar.create_safe(
f"{{...mergician({str(figure)},"
@ -276,5 +276,5 @@ const extractPoints = (points) => {
)
else:
# Spread the figure dict over props, nothing to merge.
tag.special_props.add(ImmutableVar.create_safe(f"{{...{str(figure)}}}"))
tag.special_props.append(ImmutableVar.create_safe(f"{{...{str(figure)}}}"))
return tag

View File

@ -23,3 +23,20 @@ class PropsBase(Base):
return LiteralObjectVar.create(
{format.to_camel_case(key): value for key, value in self.dict().items()}
).json()
def dict(self, *args, **kwargs):
"""Convert the object to a dictionary.
Keys will be converted to camelCase.
Args:
*args: Arguments to pass to the parent class.
**kwargs: Keyword arguments to pass to the parent class.
Returns:
The object as a dictionary.
"""
return {
format.to_camel_case(key): value
for key, value in super().dict(*args, **kwargs).items()
}

View File

@ -55,6 +55,7 @@ from .themes.layout.container import container as container
from .themes.layout.flex import flex as flex
from .themes.layout.grid import grid as grid
from .themes.layout.list import list_item as list_item
from .themes.layout.list import list_ns as list # noqa
from .themes.layout.list import ordered_list as ordered_list
from .themes.layout.list import unordered_list as unordered_list
from .themes.layout.section import section as section

View File

@ -262,26 +262,6 @@ class ThemePanel(RadixThemesComponent):
"""
return {"react": "useEffect"}
def add_hooks(self) -> list[str]:
"""Add a hook on the ThemePanel to clear chakra-ui-color-mode.
Returns:
The hooks to render.
"""
# The panel freezes the tab if the user color preference differs from the
# theme "appearance", so clear it out when theme panel is used.
return [
"""
useEffect(() => {
if (typeof window !== 'undefined') {
window.onbeforeunload = () => {
localStorage.removeItem('chakra-ui-color-mode');
}
window.onbeforeunload();
}
}, [])"""
]
class RadixThemesColorModeProvider(Component):
"""Next-themes integration for radix themes components."""

View File

@ -583,7 +583,6 @@ class Theme(RadixThemesComponent):
class ThemePanel(RadixThemesComponent):
def add_imports(self) -> dict[str, str]: ...
def add_hooks(self) -> list[str]: ...
@overload
@classmethod
def create( # type: ignore

View File

@ -9,6 +9,7 @@ from .container import container as container
from .flex import flex as flex
from .grid import grid as grid
from .list import list_item as list_item
from .list import list_ns as list # noqa
from .list import ordered_list as ordered_list
from .list import unordered_list as unordered_list
from .section import section as section

View File

@ -171,12 +171,12 @@ class ToastProps(PropsBase):
d["cancel"] = self.cancel
if isinstance(self.cancel, dict):
d["cancel"] = ToastAction(**self.cancel)
if "on_dismiss" in d:
d["on_dismiss"] = format.format_queue_events(
if "onDismiss" in d:
d["onDismiss"] = format.format_queue_events(
self.on_dismiss, _toast_callback_signature
)
if "on_auto_close" in d:
d["on_auto_close"] = format.format_queue_events(
if "onAutoClose" in d:
d["onAutoClose"] = format.format_queue_events(
self.on_auto_close, _toast_callback_signature
)
return d

View File

@ -1,19 +1,22 @@
"""Tag to conditionally render components."""
import dataclasses
from typing import Any, Dict, Optional
from reflex.components.tags.tag import Tag
from reflex.ivars.base import LiteralVar
from reflex.vars import Var
@dataclasses.dataclass()
class CondTag(Tag):
"""A conditional tag."""
# The condition to determine which component to render.
cond: Var[Any]
cond: Var[Any] = dataclasses.field(default_factory=lambda: LiteralVar.create(True))
# The code to render if the condition is true.
true_value: Dict
true_value: Dict = dataclasses.field(default_factory=dict)
# The code to render if the condition is false.
false_value: Optional[Dict]
false_value: Optional[Dict] = None

View File

@ -2,31 +2,36 @@
from __future__ import annotations
import dataclasses
import inspect
from typing import TYPE_CHECKING, Any, Callable, List, Tuple, Type, Union, get_args
from reflex.components.tags.tag import Tag
from reflex.ivars.base import ImmutableVar
from reflex.vars import Var
from reflex.ivars.sequence import LiteralArrayVar
from reflex.vars import Var, get_unique_variable_name
if TYPE_CHECKING:
from reflex.components.component import Component
@dataclasses.dataclass()
class IterTag(Tag):
"""An iterator tag."""
# The var to iterate over.
iterable: Var[List]
iterable: Var[List] = dataclasses.field(
default_factory=lambda: LiteralArrayVar.create([])
)
# The component render function for each item in the iterable.
render_fn: Callable
render_fn: Callable = dataclasses.field(default_factory=lambda: lambda x: x)
# The name of the arg var.
arg_var_name: str
arg_var_name: str = dataclasses.field(default_factory=get_unique_variable_name)
# The name of the index var.
index_var_name: str
index_var_name: str = dataclasses.field(default_factory=get_unique_variable_name)
def get_iterable_var_type(self) -> Type:
"""Get the type of the iterable var.

View File

@ -1,19 +1,22 @@
"""Tag to conditionally match cases."""
import dataclasses
from typing import Any, List
from reflex.components.tags.tag import Tag
from reflex.ivars.base import LiteralVar
from reflex.vars import Var
@dataclasses.dataclass()
class MatchTag(Tag):
"""A match tag."""
# The condition to determine which case to match.
cond: Var[Any]
cond: Var[Any] = dataclasses.field(default_factory=lambda: LiteralVar.create(True))
# The list of match cases to be matched.
match_cases: List[Any]
match_cases: List[Any] = dataclasses.field(default_factory=list)
# The catchall case to match.
default: Any
default: Any = dataclasses.field(default=LiteralVar.create(None))

View File

@ -2,22 +2,23 @@
from __future__ import annotations
from typing import Any, Dict, List, Optional, Set, Tuple, Union
import dataclasses
from typing import Any, Dict, List, Optional, Tuple, Union
from reflex.base import Base
from reflex.event import EventChain
from reflex.ivars.base import ImmutableVar, LiteralVar
from reflex.utils import format, types
class Tag(Base):
@dataclasses.dataclass()
class Tag:
"""A React tag."""
# The name of the tag.
name: str = ""
# The props of the tag.
props: Dict[str, Any] = {}
props: Dict[str, Any] = dataclasses.field(default_factory=dict)
# The inner contents of the tag.
contents: str = ""
@ -26,25 +27,18 @@ class Tag(Base):
args: Optional[Tuple[str, ...]] = None
# Special props that aren't key value pairs.
special_props: Set[ImmutableVar] = set()
special_props: List[ImmutableVar] = dataclasses.field(default_factory=list)
# The children components.
children: List[Any] = []
children: List[Any] = dataclasses.field(default_factory=list)
def __init__(self, *args, **kwargs):
"""Initialize the tag.
Args:
*args: Args to initialize the tag.
**kwargs: Kwargs to initialize the tag.
"""
# Convert any props to vars.
if "props" in kwargs:
kwargs["props"] = {
name: LiteralVar.create(value)
for name, value in kwargs["props"].items()
}
super().__init__(*args, **kwargs)
def __post_init__(self):
"""Post initialize the tag."""
object.__setattr__(
self,
"props",
{name: LiteralVar.create(value) for name, value in self.props.items()},
)
def format_props(self) -> List:
"""Format the tag's props.
@ -54,6 +48,29 @@ class Tag(Base):
"""
return format.format_props(*self.special_props, **self.props)
def set(self, **kwargs: Any):
"""Set the tag's fields.
Args:
kwargs: The fields to set.
Returns:
The tag with the fields
"""
for name, value in kwargs.items():
setattr(self, name, value)
return self
def __iter__(self):
"""Iterate over the tag's fields.
Yields:
Tuple[str, Any]: The field name and value.
"""
for field in dataclasses.fields(self):
yield field.name, getattr(self, field.name)
def add_props(self, **kwargs: Optional[Any]) -> Tag:
"""Add props to the tag.

View File

@ -117,7 +117,9 @@ class Templates(SimpleNamespace):
REFLEX_BUILD_POLL_URL = REFLEX_BUILD_BACKEND + "/api/init/{reflex_init_token}"
# The URL to fetch the generation's reflex code
REFLEX_BUILD_CODE_URL = REFLEX_BUILD_BACKEND + "/api/gen/{generation_hash}"
REFLEX_BUILD_CODE_URL = (
REFLEX_BUILD_BACKEND + "/api/gen/{generation_hash}/refactored"
)
class Dirs(SimpleNamespace):
"""Folders used by the template system of Reflex."""

View File

@ -2,6 +2,7 @@
from __future__ import annotations
import dataclasses
import inspect
import types
import urllib.parse
@ -18,7 +19,6 @@ from typing import (
)
from reflex import constants
from reflex.base import Base
from reflex.ivars.base import ImmutableVar, LiteralVar
from reflex.ivars.function import FunctionStringVar, FunctionVar
from reflex.ivars.object import ObjectVar
@ -33,7 +33,11 @@ except ImportError:
from typing_extensions import Annotated
class Event(Base):
@dataclasses.dataclass(
init=True,
frozen=True,
)
class Event:
"""An event that describes any state change in the app."""
# The token to specify the client that the event is for.
@ -43,10 +47,10 @@ class Event(Base):
name: str
# The routing data where event occurred
router_data: Dict[str, Any] = {}
router_data: Dict[str, Any] = dataclasses.field(default_factory=dict)
# The event payload.
payload: Dict[str, Any] = {}
payload: Dict[str, Any] = dataclasses.field(default_factory=dict)
@property
def substate_token(self) -> str:
@ -81,11 +85,15 @@ def background(fn):
return fn
class EventActionsMixin(Base):
@dataclasses.dataclass(
init=True,
frozen=True,
)
class EventActionsMixin:
"""Mixin for DOM event actions."""
# Whether to `preventDefault` or `stopPropagation` on the event.
event_actions: Dict[str, Union[bool, int]] = {}
event_actions: Dict[str, Union[bool, int]] = dataclasses.field(default_factory=dict)
@property
def stop_propagation(self):
@ -94,8 +102,9 @@ class EventActionsMixin(Base):
Returns:
New EventHandler-like with stopPropagation set to True.
"""
return self.copy(
update={"event_actions": {"stopPropagation": True, **self.event_actions}},
return dataclasses.replace(
self,
event_actions={"stopPropagation": True, **self.event_actions},
)
@property
@ -105,8 +114,9 @@ class EventActionsMixin(Base):
Returns:
New EventHandler-like with preventDefault set to True.
"""
return self.copy(
update={"event_actions": {"preventDefault": True, **self.event_actions}},
return dataclasses.replace(
self,
event_actions={"preventDefault": True, **self.event_actions},
)
def throttle(self, limit_ms: int):
@ -118,8 +128,9 @@ class EventActionsMixin(Base):
Returns:
New EventHandler-like with throttle set to limit_ms.
"""
return self.copy(
update={"event_actions": {"throttle": limit_ms, **self.event_actions}},
return dataclasses.replace(
self,
event_actions={"throttle": limit_ms, **self.event_actions},
)
def debounce(self, delay_ms: int):
@ -131,26 +142,25 @@ class EventActionsMixin(Base):
Returns:
New EventHandler-like with debounce set to delay_ms.
"""
return self.copy(
update={"event_actions": {"debounce": delay_ms, **self.event_actions}},
return dataclasses.replace(
self,
event_actions={"debounce": delay_ms, **self.event_actions},
)
@dataclasses.dataclass(
init=True,
frozen=True,
)
class EventHandler(EventActionsMixin):
"""An event handler responds to an event to update the state."""
# The function to call in response to the event.
fn: Any
fn: Any = dataclasses.field(default=None)
# The full name of the state class this event handler is attached to.
# Empty string means this event handler is a server side event.
state_full_name: str = ""
class Config:
"""The Pydantic config."""
# Needed to allow serialization of Callable.
frozen = True
state_full_name: str = dataclasses.field(default="")
@classmethod
def __class_getitem__(cls, args_spec: str) -> Annotated:
@ -215,6 +225,10 @@ class EventHandler(EventActionsMixin):
)
@dataclasses.dataclass(
init=True,
frozen=True,
)
class EventSpec(EventActionsMixin):
"""An event specification.
@ -223,19 +237,37 @@ class EventSpec(EventActionsMixin):
"""
# The event handler.
handler: EventHandler
handler: EventHandler = dataclasses.field(default=None) # type: ignore
# The handler on the client to process event.
client_handler_name: str = ""
client_handler_name: str = dataclasses.field(default="")
# The arguments to pass to the function.
args: Tuple[Tuple[ImmutableVar, ImmutableVar], ...] = ()
args: Tuple[Tuple[ImmutableVar, ImmutableVar], ...] = dataclasses.field(
default_factory=tuple
)
class Config:
"""The Pydantic config."""
def __init__(
self,
handler: EventHandler,
event_actions: Dict[str, Union[bool, int]] | None = None,
client_handler_name: str = "",
args: Tuple[Tuple[ImmutableVar, ImmutableVar], ...] = tuple(),
):
"""Initialize an EventSpec.
# Required to allow tuple fields.
frozen = True
Args:
event_actions: The event actions.
handler: The event handler.
client_handler_name: The client handler name.
args: The arguments to pass to the function.
"""
if event_actions is None:
event_actions = {}
object.__setattr__(self, "event_actions", event_actions)
object.__setattr__(self, "handler", handler)
object.__setattr__(self, "client_handler_name", client_handler_name)
object.__setattr__(self, "args", args or tuple())
def with_args(
self, args: Tuple[Tuple[ImmutableVar, ImmutableVar], ...]
@ -286,6 +318,9 @@ class EventSpec(EventActionsMixin):
return self.with_args(self.args + new_payload)
@dataclasses.dataclass(
frozen=True,
)
class CallableEventSpec(EventSpec):
"""Decorate an EventSpec-returning function to act as both a EventSpec and a function.
@ -305,10 +340,13 @@ class CallableEventSpec(EventSpec):
if fn is not None:
default_event_spec = fn()
super().__init__(
fn=fn, # type: ignore
**default_event_spec.dict(),
event_actions=default_event_spec.event_actions,
client_handler_name=default_event_spec.client_handler_name,
args=default_event_spec.args,
handler=default_event_spec.handler,
**kwargs,
)
object.__setattr__(self, "fn", fn)
else:
super().__init__(**kwargs)
@ -332,12 +370,16 @@ class CallableEventSpec(EventSpec):
return self.fn(*args, **kwargs)
@dataclasses.dataclass(
init=True,
frozen=True,
)
class EventChain(EventActionsMixin):
"""Container for a chain of events that will be executed in order."""
events: List[EventSpec]
events: List[EventSpec] = dataclasses.field(default_factory=list)
args_spec: Optional[Callable]
args_spec: Optional[Callable] = dataclasses.field(default=None)
# These chains can be used for their side effects when no other events are desired.
@ -345,14 +387,22 @@ stop_propagation = EventChain(events=[], args_spec=lambda: []).stop_propagation
prevent_default = EventChain(events=[], args_spec=lambda: []).prevent_default
class Target(Base):
@dataclasses.dataclass(
init=True,
frozen=True,
)
class Target:
"""A Javascript event target."""
checked: bool = False
value: Any = None
class FrontendEvent(Base):
@dataclasses.dataclass(
init=True,
frozen=True,
)
class FrontendEvent:
"""A Javascript event."""
target: Target = Target()
@ -360,7 +410,11 @@ class FrontendEvent(Base):
value: Any = None
class FileUpload(Base):
@dataclasses.dataclass(
init=True,
frozen=True,
)
class FileUpload:
"""Class to represent a file upload."""
upload_id: Optional[str] = None

View File

@ -421,6 +421,9 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
if issubclass(output, (ObjectVar, Base)):
return ToObjectOperation.create(self, var_type or dict)
if dataclasses.is_dataclass(output):
return ToObjectOperation.create(self, var_type or dict)
if issubclass(output, FunctionVar):
# if fixed_type is not None and not issubclass(fixed_type, Callable):
# raise TypeError(
@ -479,7 +482,11 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
):
return self.to(NumberVar, self._var_type)
if all(inspect.isclass(t) and issubclass(t, Base) for t in inner_types):
if all(
inspect.isclass(t)
and (issubclass(t, Base) or dataclasses.is_dataclass(t))
for t in inner_types
):
return self.to(ObjectVar, self._var_type)
return self.to(ObjectVar, self._var_type)
@ -982,6 +989,16 @@ class LiteralVar(ImmutableVar):
)
return LiteralVar.create(serialized_value, _var_data=_var_data)
if dataclasses.is_dataclass(value) and not isinstance(value, type):
return LiteralObjectVar.create(
{
k: (None if callable(v) else v)
for k, v in dataclasses.asdict(value).items()
},
_var_type=type(value),
_var_data=_var_data,
)
raise TypeError(
f"Unsupported type {type(value)} for LiteralVar. Tried to create a LiteralVar from {value}."
)

View File

@ -2,6 +2,7 @@
from __future__ import annotations
import dataclasses
from typing import TYPE_CHECKING, Optional
from reflex import constants
@ -14,6 +15,7 @@ if TYPE_CHECKING:
from reflex.app import App
@dataclasses.dataclass(init=True)
class HydrateMiddleware(Middleware):
"""Middleware to handle initial app hydration."""

View File

@ -2,10 +2,9 @@
from __future__ import annotations
from abc import ABC
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Optional
from reflex.base import Base
from reflex.event import Event
from reflex.state import BaseState, StateUpdate
@ -13,9 +12,10 @@ if TYPE_CHECKING:
from reflex.app import App
class Middleware(Base, ABC):
class Middleware(ABC):
"""Middleware to preprocess and postprocess requests."""
@abstractmethod
async def preprocess(
self, app: App, state: BaseState, event: Event
) -> Optional[StateUpdate]:

View File

@ -5,8 +5,10 @@ from __future__ import annotations
import asyncio
import contextlib
import copy
import dataclasses
import functools
import inspect
import json
import os
import uuid
from abc import ABC, abstractmethod
@ -83,13 +85,15 @@ var = immutable_computed_var
TOO_LARGE_SERIALIZED_STATE = 100 * 1024 # 100kb
class HeaderData(Base):
@dataclasses.dataclass(frozen=True)
class HeaderData:
"""An object containing headers data."""
host: str = ""
origin: str = ""
upgrade: str = ""
connection: str = ""
cookie: str = ""
pragma: str = ""
cache_control: str = ""
user_agent: str = ""
@ -105,13 +109,16 @@ class HeaderData(Base):
Args:
router_data: the router_data dict.
"""
super().__init__()
if router_data:
for k, v in router_data.get(constants.RouteVar.HEADERS, {}).items():
setattr(self, format.to_snake_case(k), v)
object.__setattr__(self, format.to_snake_case(k), v)
else:
for k in dataclasses.fields(self):
object.__setattr__(self, k.name, "")
class PageData(Base):
@dataclasses.dataclass(frozen=True)
class PageData:
"""An object containing page data."""
host: str = "" # repeated with self.headers.origin (remove or keep the duplicate?)
@ -119,7 +126,7 @@ class PageData(Base):
raw_path: str = ""
full_path: str = ""
full_raw_path: str = ""
params: dict = {}
params: dict = dataclasses.field(default_factory=dict)
def __init__(self, router_data: Optional[dict] = None):
"""Initalize the PageData object based on router_data.
@ -127,17 +134,34 @@ class PageData(Base):
Args:
router_data: the router_data dict.
"""
super().__init__()
if router_data:
self.host = router_data.get(constants.RouteVar.HEADERS, {}).get("origin")
self.path = router_data.get(constants.RouteVar.PATH, "")
self.raw_path = router_data.get(constants.RouteVar.ORIGIN, "")
self.full_path = f"{self.host}{self.path}"
self.full_raw_path = f"{self.host}{self.raw_path}"
self.params = router_data.get(constants.RouteVar.QUERY, {})
object.__setattr__(
self,
"host",
router_data.get(constants.RouteVar.HEADERS, {}).get("origin", ""),
)
object.__setattr__(
self, "path", router_data.get(constants.RouteVar.PATH, "")
)
object.__setattr__(
self, "raw_path", router_data.get(constants.RouteVar.ORIGIN, "")
)
object.__setattr__(self, "full_path", f"{self.host}{self.path}")
object.__setattr__(self, "full_raw_path", f"{self.host}{self.raw_path}")
object.__setattr__(
self, "params", router_data.get(constants.RouteVar.QUERY, {})
)
else:
object.__setattr__(self, "host", "")
object.__setattr__(self, "path", "")
object.__setattr__(self, "raw_path", "")
object.__setattr__(self, "full_path", "")
object.__setattr__(self, "full_raw_path", "")
object.__setattr__(self, "params", {})
class SessionData(Base):
@dataclasses.dataclass(frozen=True, init=False)
class SessionData:
"""An object containing session data."""
client_token: str = ""
@ -150,19 +174,24 @@ class SessionData(Base):
Args:
router_data: the router_data dict.
"""
super().__init__()
if router_data:
self.client_token = router_data.get(constants.RouteVar.CLIENT_TOKEN, "")
self.client_ip = router_data.get(constants.RouteVar.CLIENT_IP, "")
self.session_id = router_data.get(constants.RouteVar.SESSION_ID, "")
client_token = router_data.get(constants.RouteVar.CLIENT_TOKEN, "")
client_ip = router_data.get(constants.RouteVar.CLIENT_IP, "")
session_id = router_data.get(constants.RouteVar.SESSION_ID, "")
else:
client_token = client_ip = session_id = ""
object.__setattr__(self, "client_token", client_token)
object.__setattr__(self, "client_ip", client_ip)
object.__setattr__(self, "session_id", session_id)
class RouterData(Base):
@dataclasses.dataclass(frozen=True, init=False)
class RouterData:
"""An object containing RouterData."""
session: SessionData = SessionData()
headers: HeaderData = HeaderData()
page: PageData = PageData()
session: SessionData = dataclasses.field(default_factory=SessionData)
headers: HeaderData = dataclasses.field(default_factory=HeaderData)
page: PageData = dataclasses.field(default_factory=PageData)
def __init__(self, router_data: Optional[dict] = None):
"""Initialize the RouterData object.
@ -170,10 +199,30 @@ class RouterData(Base):
Args:
router_data: the router_data dict.
"""
super().__init__()
self.session = SessionData(router_data)
self.headers = HeaderData(router_data)
self.page = PageData(router_data)
object.__setattr__(self, "session", SessionData(router_data))
object.__setattr__(self, "headers", HeaderData(router_data))
object.__setattr__(self, "page", PageData(router_data))
def toJson(self) -> str:
"""Convert the object to a JSON string.
Returns:
The JSON string.
"""
return json.dumps(dataclasses.asdict(self))
@serializer
def serialize_routerdata(value: RouterData) -> str:
"""Serialize a RouterData instance.
Args:
value: The RouterData to serialize.
Returns:
The serialized RouterData.
"""
return value.toJson()
def _no_chain_background_task(
@ -249,10 +298,11 @@ def _split_substate_key(substate_key: str) -> tuple[str, str]:
return token, state_name
@dataclasses.dataclass(frozen=True, init=False)
class EventHandlerSetVar(EventHandler):
"""A special event handler to wrap setvar functionality."""
state_cls: Type[BaseState]
state_cls: Type[BaseState] = dataclasses.field(init=False)
def __init__(self, state_cls: Type[BaseState]):
"""Initialize the EventHandlerSetVar.
@ -263,8 +313,8 @@ class EventHandlerSetVar(EventHandler):
super().__init__(
fn=type(self).setvar,
state_full_name=state_cls.get_full_name(),
state_cls=state_cls, # type: ignore
)
object.__setattr__(self, "state_cls", state_cls)
def setvar(self, var_name: str, value: Any):
"""Set the state variable to the value of the event.
@ -1826,8 +1876,13 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
self.dirty_vars.update(self._always_dirty_computed_vars)
self._mark_dirty()
def dictify(value: Any):
if dataclasses.is_dataclass(value) and not isinstance(value, type):
return dataclasses.asdict(value)
return value
base_vars = {
prop_name: self.get_value(getattr(self, prop_name))
prop_name: dictify(self.get_value(getattr(self, prop_name)))
for prop_name in self.base_vars
}
if initial and include_computed:
@ -1907,9 +1962,6 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
return state
EventHandlerSetVar.update_forward_refs()
class State(BaseState):
"""The app Base State."""
@ -2341,18 +2393,29 @@ class StateProxy(wrapt.ObjectProxy):
self._self_mutable = original_mutable
class StateUpdate(Base):
@dataclasses.dataclass(
frozen=True,
)
class StateUpdate:
"""A state update sent to the frontend."""
# The state delta.
delta: Delta = {}
delta: Delta = dataclasses.field(default_factory=dict)
# Events to be added to the event queue.
events: List[Event] = []
events: List[Event] = dataclasses.field(default_factory=list)
# Whether this is the final state update for the event.
final: bool = True
def json(self) -> str:
"""Convert the state update to a JSON string.
Returns:
The state update as a JSON string.
"""
return json.dumps(dataclasses.asdict(self))
class StateManager(Base, ABC):
"""A class to manage many client states."""

View File

@ -290,7 +290,6 @@ def _format_emotion_style_pseudo_selector(key: str) -> str:
"""
prefix = None
if key.startswith("_"):
# Handle pseudo selectors in chakra style format.
prefix = "&:"
key = key[1:]
if key.startswith(":"):

View File

@ -91,3 +91,7 @@ class EventFnArgMismatch(ReflexError, TypeError):
class DynamicRouteArgShadowsStateVar(ReflexError, NameError):
"""Raised when a dynamic route arg shadows a state var."""
class GeneratedCodeHasNoFunctionDefs(ReflexError):
"""Raised when refactored code generated with flexgen has no functions defined."""

View File

@ -2,6 +2,7 @@
from __future__ import annotations
import dataclasses
import inspect
import json
import os
@ -623,6 +624,14 @@ def format_state(value: Any, key: Optional[str] = None) -> Any:
if isinstance(value, dict):
return {k: format_state(v, k) for k, v in value.items()}
# Hand dataclasses.
if dataclasses.is_dataclass(value):
if isinstance(value, type):
raise TypeError(
f"Cannot format state of type {type(value)}. Please provide an instance of the dataclass."
)
return {k: format_state(v, k) for k, v in dataclasses.asdict(value).items()}
# Handle lists, sets, typles.
if isinstance(value, types.StateIterBases):
return [format_state(v) for v in value]

View File

@ -2,10 +2,9 @@
from __future__ import annotations
import dataclasses
from collections import defaultdict
from typing import Dict, List, Optional, Tuple, Union
from reflex.base import Base
from typing import DefaultDict, Dict, List, Optional, Tuple, Union
def merge_imports(
@ -19,12 +18,22 @@ def merge_imports(
Returns:
The merged import dicts.
"""
all_imports = defaultdict(list)
all_imports: DefaultDict[str, List[ImportVar]] = defaultdict(list)
for import_dict in imports:
for lib, fields in (
import_dict if isinstance(import_dict, tuple) else import_dict.items()
):
all_imports[lib].extend(fields)
if isinstance(fields, (list, tuple, set)):
all_imports[lib].extend(
(
ImportVar(field) if isinstance(field, str) else field
for field in fields
)
)
else:
all_imports[lib].append(
ImportVar(fields) if isinstance(fields, str) else fields
)
return all_imports
@ -75,7 +84,8 @@ def collapse_imports(
}
class ImportVar(Base):
@dataclasses.dataclass(order=True, frozen=True)
class ImportVar:
"""An import var."""
# The name of the import tag.
@ -111,73 +121,6 @@ class ImportVar(Base):
else:
return self.tag or ""
def __lt__(self, other: ImportVar) -> bool:
"""Compare two ImportVar objects.
Args:
other: The other ImportVar object to compare.
Returns:
Whether this ImportVar object is less than the other.
"""
return (
self.tag,
self.is_default,
self.alias,
self.install,
self.render,
self.transpile,
) < (
other.tag,
other.is_default,
other.alias,
other.install,
other.render,
other.transpile,
)
def __eq__(self, other: ImportVar) -> bool:
"""Check if two ImportVar objects are equal.
Args:
other: The other ImportVar object to compare.
Returns:
Whether the two ImportVar objects are equal.
"""
return (
self.tag,
self.is_default,
self.alias,
self.install,
self.render,
self.transpile,
) == (
other.tag,
other.is_default,
other.alias,
other.install,
other.render,
other.transpile,
)
def __hash__(self) -> int:
"""Hash the ImportVar object.
Returns:
The hash of the ImportVar object.
"""
return hash(
(
self.tag,
self.is_default,
self.alias,
self.install,
self.render,
self.transpile,
)
)
ImportTypes = Union[str, ImportVar, List[Union[str, ImportVar]], List[ImportVar]]
ImportDict = Dict[str, ImportTypes]

View File

@ -2,6 +2,7 @@
from __future__ import annotations
import dataclasses
import functools
import glob
import importlib
@ -15,7 +16,7 @@ import shutil
import stat
import sys
import tempfile
import textwrap
import time
import zipfile
from datetime import datetime
from fileinput import FileInput
@ -32,17 +33,18 @@ from redis import exceptions
from redis.asyncio import Redis
from reflex import constants, model
from reflex.base import Base
from reflex.compiler import templates
from reflex.config import Config, get_config
from reflex.utils import console, net, path_ops, processes
from reflex.utils.exceptions import GeneratedCodeHasNoFunctionDefs
from reflex.utils.format import format_library_name
from reflex.utils.registry import _get_best_registry
CURRENTLY_INSTALLING_NODE = False
class Template(Base):
@dataclasses.dataclass(frozen=True)
class Template:
"""A template for a Reflex app."""
name: str
@ -51,7 +53,8 @@ class Template(Base):
demo_url: str
class CpuInfo(Base):
@dataclasses.dataclass(frozen=True)
class CpuInfo:
"""Model to save cpu info."""
manufacturer_id: Optional[str]
@ -1278,7 +1281,7 @@ def fetch_app_templates(version: str) -> dict[str, Template]:
None,
)
return {
tp["name"]: Template.parse_obj(tp)
tp["name"]: Template(**tp)
for tp in templates_data
if not tp["hidden"] and tp["code_url"] is not None
}
@ -1435,19 +1438,37 @@ def initialize_main_module_index_from_generation(app_name: str, generation_hash:
Args:
app_name: The name of the app.
generation_hash: The generation hash from reflex.build.
Raises:
GeneratedCodeHasNoFunctionDefs: If the fetched code has no function definitions
(the refactored reflex code is expected to have at least one root function defined).
"""
# Download the reflex code for the generation.
resp = net.get(
constants.Templates.REFLEX_BUILD_CODE_URL.format(
url = constants.Templates.REFLEX_BUILD_CODE_URL.format(
generation_hash=generation_hash
)
).raise_for_status()
resp = net.get(url)
while resp.status_code == httpx.codes.SERVICE_UNAVAILABLE:
console.debug("Waiting for the code to be generated...")
time.sleep(1)
resp = net.get(url)
resp.raise_for_status()
# Determine the name of the last function, which renders the generated code.
defined_funcs = re.findall(r"def ([a-zA-Z_]+)\(", resp.text)
if not defined_funcs:
raise GeneratedCodeHasNoFunctionDefs(
f"No function definitions found in generated code from {url!r}."
)
render_func_name = defined_funcs[-1]
def replace_content(_match):
return "\n".join(
[
"def index() -> rx.Component:",
textwrap.indent("return " + resp.text, " "),
resp.text,
"",
"" "def index() -> rx.Component:",
f" return {render_func_name}()",
"",
"",
],

View File

@ -903,7 +903,13 @@ class PyiGenerator:
# construct the import statement and handle special cases for aliases
sub_mod_attrs_imports = [
f"from .{path} import {mod if not isinstance(mod, tuple) else mod[0]} as {mod if not isinstance(mod, tuple) else mod[1]}"
+ (" # type: ignore" if mod in pyright_ignore_imports else "")
+ (
" # type: ignore"
if mod in pyright_ignore_imports
else " # noqa" # ignore ruff formatting here for cases like rx.list.
if isinstance(mod, tuple)
else ""
)
for mod, path in sub_mod_attrs.items()
]
sub_mod_attrs_imports.append("")

View File

@ -3,6 +3,7 @@
from __future__ import annotations
import asyncio
import dataclasses
import multiprocessing
import platform
import warnings
@ -144,7 +145,7 @@ def _prepare_event(event: str, **kwargs) -> dict:
"python_version": get_python_version(),
"cpu_count": get_cpu_count(),
"memory": get_memory(),
"cpu_info": dict(cpuinfo) if cpuinfo else {},
"cpu_info": dataclasses.asdict(cpuinfo) if cpuinfo else {},
**additional_fields,
},
"timestamp": stamp,

View File

@ -3,6 +3,7 @@
from __future__ import annotations
import contextlib
import dataclasses
import inspect
import sys
import types
@ -486,7 +487,11 @@ def is_valid_var_type(type_: Type) -> bool:
if is_union(type_):
return all((is_valid_var_type(arg) for arg in get_args(type_)))
return _issubclass(type_, StateVar) or serializers.has_serializer(type_)
return (
_issubclass(type_, StateVar)
or serializers.has_serializer(type_)
or dataclasses.is_dataclass(type_)
)
def is_backend_base_variable(name: str, cls: Type) -> bool:

View File

@ -1,177 +0,0 @@
import sys
from typing import List, Tuple
import pytest
from reflex_chakra.components.datadisplay.table import Tbody, Tfoot, Thead
from reflex.state import BaseState
PYTHON_GT_V38 = sys.version_info.major >= 3 and sys.version_info.minor > 8
class TableState(BaseState):
"""Test State class."""
rows_List_List_str: List[List[str]] = [["random", "row"]]
rows_List_List: List[List] = [["random", "row"]]
rows_List_str: List[str] = ["random", "row"]
rows_Tuple_List_str: Tuple[List[str]] = (["random", "row"],)
rows_Tuple_List: Tuple[List] = ["random", "row"] # type: ignore
rows_Tuple_str_str: Tuple[str, str] = (
"random",
"row",
)
rows_Tuple_Tuple_str_str: Tuple[Tuple[str, str]] = (
(
"random",
"row",
),
)
rows_Tuple_Tuple: Tuple[Tuple] = (
(
"random",
"row",
),
)
rows_str: str = "random, row"
headers_List_str: List[str] = ["header1", "header2"]
headers_Tuple_str_str: Tuple[str, str] = (
"header1",
"header2",
)
headers_str: str = "headers1, headers2"
footers_List_str: List[str] = ["footer1", "footer2"]
footers_Tuple_str_str: Tuple[str, str] = (
"footer1",
"footer2",
)
footers_str: str = "footer1, footer2"
if sys.version_info.major >= 3 and sys.version_info.minor > 8:
rows_list_list_str: list[list[str]] = [["random", "row"]]
rows_list_list: list[list] = [["random", "row"]]
rows_list_str: list[str] = ["random", "row"]
rows_tuple_list_str: tuple[list[str]] = (["random", "row"],)
rows_tuple_list: tuple[list] = ["random", "row"] # type: ignore
rows_tuple_str_str: tuple[str, str] = (
"random",
"row",
)
rows_tuple_tuple_str_str: tuple[tuple[str, str]] = (
(
"random",
"row",
),
)
rows_tuple_tuple: tuple[tuple] = (
(
"random",
"row",
),
)
valid_extras = (
[
TableState.rows_list_list_str,
TableState.rows_list_list,
TableState.rows_tuple_list_str,
TableState.rows_tuple_list,
TableState.rows_tuple_tuple_str_str,
TableState.rows_tuple_tuple,
]
if PYTHON_GT_V38
else []
)
invalid_extras = (
[TableState.rows_list_str, TableState.rows_tuple_str_str] if PYTHON_GT_V38 else []
)
@pytest.mark.parametrize(
"rows",
[
[["random", "row"]],
TableState.rows_List_List_str,
TableState.rows_List_List,
TableState.rows_Tuple_List_str,
TableState.rows_Tuple_List,
TableState.rows_Tuple_Tuple_str_str,
TableState.rows_Tuple_Tuple,
*valid_extras,
],
)
def test_create_table_body_with_valid_rows_prop(rows):
render_dict = Tbody.create(rows=rows).render()
assert render_dict["name"] == "Tbody"
assert len(render_dict["children"]) == 1
@pytest.mark.parametrize(
"rows",
[
["random", "row"],
"random, rows",
TableState.rows_List_str,
TableState.rows_Tuple_str_str,
TableState.rows_str,
*invalid_extras,
],
)
def test_create_table_body_with_invalid_rows_prop(rows):
with pytest.raises(TypeError):
Tbody.create(rows=rows)
@pytest.mark.parametrize(
"headers",
[
["random", "header"],
TableState.headers_List_str,
TableState.headers_Tuple_str_str,
],
)
def test_create_table_head_with_valid_headers_prop(headers):
render_dict = Thead.create(headers=headers).render()
assert render_dict["name"] == "Thead"
assert len(render_dict["children"]) == 1
assert render_dict["children"][0]["name"] == "Tr"
@pytest.mark.parametrize(
"headers",
[
"random, header",
TableState.headers_str,
],
)
def test_create_table_head_with_invalid_headers_prop(headers):
with pytest.raises(TypeError):
Thead.create(headers=headers)
@pytest.mark.parametrize(
"footers",
[
["random", "footers"],
TableState.footers_List_str,
TableState.footers_Tuple_str_str,
],
)
def test_create_table_footer_with_valid_footers_prop(footers):
render_dict = Tfoot.create(footers=footers).render()
assert render_dict["name"] == "Tfoot"
assert len(render_dict["children"]) == 1
assert render_dict["children"][0]["name"] == "Tr"
@pytest.mark.parametrize(
"footers",
[
"random, footers",
TableState.footers_str,
],
)
def test_create_table_footer_with_invalid_footers_prop(footers):
with pytest.raises(TypeError):
Tfoot.create(footers=footers)

View File

@ -1,5 +1,4 @@
from reflex_chakra.components.forms.form import Form
from reflex.components.radix.primitives.form import Form
from reflex.event import EventChain
from reflex.ivars.base import ImmutableVar

View File

@ -1,55 +0,0 @@
import pytest
from reflex_chakra.components.media.icon import ICON_LIST, Icon
from reflex.utils import format
def test_no_tag_errors():
"""Test that an icon without a tag raises an error."""
with pytest.raises(AttributeError):
Icon.create()
def test_children_errors():
"""Test that an icon with children raises an error."""
with pytest.raises(AttributeError):
Icon.create("child", tag="search")
@pytest.mark.parametrize(
"tag",
ICON_LIST,
)
def test_valid_icon(tag: str):
"""Test that a valid icon does not raise an error.
Args:
tag: The icon tag.
"""
icon = Icon.create(tag=tag)
assert icon.tag == format.to_title_case(tag) + "Icon"
@pytest.mark.parametrize("tag", ["", " ", "invalid", 123])
def test_invalid_icon(tag):
"""Test that an invalid icon raises an error.
Args:
tag: The icon tag.
"""
with pytest.raises(ValueError):
Icon.create(tag=tag)
@pytest.mark.parametrize(
"tag",
["Check", "Close", "eDit"],
)
def test_tag_with_capital(tag: str):
"""Test that an icon that tag with capital does not raise an error.
Args:
tag: The icon tag.
"""
icon = Icon.create(tag=tag)
assert icon.tag == format.to_title_case(tag) + "Icon"

View File

@ -2,8 +2,6 @@ from contextlib import nullcontext
from typing import Any, Dict, List, Optional, Type, Union
import pytest
import reflex_chakra as rc
from reflex_chakra.components.layout.box import Box
import reflex as rx
from reflex.base import Base
@ -16,6 +14,7 @@ from reflex.components.component import (
StatefulComponent,
custom_component,
)
from reflex.components.radix.themes.layout.box import Box
from reflex.constants import EventTriggers
from reflex.event import EventChain, EventHandler, parse_args_spec
from reflex.ivars.base import ImmutableVar, LiteralVar
@ -638,21 +637,21 @@ def test_component_create_unallowed_types(children, test_component):
"props": [],
"contents": "",
"args": None,
"special_props": set(),
"special_props": [],
"children": [
{
"name": "RadixThemesText",
"props": ['as={"p"}'],
"contents": "",
"args": None,
"special_props": set(),
"special_props": [],
"children": [
{
"name": "",
"props": [],
"contents": '{"first_text"}',
"args": None,
"special_props": set(),
"special_props": [],
"children": [],
"autofocus": False,
}
@ -680,13 +679,13 @@ def test_component_create_unallowed_types(children, test_component):
"contents": '{"first_text"}',
"name": "",
"props": [],
"special_props": set(),
"special_props": [],
}
],
"contents": "",
"name": "RadixThemesText",
"props": ['as={"p"}'],
"special_props": set(),
"special_props": [],
},
{
"args": None,
@ -699,19 +698,19 @@ def test_component_create_unallowed_types(children, test_component):
"contents": '{"second_text"}',
"name": "",
"props": [],
"special_props": set(),
"special_props": [],
}
],
"contents": "",
"name": "RadixThemesText",
"props": ['as={"p"}'],
"special_props": set(),
"special_props": [],
},
],
"contents": "",
"name": "Fragment",
"props": [],
"special_props": set(),
"special_props": [],
},
),
(
@ -731,13 +730,13 @@ def test_component_create_unallowed_types(children, test_component):
"contents": '{"first_text"}',
"name": "",
"props": [],
"special_props": set(),
"special_props": [],
}
],
"contents": "",
"name": "RadixThemesText",
"props": ['as={"p"}'],
"special_props": set(),
"special_props": [],
},
{
"args": None,
@ -758,31 +757,31 @@ def test_component_create_unallowed_types(children, test_component):
"contents": '{"second_text"}',
"name": "",
"props": [],
"special_props": set(),
"special_props": [],
}
],
"contents": "",
"name": "RadixThemesText",
"props": ['as={"p"}'],
"special_props": set(),
"special_props": [],
}
],
"contents": "",
"name": "Fragment",
"props": [],
"special_props": set(),
"special_props": [],
}
],
"contents": "",
"name": "RadixThemesBox",
"props": [],
"special_props": set(),
"special_props": [],
},
],
"contents": "",
"name": "Fragment",
"props": [],
"special_props": set(),
"special_props": [],
},
),
],
@ -1114,8 +1113,8 @@ def test_component_with_only_valid_children(fixture, request):
[
(rx.text("hi"), '<RadixThemesText as={"p"}>\n {"hi"}\n</RadixThemesText>'),
(
rx.box(rc.heading("test", size="md")),
'<RadixThemesBox>\n <Heading size={"md"}>\n {"test"}\n</Heading>\n</RadixThemesBox>',
rx.box(rx.heading("test", size="3")),
'<RadixThemesBox>\n <RadixThemesHeading size={"3"}>\n {"test"}\n</RadixThemesHeading>\n</RadixThemesBox>',
),
],
)
@ -1290,12 +1289,12 @@ class EventState(rx.State):
id="fstring-class_name",
),
pytest.param(
rx.fragment(special_props={TEST_VAR}),
rx.fragment(special_props=[TEST_VAR]),
[TEST_VAR],
id="direct-special_props",
),
pytest.param(
rx.fragment(special_props={LiteralVar.create(f"foo{TEST_VAR}bar")}),
rx.fragment(special_props=[LiteralVar.create(f"foo{TEST_VAR}bar")]),
[FORMATTED_TEST_VAR],
id="fstring-special_props",
),

View File

@ -1,5 +1,4 @@
import pytest
import reflex_chakra as rc
import reflex as rx
from reflex.components.markdown import Markdown
@ -37,9 +36,7 @@ def test_get_component(tag, expected):
def test_set_component_map():
"""Test setting the component map."""
component_map = {
"h1": lambda value: rx.box(
rc.heading(value, as_="h1", size="2xl"), padding="1em"
),
"h1": lambda value: rx.box(rx.heading(value, as_="h1"), padding="1em"),
"p": lambda value: rx.box(rx.text(value), padding="1em"),
}
md = Markdown.create("# Hello", component_map=component_map)

View File

@ -1,5 +1,6 @@
from __future__ import annotations
import dataclasses
import functools
import io
import json
@ -13,7 +14,6 @@ from typing import Generator, List, Tuple, Type
from unittest.mock import AsyncMock
import pytest
import reflex_chakra as rc
import sqlmodel
from fastapi import FastAPI, UploadFile
from starlette_admin.auth import AuthProvider
@ -268,8 +268,6 @@ def test_add_page_set_route_dynamic(index_page, windows_platform: bool):
app = App(state=EmptyState)
assert app.state is not None
route = "/test/[dynamic]"
if windows_platform:
route.lstrip("/").replace("/", "\\")
assert app.pages == {}
app.add_page(index_page, route=route)
assert app.pages.keys() == {"test/[dynamic]"}
@ -937,8 +935,6 @@ def test_dynamic_arg_shadow(
"""
arg_name = "counter"
route = f"/test/[{arg_name}]"
if windows_platform:
route.lstrip("/").replace("/", "\\")
app = app_module_mock.app = App(state=DynamicState)
assert app.state is not None
with pytest.raises(NameError):
@ -964,9 +960,6 @@ def test_multiple_dynamic_args(
arg_name = "my_arg"
route = f"/test/[{arg_name}]"
route2 = f"/test2/[{arg_name}]"
if windows_platform:
route = route.lstrip("/").replace("/", "\\")
route2 = route2.lstrip("/").replace("/", "\\")
app = app_module_mock.app = App(state=EmptyState)
app.add_page(index_page, route=route)
app.add_page(index_page, route=route2)
@ -994,8 +987,6 @@ async def test_dynamic_route_var_route_change_completed_on_load(
"""
arg_name = "dynamic"
route = f"/test/[{arg_name}]"
if windows_platform:
route.lstrip("/").replace("/", "\\")
app = app_module_mock.app = App(state=DynamicState)
assert app.state is not None
assert arg_name not in app.state.vars
@ -1062,7 +1053,7 @@ async def test_dynamic_route_var_route_change_completed_on_load(
f"comp_{arg_name}": exp_val,
constants.CompileVars.IS_HYDRATED: False,
# "side_effect_counter": exp_index,
"router": exp_router,
"router": dataclasses.asdict(exp_router),
}
},
events=[
@ -1320,13 +1311,13 @@ def test_app_wrap_priority(compilable_app: tuple[App, Path]):
tag = "Fragment1"
def _get_app_wrap_components(self) -> dict[tuple[int, str], Component]:
return {(99, "Box"): rc.box()}
return {(99, "Box"): rx.box()}
class Fragment2(Component):
tag = "Fragment2"
def _get_app_wrap_components(self) -> dict[tuple[int, str], Component]:
return {(50, "Text"): rc.text()}
return {(50, "Text"): rx.text()}
class Fragment3(Component):
tag = "Fragment3"
@ -1346,19 +1337,17 @@ def test_app_wrap_priority(compilable_app: tuple[App, Path]):
assert (
"function AppWrap({children}) {"
"return ("
"<Box>"
"<ChakraProvider theme={extendTheme(theme)}>"
"<ChakraColorModeProvider>"
"<Text>"
"<RadixThemesBox>"
'<RadixThemesText as={"p"}>'
"<RadixThemesColorModeProvider>"
"<Fragment2>"
"<Fragment>"
"{children}"
"</Fragment>"
"</Fragment2>"
"</Text>"
"</ChakraColorModeProvider>"
"</ChakraProvider>"
"</Box>"
"</RadixThemesColorModeProvider>"
"</RadixThemesText>"
"</RadixThemesBox>"
")"
"}"
) in "".join(app_js_lines)

View File

@ -2,6 +2,7 @@ from __future__ import annotations
import asyncio
import copy
import dataclasses
import datetime
import functools
import json
@ -58,6 +59,7 @@ formatted_router = {
"origin": "",
"upgrade": "",
"connection": "",
"cookie": "",
"pragma": "",
"cache_control": "",
"user_agent": "",
@ -865,8 +867,10 @@ def test_get_headers(test_state, router_data, router_data_headers):
router_data: The router data fixture.
router_data_headers: The expected headers.
"""
print(router_data_headers)
test_state.router = RouterData(router_data)
assert test_state.router.headers.dict() == {
print(test_state.router.headers)
assert dataclasses.asdict(test_state.router.headers) == {
format.to_snake_case(k): v for k, v in router_data_headers.items()
}
@ -1908,7 +1912,8 @@ async def test_state_proxy(grandchild_state: GrandchildState, mock_app: rx.App):
mock_app.event_namespace.emit.assert_called_once()
mcall = mock_app.event_namespace.emit.mock_calls[0]
assert mcall.args[0] == str(SocketEvent.EVENT)
assert json.loads(mcall.args[1]) == StateUpdate(
assert json.loads(mcall.args[1]) == dataclasses.asdict(
StateUpdate(
delta={
parent_state.get_full_name(): {
"upper": "",
@ -1922,6 +1927,7 @@ async def test_state_proxy(grandchild_state: GrandchildState, mock_app: rx.App):
},
}
)
)
assert mcall.kwargs["to"] == grandchild_state.router.session.session_id

View File

@ -553,6 +553,7 @@ formatted_router = {
"origin": "",
"upgrade": "",
"connection": "",
"cookie": "",
"pragma": "",
"cache_control": "",
"user_agent": "",

View File

@ -54,17 +54,21 @@ def test_import_var(import_var, expected_name):
(
{"react": {"Component"}},
{"react": {"Component"}, "react-dom": {"render"}},
{"react": {"Component"}, "react-dom": {"render"}},
{"react": {ImportVar("Component")}, "react-dom": {ImportVar("render")}},
),
(
{"react": {"Component"}, "next/image": {"Image"}},
{"react": {"Component"}, "react-dom": {"render"}},
{"react": {"Component"}, "react-dom": {"render"}, "next/image": {"Image"}},
{
"react": {ImportVar("Component")},
"react-dom": {ImportVar("render")},
"next/image": {ImportVar("Image")},
},
),
(
{"react": {"Component"}},
{"": {"some/custom.css"}},
{"react": {"Component"}, "": {"some/custom.css"}},
{"react": {ImportVar("Component")}, "": {ImportVar("some/custom.css")}},
),
],
)