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

View File

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

View File

@ -17,8 +17,6 @@ from . import utils
def ClientSide(): def ClientSide():
"""App for testing client-side state.""" """App for testing client-side state."""
import reflex_chakra as rc
import reflex as rx import reflex as rx
class ClientSideState(rx.State): class ClientSideState(rx.State):
@ -72,18 +70,18 @@ def ClientSide():
def index(): def index():
return rx.fragment( return rx.fragment(
rc.input( rx.input(
value=ClientSideState.router.session.client_token, value=ClientSideState.router.session.client_token,
is_read_only=True, is_read_only=True,
id="token", id="token",
), ),
rc.input( rx.input(
placeholder="state var", placeholder="state var",
value=ClientSideState.state_var, value=ClientSideState.state_var,
on_change=ClientSideState.set_state_var, # type: ignore on_change=ClientSideState.set_state_var, # type: ignore
id="state_var", id="state_var",
), ),
rc.input( rx.input(
placeholder="input value", placeholder="input value",
value=ClientSideState.input_value, value=ClientSideState.input_value,
on_change=ClientSideState.set_input_value, # type: ignore on_change=ClientSideState.set_input_value, # type: ignore
@ -313,7 +311,6 @@ async def test_client_side_state(
# no cookies should be set yet! # no cookies should be set yet!
assert not driver.get_cookies() assert not driver.get_cookies()
local_storage_items = local_storage.items() local_storage_items = local_storage.items()
local_storage_items.pop("chakra-ui-color-mode", None)
local_storage_items.pop("last_compiled_time", None) local_storage_items.pop("last_compiled_time", None)
assert not local_storage_items 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) assert f"{sub_state_name}.c3" not in cookie_info_map(driver)
local_storage_items = local_storage.items() local_storage_items = local_storage.items()
local_storage_items.pop("chakra-ui-color-mode", None)
local_storage_items.pop("last_compiled_time", 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}.l1") == "l1 value"
assert local_storage_items.pop(f"{sub_state_name}.l2") == "l2 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.""" """App for testing dynamic routes."""
from typing import List from typing import List
import reflex_chakra as rc
import reflex as rx import reflex as rx
class DynamicState(rx.State): class DynamicState(rx.State):
@ -41,13 +39,13 @@ def DynamicRoute():
def index(): def index():
return rx.fragment( return rx.fragment(
rc.input( rx.input(
value=DynamicState.router.session.client_token, value=DynamicState.router.session.client_token,
is_read_only=True, is_read_only=True,
id="token", id="token",
), ),
rc.input(value=rx.State.page_id, is_read_only=True, id="page_id"), # type: ignore rx.input(value=rx.State.page_id, is_read_only=True, id="page_id"), # type: ignore
rc.input( rx.input(
value=DynamicState.router.page.raw_path, value=DynamicState.router.page.raw_path,
is_read_only=True, is_read_only=True,
id="raw_path", id="raw_path",
@ -60,10 +58,10 @@ def DynamicRoute():
id="link_page_next", # type: ignore id="link_page_next", # type: ignore
), ),
rx.link("missing", href="/missing", id="link_missing"), rx.link("missing", href="/missing", id="link_missing"),
rc.list( rx.list( # type: ignore
rx.foreach( rx.foreach(
DynamicState.order, # type: ignore 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.""" """App for testing event_actions."""
from typing import List, Optional from typing import List, Optional
import reflex_chakra as rc
import reflex as rx import reflex as rx
class EventActionState(rx.State): class EventActionState(rx.State):
@ -55,7 +53,7 @@ def TestEventAction():
def index(): def index():
return rx.vstack( return rx.vstack(
rc.input( rx.input(
value=EventActionState.router.session.client_token, value=EventActionState.router.session.client_token,
is_read_only=True, is_read_only=True,
id="token", id="token",
@ -148,10 +146,10 @@ def TestEventAction():
200 200
).stop_propagation, ).stop_propagation,
), ),
rc.list( rx.list( # type: ignore
rx.foreach( rx.foreach(
EventActionState.order, # type: ignore EventActionState.order, # type: ignore
rc.list_item, rx.list_item,
), ),
), ),
on_click=EventActionState.on_click("outer"), # type: ignore on_click=EventActionState.on_click("outer"), # type: ignore

View File

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

View File

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

View File

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

View File

@ -10,89 +10,45 @@ from reflex.testing import AppHarness
def Table(): def Table():
"""App using table component.""" """App using table component."""
from typing import List
import reflex_chakra as rc
import reflex as rx 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 = rx.App(state=rx.State)
@app.add_page @app.add_page
def index(): def index():
return rx.center( return rx.center(
rc.input( rx.input(
id="token", id="token",
value=TableState.router.session.client_token, value=rx.State.router.session.client_token,
is_read_only=True, is_read_only=True,
), ),
rc.table_container( rx.table.root(
rc.table( rx.table.header(
headers=TableState.headers, rx.table.row(
rows=TableState.rows, rx.table.column_header_cell("Name"),
footers=TableState.footers, rx.table.column_header_cell("Age"),
caption=TableState.caption, rx.table.column_header_cell("Location"),
variant="striped", ),
color_scheme="blue", ),
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%", 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() driver.quit()
@pytest.mark.parametrize("route", ["", "/another"]) def test_table(driver, table: AppHarness):
def test_table(driver, table: AppHarness, route):
"""Test that a table component is rendered properly. """Test that a table component is rendered properly.
Args: Args:
driver: Selenium WebDriver open to the app driver: Selenium WebDriver open to the app
table: Harness for Table 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" assert table.app_instance is not None, "app is not running"
thead = driver.find_element(By.TAG_NAME, "thead") thead = driver.find_element(By.TAG_NAME, "thead")
# poll till page is fully loaded. # poll till page is fully loaded.
table.poll_for_content(element=thead) table.poll_for_content(element=thead)
# check headers # 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 # check first row value
assert ( assert (
driver.find_element(By.TAG_NAME, "tbody") driver.find_element(By.TAG_NAME, "tbody")
@ -162,12 +115,3 @@ def test_table(driver, table: AppHarness, route):
.text .text
== "John 30 New York" == "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 from pathlib import Path
import reflex_chakra as rc
import reflex as rx import reflex as rx
class UnusedState(rx.State): class UnusedState(rx.State):
@ -36,7 +34,7 @@ def TailwindApp(
def index(): def index():
return rx.el.div( 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.el.p(paragraph_text, class_name=paragraph_class_name),
rx.text(paragraph_text, as_="p", 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"), rx.el.div("Test external stylesheet", class_name="external"),

View File

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

View File

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

View File

@ -27,7 +27,7 @@ function AppWrap({children}) {
export default function MyApp({ Component, pageProps }) { export default function MyApp({ Component, pageProps }) {
return ( return (
<ThemeProvider defaultTheme={ defaultColorMode } storageKey="chakra-ui-color-mode" attribute="class"> <ThemeProvider defaultTheme={ defaultColorMode } attribute="class">
<AppWrap> <AppWrap>
<StateProvider> <StateProvider>
<EventLoopProvider> <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.flex import flex as flex
from .components.radix.themes.layout.grid import grid as grid 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_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 ordered_list as ordered_list
from .components.radix.themes.layout.list import unordered_list as unordered_list from .components.radix.themes.layout.list import unordered_list as unordered_list
from .components.radix.themes.layout.section import section as section from .components.radix.themes.layout.section import section as section

View File

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

View File

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

View File

@ -247,9 +247,9 @@ class Upload(MemoizationLeaf):
} }
# The file input to use. # The file input to use.
upload = Input.create(type="file") upload = Input.create(type="file")
upload.special_props = { upload.special_props = [
ImmutableVar(_var_name="{...getInputProps()}", _var_type=None) ImmutableVar(_var_name="{...getInputProps()}", _var_type=None)
} ]
# The dropzone to use. # The dropzone to use.
zone = Box.create( zone = Box.create(
@ -257,9 +257,9 @@ class Upload(MemoizationLeaf):
*children, *children,
**{k: v for k, v in props.items() if k not in supported_props}, **{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) ImmutableVar(_var_name="{...getRootProps()}", _var_type=None)
} ]
# Create the component. # Create the component.
upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID) 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.""" """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.components.el.element import Element
from reflex.ivars.base import ImmutableVar from reflex.ivars.base import ImmutableVar
@ -90,9 +90,9 @@ class StyleEl(Element): # noqa: E742
media: Var[Union[str, int, bool]] media: Var[Union[str, int, bool]]
special_props: Set[ImmutableVar] = { special_props: List[ImmutableVar] = [
ImmutableVar.create_safe("suppressHydrationWarning") ImmutableVar.create_safe("suppressHydrationWarning")
} ]
base = Base.create base = Base.create

View File

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

View File

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

View File

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

View File

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

View File

@ -23,3 +23,20 @@ class PropsBase(Base):
return LiteralObjectVar.create( return LiteralObjectVar.create(
{format.to_camel_case(key): value for key, value in self.dict().items()} {format.to_camel_case(key): value for key, value in self.dict().items()}
).json() ).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.flex import flex as flex
from .themes.layout.grid import grid as grid from .themes.layout.grid import grid as grid
from .themes.layout.list import list_item as list_item 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 ordered_list as ordered_list
from .themes.layout.list import unordered_list as unordered_list from .themes.layout.list import unordered_list as unordered_list
from .themes.layout.section import section as section from .themes.layout.section import section as section

View File

@ -262,26 +262,6 @@ class ThemePanel(RadixThemesComponent):
""" """
return {"react": "useEffect"} 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): class RadixThemesColorModeProvider(Component):
"""Next-themes integration for radix themes components.""" """Next-themes integration for radix themes components."""

View File

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

View File

@ -9,6 +9,7 @@ from .container import container as container
from .flex import flex as flex from .flex import flex as flex
from .grid import grid as grid from .grid import grid as grid
from .list import list_item as list_item 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 ordered_list as ordered_list
from .list import unordered_list as unordered_list from .list import unordered_list as unordered_list
from .section import section as section from .section import section as section

View File

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

View File

@ -1,19 +1,22 @@
"""Tag to conditionally render components.""" """Tag to conditionally render components."""
import dataclasses
from typing import Any, Dict, Optional from typing import Any, Dict, Optional
from reflex.components.tags.tag import Tag from reflex.components.tags.tag import Tag
from reflex.ivars.base import LiteralVar
from reflex.vars import Var from reflex.vars import Var
@dataclasses.dataclass()
class CondTag(Tag): class CondTag(Tag):
"""A conditional tag.""" """A conditional tag."""
# The condition to determine which component to render. # 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. # 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. # 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 from __future__ import annotations
import dataclasses
import inspect import inspect
from typing import TYPE_CHECKING, Any, Callable, List, Tuple, Type, Union, get_args from typing import TYPE_CHECKING, Any, Callable, List, Tuple, Type, Union, get_args
from reflex.components.tags.tag import Tag from reflex.components.tags.tag import Tag
from reflex.ivars.base import ImmutableVar 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: if TYPE_CHECKING:
from reflex.components.component import Component from reflex.components.component import Component
@dataclasses.dataclass()
class IterTag(Tag): class IterTag(Tag):
"""An iterator tag.""" """An iterator tag."""
# The var to iterate over. # 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. # 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. # 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. # 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: def get_iterable_var_type(self) -> Type:
"""Get the type of the iterable var. """Get the type of the iterable var.

View File

@ -1,19 +1,22 @@
"""Tag to conditionally match cases.""" """Tag to conditionally match cases."""
import dataclasses
from typing import Any, List from typing import Any, List
from reflex.components.tags.tag import Tag from reflex.components.tags.tag import Tag
from reflex.ivars.base import LiteralVar
from reflex.vars import Var from reflex.vars import Var
@dataclasses.dataclass()
class MatchTag(Tag): class MatchTag(Tag):
"""A match tag.""" """A match tag."""
# The condition to determine which case to match. # 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. # 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. # 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 __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.event import EventChain
from reflex.ivars.base import ImmutableVar, LiteralVar from reflex.ivars.base import ImmutableVar, LiteralVar
from reflex.utils import format, types from reflex.utils import format, types
class Tag(Base): @dataclasses.dataclass()
class Tag:
"""A React tag.""" """A React tag."""
# The name of the tag. # The name of the tag.
name: str = "" name: str = ""
# The props of the tag. # The props of the tag.
props: Dict[str, Any] = {} props: Dict[str, Any] = dataclasses.field(default_factory=dict)
# The inner contents of the tag. # The inner contents of the tag.
contents: str = "" contents: str = ""
@ -26,25 +27,18 @@ class Tag(Base):
args: Optional[Tuple[str, ...]] = None args: Optional[Tuple[str, ...]] = None
# Special props that aren't key value pairs. # 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. # The children components.
children: List[Any] = [] children: List[Any] = dataclasses.field(default_factory=list)
def __init__(self, *args, **kwargs): def __post_init__(self):
"""Initialize the tag. """Post initialize the tag."""
object.__setattr__(
Args: self,
*args: Args to initialize the tag. "props",
**kwargs: Kwargs to initialize the tag. {name: LiteralVar.create(value) for name, value in self.props.items()},
""" )
# 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 format_props(self) -> List: def format_props(self) -> List:
"""Format the tag's props. """Format the tag's props.
@ -54,6 +48,29 @@ class Tag(Base):
""" """
return format.format_props(*self.special_props, **self.props) 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: def add_props(self, **kwargs: Optional[Any]) -> Tag:
"""Add props to the 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}" REFLEX_BUILD_POLL_URL = REFLEX_BUILD_BACKEND + "/api/init/{reflex_init_token}"
# The URL to fetch the generation's reflex code # 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): class Dirs(SimpleNamespace):
"""Folders used by the template system of Reflex.""" """Folders used by the template system of Reflex."""

View File

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

View File

@ -421,6 +421,9 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
if issubclass(output, (ObjectVar, Base)): if issubclass(output, (ObjectVar, Base)):
return ToObjectOperation.create(self, var_type or dict) 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 issubclass(output, FunctionVar):
# if fixed_type is not None and not issubclass(fixed_type, Callable): # if fixed_type is not None and not issubclass(fixed_type, Callable):
# raise TypeError( # raise TypeError(
@ -479,7 +482,11 @@ class ImmutableVar(Var, Generic[VAR_TYPE]):
): ):
return self.to(NumberVar, self._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)
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) 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( raise TypeError(
f"Unsupported type {type(value)} for LiteralVar. Tried to create a LiteralVar from {value}." f"Unsupported type {type(value)} for LiteralVar. Tried to create a LiteralVar from {value}."
) )

View File

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

View File

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

View File

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

View File

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

View File

@ -91,3 +91,7 @@ class EventFnArgMismatch(ReflexError, TypeError):
class DynamicRouteArgShadowsStateVar(ReflexError, NameError): class DynamicRouteArgShadowsStateVar(ReflexError, NameError):
"""Raised when a dynamic route arg shadows a state var.""" """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 from __future__ import annotations
import dataclasses
import inspect import inspect
import json import json
import os import os
@ -623,6 +624,14 @@ def format_state(value: Any, key: Optional[str] = None) -> Any:
if isinstance(value, dict): if isinstance(value, dict):
return {k: format_state(v, k) for k, v in value.items()} 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. # Handle lists, sets, typles.
if isinstance(value, types.StateIterBases): if isinstance(value, types.StateIterBases):
return [format_state(v) for v in value] return [format_state(v) for v in value]

View File

@ -2,10 +2,9 @@
from __future__ import annotations from __future__ import annotations
import dataclasses
from collections import defaultdict from collections import defaultdict
from typing import Dict, List, Optional, Tuple, Union from typing import DefaultDict, Dict, List, Optional, Tuple, Union
from reflex.base import Base
def merge_imports( def merge_imports(
@ -19,12 +18,22 @@ def merge_imports(
Returns: Returns:
The merged import dicts. The merged import dicts.
""" """
all_imports = defaultdict(list) all_imports: DefaultDict[str, List[ImportVar]] = defaultdict(list)
for import_dict in imports: for import_dict in imports:
for lib, fields in ( for lib, fields in (
import_dict if isinstance(import_dict, tuple) else import_dict.items() 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 return all_imports
@ -75,7 +84,8 @@ def collapse_imports(
} }
class ImportVar(Base): @dataclasses.dataclass(order=True, frozen=True)
class ImportVar:
"""An import var.""" """An import var."""
# The name of the import tag. # The name of the import tag.
@ -111,73 +121,6 @@ class ImportVar(Base):
else: else:
return self.tag or "" 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]] ImportTypes = Union[str, ImportVar, List[Union[str, ImportVar]], List[ImportVar]]
ImportDict = Dict[str, ImportTypes] ImportDict = Dict[str, ImportTypes]

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
import dataclasses
import functools import functools
import glob import glob
import importlib import importlib
@ -15,7 +16,7 @@ import shutil
import stat import stat
import sys import sys
import tempfile import tempfile
import textwrap import time
import zipfile import zipfile
from datetime import datetime from datetime import datetime
from fileinput import FileInput from fileinput import FileInput
@ -32,17 +33,18 @@ from redis import exceptions
from redis.asyncio import Redis from redis.asyncio import Redis
from reflex import constants, model from reflex import constants, model
from reflex.base import Base
from reflex.compiler import templates from reflex.compiler import templates
from reflex.config import Config, get_config from reflex.config import Config, get_config
from reflex.utils import console, net, path_ops, processes 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.format import format_library_name
from reflex.utils.registry import _get_best_registry from reflex.utils.registry import _get_best_registry
CURRENTLY_INSTALLING_NODE = False CURRENTLY_INSTALLING_NODE = False
class Template(Base): @dataclasses.dataclass(frozen=True)
class Template:
"""A template for a Reflex app.""" """A template for a Reflex app."""
name: str name: str
@ -51,7 +53,8 @@ class Template(Base):
demo_url: str demo_url: str
class CpuInfo(Base): @dataclasses.dataclass(frozen=True)
class CpuInfo:
"""Model to save cpu info.""" """Model to save cpu info."""
manufacturer_id: Optional[str] manufacturer_id: Optional[str]
@ -1278,7 +1281,7 @@ def fetch_app_templates(version: str) -> dict[str, Template]:
None, None,
) )
return { return {
tp["name"]: Template.parse_obj(tp) tp["name"]: Template(**tp)
for tp in templates_data for tp in templates_data
if not tp["hidden"] and tp["code_url"] is not None 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: Args:
app_name: The name of the app. app_name: The name of the app.
generation_hash: The generation hash from reflex.build. 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. # Download the reflex code for the generation.
resp = net.get( url = constants.Templates.REFLEX_BUILD_CODE_URL.format(
constants.Templates.REFLEX_BUILD_CODE_URL.format(
generation_hash=generation_hash 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): def replace_content(_match):
return "\n".join( return "\n".join(
[ [
"def index() -> rx.Component:", resp.text,
textwrap.indent("return " + 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 # construct the import statement and handle special cases for aliases
sub_mod_attrs_imports = [ 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]}" 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() for mod, path in sub_mod_attrs.items()
] ]
sub_mod_attrs_imports.append("") sub_mod_attrs_imports.append("")

View File

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

View File

@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
import contextlib import contextlib
import dataclasses
import inspect import inspect
import sys import sys
import types import types
@ -486,7 +487,11 @@ def is_valid_var_type(type_: Type) -> bool:
if is_union(type_): if is_union(type_):
return all((is_valid_var_type(arg) for arg in get_args(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: 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.event import EventChain
from reflex.ivars.base import ImmutableVar 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 from typing import Any, Dict, List, Optional, Type, Union
import pytest import pytest
import reflex_chakra as rc
from reflex_chakra.components.layout.box import Box
import reflex as rx import reflex as rx
from reflex.base import Base from reflex.base import Base
@ -16,6 +14,7 @@ from reflex.components.component import (
StatefulComponent, StatefulComponent,
custom_component, custom_component,
) )
from reflex.components.radix.themes.layout.box import Box
from reflex.constants import EventTriggers from reflex.constants import EventTriggers
from reflex.event import EventChain, EventHandler, parse_args_spec from reflex.event import EventChain, EventHandler, parse_args_spec
from reflex.ivars.base import ImmutableVar, LiteralVar from reflex.ivars.base import ImmutableVar, LiteralVar
@ -638,21 +637,21 @@ def test_component_create_unallowed_types(children, test_component):
"props": [], "props": [],
"contents": "", "contents": "",
"args": None, "args": None,
"special_props": set(), "special_props": [],
"children": [ "children": [
{ {
"name": "RadixThemesText", "name": "RadixThemesText",
"props": ['as={"p"}'], "props": ['as={"p"}'],
"contents": "", "contents": "",
"args": None, "args": None,
"special_props": set(), "special_props": [],
"children": [ "children": [
{ {
"name": "", "name": "",
"props": [], "props": [],
"contents": '{"first_text"}', "contents": '{"first_text"}',
"args": None, "args": None,
"special_props": set(), "special_props": [],
"children": [], "children": [],
"autofocus": False, "autofocus": False,
} }
@ -680,13 +679,13 @@ def test_component_create_unallowed_types(children, test_component):
"contents": '{"first_text"}', "contents": '{"first_text"}',
"name": "", "name": "",
"props": [], "props": [],
"special_props": set(), "special_props": [],
} }
], ],
"contents": "", "contents": "",
"name": "RadixThemesText", "name": "RadixThemesText",
"props": ['as={"p"}'], "props": ['as={"p"}'],
"special_props": set(), "special_props": [],
}, },
{ {
"args": None, "args": None,
@ -699,19 +698,19 @@ def test_component_create_unallowed_types(children, test_component):
"contents": '{"second_text"}', "contents": '{"second_text"}',
"name": "", "name": "",
"props": [], "props": [],
"special_props": set(), "special_props": [],
} }
], ],
"contents": "", "contents": "",
"name": "RadixThemesText", "name": "RadixThemesText",
"props": ['as={"p"}'], "props": ['as={"p"}'],
"special_props": set(), "special_props": [],
}, },
], ],
"contents": "", "contents": "",
"name": "Fragment", "name": "Fragment",
"props": [], "props": [],
"special_props": set(), "special_props": [],
}, },
), ),
( (
@ -731,13 +730,13 @@ def test_component_create_unallowed_types(children, test_component):
"contents": '{"first_text"}', "contents": '{"first_text"}',
"name": "", "name": "",
"props": [], "props": [],
"special_props": set(), "special_props": [],
} }
], ],
"contents": "", "contents": "",
"name": "RadixThemesText", "name": "RadixThemesText",
"props": ['as={"p"}'], "props": ['as={"p"}'],
"special_props": set(), "special_props": [],
}, },
{ {
"args": None, "args": None,
@ -758,31 +757,31 @@ def test_component_create_unallowed_types(children, test_component):
"contents": '{"second_text"}', "contents": '{"second_text"}',
"name": "", "name": "",
"props": [], "props": [],
"special_props": set(), "special_props": [],
} }
], ],
"contents": "", "contents": "",
"name": "RadixThemesText", "name": "RadixThemesText",
"props": ['as={"p"}'], "props": ['as={"p"}'],
"special_props": set(), "special_props": [],
} }
], ],
"contents": "", "contents": "",
"name": "Fragment", "name": "Fragment",
"props": [], "props": [],
"special_props": set(), "special_props": [],
} }
], ],
"contents": "", "contents": "",
"name": "RadixThemesBox", "name": "RadixThemesBox",
"props": [], "props": [],
"special_props": set(), "special_props": [],
}, },
], ],
"contents": "", "contents": "",
"name": "Fragment", "name": "Fragment",
"props": [], "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.text("hi"), '<RadixThemesText as={"p"}>\n {"hi"}\n</RadixThemesText>'),
( (
rx.box(rc.heading("test", size="md")), rx.box(rx.heading("test", size="3")),
'<RadixThemesBox>\n <Heading size={"md"}>\n {"test"}\n</Heading>\n</RadixThemesBox>', '<RadixThemesBox>\n <RadixThemesHeading size={"3"}>\n {"test"}\n</RadixThemesHeading>\n</RadixThemesBox>',
), ),
], ],
) )
@ -1290,12 +1289,12 @@ class EventState(rx.State):
id="fstring-class_name", id="fstring-class_name",
), ),
pytest.param( pytest.param(
rx.fragment(special_props={TEST_VAR}), rx.fragment(special_props=[TEST_VAR]),
[TEST_VAR], [TEST_VAR],
id="direct-special_props", id="direct-special_props",
), ),
pytest.param( 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], [FORMATTED_TEST_VAR],
id="fstring-special_props", id="fstring-special_props",
), ),

View File

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

View File

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

View File

@ -2,6 +2,7 @@ from __future__ import annotations
import asyncio import asyncio
import copy import copy
import dataclasses
import datetime import datetime
import functools import functools
import json import json
@ -58,6 +59,7 @@ formatted_router = {
"origin": "", "origin": "",
"upgrade": "", "upgrade": "",
"connection": "", "connection": "",
"cookie": "",
"pragma": "", "pragma": "",
"cache_control": "", "cache_control": "",
"user_agent": "", "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: The router data fixture.
router_data_headers: The expected headers. router_data_headers: The expected headers.
""" """
print(router_data_headers)
test_state.router = RouterData(router_data) 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() 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() mock_app.event_namespace.emit.assert_called_once()
mcall = mock_app.event_namespace.emit.mock_calls[0] mcall = mock_app.event_namespace.emit.mock_calls[0]
assert mcall.args[0] == str(SocketEvent.EVENT) assert mcall.args[0] == str(SocketEvent.EVENT)
assert json.loads(mcall.args[1]) == StateUpdate( assert json.loads(mcall.args[1]) == dataclasses.asdict(
StateUpdate(
delta={ delta={
parent_state.get_full_name(): { parent_state.get_full_name(): {
"upper": "", "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 assert mcall.kwargs["to"] == grandchild_state.router.session.session_id

View File

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

View File

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