Skip to content

Commit

Permalink
Merge branch 'main' into lendemor/fix_order_of_hooks_and_memoized_evt…
Browse files Browse the repository at this point in the history
…_trigger
  • Loading branch information
Lendemor committed Dec 11, 2024
2 parents bf1b6c6 + d75a708 commit 126a971
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 7 deletions.
4 changes: 2 additions & 2 deletions reflex/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -947,12 +947,12 @@ def get_compilation_time() -> str:
is not None
):
executor = concurrent.futures.ProcessPoolExecutor(
max_workers=number_of_processes,
max_workers=number_of_processes or None,
mp_context=multiprocessing.get_context("fork"),
)
else:
executor = concurrent.futures.ThreadPoolExecutor(
max_workers=environment.REFLEX_COMPILE_THREADS.get()
max_workers=environment.REFLEX_COMPILE_THREADS.get() or None
)

for route, component in zip(self.pages, page_components):
Expand Down
4 changes: 2 additions & 2 deletions reflex/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import Optional

from reflex import constants
from reflex.utils.exec import is_backend_only
from reflex.config import EnvironmentVariables


def asset(
Expand Down Expand Up @@ -52,7 +52,7 @@ def asset(
The relative URL to the asset.
"""
assets = constants.Dirs.APP_ASSETS
backend_only = is_backend_only()
backend_only = EnvironmentVariables.REFLEX_BACKEND_ONLY.get()

# Local asset handling
if not shared:
Expand Down
1 change: 1 addition & 0 deletions reflex/components/radix/themes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ def _render(self, props: dict[str, Any] | None = None) -> Tag:
_js_expr="{...theme.styles.global[':root'], ...theme.styles.global.body}"
),
)
tag.remove_props("appearance")
return tag


Expand Down
10 changes: 10 additions & 0 deletions reflex/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
ReflexRuntimeError,
SetUndefinedStateVarError,
StateSchemaMismatchError,
StateSerializationError,
StateTooLargeError,
)
from reflex.utils.exec import is_testing_env
Expand Down Expand Up @@ -2193,8 +2194,12 @@ def _serialize(self) -> bytes:
Returns:
The serialized state.
Raises:
StateSerializationError: If the state cannot be serialized.
"""
payload = b""
error = ""
try:
payload = pickle.dumps((self._to_schema(), self))
except HANDLED_PICKLE_ERRORS as og_pickle_error:
Expand All @@ -2214,8 +2219,13 @@ def _serialize(self) -> bytes:
except HANDLED_PICKLE_ERRORS as ex:
error += f"Dill was also unable to pickle the state: {ex}"
console.warn(error)

if environment.REFLEX_PERF_MODE.get() != PerformanceMode.OFF:
self._check_state_size(len(payload))

if not payload:
raise StateSerializationError(error)

return payload

@classmethod
Expand Down
4 changes: 4 additions & 0 deletions reflex/utils/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ class StateTooLargeError(ReflexError):
"""Raised when the state is too large to be serialized."""


class StateSerializationError(ReflexError):
"""Raised when the state cannot be serialized."""


class SystemPackageMissingError(ReflexError):
"""Raised when a system package is missing."""

Expand Down
218 changes: 218 additions & 0 deletions tests/integration/tests_playwright/test_appearance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
from typing import Generator

import pytest
from playwright.sync_api import Page, expect

from reflex.testing import AppHarness


def DefaultLightModeApp():
import reflex as rx
from reflex.style import color_mode

app = rx.App(theme=rx.theme(appearance="light"))

@app.add_page
def index():
return rx.text(color_mode)


def DefaultDarkModeApp():
import reflex as rx
from reflex.style import color_mode

app = rx.App(theme=rx.theme(appearance="dark"))

@app.add_page
def index():
return rx.text(color_mode)


def DefaultSystemModeApp():
import reflex as rx
from reflex.style import color_mode

app = rx.App()

@app.add_page
def index():
return rx.text(color_mode)


def ColorToggleApp():
import reflex as rx
from reflex.style import color_mode, resolved_color_mode, set_color_mode

app = rx.App(theme=rx.theme(appearance="light"))

@app.add_page
def index():
return rx.box(
rx.segmented_control.root(
rx.segmented_control.item(
rx.icon(tag="monitor", size=20),
value="system",
),
rx.segmented_control.item(
rx.icon(tag="sun", size=20),
value="light",
),
rx.segmented_control.item(
rx.icon(tag="moon", size=20),
value="dark",
),
on_change=set_color_mode,
variant="classic",
radius="large",
value=color_mode,
),
rx.text(color_mode, id="current_color_mode"),
rx.text(resolved_color_mode, id="resolved_color_mode"),
rx.text(rx.color_mode_cond("LightMode", "DarkMode"), id="color_mode_cond"),
)


@pytest.fixture()
def light_mode_app(tmp_path_factory) -> Generator[AppHarness, None, None]:
"""Start DefaultLightMode app at tmp_path via AppHarness.
Args:
tmp_path_factory: pytest tmp_path_factory fixture
Yields:
running AppHarness instance
"""
with AppHarness.create(
root=tmp_path_factory.mktemp("appearance_app"),
app_source=DefaultLightModeApp, # type: ignore
) as harness:
assert harness.app_instance is not None, "app is not running"
yield harness


@pytest.fixture()
def dark_mode_app(tmp_path_factory) -> Generator[AppHarness, None, None]:
"""Start DefaultDarkMode app at tmp_path via AppHarness.
Args:
tmp_path_factory: pytest tmp_path_factory fixture
Yields:
running AppHarness instance
"""
with AppHarness.create(
root=tmp_path_factory.mktemp("appearance_app"),
app_source=DefaultDarkModeApp, # type: ignore
) as harness:
assert harness.app_instance is not None, "app is not running"
yield harness


@pytest.fixture()
def system_mode_app(tmp_path_factory) -> Generator[AppHarness, None, None]:
"""Start DefaultSystemMode app at tmp_path via AppHarness.
Args:
tmp_path_factory: pytest tmp_path_factory fixture
Yields:
running AppHarness instance
"""
with AppHarness.create(
root=tmp_path_factory.mktemp("appearance_app"),
app_source=DefaultSystemModeApp, # type: ignore
) as harness:
assert harness.app_instance is not None, "app is not running"
yield harness


@pytest.fixture()
def color_toggle_app(tmp_path_factory) -> Generator[AppHarness, None, None]:
"""Start ColorToggle app at tmp_path via AppHarness.
Args:
tmp_path_factory: pytest tmp_path_factory fixture
Yields:
running AppHarness instance
"""
with AppHarness.create(
root=tmp_path_factory.mktemp("appearance_app"),
app_source=ColorToggleApp, # type: ignore
) as harness:
assert harness.app_instance is not None, "app is not running"
yield harness


def test_appearance_light_mode(light_mode_app: AppHarness, page: Page):
assert light_mode_app.frontend_url is not None
page.goto(light_mode_app.frontend_url)

expect(page.get_by_text("light")).to_be_visible()


def test_appearance_dark_mode(dark_mode_app: AppHarness, page: Page):
assert dark_mode_app.frontend_url is not None
page.goto(dark_mode_app.frontend_url)

expect(page.get_by_text("dark")).to_be_visible()


def test_appearance_system_mode(system_mode_app: AppHarness, page: Page):
assert system_mode_app.frontend_url is not None
page.goto(system_mode_app.frontend_url)

expect(page.get_by_text("system")).to_be_visible()


def test_appearance_color_toggle(color_toggle_app: AppHarness, page: Page):
assert color_toggle_app.frontend_url is not None
page.goto(color_toggle_app.frontend_url)

# Radio buttons locators.
radio_system = page.get_by_role("radio").nth(0)
radio_light = page.get_by_role("radio").nth(1)
radio_dark = page.get_by_role("radio").nth(2)

# Text locators to check.
current_color_mode = page.locator("id=current_color_mode")
resolved_color_mode = page.locator("id=resolved_color_mode")
color_mode_cond = page.locator("id=color_mode_cond")
root_body = page.locator('div[data-is-root-theme="true"]')

# Background colors.
dark_background = "rgb(17, 17, 19)" # value based on dark native appearance, can change depending on the browser
light_background = "rgb(255, 255, 255)"

# check initial state
expect(current_color_mode).to_have_text("light")
expect(resolved_color_mode).to_have_text("light")
expect(color_mode_cond).to_have_text("LightMode")
expect(root_body).to_have_css("background-color", light_background)

# click dark mode
radio_dark.click()
expect(current_color_mode).to_have_text("dark")
expect(resolved_color_mode).to_have_text("dark")
expect(color_mode_cond).to_have_text("DarkMode")
expect(root_body).to_have_css("background-color", dark_background)

# click light mode
radio_light.click()
expect(current_color_mode).to_have_text("light")
expect(resolved_color_mode).to_have_text("light")
expect(color_mode_cond).to_have_text("LightMode")
expect(root_body).to_have_css("background-color", light_background)
page.reload()
expect(root_body).to_have_css("background-color", light_background)

# click system mode
radio_system.click()
expect(current_color_mode).to_have_text("system")
expect(resolved_color_mode).to_have_text("light")
expect(color_mode_cond).to_have_text("LightMode")
expect(root_body).to_have_css("background-color", light_background)
11 changes: 8 additions & 3 deletions tests/units/test_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@
)
from reflex.testing import chdir
from reflex.utils import format, prerequisites, types
from reflex.utils.exceptions import ReflexRuntimeError, SetUndefinedStateVarError
from reflex.utils.exceptions import (
ReflexRuntimeError,
SetUndefinedStateVarError,
StateSerializationError,
)
from reflex.utils.format import json_dumps
from reflex.vars.base import Var, computed_var
from tests.units.states.mutation import MutableSQLAModel, MutableTestState
Expand Down Expand Up @@ -3433,8 +3437,9 @@ class DillState(BaseState):
# Some object, like generator, are still unpicklable with dill.
state3 = DillState(_reflex_internal_init=True) # type: ignore
state3._g = (i for i in range(10))
pk3 = state3._serialize()
assert len(pk3) == 0

with pytest.raises(StateSerializationError):
_ = state3._serialize()


def test_typed_state() -> None:
Expand Down

0 comments on commit 126a971

Please sign in to comment.