Merge branch 'main' into lendemor/stop_ignoring_some_lint_rules

This commit is contained in:
Lendemor 2024-11-06 14:57:14 +01:00
commit f4588abc06
78 changed files with 1257 additions and 702 deletions

View File

@ -178,11 +178,6 @@ export const applyEvent = async (event, socket) => {
return false; return false;
} }
if (event.name == "_console") {
console.log(event.payload.message);
return false;
}
if (event.name == "_remove_cookie") { if (event.name == "_remove_cookie") {
cookies.remove(event.payload.key, { ...event.payload.options }); cookies.remove(event.payload.key, { ...event.payload.options });
queueEventIfSocketExists(initialEvents(), socket); queueEventIfSocketExists(initialEvents(), socket);
@ -213,12 +208,6 @@ export const applyEvent = async (event, socket) => {
return false; return false;
} }
if (event.name == "_set_clipboard") {
const content = event.payload.content;
navigator.clipboard.writeText(content);
return false;
}
if (event.name == "_download") { if (event.name == "_download") {
const a = document.createElement("a"); const a = document.createElement("a");
a.hidden = true; a.hidden = true;
@ -233,11 +222,6 @@ export const applyEvent = async (event, socket) => {
return false; return false;
} }
if (event.name == "_alert") {
alert(event.payload.message);
return false;
}
if (event.name == "_set_focus") { if (event.name == "_set_focus") {
const ref = const ref =
event.payload.ref in refs ? refs[event.payload.ref] : event.payload.ref; event.payload.ref in refs ? refs[event.payload.ref] : event.payload.ref;
@ -254,6 +238,25 @@ export const applyEvent = async (event, socket) => {
return false; return false;
} }
if (event.name == "_call_function") {
try {
const eval_result = event.payload.function();
if (event.payload.callback) {
if (!!eval_result && typeof eval_result.then === "function") {
event.payload.callback(await eval_result);
} else {
event.payload.callback(eval_result);
}
}
} catch (e) {
console.log("_call_function", e);
if (window && window?.onerror) {
window.onerror(e.message, null, null, null, e);
}
}
return false;
}
if (event.name == "_call_script") { if (event.name == "_call_script") {
try { try {
const eval_result = eval(event.payload.javascript_code); const eval_result = eval(event.payload.javascript_code);

View File

@ -303,10 +303,13 @@ _MAPPING: dict = {
"EventHandler", "EventHandler",
"background", "background",
"call_script", "call_script",
"call_function",
"run_script",
"clear_local_storage", "clear_local_storage",
"clear_session_storage", "clear_session_storage",
"console_log", "console_log",
"download", "download",
"noop",
"prevent_default", "prevent_default",
"redirect", "redirect",
"remove_cookie", "remove_cookie",
@ -333,6 +336,7 @@ _MAPPING: dict = {
"State", "State",
"dynamic", "dynamic",
], ],
"istate.wrappers": ["get_state"],
"style": ["Style", "toggle_color_mode"], "style": ["Style", "toggle_color_mode"],
"utils.imports": ["ImportVar"], "utils.imports": ["ImportVar"],
"utils.serializers": ["serializer"], "utils.serializers": ["serializer"],

View File

@ -155,17 +155,20 @@ from .constants import Env as Env
from .event import EventChain as EventChain from .event import EventChain as EventChain
from .event import EventHandler as EventHandler from .event import EventHandler as EventHandler
from .event import background as background from .event import background as background
from .event import call_function as call_function
from .event import call_script as call_script from .event import call_script as call_script
from .event import clear_local_storage as clear_local_storage from .event import clear_local_storage as clear_local_storage
from .event import clear_session_storage as clear_session_storage from .event import clear_session_storage as clear_session_storage
from .event import console_log as console_log from .event import console_log as console_log
from .event import download as download from .event import download as download
from .event import event as event from .event import event as event
from .event import noop as noop
from .event import prevent_default as prevent_default from .event import prevent_default as prevent_default
from .event import redirect as redirect from .event import redirect as redirect
from .event import remove_cookie as remove_cookie from .event import remove_cookie as remove_cookie
from .event import remove_local_storage as remove_local_storage from .event import remove_local_storage as remove_local_storage
from .event import remove_session_storage as remove_session_storage from .event import remove_session_storage as remove_session_storage
from .event import run_script as run_script
from .event import scroll_to as scroll_to from .event import scroll_to as scroll_to
from .event import set_clipboard as set_clipboard from .event import set_clipboard as set_clipboard
from .event import set_focus as set_focus from .event import set_focus as set_focus
@ -177,6 +180,7 @@ from .experimental import _x as _x
from .istate.storage import Cookie as Cookie from .istate.storage import Cookie as Cookie
from .istate.storage import LocalStorage as LocalStorage from .istate.storage import LocalStorage as LocalStorage
from .istate.storage import SessionStorage as SessionStorage from .istate.storage import SessionStorage as SessionStorage
from .istate.wrappers import get_state as get_state
from .middleware import Middleware as Middleware from .middleware import Middleware as Middleware
from .middleware import middleware as middleware from .middleware import middleware as middleware
from .model import Model as Model from .model import Model as Model

View File

@ -12,7 +12,6 @@ import inspect
import io import io
import json import json
import multiprocessing import multiprocessing
import os
import platform import platform
import sys import sys
import traceback import traceback
@ -96,7 +95,7 @@ from reflex.state import (
code_uses_state_contexts, code_uses_state_contexts,
) )
from reflex.utils import codespaces, console, exceptions, format, prerequisites, types from reflex.utils import codespaces, console, exceptions, format, prerequisites, types
from reflex.utils.exec import is_prod_mode, is_testing_env, should_skip_compile from reflex.utils.exec import is_prod_mode, is_testing_env
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
if TYPE_CHECKING: if TYPE_CHECKING:
@ -507,7 +506,7 @@ class App(MiddlewareMixin, LifespanMixin):
# Check if the route given is valid # Check if the route given is valid
verify_route_validity(route) verify_route_validity(route)
if route in self.unevaluated_pages and os.getenv(constants.RELOAD_CONFIG): if route in self.unevaluated_pages and environment.RELOAD_CONFIG.is_set():
# when the app is reloaded(typically for app harness tests), we should maintain # when the app is reloaded(typically for app harness tests), we should maintain
# the latest render function of a route.This applies typically to decorated pages # the latest render function of a route.This applies typically to decorated pages
# since they are only added when app._compile is called. # since they are only added when app._compile is called.
@ -724,7 +723,7 @@ class App(MiddlewareMixin, LifespanMixin):
Whether the app should be compiled. Whether the app should be compiled.
""" """
# Check the environment variable. # Check the environment variable.
if should_skip_compile(): if environment.REFLEX_SKIP_COMPILE.get():
return False return False
nocompile = prerequisites.get_web_dir() / constants.NOCOMPILE_FILE nocompile = prerequisites.get_web_dir() / constants.NOCOMPILE_FILE
@ -947,7 +946,7 @@ class App(MiddlewareMixin, LifespanMixin):
executor = None executor = None
if ( if (
platform.system() in ("Linux", "Darwin") platform.system() in ("Linux", "Darwin")
and (number_of_processes := environment.REFLEX_COMPILE_PROCESSES) and (number_of_processes := environment.REFLEX_COMPILE_PROCESSES.get())
is not None is not None
): ):
executor = concurrent.futures.ProcessPoolExecutor( executor = concurrent.futures.ProcessPoolExecutor(
@ -956,7 +955,7 @@ class App(MiddlewareMixin, LifespanMixin):
) )
else: else:
executor = concurrent.futures.ThreadPoolExecutor( executor = concurrent.futures.ThreadPoolExecutor(
max_workers=environment.REFLEX_COMPILE_THREADS max_workers=environment.REFLEX_COMPILE_THREADS.get()
) )
for route, component in zip(self.pages, page_components): for route, component in zip(self.pages, page_components):

View File

@ -16,9 +16,6 @@ except ModuleNotFoundError:
from pydantic.fields import ModelField # type: ignore from pydantic.fields import ModelField # type: ignore
from reflex import constants
def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None: def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None:
"""Ensure that the field's name does not shadow an existing attribute of the model. """Ensure that the field's name does not shadow an existing attribute of the model.
@ -31,7 +28,8 @@ def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None
""" """
from reflex.utils.exceptions import VarNameError from reflex.utils.exceptions import VarNameError
reload = os.getenv(constants.RELOAD_CONFIG) == "True" # can't use reflex.config.environment here cause of circular import
reload = os.getenv("__RELOAD_CONFIG", "").lower() == "true"
for base in bases: for base in bases:
try: try:
if not reload and getattr(base, field_name, None): if not reload and getattr(base, field_name, None):

View File

@ -527,7 +527,7 @@ def remove_tailwind_from_postcss() -> tuple[str, str]:
def purge_web_pages_dir(): def purge_web_pages_dir():
"""Empty out .web/pages directory.""" """Empty out .web/pages directory."""
if not is_prod_mode() and environment.REFLEX_PERSIST_WEB_DIR: if not is_prod_mode() and environment.REFLEX_PERSIST_WEB_DIR.get():
# Skip purging the web directory in dev mode if REFLEX_PERSIST_WEB_DIR is set. # Skip purging the web directory in dev mode if REFLEX_PERSIST_WEB_DIR is set.
return return

View File

@ -8,7 +8,7 @@ from __future__ import annotations
from typing import Literal from typing import Literal
from reflex.components.component import Component from reflex.components.component import Component
from reflex.event import EventHandler, empty_event from reflex.event import EventHandler, no_args_event_spec
from reflex.vars.base import LiteralVar, Var from reflex.vars.base import LiteralVar, Var
@ -35,13 +35,13 @@ class Script(Component):
) )
# Triggered when the script is loading # Triggered when the script is loading
on_load: EventHandler[empty_event] on_load: EventHandler[no_args_event_spec]
# Triggered when the script has loaded # Triggered when the script has loaded
on_ready: EventHandler[empty_event] on_ready: EventHandler[no_args_event_spec]
# Triggered when the script has errored # Triggered when the script has errored
on_error: EventHandler[empty_event] on_error: EventHandler[no_args_event_spec]
@classmethod @classmethod
def create(cls, *children, **props) -> Component: def create(cls, *children, **props) -> Component:

View File

@ -47,8 +47,8 @@ from reflex.event import (
EventVar, EventVar,
call_event_fn, call_event_fn,
call_event_handler, call_event_handler,
empty_event,
get_handler_args, get_handler_args,
no_args_event_spec,
) )
from reflex.style import Style, format_as_emotion from reflex.style import Style, format_as_emotion
from reflex.utils import format, imports, types from reflex.utils import format, imports, types
@ -636,21 +636,21 @@ class Component(BaseComponent, ABC):
""" """
default_triggers = { default_triggers = {
EventTriggers.ON_FOCUS: empty_event, EventTriggers.ON_FOCUS: no_args_event_spec,
EventTriggers.ON_BLUR: empty_event, EventTriggers.ON_BLUR: no_args_event_spec,
EventTriggers.ON_CLICK: empty_event, EventTriggers.ON_CLICK: no_args_event_spec,
EventTriggers.ON_CONTEXT_MENU: empty_event, EventTriggers.ON_CONTEXT_MENU: no_args_event_spec,
EventTriggers.ON_DOUBLE_CLICK: empty_event, EventTriggers.ON_DOUBLE_CLICK: no_args_event_spec,
EventTriggers.ON_MOUSE_DOWN: empty_event, EventTriggers.ON_MOUSE_DOWN: no_args_event_spec,
EventTriggers.ON_MOUSE_ENTER: empty_event, EventTriggers.ON_MOUSE_ENTER: no_args_event_spec,
EventTriggers.ON_MOUSE_LEAVE: empty_event, EventTriggers.ON_MOUSE_LEAVE: no_args_event_spec,
EventTriggers.ON_MOUSE_MOVE: empty_event, EventTriggers.ON_MOUSE_MOVE: no_args_event_spec,
EventTriggers.ON_MOUSE_OUT: empty_event, EventTriggers.ON_MOUSE_OUT: no_args_event_spec,
EventTriggers.ON_MOUSE_OVER: empty_event, EventTriggers.ON_MOUSE_OVER: no_args_event_spec,
EventTriggers.ON_MOUSE_UP: empty_event, EventTriggers.ON_MOUSE_UP: no_args_event_spec,
EventTriggers.ON_SCROLL: empty_event, EventTriggers.ON_SCROLL: no_args_event_spec,
EventTriggers.ON_MOUNT: empty_event, EventTriggers.ON_MOUNT: no_args_event_spec,
EventTriggers.ON_UNMOUNT: empty_event, EventTriggers.ON_UNMOUNT: no_args_event_spec,
} }
# Look for component specific triggers, # Look for component specific triggers,
@ -661,7 +661,7 @@ class Component(BaseComponent, ABC):
annotation = field.annotation annotation = field.annotation
if (metadata := getattr(annotation, "__metadata__", None)) is not None: if (metadata := getattr(annotation, "__metadata__", None)) is not None:
args_spec = metadata[0] args_spec = metadata[0]
default_triggers[field.name] = args_spec or (empty_event) # type: ignore default_triggers[field.name] = args_spec or (no_args_event_spec) # type: ignore
return default_triggers return default_triggers
def __repr__(self) -> str: def __repr__(self) -> str:
@ -1722,7 +1722,7 @@ class CustomComponent(Component):
value = self._create_event_chain( value = self._create_event_chain(
value=value, value=value,
args_spec=event_triggers_in_component_declaration.get( args_spec=event_triggers_in_component_declaration.get(
key, empty_event key, no_args_event_spec
), ),
key=key, key=key,
) )

View File

@ -6,7 +6,7 @@ from typing import Dict, List, Tuple, Union
from reflex.components.base.fragment import Fragment from reflex.components.base.fragment import Fragment
from reflex.components.tags.tag import Tag from reflex.components.tags.tag import Tag
from reflex.event import EventChain, EventHandler, identity_event from reflex.event import EventChain, EventHandler, passthrough_event_spec
from reflex.utils.format import format_prop, wrap from reflex.utils.format import format_prop, wrap
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
from reflex.vars import get_unique_variable_name from reflex.vars import get_unique_variable_name
@ -20,7 +20,7 @@ class Clipboard(Fragment):
targets: Var[List[str]] targets: Var[List[str]]
# Called when the user pastes data into the document. Data is a list of tuples of (mime_type, data). Binary types will be base64 encoded as a data uri. # Called when the user pastes data into the document. Data is a list of tuples of (mime_type, data). Binary types will be base64 encoded as a data uri.
on_paste: EventHandler[identity_event(List[Tuple[str, str]])] on_paste: EventHandler[passthrough_event_spec(List[Tuple[str, str]])]
# Save the original event actions for the on_paste event. # Save the original event actions for the on_paste event.
on_paste_event_actions: Var[Dict[str, Union[bool, int]]] on_paste_event_actions: Var[Dict[str, Union[bool, int]]]

View File

@ -6,7 +6,9 @@
from typing import Any, Dict, List, Optional, Union, overload from typing import Any, Dict, List, Optional, Union, overload
from reflex.components.base.fragment import Fragment from reflex.components.base.fragment import Fragment
from reflex.event import EventType from reflex.event import (
EventType,
)
from reflex.style import Style from reflex.style import Style
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
from reflex.vars.base import Var from reflex.vars.base import Var

View File

@ -6,7 +6,7 @@ from typing import Any, Type, Union
from reflex.components.component import Component from reflex.components.component import Component
from reflex.constants import EventTriggers from reflex.constants import EventTriggers
from reflex.event import EventHandler, empty_event from reflex.event import EventHandler, no_args_event_spec
from reflex.vars import VarData from reflex.vars import VarData
from reflex.vars.base import Var from reflex.vars.base import Var
@ -46,7 +46,7 @@ class DebounceInput(Component):
element: Var[Type[Component]] element: Var[Type[Component]]
# Fired when the input value changes # Fired when the input value changes
on_change: EventHandler[empty_event] on_change: EventHandler[no_args_event_spec]
@classmethod @classmethod
def create(cls, *children: Component, **props: Any) -> Component: def create(cls, *children: Component, **props: Any) -> Component:

View File

@ -15,15 +15,15 @@ from reflex.components.el.elements.forms import Input
from reflex.components.radix.themes.layout.box import Box from reflex.components.radix.themes.layout.box import Box
from reflex.config import environment from reflex.config import environment
from reflex.constants import Dirs from reflex.constants import Dirs
from reflex.constants.compiler import Imports from reflex.constants.compiler import Hooks, Imports
from reflex.event import ( from reflex.event import (
CallableEventSpec, CallableEventSpec,
EventChain, EventChain,
EventHandler, EventHandler,
EventSpec, EventSpec,
call_event_fn, call_event_fn,
call_script,
parse_args_spec, parse_args_spec,
run_script,
) )
from reflex.utils import format from reflex.utils import format
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
@ -106,8 +106,8 @@ def clear_selected_files(id_: str = DEFAULT_UPLOAD_ID) -> EventSpec:
""" """
# UploadFilesProvider assigns a special function to clear selected files # UploadFilesProvider assigns a special function to clear selected files
# into the shared global refs object to make it accessible outside a React # into the shared global refs object to make it accessible outside a React
# component via `call_script` (otherwise backend could never clear files). # component via `run_script` (otherwise backend could never clear files).
return call_script(f"refs['__clear_selected_files']({id_!r})") return run_script(f"refs['__clear_selected_files']({id_!r})")
def cancel_upload(upload_id: str) -> EventSpec: def cancel_upload(upload_id: str) -> EventSpec:
@ -119,7 +119,7 @@ def cancel_upload(upload_id: str) -> EventSpec:
Returns: Returns:
An event spec that cancels the upload when triggered. An event spec that cancels the upload when triggered.
""" """
return call_script( return run_script(
f"upload_controllers[{str(LiteralVar.create(upload_id))}]?.abort()" f"upload_controllers[{str(LiteralVar.create(upload_id))}]?.abort()"
) )
@ -132,7 +132,7 @@ def get_upload_dir() -> Path:
""" """
Upload.is_used = True Upload.is_used = True
uploaded_files_dir = environment.REFLEX_UPLOADED_FILES_DIR uploaded_files_dir = environment.REFLEX_UPLOADED_FILES_DIR.get()
uploaded_files_dir.mkdir(parents=True, exist_ok=True) uploaded_files_dir.mkdir(parents=True, exist_ok=True)
return uploaded_files_dir return uploaded_files_dir
@ -285,20 +285,18 @@ class Upload(MemoizationLeaf):
format.to_camel_case(key): value for key, value in upload_props.items() format.to_camel_case(key): value for key, value in upload_props.items()
} }
use_dropzone_arguements = { use_dropzone_arguments = {
"onDrop": event_var, "onDrop": event_var,
**upload_props, **upload_props,
} }
left_side = f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} " left_side = f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} "
right_side = f"useDropzone({str(Var.create(use_dropzone_arguements))})" right_side = f"useDropzone({str(Var.create(use_dropzone_arguments))})"
var_data = VarData.merge( var_data = VarData.merge(
VarData( VarData(
imports=Imports.EVENTS, imports=Imports.EVENTS,
hooks={ hooks={Hooks.EVENTS: None},
"const [addEvents, connectError] = useContext(EventLoopContext);": None
},
), ),
event_var._get_all_var_data(), event_var._get_all_var_data(),
VarData( VarData(

View File

@ -10,7 +10,7 @@ from typing_extensions import TypedDict
from reflex.base import Base from reflex.base import Base
from reflex.components.component import Component, NoSSRComponent from reflex.components.component import Component, NoSSRComponent
from reflex.components.literals import LiteralRowMarker from reflex.components.literals import LiteralRowMarker
from reflex.event import EventHandler, empty_event, identity_event from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
from reflex.utils import console, format, types from reflex.utils import console, format, types
from reflex.utils.imports import ImportDict, ImportVar from reflex.utils.imports import ImportDict, ImportVar
from reflex.utils.serializers import serializer from reflex.utils.serializers import serializer
@ -284,56 +284,58 @@ class DataEditor(NoSSRComponent):
theme: Var[Union[DataEditorTheme, Dict]] theme: Var[Union[DataEditorTheme, Dict]]
# Fired when a cell is activated. # Fired when a cell is activated.
on_cell_activated: EventHandler[identity_event(Tuple[int, int])] on_cell_activated: EventHandler[passthrough_event_spec(Tuple[int, int])]
# Fired when a cell is clicked. # Fired when a cell is clicked.
on_cell_clicked: EventHandler[identity_event(Tuple[int, int])] on_cell_clicked: EventHandler[passthrough_event_spec(Tuple[int, int])]
# Fired when a cell is right-clicked. # Fired when a cell is right-clicked.
on_cell_context_menu: EventHandler[identity_event(Tuple[int, int])] on_cell_context_menu: EventHandler[passthrough_event_spec(Tuple[int, int])]
# Fired when a cell is edited. # Fired when a cell is edited.
on_cell_edited: EventHandler[identity_event(Tuple[int, int], GridCell)] on_cell_edited: EventHandler[passthrough_event_spec(Tuple[int, int], GridCell)]
# Fired when a group header is clicked. # Fired when a group header is clicked.
on_group_header_clicked: EventHandler[identity_event(Tuple[int, int], GridCell)] on_group_header_clicked: EventHandler[
passthrough_event_spec(Tuple[int, int], GridCell)
]
# Fired when a group header is right-clicked. # Fired when a group header is right-clicked.
on_group_header_context_menu: EventHandler[ on_group_header_context_menu: EventHandler[
identity_event(int, GroupHeaderClickedEventArgs) passthrough_event_spec(int, GroupHeaderClickedEventArgs)
] ]
# Fired when a group header is renamed. # Fired when a group header is renamed.
on_group_header_renamed: EventHandler[identity_event(str, str)] on_group_header_renamed: EventHandler[passthrough_event_spec(str, str)]
# Fired when a header is clicked. # Fired when a header is clicked.
on_header_clicked: EventHandler[identity_event(Tuple[int, int])] on_header_clicked: EventHandler[passthrough_event_spec(Tuple[int, int])]
# Fired when a header is right-clicked. # Fired when a header is right-clicked.
on_header_context_menu: EventHandler[identity_event(Tuple[int, int])] on_header_context_menu: EventHandler[passthrough_event_spec(Tuple[int, int])]
# Fired when a header menu item is clicked. # Fired when a header menu item is clicked.
on_header_menu_click: EventHandler[identity_event(int, Rectangle)] on_header_menu_click: EventHandler[passthrough_event_spec(int, Rectangle)]
# Fired when an item is hovered. # Fired when an item is hovered.
on_item_hovered: EventHandler[identity_event(Tuple[int, int])] on_item_hovered: EventHandler[passthrough_event_spec(Tuple[int, int])]
# Fired when a selection is deleted. # Fired when a selection is deleted.
on_delete: EventHandler[identity_event(GridSelection)] on_delete: EventHandler[passthrough_event_spec(GridSelection)]
# Fired when editing is finished. # Fired when editing is finished.
on_finished_editing: EventHandler[ on_finished_editing: EventHandler[
identity_event(Union[GridCell, None], tuple[int, int]) passthrough_event_spec(Union[GridCell, None], tuple[int, int])
] ]
# Fired when a row is appended. # Fired when a row is appended.
on_row_appended: EventHandler[empty_event] on_row_appended: EventHandler[no_args_event_spec]
# Fired when the selection is cleared. # Fired when the selection is cleared.
on_selection_cleared: EventHandler[empty_event] on_selection_cleared: EventHandler[no_args_event_spec]
# Fired when a column is resized. # Fired when a column is resized.
on_column_resize: EventHandler[identity_event(GridColumn, int)] on_column_resize: EventHandler[passthrough_event_spec(GridColumn, int)]
def add_imports(self) -> ImportDict: def add_imports(self) -> ImportDict:
"""Add imports for the component. """Add imports for the component.

View File

@ -14,7 +14,7 @@ from reflex.components.el.elements.forms import Button
from reflex.components.lucide.icon import Icon from reflex.components.lucide.icon import Icon
from reflex.components.props import NoExtrasAllowedProps from reflex.components.props import NoExtrasAllowedProps
from reflex.components.radix.themes.layout.box import Box from reflex.components.radix.themes.layout.box import Box
from reflex.event import call_script, set_clipboard from reflex.event import run_script, set_clipboard
from reflex.style import Style from reflex.style import Style
from reflex.utils.exceptions import VarTypeError from reflex.utils.exceptions import VarTypeError
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
@ -29,7 +29,7 @@ def copy_script() -> Any:
Returns: Returns:
Any: The result of calling the script. Any: The result of calling the script.
""" """
return call_script( return run_script(
f""" f"""
// Event listener for the parent click // Event listener for the parent click
document.addEventListener('click', function(event) {{ document.addEventListener('click', function(event) {{
@ -67,7 +67,7 @@ document.addEventListener('click', function(event) {{
}} else {{ }} else {{
// console.error('Parent element not found.'); // console.error('Parent element not found.');
}} }}
}}); }})
""" """
) )

View File

@ -4,7 +4,7 @@ import dataclasses
from typing import List, Optional from typing import List, Optional
from reflex.components.component import NoSSRComponent from reflex.components.component import NoSSRComponent
from reflex.event import EventHandler, identity_event from reflex.event import EventHandler, passthrough_event_spec
from reflex.utils.imports import ImportDict from reflex.utils.imports import ImportDict
from reflex.vars.base import LiteralVar, Var from reflex.vars.base import LiteralVar, Var
@ -96,7 +96,7 @@ class Moment(NoSSRComponent):
locale: Var[str] locale: Var[str]
# Fires when the date changes. # Fires when the date changes.
on_change: EventHandler[identity_event(str)] on_change: EventHandler[passthrough_event_spec(str)]
def add_imports(self) -> ImportDict: def add_imports(self) -> ImportDict:
"""Add the imports for the Moment component. """Add the imports for the Moment component.

View File

@ -2,7 +2,7 @@
from typing import Any, Literal, Optional, Union from typing import Any, Literal, Optional, Union
from reflex.event import EventHandler, empty_event from reflex.event import EventHandler, no_args_event_spec
from reflex.utils import types from reflex.utils import types
from reflex.vars.base import Var from reflex.vars.base import Var
@ -56,10 +56,10 @@ class Image(NextComponent):
blurDataURL: Var[str] blurDataURL: Var[str]
# Fires when the image has loaded. # Fires when the image has loaded.
on_load: EventHandler[empty_event] on_load: EventHandler[no_args_event_spec]
# Fires when the image has an error. # Fires when the image has an error.
on_error: EventHandler[empty_event] on_error: EventHandler[no_args_event_spec]
@classmethod @classmethod
def create( def create(

View File

@ -10,7 +10,7 @@ from reflex.components.component import Component, ComponentNamespace
from reflex.components.radix.primitives.base import RadixPrimitiveComponent from reflex.components.radix.primitives.base import RadixPrimitiveComponent
from reflex.components.radix.themes.base import Theme from reflex.components.radix.themes.base import Theme
from reflex.components.radix.themes.layout.flex import Flex from reflex.components.radix.themes.layout.flex import Flex
from reflex.event import EventHandler, empty_event, identity_event from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
from reflex.utils import console from reflex.utils import console
from reflex.vars.base import Var from reflex.vars.base import Var
@ -40,7 +40,7 @@ class DrawerRoot(DrawerComponent):
open: Var[bool] open: Var[bool]
# Fires when the drawer is opened or closed. # Fires when the drawer is opened or closed.
on_open_change: EventHandler[identity_event(bool)] on_open_change: EventHandler[passthrough_event_spec(bool)]
# When `False`, it allows interaction with elements outside of the drawer without closing it. Defaults to `True`. # When `False`, it allows interaction with elements outside of the drawer without closing it. Defaults to `True`.
modal: Var[bool] modal: Var[bool]
@ -49,7 +49,7 @@ class DrawerRoot(DrawerComponent):
direction: Var[LiteralDirectionType] direction: Var[LiteralDirectionType]
# Gets triggered after the open or close animation ends, it receives an open argument with the open state of the drawer by the time the function was triggered. # Gets triggered after the open or close animation ends, it receives an open argument with the open state of the drawer by the time the function was triggered.
on_animation_end: EventHandler[identity_event(bool)] on_animation_end: EventHandler[passthrough_event_spec(bool)]
# When `False`, dragging, clicking outside, pressing esc, etc. will not close the drawer. Use this in combination with the open prop, otherwise you won't be able to open/close the drawer. # When `False`, dragging, clicking outside, pressing esc, etc. will not close the drawer. Use this in combination with the open prop, otherwise you won't be able to open/close the drawer.
dismissible: Var[bool] dismissible: Var[bool]
@ -141,19 +141,19 @@ class DrawerContent(DrawerComponent):
return {"css": base_style} return {"css": base_style}
# Fired when the drawer content is opened. Deprecated. # Fired when the drawer content is opened. Deprecated.
on_open_auto_focus: EventHandler[empty_event] on_open_auto_focus: EventHandler[no_args_event_spec]
# Fired when the drawer content is closed. Deprecated. # Fired when the drawer content is closed. Deprecated.
on_close_auto_focus: EventHandler[empty_event] on_close_auto_focus: EventHandler[no_args_event_spec]
# Fired when the escape key is pressed. Deprecated. # Fired when the escape key is pressed. Deprecated.
on_escape_key_down: EventHandler[empty_event] on_escape_key_down: EventHandler[no_args_event_spec]
# Fired when the pointer is down outside the drawer content. Deprecated. # Fired when the pointer is down outside the drawer content. Deprecated.
on_pointer_down_outside: EventHandler[empty_event] on_pointer_down_outside: EventHandler[no_args_event_spec]
# Fired when interacting outside the drawer content. Deprecated. # Fired when interacting outside the drawer content. Deprecated.
on_interact_outside: EventHandler[empty_event] on_interact_outside: EventHandler[no_args_event_spec]
@classmethod @classmethod
def create(cls, *children, **props): def create(cls, *children, **props):

View File

@ -8,7 +8,7 @@ from reflex.components.component import ComponentNamespace
from reflex.components.core.debounce import DebounceInput from reflex.components.core.debounce import DebounceInput
from reflex.components.el.elements.forms import Form as HTMLForm from reflex.components.el.elements.forms import Form as HTMLForm
from reflex.components.radix.themes.components.text_field import TextFieldRoot from reflex.components.radix.themes.components.text_field import TextFieldRoot
from reflex.event import EventHandler, empty_event from reflex.event import EventHandler, no_args_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
from .base import RadixPrimitiveComponentWithClassName from .base import RadixPrimitiveComponentWithClassName
@ -28,7 +28,7 @@ class FormRoot(FormComponent, HTMLForm):
alias = "RadixFormRoot" alias = "RadixFormRoot"
# Fired when the errors are cleared. # Fired when the errors are cleared.
on_clear_server_errors: EventHandler[empty_event] on_clear_server_errors: EventHandler[no_args_event_spec]
def add_style(self) -> dict[str, Any] | None: def add_style(self) -> dict[str, Any] | None:
"""Add style to the component. """Add style to the component.

View File

@ -5,7 +5,7 @@ from typing import Literal
from reflex.components.component import ComponentNamespace from reflex.components.component import ComponentNamespace
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.components.el import elements from reflex.components.el import elements
from reflex.event import EventHandler, empty_event, identity_event from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import RadixThemesComponent, RadixThemesTriggerComponent from ..base import RadixThemesComponent, RadixThemesTriggerComponent
@ -22,7 +22,7 @@ class AlertDialogRoot(RadixThemesComponent):
open: Var[bool] open: Var[bool]
# Fired when the open state changes. # Fired when the open state changes.
on_open_change: EventHandler[identity_event(bool)] on_open_change: EventHandler[passthrough_event_spec(bool)]
# The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. # The open state of the dialog when it is initially rendered. Use when you do not need to control its open state.
default_open: Var[bool] default_open: Var[bool]
@ -46,13 +46,13 @@ class AlertDialogContent(elements.Div, RadixThemesComponent):
force_mount: Var[bool] force_mount: Var[bool]
# Fired when the dialog is opened. # Fired when the dialog is opened.
on_open_auto_focus: EventHandler[empty_event] on_open_auto_focus: EventHandler[no_args_event_spec]
# Fired when the dialog is closed. # Fired when the dialog is closed.
on_close_auto_focus: EventHandler[empty_event] on_close_auto_focus: EventHandler[no_args_event_spec]
# Fired when the escape key is pressed. # Fired when the escape key is pressed.
on_escape_key_down: EventHandler[empty_event] on_escape_key_down: EventHandler[no_args_event_spec]
class AlertDialogTitle(RadixThemesComponent): class AlertDialogTitle(RadixThemesComponent):

View File

@ -6,7 +6,7 @@ from reflex.components.component import Component, ComponentNamespace
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.components.radix.themes.layout.flex import Flex from reflex.components.radix.themes.layout.flex import Flex
from reflex.components.radix.themes.typography.text import Text from reflex.components.radix.themes.typography.text import Text
from reflex.event import EventHandler, identity_event from reflex.event import EventHandler, passthrough_event_spec
from reflex.vars.base import LiteralVar, Var from reflex.vars.base import LiteralVar, Var
from ..base import ( from ..base import (
@ -61,7 +61,7 @@ class Checkbox(RadixThemesComponent):
_rename_props = {"onChange": "onCheckedChange"} _rename_props = {"onChange": "onCheckedChange"}
# Fired when the checkbox is checked or unchecked. # Fired when the checkbox is checked or unchecked.
on_change: EventHandler[identity_event(bool)] on_change: EventHandler[passthrough_event_spec(bool)]
class HighLevelCheckbox(RadixThemesComponent): class HighLevelCheckbox(RadixThemesComponent):
@ -112,7 +112,7 @@ class HighLevelCheckbox(RadixThemesComponent):
_rename_props = {"onChange": "onCheckedChange"} _rename_props = {"onChange": "onCheckedChange"}
# Fired when the checkbox is checked or unchecked. # Fired when the checkbox is checked or unchecked.
on_change: EventHandler[identity_event(bool)] on_change: EventHandler[passthrough_event_spec(bool)]
@classmethod @classmethod
def create(cls, text: Var[str] = LiteralVar.create(""), **props) -> Component: def create(cls, text: Var[str] = LiteralVar.create(""), **props) -> Component:

View File

@ -4,7 +4,7 @@ from typing import Dict, List, Literal, Union
from reflex.components.component import ComponentNamespace from reflex.components.component import ComponentNamespace
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.event import EventHandler, empty_event, identity_event from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import ( from ..base import (
@ -39,7 +39,7 @@ class ContextMenuRoot(RadixThemesComponent):
_invalid_children: List[str] = ["ContextMenuItem"] _invalid_children: List[str] = ["ContextMenuItem"]
# Fired when the open state changes. # Fired when the open state changes.
on_open_change: EventHandler[identity_event(bool)] on_open_change: EventHandler[passthrough_event_spec(bool)]
# The reading direction of submenus when applicable. If omitted, inherits globally from DirectionProvider or assumes LTR (left-to-right) reading mode. # The reading direction of submenus when applicable. If omitted, inherits globally from DirectionProvider or assumes LTR (left-to-right) reading mode.
dir: Var[LiteralDirType] dir: Var[LiteralDirType]
@ -109,19 +109,19 @@ class ContextMenuContent(RadixThemesComponent):
hide_when_detached: Var[bool] hide_when_detached: Var[bool]
# Fired when focus moves back after closing. # Fired when focus moves back after closing.
on_close_auto_focus: EventHandler[empty_event] on_close_auto_focus: EventHandler[no_args_event_spec]
# Fired when the escape key is pressed. # Fired when the escape key is pressed.
on_escape_key_down: EventHandler[empty_event] on_escape_key_down: EventHandler[no_args_event_spec]
# Fired when a pointer down event happens outside the context menu. # Fired when a pointer down event happens outside the context menu.
on_pointer_down_outside: EventHandler[empty_event] on_pointer_down_outside: EventHandler[no_args_event_spec]
# Fired when focus moves outside the context menu. # Fired when focus moves outside the context menu.
on_focus_outside: EventHandler[empty_event] on_focus_outside: EventHandler[no_args_event_spec]
# Fired when the pointer interacts outside the context menu. # Fired when the pointer interacts outside the context menu.
on_interact_outside: EventHandler[empty_event] on_interact_outside: EventHandler[no_args_event_spec]
class ContextMenuSub(RadixThemesComponent): class ContextMenuSub(RadixThemesComponent):
@ -136,7 +136,7 @@ class ContextMenuSub(RadixThemesComponent):
default_open: Var[bool] default_open: Var[bool]
# Fired when the open state changes. # Fired when the open state changes.
on_open_change: EventHandler[identity_event(bool)] on_open_change: EventHandler[passthrough_event_spec(bool)]
class ContextMenuSubTrigger(RadixThemesComponent): class ContextMenuSubTrigger(RadixThemesComponent):
@ -191,16 +191,16 @@ class ContextMenuSubContent(RadixThemesComponent):
_valid_parents: List[str] = ["ContextMenuSub"] _valid_parents: List[str] = ["ContextMenuSub"]
# Fired when the escape key is pressed. # Fired when the escape key is pressed.
on_escape_key_down: EventHandler[empty_event] on_escape_key_down: EventHandler[no_args_event_spec]
# Fired when a pointer down event happens outside the context menu. # Fired when a pointer down event happens outside the context menu.
on_pointer_down_outside: EventHandler[empty_event] on_pointer_down_outside: EventHandler[no_args_event_spec]
# Fired when focus moves outside the context menu. # Fired when focus moves outside the context menu.
on_focus_outside: EventHandler[empty_event] on_focus_outside: EventHandler[no_args_event_spec]
# Fired when interacting outside the context menu. # Fired when interacting outside the context menu.
on_interact_outside: EventHandler[empty_event] on_interact_outside: EventHandler[no_args_event_spec]
class ContextMenuItem(RadixThemesComponent): class ContextMenuItem(RadixThemesComponent):
@ -226,7 +226,7 @@ class ContextMenuItem(RadixThemesComponent):
_valid_parents: List[str] = ["ContextMenuContent", "ContextMenuSubContent"] _valid_parents: List[str] = ["ContextMenuContent", "ContextMenuSubContent"]
# Fired when the item is selected. # Fired when the item is selected.
on_select: EventHandler[empty_event] on_select: EventHandler[no_args_event_spec]
class ContextMenuSeparator(RadixThemesComponent): class ContextMenuSeparator(RadixThemesComponent):

View File

@ -5,7 +5,7 @@ from typing import Literal
from reflex.components.component import ComponentNamespace from reflex.components.component import ComponentNamespace
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.components.el import elements from reflex.components.el import elements
from reflex.event import EventHandler, empty_event, identity_event from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import ( from ..base import (
@ -23,7 +23,7 @@ class DialogRoot(RadixThemesComponent):
open: Var[bool] open: Var[bool]
# Fired when the open state changes. # Fired when the open state changes.
on_open_change: EventHandler[identity_event(bool)] on_open_change: EventHandler[passthrough_event_spec(bool)]
# The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. # The open state of the dialog when it is initially rendered. Use when you do not need to control its open state.
default_open: Var[bool] default_open: Var[bool]
@ -50,19 +50,19 @@ class DialogContent(elements.Div, RadixThemesComponent):
size: Var[Responsive[Literal["1", "2", "3", "4"]]] size: Var[Responsive[Literal["1", "2", "3", "4"]]]
# Fired when the dialog is opened. # Fired when the dialog is opened.
on_open_auto_focus: EventHandler[empty_event] on_open_auto_focus: EventHandler[no_args_event_spec]
# Fired when the dialog is closed. # Fired when the dialog is closed.
on_close_auto_focus: EventHandler[empty_event] on_close_auto_focus: EventHandler[no_args_event_spec]
# Fired when the escape key is pressed. # Fired when the escape key is pressed.
on_escape_key_down: EventHandler[empty_event] on_escape_key_down: EventHandler[no_args_event_spec]
# Fired when the pointer is down outside the dialog. # Fired when the pointer is down outside the dialog.
on_pointer_down_outside: EventHandler[empty_event] on_pointer_down_outside: EventHandler[no_args_event_spec]
# Fired when the pointer interacts outside the dialog. # Fired when the pointer interacts outside the dialog.
on_interact_outside: EventHandler[empty_event] on_interact_outside: EventHandler[no_args_event_spec]
class DialogDescription(RadixThemesComponent): class DialogDescription(RadixThemesComponent):

View File

@ -4,7 +4,7 @@ from typing import Dict, List, Literal, Union
from reflex.components.component import ComponentNamespace from reflex.components.component import ComponentNamespace
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.event import EventHandler, empty_event, identity_event from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import ( from ..base import (
@ -49,7 +49,7 @@ class DropdownMenuRoot(RadixThemesComponent):
_invalid_children: List[str] = ["DropdownMenuItem"] _invalid_children: List[str] = ["DropdownMenuItem"]
# Fired when the open state changes. # Fired when the open state changes.
on_open_change: EventHandler[identity_event(bool)] on_open_change: EventHandler[passthrough_event_spec(bool)]
class DropdownMenuTrigger(RadixThemesTriggerComponent): class DropdownMenuTrigger(RadixThemesTriggerComponent):
@ -116,19 +116,19 @@ class DropdownMenuContent(RadixThemesComponent):
hide_when_detached: Var[bool] hide_when_detached: Var[bool]
# Fired when the dialog is closed. # Fired when the dialog is closed.
on_close_auto_focus: EventHandler[empty_event] on_close_auto_focus: EventHandler[no_args_event_spec]
# Fired when the escape key is pressed. # Fired when the escape key is pressed.
on_escape_key_down: EventHandler[empty_event] on_escape_key_down: EventHandler[no_args_event_spec]
# Fired when the pointer is down outside the dialog. # Fired when the pointer is down outside the dialog.
on_pointer_down_outside: EventHandler[empty_event] on_pointer_down_outside: EventHandler[no_args_event_spec]
# Fired when focus moves outside the dialog. # Fired when focus moves outside the dialog.
on_focus_outside: EventHandler[empty_event] on_focus_outside: EventHandler[no_args_event_spec]
# Fired when the pointer interacts outside the dialog. # Fired when the pointer interacts outside the dialog.
on_interact_outside: EventHandler[empty_event] on_interact_outside: EventHandler[no_args_event_spec]
class DropdownMenuSubTrigger(RadixThemesTriggerComponent): class DropdownMenuSubTrigger(RadixThemesTriggerComponent):
@ -160,7 +160,7 @@ class DropdownMenuSub(RadixThemesComponent):
default_open: Var[bool] default_open: Var[bool]
# Fired when the open state changes. # Fired when the open state changes.
on_open_change: EventHandler[identity_event(bool)] on_open_change: EventHandler[passthrough_event_spec(bool)]
class DropdownMenuSubContent(RadixThemesComponent): class DropdownMenuSubContent(RadixThemesComponent):
@ -198,16 +198,16 @@ class DropdownMenuSubContent(RadixThemesComponent):
_valid_parents: List[str] = ["DropdownMenuSub"] _valid_parents: List[str] = ["DropdownMenuSub"]
# Fired when the escape key is pressed. # Fired when the escape key is pressed.
on_escape_key_down: EventHandler[empty_event] on_escape_key_down: EventHandler[no_args_event_spec]
# Fired when the pointer is down outside the dialog. # Fired when the pointer is down outside the dialog.
on_pointer_down_outside: EventHandler[empty_event] on_pointer_down_outside: EventHandler[no_args_event_spec]
# Fired when focus moves outside the dialog. # Fired when focus moves outside the dialog.
on_focus_outside: EventHandler[empty_event] on_focus_outside: EventHandler[no_args_event_spec]
# Fired when the pointer interacts outside the dialog. # Fired when the pointer interacts outside the dialog.
on_interact_outside: EventHandler[empty_event] on_interact_outside: EventHandler[no_args_event_spec]
class DropdownMenuItem(RadixThemesComponent): class DropdownMenuItem(RadixThemesComponent):
@ -233,7 +233,7 @@ class DropdownMenuItem(RadixThemesComponent):
_valid_parents: List[str] = ["DropdownMenuContent", "DropdownMenuSubContent"] _valid_parents: List[str] = ["DropdownMenuContent", "DropdownMenuSubContent"]
# Fired when the item is selected. # Fired when the item is selected.
on_select: EventHandler[empty_event] on_select: EventHandler[no_args_event_spec]
class DropdownMenuSeparator(RadixThemesComponent): class DropdownMenuSeparator(RadixThemesComponent):

View File

@ -5,7 +5,7 @@ from typing import Dict, Literal, Union
from reflex.components.component import ComponentNamespace from reflex.components.component import ComponentNamespace
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.components.el import elements from reflex.components.el import elements
from reflex.event import EventHandler, identity_event from reflex.event import EventHandler, passthrough_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import ( from ..base import (
@ -32,7 +32,7 @@ class HoverCardRoot(RadixThemesComponent):
close_delay: Var[int] close_delay: Var[int]
# Fired when the open state changes. # Fired when the open state changes.
on_open_change: EventHandler[identity_event(bool)] on_open_change: EventHandler[passthrough_event_spec(bool)]
class HoverCardTrigger(RadixThemesTriggerComponent): class HoverCardTrigger(RadixThemesTriggerComponent):

View File

@ -5,7 +5,7 @@ from typing import Dict, Literal, Union
from reflex.components.component import ComponentNamespace from reflex.components.component import ComponentNamespace
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.components.el import elements from reflex.components.el import elements
from reflex.event import EventHandler, empty_event, identity_event from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import ( from ..base import (
@ -26,7 +26,7 @@ class PopoverRoot(RadixThemesComponent):
modal: Var[bool] modal: Var[bool]
# Fired when the open state changes. # Fired when the open state changes.
on_open_change: EventHandler[identity_event(bool)] on_open_change: EventHandler[passthrough_event_spec(bool)]
# The open state of the popover when it is initially rendered. Use when you do not need to control its open state. # The open state of the popover when it is initially rendered. Use when you do not need to control its open state.
default_open: Var[bool] default_open: Var[bool]
@ -71,22 +71,22 @@ class PopoverContent(elements.Div, RadixThemesComponent):
hide_when_detached: Var[bool] hide_when_detached: Var[bool]
# Fired when the dialog is opened. # Fired when the dialog is opened.
on_open_auto_focus: EventHandler[empty_event] on_open_auto_focus: EventHandler[no_args_event_spec]
# Fired when the dialog is closed. # Fired when the dialog is closed.
on_close_auto_focus: EventHandler[empty_event] on_close_auto_focus: EventHandler[no_args_event_spec]
# Fired when the escape key is pressed. # Fired when the escape key is pressed.
on_escape_key_down: EventHandler[empty_event] on_escape_key_down: EventHandler[no_args_event_spec]
# Fired when the pointer is down outside the dialog. # Fired when the pointer is down outside the dialog.
on_pointer_down_outside: EventHandler[empty_event] on_pointer_down_outside: EventHandler[no_args_event_spec]
# Fired when focus moves outside the dialog. # Fired when focus moves outside the dialog.
on_focus_outside: EventHandler[empty_event] on_focus_outside: EventHandler[no_args_event_spec]
# Fired when the pointer interacts outside the dialog. # Fired when the pointer interacts outside the dialog.
on_interact_outside: EventHandler[empty_event] on_interact_outside: EventHandler[no_args_event_spec]
class PopoverClose(RadixThemesTriggerComponent): class PopoverClose(RadixThemesTriggerComponent):

View File

@ -4,7 +4,7 @@ from types import SimpleNamespace
from typing import Literal, Union from typing import Literal, Union
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.event import EventHandler, identity_event from reflex.event import EventHandler, passthrough_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import LiteralAccentColor, RadixThemesComponent from ..base import LiteralAccentColor, RadixThemesComponent
@ -65,7 +65,7 @@ class RadioCardsRoot(RadixThemesComponent):
loop: Var[bool] loop: Var[bool]
# Event handler called when the value changes. # Event handler called when the value changes.
on_value_change: EventHandler[identity_event(str)] on_value_change: EventHandler[passthrough_event_spec(str)]
class RadioCardsItem(RadixThemesComponent): class RadioCardsItem(RadixThemesComponent):

View File

@ -9,7 +9,7 @@ from reflex.components.component import Component, ComponentNamespace
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.components.radix.themes.layout.flex import Flex from reflex.components.radix.themes.layout.flex import Flex
from reflex.components.radix.themes.typography.text import Text from reflex.components.radix.themes.typography.text import Text
from reflex.event import EventHandler, identity_event from reflex.event import EventHandler, passthrough_event_spec
from reflex.utils import types from reflex.utils import types
from reflex.vars.base import LiteralVar, Var from reflex.vars.base import LiteralVar, Var
from reflex.vars.sequence import StringVar from reflex.vars.sequence import StringVar
@ -59,7 +59,7 @@ class RadioGroupRoot(RadixThemesComponent):
_rename_props = {"onChange": "onValueChange"} _rename_props = {"onChange": "onValueChange"}
# Fired when the value of the radio group changes. # Fired when the value of the radio group changes.
on_change: EventHandler[identity_event(str)] on_change: EventHandler[passthrough_event_spec(str)]
class RadioGroupItem(RadixThemesComponent): class RadioGroupItem(RadixThemesComponent):

View File

@ -5,7 +5,7 @@ from typing import List, Literal, Union
import reflex as rx import reflex as rx
from reflex.components.component import Component, ComponentNamespace from reflex.components.component import Component, ComponentNamespace
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.event import empty_event, identity_event from reflex.event import no_args_event_spec, passthrough_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import ( from ..base import (
@ -48,10 +48,10 @@ class SelectRoot(RadixThemesComponent):
_rename_props = {"onChange": "onValueChange"} _rename_props = {"onChange": "onValueChange"}
# Fired when the value of the select changes. # Fired when the value of the select changes.
on_change: rx.EventHandler[identity_event(str)] on_change: rx.EventHandler[passthrough_event_spec(str)]
# Fired when the select is opened or closed. # Fired when the select is opened or closed.
on_open_change: rx.EventHandler[identity_event(bool)] on_open_change: rx.EventHandler[passthrough_event_spec(bool)]
class SelectTrigger(RadixThemesComponent): class SelectTrigger(RadixThemesComponent):
@ -104,13 +104,13 @@ class SelectContent(RadixThemesComponent):
align_offset: Var[int] align_offset: Var[int]
# Fired when the select content is closed. # Fired when the select content is closed.
on_close_auto_focus: rx.EventHandler[empty_event] on_close_auto_focus: rx.EventHandler[no_args_event_spec]
# Fired when the escape key is pressed. # Fired when the escape key is pressed.
on_escape_key_down: rx.EventHandler[empty_event] on_escape_key_down: rx.EventHandler[no_args_event_spec]
# Fired when a pointer down event happens outside the select content. # Fired when a pointer down event happens outside the select content.
on_pointer_down_outside: rx.EventHandler[empty_event] on_pointer_down_outside: rx.EventHandler[no_args_event_spec]
class SelectGroup(RadixThemesComponent): class SelectGroup(RadixThemesComponent):

View File

@ -6,7 +6,7 @@ from typing import List, Literal, Optional, Union
from reflex.components.component import Component from reflex.components.component import Component
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.event import EventHandler, identity_event from reflex.event import EventHandler, passthrough_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import ( from ..base import (
@ -15,9 +15,9 @@ from ..base import (
) )
on_value_event_spec = ( on_value_event_spec = (
identity_event(list[Union[int, float]]), passthrough_event_spec(list[Union[int, float]]),
identity_event(list[int]), passthrough_event_spec(list[int]),
identity_event(list[float]), passthrough_event_spec(list[float]),
) )

View File

@ -6,16 +6,16 @@
from typing import Any, Dict, List, Literal, Optional, Union, overload from typing import Any, Dict, List, Literal, Optional, Union, overload
from reflex.components.core.breakpoints import Breakpoints from reflex.components.core.breakpoints import Breakpoints
from reflex.event import EventType, identity_event from reflex.event import EventType, passthrough_event_spec
from reflex.style import Style from reflex.style import Style
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import RadixThemesComponent from ..base import RadixThemesComponent
on_value_event_spec = ( on_value_event_spec = (
identity_event(list[Union[int, float]]), passthrough_event_spec(list[Union[int, float]]),
identity_event(list[int]), passthrough_event_spec(list[int]),
identity_event(list[float]), passthrough_event_spec(list[float]),
) )
class Slider(RadixThemesComponent): class Slider(RadixThemesComponent):

View File

@ -3,7 +3,7 @@
from typing import Literal from typing import Literal
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.event import EventHandler, identity_event from reflex.event import EventHandler, passthrough_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import ( from ..base import (
@ -59,7 +59,7 @@ class Switch(RadixThemesComponent):
_rename_props = {"onChange": "onCheckedChange"} _rename_props = {"onChange": "onCheckedChange"}
# Fired when the value of the switch changes # Fired when the value of the switch changes
on_change: EventHandler[identity_event(bool)] on_change: EventHandler[passthrough_event_spec(bool)]
switch = Switch.create switch = Switch.create

View File

@ -7,7 +7,7 @@ from typing import Any, Dict, List, Literal
from reflex.components.component import Component, ComponentNamespace from reflex.components.component import Component, ComponentNamespace
from reflex.components.core.breakpoints import Responsive from reflex.components.core.breakpoints import Responsive
from reflex.components.core.colors import color from reflex.components.core.colors import color
from reflex.event import EventHandler, identity_event from reflex.event import EventHandler, passthrough_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import ( from ..base import (
@ -42,7 +42,7 @@ class TabsRoot(RadixThemesComponent):
_rename_props = {"onChange": "onValueChange"} _rename_props = {"onChange": "onValueChange"}
# Fired when the value of the tabs changes. # Fired when the value of the tabs changes.
on_change: EventHandler[identity_event(str)] on_change: EventHandler[passthrough_event_spec(str)]
def add_style(self) -> Dict[str, Any] | None: def add_style(self) -> Dict[str, Any] | None:
"""Add style for the component. """Add style for the component.

View File

@ -3,7 +3,7 @@
from typing import Dict, Literal, Union from typing import Dict, Literal, Union
from reflex.components.component import Component from reflex.components.component import Component
from reflex.event import EventHandler, empty_event, identity_event from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
from reflex.utils import format from reflex.utils import format
from reflex.vars.base import Var from reflex.vars.base import Var
@ -85,13 +85,13 @@ class Tooltip(RadixThemesComponent):
aria_label: Var[str] aria_label: Var[str]
# Fired when the open state changes. # Fired when the open state changes.
on_open_change: EventHandler[identity_event(bool)] on_open_change: EventHandler[passthrough_event_spec(bool)]
# Fired when the escape key is pressed. # Fired when the escape key is pressed.
on_escape_key_down: EventHandler[empty_event] on_escape_key_down: EventHandler[no_args_event_spec]
# Fired when the pointer is down outside the tooltip. # Fired when the pointer is down outside the tooltip.
on_pointer_down_outside: EventHandler[empty_event] on_pointer_down_outside: EventHandler[no_args_event_spec]
@classmethod @classmethod
def create(cls, *children, **props) -> Component: def create(cls, *children, **props) -> Component:

View File

@ -5,7 +5,7 @@ from __future__ import annotations
from typing_extensions import TypedDict from typing_extensions import TypedDict
from reflex.components.component import NoSSRComponent from reflex.components.component import NoSSRComponent
from reflex.event import EventHandler, empty_event, identity_event from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
@ -57,49 +57,49 @@ class ReactPlayer(NoSSRComponent):
height: Var[str] height: Var[str]
# Called when media is loaded and ready to play. If playing is set to true, media will play immediately. # Called when media is loaded and ready to play. If playing is set to true, media will play immediately.
on_ready: EventHandler[empty_event] on_ready: EventHandler[no_args_event_spec]
# Called when media starts playing. # Called when media starts playing.
on_start: EventHandler[empty_event] on_start: EventHandler[no_args_event_spec]
# Called when media starts or resumes playing after pausing or buffering. # Called when media starts or resumes playing after pausing or buffering.
on_play: EventHandler[empty_event] on_play: EventHandler[no_args_event_spec]
# Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds. eg { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 } # Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds. eg { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 }
on_progress: EventHandler[identity_event(Progress)] on_progress: EventHandler[passthrough_event_spec(Progress)]
# Callback containing duration of the media, in seconds. # Callback containing duration of the media, in seconds.
on_duration: EventHandler[identity_event(float)] on_duration: EventHandler[passthrough_event_spec(float)]
# Called when media is paused. # Called when media is paused.
on_pause: EventHandler[empty_event] on_pause: EventHandler[no_args_event_spec]
# Called when media starts buffering. # Called when media starts buffering.
on_buffer: EventHandler[empty_event] on_buffer: EventHandler[no_args_event_spec]
# Called when media has finished buffering. Works for files, YouTube and Facebook. # Called when media has finished buffering. Works for files, YouTube and Facebook.
on_buffer_end: EventHandler[empty_event] on_buffer_end: EventHandler[no_args_event_spec]
# Called when media seeks with seconds parameter. # Called when media seeks with seconds parameter.
on_seek: EventHandler[identity_event(float)] on_seek: EventHandler[passthrough_event_spec(float)]
# Called when playback rate of the player changed. Only supported by YouTube, Vimeo (if enabled), Wistia, and file paths. # Called when playback rate of the player changed. Only supported by YouTube, Vimeo (if enabled), Wistia, and file paths.
on_playback_rate_change: EventHandler[empty_event] on_playback_rate_change: EventHandler[no_args_event_spec]
# Called when playback quality of the player changed. Only supported by YouTube (if enabled). # Called when playback quality of the player changed. Only supported by YouTube (if enabled).
on_playback_quality_change: EventHandler[empty_event] on_playback_quality_change: EventHandler[no_args_event_spec]
# Called when media finishes playing. Does not fire when loop is set to true. # Called when media finishes playing. Does not fire when loop is set to true.
on_ended: EventHandler[empty_event] on_ended: EventHandler[no_args_event_spec]
# Called when an error occurs whilst attempting to play media. # Called when an error occurs whilst attempting to play media.
on_error: EventHandler[empty_event] on_error: EventHandler[no_args_event_spec]
# Called when user clicks the light mode preview. # Called when user clicks the light mode preview.
on_click_preview: EventHandler[empty_event] on_click_preview: EventHandler[no_args_event_spec]
# Called when picture-in-picture mode is enabled. # Called when picture-in-picture mode is enabled.
on_enable_pip: EventHandler[empty_event] on_enable_pip: EventHandler[no_args_event_spec]
# Called when picture-in-picture mode is disabled. # Called when picture-in-picture mode is disabled.
on_disable_pip: EventHandler[empty_event] on_disable_pip: EventHandler[no_args_event_spec]

View File

@ -6,7 +6,7 @@ from typing import Any, Dict, List, Union
from reflex.constants import EventTriggers from reflex.constants import EventTriggers
from reflex.constants.colors import Color from reflex.constants.colors import Color
from reflex.event import EventHandler, empty_event from reflex.event import EventHandler, no_args_event_spec
from reflex.vars.base import LiteralVar, Var from reflex.vars.base import LiteralVar, Var
from .recharts import ( from .recharts import (
@ -109,25 +109,25 @@ class Axis(Recharts):
text_anchor: Var[LiteralTextAnchor] text_anchor: Var[LiteralTextAnchor]
# The customized event handler of click on the ticks of this axis # The customized event handler of click on the ticks of this axis
on_click: EventHandler[empty_event] on_click: EventHandler[no_args_event_spec]
# The customized event handler of mousedown on the ticks of this axis # The customized event handler of mousedown on the ticks of this axis
on_mouse_down: EventHandler[empty_event] on_mouse_down: EventHandler[no_args_event_spec]
# The customized event handler of mouseup on the ticks of this axis # The customized event handler of mouseup on the ticks of this axis
on_mouse_up: EventHandler[empty_event] on_mouse_up: EventHandler[no_args_event_spec]
# The customized event handler of mousemove on the ticks of this axis # The customized event handler of mousemove on the ticks of this axis
on_mouse_move: EventHandler[empty_event] on_mouse_move: EventHandler[no_args_event_spec]
# The customized event handler of mouseout on the ticks of this axis # The customized event handler of mouseout on the ticks of this axis
on_mouse_out: EventHandler[empty_event] on_mouse_out: EventHandler[no_args_event_spec]
# The customized event handler of mouseenter on the ticks of this axis # The customized event handler of mouseenter on the ticks of this axis
on_mouse_enter: EventHandler[empty_event] on_mouse_enter: EventHandler[no_args_event_spec]
# The customized event handler of mouseleave on the ticks of this axis # The customized event handler of mouseleave on the ticks of this axis
on_mouse_leave: EventHandler[empty_event] on_mouse_leave: EventHandler[no_args_event_spec]
class XAxis(Axis): class XAxis(Axis):
@ -252,7 +252,7 @@ class Brush(Recharts):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_CHANGE: empty_event, EventTriggers.ON_CHANGE: no_args_event_spec,
} }
@ -293,34 +293,34 @@ class Cartesian(Recharts):
name: Var[Union[str, int]] name: Var[Union[str, int]]
# The customized event handler of animation start # The customized event handler of animation start
on_animation_start: EventHandler[empty_event] on_animation_start: EventHandler[no_args_event_spec]
# The customized event handler of animation end # The customized event handler of animation end
on_animation_end: EventHandler[empty_event] on_animation_end: EventHandler[no_args_event_spec]
# The customized event handler of click on the component in this group # The customized event handler of click on the component in this group
on_click: EventHandler[empty_event] on_click: EventHandler[no_args_event_spec]
# The customized event handler of mousedown on the component in this group # The customized event handler of mousedown on the component in this group
on_mouse_down: EventHandler[empty_event] on_mouse_down: EventHandler[no_args_event_spec]
# The customized event handler of mouseup on the component in this group # The customized event handler of mouseup on the component in this group
on_mouse_up: EventHandler[empty_event] on_mouse_up: EventHandler[no_args_event_spec]
# The customized event handler of mousemove on the component in this group # The customized event handler of mousemove on the component in this group
on_mouse_move: EventHandler[empty_event] on_mouse_move: EventHandler[no_args_event_spec]
# The customized event handler of mouseover on the component in this group # The customized event handler of mouseover on the component in this group
on_mouse_over: EventHandler[empty_event] on_mouse_over: EventHandler[no_args_event_spec]
# The customized event handler of mouseout on the component in this group # The customized event handler of mouseout on the component in this group
on_mouse_out: EventHandler[empty_event] on_mouse_out: EventHandler[no_args_event_spec]
# The customized event handler of mouseenter on the component in this group # The customized event handler of mouseenter on the component in this group
on_mouse_enter: EventHandler[empty_event] on_mouse_enter: EventHandler[no_args_event_spec]
# The customized event handler of mouseleave on the component in this group # The customized event handler of mouseleave on the component in this group
on_mouse_leave: EventHandler[empty_event] on_mouse_leave: EventHandler[no_args_event_spec]
class Area(Cartesian): class Area(Cartesian):
@ -526,28 +526,28 @@ class Scatter(Recharts):
animation_easing: Var[LiteralAnimationEasing] animation_easing: Var[LiteralAnimationEasing]
# The customized event handler of click on the component in this group # The customized event handler of click on the component in this group
on_click: EventHandler[empty_event] on_click: EventHandler[no_args_event_spec]
# The customized event handler of mousedown on the component in this group # The customized event handler of mousedown on the component in this group
on_mouse_down: EventHandler[empty_event] on_mouse_down: EventHandler[no_args_event_spec]
# The customized event handler of mouseup on the component in this group # The customized event handler of mouseup on the component in this group
on_mouse_up: EventHandler[empty_event] on_mouse_up: EventHandler[no_args_event_spec]
# The customized event handler of mousemove on the component in this group # The customized event handler of mousemove on the component in this group
on_mouse_move: EventHandler[empty_event] on_mouse_move: EventHandler[no_args_event_spec]
# The customized event handler of mouseover on the component in this group # The customized event handler of mouseover on the component in this group
on_mouse_over: EventHandler[empty_event] on_mouse_over: EventHandler[no_args_event_spec]
# The customized event handler of mouseout on the component in this group # The customized event handler of mouseout on the component in this group
on_mouse_out: EventHandler[empty_event] on_mouse_out: EventHandler[no_args_event_spec]
# The customized event handler of mouseenter on the component in this group # The customized event handler of mouseenter on the component in this group
on_mouse_enter: EventHandler[empty_event] on_mouse_enter: EventHandler[no_args_event_spec]
# The customized event handler of mouseleave on the component in this group # The customized event handler of mouseleave on the component in this group
on_mouse_leave: EventHandler[empty_event] on_mouse_leave: EventHandler[no_args_event_spec]
class Funnel(Recharts): class Funnel(Recharts):
@ -591,34 +591,34 @@ class Funnel(Recharts):
_valid_children: List[str] = ["LabelList", "Cell"] _valid_children: List[str] = ["LabelList", "Cell"]
# The customized event handler of animation start # The customized event handler of animation start
on_animation_start: EventHandler[empty_event] on_animation_start: EventHandler[no_args_event_spec]
# The customized event handler of animation end # The customized event handler of animation end
on_animation_end: EventHandler[empty_event] on_animation_end: EventHandler[no_args_event_spec]
# The customized event handler of click on the component in this group # The customized event handler of click on the component in this group
on_click: EventHandler[empty_event] on_click: EventHandler[no_args_event_spec]
# The customized event handler of mousedown on the component in this group # The customized event handler of mousedown on the component in this group
on_mouse_down: EventHandler[empty_event] on_mouse_down: EventHandler[no_args_event_spec]
# The customized event handler of mouseup on the component in this group # The customized event handler of mouseup on the component in this group
on_mouse_up: EventHandler[empty_event] on_mouse_up: EventHandler[no_args_event_spec]
# The customized event handler of mousemove on the component in this group # The customized event handler of mousemove on the component in this group
on_mouse_move: EventHandler[empty_event] on_mouse_move: EventHandler[no_args_event_spec]
# The customized event handler of mouseover on the component in this group # The customized event handler of mouseover on the component in this group
on_mouse_over: EventHandler[empty_event] on_mouse_over: EventHandler[no_args_event_spec]
# The customized event handler of mouseout on the component in this group # The customized event handler of mouseout on the component in this group
on_mouse_out: EventHandler[empty_event] on_mouse_out: EventHandler[no_args_event_spec]
# The customized event handler of mouseenter on the component in this group # The customized event handler of mouseenter on the component in this group
on_mouse_enter: EventHandler[empty_event] on_mouse_enter: EventHandler[no_args_event_spec]
# The customized event handler of mouseleave on the component in this group # The customized event handler of mouseleave on the component in this group
on_mouse_leave: EventHandler[empty_event] on_mouse_leave: EventHandler[no_args_event_spec]
class ErrorBar(Recharts): class ErrorBar(Recharts):
@ -715,28 +715,28 @@ class ReferenceDot(Reference):
_valid_children: List[str] = ["Label"] _valid_children: List[str] = ["Label"]
# The customized event handler of click on the component in this chart # The customized event handler of click on the component in this chart
on_click: EventHandler[empty_event] on_click: EventHandler[no_args_event_spec]
# The customized event handler of mousedown on the component in this chart # The customized event handler of mousedown on the component in this chart
on_mouse_down: EventHandler[empty_event] on_mouse_down: EventHandler[no_args_event_spec]
# The customized event handler of mouseup on the component in this chart # The customized event handler of mouseup on the component in this chart
on_mouse_up: EventHandler[empty_event] on_mouse_up: EventHandler[no_args_event_spec]
# The customized event handler of mouseover on the component in this chart # The customized event handler of mouseover on the component in this chart
on_mouse_over: EventHandler[empty_event] on_mouse_over: EventHandler[no_args_event_spec]
# The customized event handler of mouseout on the component in this chart # The customized event handler of mouseout on the component in this chart
on_mouse_out: EventHandler[empty_event] on_mouse_out: EventHandler[no_args_event_spec]
# The customized event handler of mouseenter on the component in this chart # The customized event handler of mouseenter on the component in this chart
on_mouse_enter: EventHandler[empty_event] on_mouse_enter: EventHandler[no_args_event_spec]
# The customized event handler of mousemove on the component in this chart # The customized event handler of mousemove on the component in this chart
on_mouse_move: EventHandler[empty_event] on_mouse_move: EventHandler[no_args_event_spec]
# The customized event handler of mouseleave on the component in this chart # The customized event handler of mouseleave on the component in this chart
on_mouse_leave: EventHandler[empty_event] on_mouse_leave: EventHandler[no_args_event_spec]
class ReferenceArea(Recharts): class ReferenceArea(Recharts):

View File

@ -8,7 +8,7 @@ from reflex.components.component import Component
from reflex.components.recharts.general import ResponsiveContainer from reflex.components.recharts.general import ResponsiveContainer
from reflex.constants import EventTriggers from reflex.constants import EventTriggers
from reflex.constants.colors import Color from reflex.constants.colors import Color
from reflex.event import EventHandler, empty_event from reflex.event import EventHandler, no_args_event_spec
from reflex.vars.base import Var from reflex.vars.base import Var
from .recharts import ( from .recharts import (
@ -31,16 +31,16 @@ class ChartBase(RechartsCharts):
height: Var[Union[str, int]] = "100%" # type: ignore height: Var[Union[str, int]] = "100%" # type: ignore
# The customized event handler of click on the component in this chart # The customized event handler of click on the component in this chart
on_click: EventHandler[empty_event] on_click: EventHandler[no_args_event_spec]
# The customized event handler of mouseenter on the component in this chart # The customized event handler of mouseenter on the component in this chart
on_mouse_enter: EventHandler[empty_event] on_mouse_enter: EventHandler[no_args_event_spec]
# The customized event handler of mousemove on the component in this chart # The customized event handler of mousemove on the component in this chart
on_mouse_move: EventHandler[empty_event] on_mouse_move: EventHandler[no_args_event_spec]
# The customized event handler of mouseleave on the component in this chart # The customized event handler of mouseleave on the component in this chart
on_mouse_leave: EventHandler[empty_event] on_mouse_leave: EventHandler[no_args_event_spec]
@staticmethod @staticmethod
def _ensure_valid_dimension(name: str, value: Any) -> None: def _ensure_valid_dimension(name: str, value: Any) -> None:
@ -270,16 +270,16 @@ class PieChart(ChartBase):
] ]
# The customized event handler of mousedown on the sectors in this group # The customized event handler of mousedown on the sectors in this group
on_mouse_down: EventHandler[empty_event] on_mouse_down: EventHandler[no_args_event_spec]
# The customized event handler of mouseup on the sectors in this group # The customized event handler of mouseup on the sectors in this group
on_mouse_up: EventHandler[empty_event] on_mouse_up: EventHandler[no_args_event_spec]
# The customized event handler of mouseover on the sectors in this group # The customized event handler of mouseover on the sectors in this group
on_mouse_over: EventHandler[empty_event] on_mouse_over: EventHandler[no_args_event_spec]
# The customized event handler of mouseout on the sectors in this group # The customized event handler of mouseout on the sectors in this group
on_mouse_out: EventHandler[empty_event] on_mouse_out: EventHandler[no_args_event_spec]
class RadarChart(ChartBase): class RadarChart(ChartBase):
@ -330,9 +330,9 @@ class RadarChart(ChartBase):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_CLICK: empty_event, EventTriggers.ON_CLICK: no_args_event_spec,
EventTriggers.ON_MOUSE_ENTER: empty_event, EventTriggers.ON_MOUSE_ENTER: no_args_event_spec,
EventTriggers.ON_MOUSE_LEAVE: empty_event, EventTriggers.ON_MOUSE_LEAVE: no_args_event_spec,
} }
@ -419,14 +419,14 @@ class ScatterChart(ChartBase):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_CLICK: empty_event, EventTriggers.ON_CLICK: no_args_event_spec,
EventTriggers.ON_MOUSE_DOWN: empty_event, EventTriggers.ON_MOUSE_DOWN: no_args_event_spec,
EventTriggers.ON_MOUSE_UP: empty_event, EventTriggers.ON_MOUSE_UP: no_args_event_spec,
EventTriggers.ON_MOUSE_MOVE: empty_event, EventTriggers.ON_MOUSE_MOVE: no_args_event_spec,
EventTriggers.ON_MOUSE_OVER: empty_event, EventTriggers.ON_MOUSE_OVER: no_args_event_spec,
EventTriggers.ON_MOUSE_OUT: empty_event, EventTriggers.ON_MOUSE_OUT: no_args_event_spec,
EventTriggers.ON_MOUSE_ENTER: empty_event, EventTriggers.ON_MOUSE_ENTER: no_args_event_spec,
EventTriggers.ON_MOUSE_LEAVE: empty_event, EventTriggers.ON_MOUSE_LEAVE: no_args_event_spec,
} }
@ -488,10 +488,10 @@ class Treemap(RechartsCharts):
animation_easing: Var[LiteralAnimationEasing] animation_easing: Var[LiteralAnimationEasing]
# The customized event handler of animation start # The customized event handler of animation start
on_animation_start: EventHandler[empty_event] on_animation_start: EventHandler[no_args_event_spec]
# The customized event handler of animation end # The customized event handler of animation end
on_animation_end: EventHandler[empty_event] on_animation_end: EventHandler[no_args_event_spec]
@classmethod @classmethod
def create(cls, *children, **props) -> Component: def create(cls, *children, **props) -> Component:

View File

@ -6,7 +6,7 @@ from typing import Any, Dict, List, Union
from reflex.components.component import MemoizationLeaf from reflex.components.component import MemoizationLeaf
from reflex.constants.colors import Color from reflex.constants.colors import Color
from reflex.event import EventHandler, empty_event from reflex.event import EventHandler, no_args_event_spec
from reflex.vars.base import LiteralVar, Var from reflex.vars.base import LiteralVar, Var
from .recharts import ( from .recharts import (
@ -46,7 +46,7 @@ class ResponsiveContainer(Recharts, MemoizationLeaf):
debounce: Var[int] debounce: Var[int]
# If specified provides a callback providing the updated chart width and height values. # If specified provides a callback providing the updated chart width and height values.
on_resize: EventHandler[empty_event] on_resize: EventHandler[no_args_event_spec]
# Valid children components # Valid children components
_valid_children: List[str] = [ _valid_children: List[str] = [
@ -104,28 +104,28 @@ class Legend(Recharts):
margin: Var[Dict[str, Any]] margin: Var[Dict[str, Any]]
# The customized event handler of click on the items in this group # The customized event handler of click on the items in this group
on_click: EventHandler[empty_event] on_click: EventHandler[no_args_event_spec]
# The customized event handler of mousedown on the items in this group # The customized event handler of mousedown on the items in this group
on_mouse_down: EventHandler[empty_event] on_mouse_down: EventHandler[no_args_event_spec]
# The customized event handler of mouseup on the items in this group # The customized event handler of mouseup on the items in this group
on_mouse_up: EventHandler[empty_event] on_mouse_up: EventHandler[no_args_event_spec]
# The customized event handler of mousemove on the items in this group # The customized event handler of mousemove on the items in this group
on_mouse_move: EventHandler[empty_event] on_mouse_move: EventHandler[no_args_event_spec]
# The customized event handler of mouseover on the items in this group # The customized event handler of mouseover on the items in this group
on_mouse_over: EventHandler[empty_event] on_mouse_over: EventHandler[no_args_event_spec]
# The customized event handler of mouseout on the items in this group # The customized event handler of mouseout on the items in this group
on_mouse_out: EventHandler[empty_event] on_mouse_out: EventHandler[no_args_event_spec]
# The customized event handler of mouseenter on the items in this group # The customized event handler of mouseenter on the items in this group
on_mouse_enter: EventHandler[empty_event] on_mouse_enter: EventHandler[no_args_event_spec]
# The customized event handler of mouseleave on the items in this group # The customized event handler of mouseleave on the items in this group
on_mouse_leave: EventHandler[empty_event] on_mouse_leave: EventHandler[no_args_event_spec]
class GraphingTooltip(Recharts): class GraphingTooltip(Recharts):
@ -135,16 +135,16 @@ class GraphingTooltip(Recharts):
alias = "RechartsTooltip" alias = "RechartsTooltip"
# The separator between name and value. # The separator between name and value. Default: ":"
separator: Var[str] separator: Var[str]
# The offset size of tooltip. Number # The offset size of tooltip. Number. Default: 10
offset: Var[int] offset: Var[int]
# When an item of the payload has value null or undefined, this item won't be displayed. # When an item of the payload has value null or undefined, this item won't be displayed. Default: True
filter_null: Var[bool] filter_null: Var[bool]
# If set false, no cursor will be drawn when tooltip is active. # If set false, no cursor will be drawn when tooltip is active. Default: {"strokeWidth": 1, "fill": rx.color("gray", 3)}
cursor: Var[Union[Dict[str, Any], bool]] = LiteralVar.create( cursor: Var[Union[Dict[str, Any], bool]] = LiteralVar.create(
{ {
"strokeWidth": 1, "strokeWidth": 1,
@ -155,16 +155,17 @@ class GraphingTooltip(Recharts):
# The box of viewing area, which has the shape of {x: someVal, y: someVal, width: someVal, height: someVal}, usually calculated internally. # The box of viewing area, which has the shape of {x: someVal, y: someVal, width: someVal, height: someVal}, usually calculated internally.
view_box: Var[Dict[str, Any]] view_box: Var[Dict[str, Any]]
# The style of default tooltip content item which is a li element. DEFAULT: {} # The style of default tooltip content item which is a li element. Default: {"color": rx.color("gray", 12)}
item_style: Var[Dict[str, Any]] = LiteralVar.create( item_style: Var[Dict[str, Any]] = LiteralVar.create(
{ {
"color": Color("gray", 12), "color": Color("gray", 12),
} }
) )
# The style of tooltip wrapper which is a dom element. DEFAULT: {} # The style of tooltip wrapper which is a dom element. Default: {}
wrapper_style: Var[Dict[str, Any]] wrapper_style: Var[Dict[str, Any]]
# The style of tooltip content which is a dom element. DEFAULT: {}
# The style of tooltip content which is a dom element. Default: {"background": rx.color("gray", 1), "borderColor": rx.color("gray", 4), "borderRadius": "8px"}
content_style: Var[Dict[str, Any]] = LiteralVar.create( content_style: Var[Dict[str, Any]] = LiteralVar.create(
{ {
"background": Color("gray", 1), "background": Color("gray", 1),
@ -173,30 +174,28 @@ class GraphingTooltip(Recharts):
} }
) )
# The style of default tooltip label which is a p element. DEFAULT: {} # The style of default tooltip label which is a p element. Default: {"color": rx.color("gray", 11)}
label_style: Var[Dict[str, Any]] = LiteralVar.create({"color": Color("gray", 11)}) label_style: Var[Dict[str, Any]] = LiteralVar.create({"color": Color("gray", 11)})
# This option allows the tooltip to extend beyond the viewBox of the chart itself. DEFAULT: { x: false, y: false } # This option allows the tooltip to extend beyond the viewBox of the chart itself. Default: {"x": False, "y": False}
allow_escape_view_box: Var[Dict[str, bool]] = LiteralVar.create( allow_escape_view_box: Var[Dict[str, bool]]
{"x": False, "y": False}
)
# If set true, the tooltip is displayed. If set false, the tooltip is hidden, usually calculated internally. # If set true, the tooltip is displayed. If set false, the tooltip is hidden, usually calculated internally. Default: False
active: Var[bool] active: Var[bool]
# If this field is set, the tooltip position will be fixed and will not move anymore. # If this field is set, the tooltip position will be fixed and will not move anymore.
position: Var[Dict[str, Any]] position: Var[Dict[str, Any]]
# The coordinate of tooltip which is usually calculated internally. # The coordinate of tooltip which is usually calculated internally. Default: {"x": 0, "y": 0}
coordinate: Var[Dict[str, Any]] coordinate: Var[Dict[str, Any]]
# If set false, animation of tooltip will be disabled. DEFAULT: true in CSR, and false in SSR # If set false, animation of tooltip will be disabled. Default: True
is_animation_active: Var[bool] is_animation_active: Var[bool]
# Specifies the duration of animation, the unit of this option is ms. DEFAULT: 1500 # Specifies the duration of animation, the unit of this option is ms. Default: 1500
animation_duration: Var[int] animation_duration: Var[int]
# The type of easing function. DEFAULT: 'ease' # The type of easing function. Default: "ease"
animation_easing: Var[LiteralAnimationEasing] animation_easing: Var[LiteralAnimationEasing]

View File

@ -255,22 +255,22 @@ class GraphingTooltip(Recharts):
Args: Args:
*children: The children of the component. *children: The children of the component.
separator: The separator between name and value. separator: The separator between name and value. Default: ":"
offset: The offset size of tooltip. Number offset: The offset size of tooltip. Number. Default: 10
filter_null: When an item of the payload has value null or undefined, this item won't be displayed. filter_null: When an item of the payload has value null or undefined, this item won't be displayed. Default: True
cursor: If set false, no cursor will be drawn when tooltip is active. cursor: If set false, no cursor will be drawn when tooltip is active. Default: {"strokeWidth": 1, "fill": rx.color("gray", 3)}
view_box: The box of viewing area, which has the shape of {x: someVal, y: someVal, width: someVal, height: someVal}, usually calculated internally. view_box: The box of viewing area, which has the shape of {x: someVal, y: someVal, width: someVal, height: someVal}, usually calculated internally.
item_style: The style of default tooltip content item which is a li element. DEFAULT: {} item_style: The style of default tooltip content item which is a li element. Default: {"color": rx.color("gray", 12)}
wrapper_style: The style of tooltip wrapper which is a dom element. DEFAULT: {} wrapper_style: The style of tooltip wrapper which is a dom element. Default: {}
content_style: The style of tooltip content which is a dom element. DEFAULT: {} content_style: The style of tooltip content which is a dom element. Default: {"background": rx.color("gray", 1), "borderColor": rx.color("gray", 4), "borderRadius": "8px"}
label_style: The style of default tooltip label which is a p element. DEFAULT: {} label_style: The style of default tooltip label which is a p element. Default: {"color": rx.color("gray", 11)}
allow_escape_view_box: This option allows the tooltip to extend beyond the viewBox of the chart itself. DEFAULT: { x: false, y: false } allow_escape_view_box: This option allows the tooltip to extend beyond the viewBox of the chart itself. Default: {"x": False, "y": False}
active: If set true, the tooltip is displayed. If set false, the tooltip is hidden, usually calculated internally. active: If set true, the tooltip is displayed. If set false, the tooltip is hidden, usually calculated internally. Default: False
position: If this field is set, the tooltip position will be fixed and will not move anymore. position: If this field is set, the tooltip position will be fixed and will not move anymore.
coordinate: The coordinate of tooltip which is usually calculated internally. coordinate: The coordinate of tooltip which is usually calculated internally. Default: {"x": 0, "y": 0}
is_animation_active: If set false, animation of tooltip will be disabled. DEFAULT: true in CSR, and false in SSR is_animation_active: If set false, animation of tooltip will be disabled. Default: True
animation_duration: Specifies the duration of animation, the unit of this option is ms. DEFAULT: 1500 animation_duration: Specifies the duration of animation, the unit of this option is ms. Default: 1500
animation_easing: The type of easing function. DEFAULT: 'ease' animation_easing: The type of easing function. Default: "ease"
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -6,7 +6,7 @@ from typing import Any, Dict, List, Union
from reflex.constants import EventTriggers from reflex.constants import EventTriggers
from reflex.constants.colors import Color from reflex.constants.colors import Color
from reflex.event import EventHandler, empty_event from reflex.event import EventHandler, no_args_event_spec
from reflex.vars.base import LiteralVar, Var from reflex.vars.base import LiteralVar, Var
from .recharts import ( from .recharts import (
@ -103,14 +103,14 @@ class Pie(Recharts):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_ANIMATION_START: empty_event, EventTriggers.ON_ANIMATION_START: no_args_event_spec,
EventTriggers.ON_ANIMATION_END: empty_event, EventTriggers.ON_ANIMATION_END: no_args_event_spec,
EventTriggers.ON_CLICK: empty_event, EventTriggers.ON_CLICK: no_args_event_spec,
EventTriggers.ON_MOUSE_MOVE: empty_event, EventTriggers.ON_MOUSE_MOVE: no_args_event_spec,
EventTriggers.ON_MOUSE_OVER: empty_event, EventTriggers.ON_MOUSE_OVER: no_args_event_spec,
EventTriggers.ON_MOUSE_OUT: empty_event, EventTriggers.ON_MOUSE_OUT: no_args_event_spec,
EventTriggers.ON_MOUSE_ENTER: empty_event, EventTriggers.ON_MOUSE_ENTER: no_args_event_spec,
EventTriggers.ON_MOUSE_LEAVE: empty_event, EventTriggers.ON_MOUSE_LEAVE: no_args_event_spec,
} }
@ -167,8 +167,8 @@ class Radar(Recharts):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_ANIMATION_START: empty_event, EventTriggers.ON_ANIMATION_START: no_args_event_spec,
EventTriggers.ON_ANIMATION_END: empty_event, EventTriggers.ON_ANIMATION_END: no_args_event_spec,
} }
@ -219,14 +219,14 @@ class RadialBar(Recharts):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_CLICK: empty_event, EventTriggers.ON_CLICK: no_args_event_spec,
EventTriggers.ON_MOUSE_MOVE: empty_event, EventTriggers.ON_MOUSE_MOVE: no_args_event_spec,
EventTriggers.ON_MOUSE_OVER: empty_event, EventTriggers.ON_MOUSE_OVER: no_args_event_spec,
EventTriggers.ON_MOUSE_OUT: empty_event, EventTriggers.ON_MOUSE_OUT: no_args_event_spec,
EventTriggers.ON_MOUSE_ENTER: empty_event, EventTriggers.ON_MOUSE_ENTER: no_args_event_spec,
EventTriggers.ON_MOUSE_LEAVE: empty_event, EventTriggers.ON_MOUSE_LEAVE: no_args_event_spec,
EventTriggers.ON_ANIMATION_START: empty_event, EventTriggers.ON_ANIMATION_START: no_args_event_spec,
EventTriggers.ON_ANIMATION_END: empty_event, EventTriggers.ON_ANIMATION_END: no_args_event_spec,
} }
@ -277,28 +277,28 @@ class PolarAngleAxis(Recharts):
_valid_children: List[str] = ["Label"] _valid_children: List[str] = ["Label"]
# The customized event handler of click on the ticks of this axis. # The customized event handler of click on the ticks of this axis.
on_click: EventHandler[empty_event] on_click: EventHandler[no_args_event_spec]
# The customized event handler of mousedown on the the ticks of this axis. # The customized event handler of mousedown on the the ticks of this axis.
on_mouse_down: EventHandler[empty_event] on_mouse_down: EventHandler[no_args_event_spec]
# The customized event handler of mouseup on the ticks of this axis. # The customized event handler of mouseup on the ticks of this axis.
on_mouse_up: EventHandler[empty_event] on_mouse_up: EventHandler[no_args_event_spec]
# The customized event handler of mousemove on the ticks of this axis. # The customized event handler of mousemove on the ticks of this axis.
on_mouse_move: EventHandler[empty_event] on_mouse_move: EventHandler[no_args_event_spec]
# The customized event handler of mouseover on the ticks of this axis. # The customized event handler of mouseover on the ticks of this axis.
on_mouse_over: EventHandler[empty_event] on_mouse_over: EventHandler[no_args_event_spec]
# The customized event handler of mouseout on the ticks of this axis. # The customized event handler of mouseout on the ticks of this axis.
on_mouse_out: EventHandler[empty_event] on_mouse_out: EventHandler[no_args_event_spec]
# The customized event handler of moustenter on the ticks of this axis. # The customized event handler of moustenter on the ticks of this axis.
on_mouse_enter: EventHandler[empty_event] on_mouse_enter: EventHandler[no_args_event_spec]
# The customized event handler of mouseleave on the ticks of this axis. # The customized event handler of mouseleave on the ticks of this axis.
on_mouse_leave: EventHandler[empty_event] on_mouse_leave: EventHandler[no_args_event_spec]
class PolarGrid(Recharts): class PolarGrid(Recharts):
@ -392,12 +392,12 @@ class PolarRadiusAxis(Recharts):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_CLICK: empty_event, EventTriggers.ON_CLICK: no_args_event_spec,
EventTriggers.ON_MOUSE_MOVE: empty_event, EventTriggers.ON_MOUSE_MOVE: no_args_event_spec,
EventTriggers.ON_MOUSE_OVER: empty_event, EventTriggers.ON_MOUSE_OVER: no_args_event_spec,
EventTriggers.ON_MOUSE_OUT: empty_event, EventTriggers.ON_MOUSE_OUT: no_args_event_spec,
EventTriggers.ON_MOUSE_ENTER: empty_event, EventTriggers.ON_MOUSE_ENTER: no_args_event_spec,
EventTriggers.ON_MOUSE_LEAVE: empty_event, EventTriggers.ON_MOUSE_LEAVE: no_args_event_spec,
} }

View File

@ -8,10 +8,7 @@ from reflex.base import Base
from reflex.components.component import Component, ComponentNamespace from reflex.components.component import Component, ComponentNamespace
from reflex.components.lucide.icon import Icon from reflex.components.lucide.icon import Icon
from reflex.components.props import NoExtrasAllowedProps, PropsBase from reflex.components.props import NoExtrasAllowedProps, PropsBase
from reflex.event import ( from reflex.event import EventSpec, run_script
EventSpec,
call_script,
)
from reflex.style import Style, resolved_color_mode from reflex.style import Style, resolved_color_mode
from reflex.utils import format from reflex.utils import format
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
@ -260,7 +257,7 @@ class Toaster(Component):
toast = f"{toast_command}(`{message}`)" toast = f"{toast_command}(`{message}`)"
toast_action = Var(_js_expr=toast) toast_action = Var(_js_expr=toast)
return call_script(toast_action) return run_script(toast_action)
@staticmethod @staticmethod
def toast_info(message: str = "", **kwargs): def toast_info(message: str = "", **kwargs):
@ -336,7 +333,7 @@ class Toaster(Component):
dismiss_action = Var( dismiss_action = Var(
_js_expr=dismiss, _var_data=VarData.merge(dismiss_var_data) _js_expr=dismiss, _var_data=VarData.merge(dismiss_var_data)
) )
return call_script(dismiss_action) return run_script(dismiss_action)
@classmethod @classmethod
def create(cls, *children, **props) -> Component: def create(cls, *children, **props) -> Component:

View File

@ -7,7 +7,7 @@ from typing import Dict, List, Literal, Optional, Tuple, Union
from reflex.base import Base from reflex.base import Base
from reflex.components.component import Component, NoSSRComponent from reflex.components.component import Component, NoSSRComponent
from reflex.event import EventHandler, empty_event, identity_event from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
from reflex.utils.format import to_camel_case from reflex.utils.format import to_camel_case
from reflex.utils.imports import ImportDict, ImportVar from reflex.utils.imports import ImportDict, ImportVar
from reflex.vars.base import Var from reflex.vars.base import Var
@ -207,31 +207,31 @@ class Editor(NoSSRComponent):
disable_toolbar: Var[bool] disable_toolbar: Var[bool]
# Fired when the editor content changes. # Fired when the editor content changes.
on_change: EventHandler[identity_event(str)] on_change: EventHandler[passthrough_event_spec(str)]
# Fired when the something is inputted in the editor. # Fired when the something is inputted in the editor.
on_input: EventHandler[empty_event] on_input: EventHandler[no_args_event_spec]
# Fired when the editor loses focus. # Fired when the editor loses focus.
on_blur: EventHandler[on_blur_spec] on_blur: EventHandler[on_blur_spec]
# Fired when the editor is loaded. # Fired when the editor is loaded.
on_load: EventHandler[identity_event(bool)] on_load: EventHandler[passthrough_event_spec(bool)]
# Fired when the editor content is copied. # Fired when the editor content is copied.
on_copy: EventHandler[empty_event] on_copy: EventHandler[no_args_event_spec]
# Fired when the editor content is cut. # Fired when the editor content is cut.
on_cut: EventHandler[empty_event] on_cut: EventHandler[no_args_event_spec]
# Fired when the editor content is pasted. # Fired when the editor content is pasted.
on_paste: EventHandler[on_paste_spec] on_paste: EventHandler[on_paste_spec]
# Fired when the code view is toggled. # Fired when the code view is toggled.
toggle_code_view: EventHandler[identity_event(bool)] toggle_code_view: EventHandler[passthrough_event_spec(bool)]
# Fired when the full screen mode is toggled. # Fired when the full screen mode is toggled.
toggle_full_screen: EventHandler[identity_event(bool)] toggle_full_screen: EventHandler[passthrough_event_spec(bool)]
def add_imports(self) -> ImportDict: def add_imports(self) -> ImportDict:
"""Add imports for the Editor component. """Add imports for the Editor component.

View File

@ -10,7 +10,17 @@ import os
import sys import sys
import urllib.parse import urllib.parse
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional, Set from typing import (
TYPE_CHECKING,
Any,
Dict,
Generic,
List,
Optional,
Set,
TypeVar,
get_args,
)
from typing_extensions import Annotated, get_type_hints from typing_extensions import Annotated, get_type_hints
@ -300,6 +310,141 @@ def interpret_env_var_value(
) )
T = TypeVar("T")
class EnvVar(Generic[T]):
"""Environment variable."""
name: str
default: Any
type_: T
def __init__(self, name: str, default: Any, type_: T) -> None:
"""Initialize the environment variable.
Args:
name: The environment variable name.
default: The default value.
type_: The type of the value.
"""
self.name = name
self.default = default
self.type_ = type_
def interpret(self, value: str) -> T:
"""Interpret the environment variable value.
Args:
value: The environment variable value.
Returns:
The interpreted value.
"""
return interpret_env_var_value(value, self.type_, self.name)
def getenv(self) -> Optional[T]:
"""Get the interpreted environment variable value.
Returns:
The environment variable value.
"""
env_value = os.getenv(self.name, None)
if env_value is not None:
return self.interpret(env_value)
return None
def is_set(self) -> bool:
"""Check if the environment variable is set.
Returns:
True if the environment variable is set.
"""
return self.name in os.environ
def get(self) -> T:
"""Get the interpreted environment variable value or the default value if not set.
Returns:
The interpreted value.
"""
env_value = self.getenv()
if env_value is not None:
return env_value
return self.default
def set(self, value: T | None) -> None:
"""Set the environment variable. None unsets the variable.
Args:
value: The value to set.
"""
if value is None:
_ = os.environ.pop(self.name, None)
else:
if isinstance(value, enum.Enum):
value = value.value
os.environ[self.name] = str(value)
class env_var: # type: ignore
"""Descriptor for environment variables."""
name: str
default: Any
internal: bool = False
def __init__(self, default: Any, internal: bool = False) -> None:
"""Initialize the descriptor.
Args:
default: The default value.
internal: Whether the environment variable is reflex internal.
"""
self.default = default
self.internal = internal
def __set_name__(self, owner, name):
"""Set the name of the descriptor.
Args:
owner: The owner class.
name: The name of the descriptor.
"""
self.name = name
def __get__(self, instance, owner):
"""Get the EnvVar instance.
Args:
instance: The instance.
owner: The owner class.
Returns:
The EnvVar instance.
"""
type_ = get_args(get_type_hints(owner)[self.name])[0]
env_name = self.name
if self.internal:
env_name = f"__{env_name}"
return EnvVar(name=env_name, default=self.default, type_=type_)
if TYPE_CHECKING:
def env_var(default, internal=False) -> EnvVar:
"""Typing helper for the env_var descriptor.
Args:
default: The default value.
internal: Whether the environment variable is reflex internal.
Returns:
The EnvVar instance.
"""
return default
class PathExistsFlag: class PathExistsFlag:
"""Flag to indicate that a path must exist.""" """Flag to indicate that a path must exist."""
@ -307,83 +452,98 @@ class PathExistsFlag:
ExistingPath = Annotated[Path, PathExistsFlag] ExistingPath = Annotated[Path, PathExistsFlag]
@dataclasses.dataclass(init=False)
class EnvironmentVariables: class EnvironmentVariables:
"""Environment variables class to instantiate environment variables.""" """Environment variables class to instantiate environment variables."""
# Whether to use npm over bun to install frontend packages. # Whether to use npm over bun to install frontend packages.
REFLEX_USE_NPM: bool = False REFLEX_USE_NPM: EnvVar[bool] = env_var(False)
# The npm registry to use. # The npm registry to use.
NPM_CONFIG_REGISTRY: Optional[str] = None NPM_CONFIG_REGISTRY: EnvVar[Optional[str]] = env_var(None)
# Whether to use Granian for the backend. Otherwise, use Uvicorn. # Whether to use Granian for the backend. Otherwise, use Uvicorn.
REFLEX_USE_GRANIAN: bool = False REFLEX_USE_GRANIAN: EnvVar[bool] = env_var(False)
# The username to use for authentication on python package repository. Username and password must both be provided. # The username to use for authentication on python package repository. Username and password must both be provided.
TWINE_USERNAME: Optional[str] = None TWINE_USERNAME: EnvVar[Optional[str]] = env_var(None)
# The password to use for authentication on python package repository. Username and password must both be provided. # The password to use for authentication on python package repository. Username and password must both be provided.
TWINE_PASSWORD: Optional[str] = None TWINE_PASSWORD: EnvVar[Optional[str]] = env_var(None)
# Whether to use the system installed bun. If set to false, bun will be bundled with the app. # Whether to use the system installed bun. If set to false, bun will be bundled with the app.
REFLEX_USE_SYSTEM_BUN: bool = False REFLEX_USE_SYSTEM_BUN: EnvVar[bool] = env_var(False)
# Whether to use the system installed node and npm. If set to false, node and npm will be bundled with the app. # Whether to use the system installed node and npm. If set to false, node and npm will be bundled with the app.
REFLEX_USE_SYSTEM_NODE: bool = False REFLEX_USE_SYSTEM_NODE: EnvVar[bool] = env_var(False)
# The working directory for the next.js commands. # The working directory for the next.js commands.
REFLEX_WEB_WORKDIR: Path = Path(constants.Dirs.WEB) REFLEX_WEB_WORKDIR: EnvVar[Path] = env_var(Path(constants.Dirs.WEB))
# Path to the alembic config file # Path to the alembic config file
ALEMBIC_CONFIG: ExistingPath = Path(constants.ALEMBIC_CONFIG) ALEMBIC_CONFIG: EnvVar[ExistingPath] = env_var(Path(constants.ALEMBIC_CONFIG))
# Disable SSL verification for HTTPX requests. # Disable SSL verification for HTTPX requests.
SSL_NO_VERIFY: bool = False SSL_NO_VERIFY: EnvVar[bool] = env_var(False)
# The directory to store uploaded files. # The directory to store uploaded files.
REFLEX_UPLOADED_FILES_DIR: Path = Path(constants.Dirs.UPLOADED_FILES) REFLEX_UPLOADED_FILES_DIR: EnvVar[Path] = env_var(
Path(constants.Dirs.UPLOADED_FILES)
)
# Whether to use seperate processes to compile the frontend and how many. If not set, defaults to thread executor. # Whether to use separate processes to compile the frontend and how many. If not set, defaults to thread executor.
REFLEX_COMPILE_PROCESSES: Optional[int] = None REFLEX_COMPILE_PROCESSES: EnvVar[Optional[int]] = env_var(None)
# Whether to use seperate threads to compile the frontend and how many. Defaults to `min(32, os.cpu_count() + 4)`. # Whether to use separate threads to compile the frontend and how many. Defaults to `min(32, os.cpu_count() + 4)`.
REFLEX_COMPILE_THREADS: Optional[int] = None REFLEX_COMPILE_THREADS: EnvVar[Optional[int]] = env_var(None)
# The directory to store reflex dependencies. # The directory to store reflex dependencies.
REFLEX_DIR: Path = Path(constants.Reflex.DIR) REFLEX_DIR: EnvVar[Path] = env_var(Path(constants.Reflex.DIR))
# Whether to print the SQL queries if the log level is INFO or lower. # Whether to print the SQL queries if the log level is INFO or lower.
SQLALCHEMY_ECHO: bool = False SQLALCHEMY_ECHO: EnvVar[bool] = env_var(False)
# Whether to ignore the redis config error. Some redis servers only allow out-of-band configuration. # Whether to ignore the redis config error. Some redis servers only allow out-of-band configuration.
REFLEX_IGNORE_REDIS_CONFIG_ERROR: bool = False REFLEX_IGNORE_REDIS_CONFIG_ERROR: EnvVar[bool] = env_var(False)
# Whether to skip purging the web directory in dev mode. # Whether to skip purging the web directory in dev mode.
REFLEX_PERSIST_WEB_DIR: bool = False REFLEX_PERSIST_WEB_DIR: EnvVar[bool] = env_var(False)
# The reflex.build frontend host. # The reflex.build frontend host.
REFLEX_BUILD_FRONTEND: str = constants.Templates.REFLEX_BUILD_FRONTEND REFLEX_BUILD_FRONTEND: EnvVar[str] = env_var(
constants.Templates.REFLEX_BUILD_FRONTEND
)
# The reflex.build backend host. # The reflex.build backend host.
REFLEX_BUILD_BACKEND: str = constants.Templates.REFLEX_BUILD_BACKEND REFLEX_BUILD_BACKEND: EnvVar[str] = env_var(
constants.Templates.REFLEX_BUILD_BACKEND
)
def __init__(self): # This env var stores the execution mode of the app
"""Initialize the environment variables.""" REFLEX_ENV_MODE: EnvVar[constants.Env] = env_var(constants.Env.DEV)
type_hints = get_type_hints(type(self))
for field in dataclasses.fields(self): # Whether to run the backend only. Exclusive with REFLEX_FRONTEND_ONLY.
raw_value = os.getenv(field.name, None) REFLEX_BACKEND_ONLY: EnvVar[bool] = env_var(False)
field.type = type_hints.get(field.name) or field.type # Whether to run the frontend only. Exclusive with REFLEX_BACKEND_ONLY.
REFLEX_FRONTEND_ONLY: EnvVar[bool] = env_var(False)
value = ( # Reflex internal env to reload the config.
interpret_env_var_value(raw_value, field.type, field.name) RELOAD_CONFIG: EnvVar[bool] = env_var(False, internal=True)
if raw_value is not None
else get_default_value_for_field(field)
)
setattr(self, field.name, value) # If this env var is set to "yes", App.compile will be a no-op
REFLEX_SKIP_COMPILE: EnvVar[bool] = env_var(False, internal=True)
# Whether to run app harness tests in headless mode.
APP_HARNESS_HEADLESS: EnvVar[bool] = env_var(False)
# Which app harness driver to use.
APP_HARNESS_DRIVER: EnvVar[str] = env_var("Chrome")
# Arguments to pass to the app harness driver.
APP_HARNESS_DRIVER_ARGS: EnvVar[str] = env_var("")
# Where to save screenshots when tests fail.
SCREENSHOT_DIR: EnvVar[Optional[Path]] = env_var(None)
environment = EnvironmentVariables() environment = EnvironmentVariables()

View File

@ -2,18 +2,13 @@
from .base import ( from .base import (
COOKIES, COOKIES,
ENV_BACKEND_ONLY_ENV_VAR,
ENV_FRONTEND_ONLY_ENV_VAR,
ENV_MODE_ENV_VAR,
IS_WINDOWS, IS_WINDOWS,
LOCAL_STORAGE, LOCAL_STORAGE,
POLLING_MAX_HTTP_BUFFER_SIZE, POLLING_MAX_HTTP_BUFFER_SIZE,
PYTEST_CURRENT_TEST, PYTEST_CURRENT_TEST,
REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_CLOSING_TAG,
REFLEX_VAR_OPENING_TAG, REFLEX_VAR_OPENING_TAG,
RELOAD_CONFIG,
SESSION_STORAGE, SESSION_STORAGE,
SKIP_COMPILE_ENV_VAR,
ColorMode, ColorMode,
Dirs, Dirs,
Env, Env,
@ -106,7 +101,6 @@ __ALL__ = [
POLLING_MAX_HTTP_BUFFER_SIZE, POLLING_MAX_HTTP_BUFFER_SIZE,
PYTEST_CURRENT_TEST, PYTEST_CURRENT_TEST,
Reflex, Reflex,
RELOAD_CONFIG,
RequirementsTxt, RequirementsTxt,
RouteArgType, RouteArgType,
RouteRegex, RouteRegex,
@ -116,7 +110,6 @@ __ALL__ = [
ROUTER_DATA_INCLUDE, ROUTER_DATA_INCLUDE,
ROUTE_NOT_FOUND, ROUTE_NOT_FOUND,
SETTER_PREFIX, SETTER_PREFIX,
SKIP_COMPILE_ENV_VAR,
SocketEvent, SocketEvent,
StateManagerMode, StateManagerMode,
Tailwind, Tailwind,

View File

@ -112,7 +112,7 @@ class Templates(SimpleNamespace):
from reflex.config import environment from reflex.config import environment
return ( return (
environment.REFLEX_BUILD_FRONTEND environment.REFLEX_BUILD_FRONTEND.get()
+ "/gen?reflex_init_token={reflex_init_token}" + "/gen?reflex_init_token={reflex_init_token}"
) )
@ -126,7 +126,7 @@ class Templates(SimpleNamespace):
""" """
from reflex.config import environment from reflex.config import environment
return environment.REFLEX_BUILD_BACKEND + "/api/init/{reflex_init_token}" return environment.REFLEX_BUILD_BACKEND.get() + "/api/init/{reflex_init_token}"
@classproperty @classproperty
@classmethod @classmethod
@ -139,7 +139,8 @@ class Templates(SimpleNamespace):
from reflex.config import environment from reflex.config import environment
return ( return (
environment.REFLEX_BUILD_BACKEND + "/api/gen/{generation_hash}/refactored" environment.REFLEX_BUILD_BACKEND.get()
+ "/api/gen/{generation_hash}/refactored"
) )
class Dirs(SimpleNamespace): class Dirs(SimpleNamespace):
@ -239,19 +240,9 @@ COOKIES = "cookies"
LOCAL_STORAGE = "local_storage" LOCAL_STORAGE = "local_storage"
SESSION_STORAGE = "session_storage" SESSION_STORAGE = "session_storage"
# If this env var is set to "yes", App.compile will be a no-op
SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE"
# This env var stores the execution mode of the app
ENV_MODE_ENV_VAR = "REFLEX_ENV_MODE"
ENV_BACKEND_ONLY_ENV_VAR = "REFLEX_BACKEND_ONLY"
ENV_FRONTEND_ONLY_ENV_VAR = "REFLEX_FRONTEND_ONLY"
# Testing variables. # Testing variables.
# Testing os env set by pytest when running a test case. # Testing os env set by pytest when running a test case.
PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST" PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"
RELOAD_CONFIG = "__REFLEX_RELOAD_CONFIG"
REFLEX_VAR_OPENING_TAG = "<reflex.Var>" REFLEX_VAR_OPENING_TAG = "<reflex.Var>"
REFLEX_VAR_CLOSING_TAG = "</reflex.Var>" REFLEX_VAR_CLOSING_TAG = "</reflex.Var>"

View File

@ -63,7 +63,7 @@ class Bun(SimpleNamespace):
""" """
from reflex.config import environment from reflex.config import environment
return environment.REFLEX_DIR / "bun" return environment.REFLEX_DIR.get() / "bun"
@classproperty @classproperty
@classmethod @classmethod
@ -100,7 +100,7 @@ class Fnm(SimpleNamespace):
""" """
from reflex.config import environment from reflex.config import environment
return environment.REFLEX_DIR / "fnm" return environment.REFLEX_DIR.get() / "fnm"
@classproperty @classproperty
@classmethod @classmethod
@ -177,7 +177,7 @@ class PackageJson(SimpleNamespace):
"@emotion/react": "11.13.3", "@emotion/react": "11.13.3",
"axios": "1.7.7", "axios": "1.7.7",
"json5": "2.2.3", "json5": "2.2.3",
"next": "15.0.1", "next": "14.2.16",
"next-sitemap": "4.2.3", "next-sitemap": "4.2.3",
"next-themes": "0.3.0", "next-themes": "0.3.0",
"react": "18.3.1", "react": "18.3.1",

View File

@ -609,14 +609,14 @@ def publish(
help="The API token to use for authentication on python package repository. If token is provided, no username/password should be provided at the same time", help="The API token to use for authentication on python package repository. If token is provided, no username/password should be provided at the same time",
), ),
username: Optional[str] = typer.Option( username: Optional[str] = typer.Option(
environment.TWINE_USERNAME, environment.TWINE_USERNAME.get(),
"-u", "-u",
"--username", "--username",
show_default="TWINE_USERNAME environment variable value if set", show_default="TWINE_USERNAME environment variable value if set",
help="The username to use for authentication on python package repository. Username and password must both be provided.", help="The username to use for authentication on python package repository. Username and password must both be provided.",
), ),
password: Optional[str] = typer.Option( password: Optional[str] = typer.Option(
environment.TWINE_PASSWORD, environment.TWINE_PASSWORD.get(),
"-p", "-p",
"--password", "--password",
show_default="TWINE_PASSWORD environment variable value if set", show_default="TWINE_PASSWORD environment variable value if set",

View File

@ -466,7 +466,7 @@ def key_event(e: Var[JavasciptKeyboardEvent]) -> Tuple[Var[str]]:
return (e.key,) return (e.key,)
def empty_event() -> Tuple[()]: def no_args_event_spec() -> Tuple[()]:
"""Empty event handler. """Empty event handler.
Returns: Returns:
@ -476,43 +476,14 @@ def empty_event() -> Tuple[()]:
# 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.
stop_propagation = EventChain(events=[], args_spec=empty_event).stop_propagation stop_propagation = EventChain(events=[], args_spec=no_args_event_spec).stop_propagation
prevent_default = EventChain(events=[], args_spec=empty_event).prevent_default prevent_default = EventChain(events=[], args_spec=no_args_event_spec).prevent_default
T = TypeVar("T") T = TypeVar("T")
U = TypeVar("U") U = TypeVar("U")
# def identity_event(event_type: Type[T]) -> Callable[[Var[T]], Tuple[Var[T]]]:
# """A helper function that returns the input event as output.
# Args:
# event_type: The type of the event.
# Returns:
# A function that returns the input event as output.
# """
# def inner(ev: Var[T]) -> Tuple[Var[T]]:
# return (ev,)
# inner.__signature__ = inspect.signature(inner).replace( # type: ignore
# parameters=[
# inspect.Parameter(
# "ev",
# kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
# annotation=Var[event_type],
# )
# ],
# return_annotation=Tuple[Var[event_type]],
# )
# inner.__annotations__["ev"] = Var[event_type]
# inner.__annotations__["return"] = Tuple[Var[event_type]]
# return inner
class IdentityEventReturn(Generic[T], Protocol): class IdentityEventReturn(Generic[T], Protocol):
"""Protocol for an identity event return.""" """Protocol for an identity event return."""
@ -529,20 +500,22 @@ class IdentityEventReturn(Generic[T], Protocol):
@overload @overload
def identity_event(event_type: Type[T], /) -> Callable[[Var[T]], Tuple[Var[T]]]: ... # type: ignore def passthrough_event_spec(
event_type: Type[T], /
) -> Callable[[Var[T]], Tuple[Var[T]]]: ... # type: ignore
@overload @overload
def identity_event( def passthrough_event_spec(
event_type_1: Type[T], event_type2: Type[U], / event_type_1: Type[T], event_type2: Type[U], /
) -> Callable[[Var[T], Var[U]], Tuple[Var[T], Var[U]]]: ... ) -> Callable[[Var[T], Var[U]], Tuple[Var[T], Var[U]]]: ...
@overload @overload
def identity_event(*event_types: Type[T]) -> IdentityEventReturn[T]: ... def passthrough_event_spec(*event_types: Type[T]) -> IdentityEventReturn[T]: ...
def identity_event(*event_types: Type[T]) -> IdentityEventReturn[T]: # type: ignore def passthrough_event_spec(*event_types: Type[T]) -> IdentityEventReturn[T]: # type: ignore
"""A helper function that returns the input event as output. """A helper function that returns the input event as output.
Args: Args:
@ -733,7 +706,16 @@ def console_log(message: str | Var[str]) -> EventSpec:
Returns: Returns:
An event to log the message. An event to log the message.
""" """
return server_side("_console", get_fn_signature(console_log), message=message) return run_script(Var("console").to(dict).log.to(FunctionVar).call(message))
def noop() -> EventSpec:
"""Do nothing.
Returns:
An event to do nothing.
"""
return run_script(Var.create(None))
def back() -> EventSpec: def back() -> EventSpec:
@ -742,7 +724,9 @@ def back() -> EventSpec:
Returns: Returns:
An event to go back one page. An event to go back one page.
""" """
return call_script("window.history.back()") return run_script(
Var("window").to(dict).history.to(dict).back.to(FunctionVar).call()
)
def window_alert(message: str | Var[str]) -> EventSpec: def window_alert(message: str | Var[str]) -> EventSpec:
@ -754,7 +738,7 @@ def window_alert(message: str | Var[str]) -> EventSpec:
Returns: Returns:
An event to alert the message. An event to alert the message.
""" """
return server_side("_alert", get_fn_signature(window_alert), message=message) return run_script(Var("window").to(dict).alert.to(FunctionVar).call(message))
def set_focus(ref: str) -> EventSpec: def set_focus(ref: str) -> EventSpec:
@ -785,12 +769,12 @@ def scroll_to(elem_id: str, align_to_top: bool | Var[bool] = True) -> EventSpec:
""" """
get_element_by_id = FunctionStringVar.create("document.getElementById") get_element_by_id = FunctionStringVar.create("document.getElementById")
return call_script( return run_script(
get_element_by_id(elem_id) get_element_by_id(elem_id)
.call(elem_id) .call(elem_id)
.to(ObjectVar) .to(ObjectVar)
.scrollIntoView.to(FunctionVar) .scrollIntoView.to(FunctionVar)
.call(align_to_top) .call(align_to_top),
) )
@ -897,10 +881,12 @@ def set_clipboard(content: str) -> EventSpec:
Returns: Returns:
EventSpec: An event to set some content in the clipboard. EventSpec: An event to set some content in the clipboard.
""" """
return server_side( return run_script(
"_set_clipboard", Var("navigator")
get_fn_signature(set_clipboard), .to(dict)
content=content, .clipboard.to(dict)
.writeText.to(FunctionVar)
.call(content)
) )
@ -987,13 +973,7 @@ def _callback_arg_spec(eval_result):
def call_script( def call_script(
javascript_code: str | Var[str], javascript_code: str | Var[str],
callback: ( callback: EventType | None = None,
EventSpec
| EventHandler
| Callable
| List[EventSpec | EventHandler | Callable]
| None
) = None,
) -> EventSpec: ) -> EventSpec:
"""Create an event handler that executes arbitrary javascript code. """Create an event handler that executes arbitrary javascript code.
@ -1007,12 +987,10 @@ def call_script(
callback_kwargs = {} callback_kwargs = {}
if callback is not None: if callback is not None:
callback_kwargs = { callback_kwargs = {
"callback": str( "callback": format.format_queue_events(
format.format_queue_events( callback,
callback, args_spec=lambda result: [result],
args_spec=lambda result: [result], )._js_expr,
),
),
} }
if isinstance(javascript_code, str): if isinstance(javascript_code, str):
# When there is VarData, include it and eval the JS code inline on the client. # When there is VarData, include it and eval the JS code inline on the client.
@ -1032,6 +1010,62 @@ def call_script(
) )
def call_function(
javascript_code: str | Var,
callback: EventType | None = None,
) -> EventSpec:
"""Create an event handler that executes arbitrary javascript code.
Args:
javascript_code: The code to execute.
callback: EventHandler that will receive the result of evaluating the javascript code.
Returns:
EventSpec: An event that will execute the client side javascript.
"""
callback_kwargs = {}
if callback is not None:
callback_kwargs = {
"callback": format.format_queue_events(
callback,
args_spec=lambda result: [result],
),
}
javascript_code = (
Var(javascript_code) if isinstance(javascript_code, str) else javascript_code
)
return server_side(
"_call_function",
get_fn_signature(call_function),
function=javascript_code,
**callback_kwargs,
)
def run_script(
javascript_code: str | Var,
callback: EventType | None = None,
) -> EventSpec:
"""Create an event handler that executes arbitrary javascript code.
Args:
javascript_code: The code to execute.
callback: EventHandler that will receive the result of evaluating the javascript code.
Returns:
EventSpec: An event that will execute the client side javascript.
"""
javascript_code = (
Var(javascript_code) if isinstance(javascript_code, str) else javascript_code
)
return call_function(
ArgsFunctionOperation.create(tuple(), javascript_code), callback
)
def get_event(state, event): def get_event(state, event):
"""Get the event from the given state. """Get the event from the given state.
@ -1822,13 +1856,14 @@ class EventNamespace(types.SimpleNamespace):
check_fn_match_arg_spec = staticmethod(check_fn_match_arg_spec) check_fn_match_arg_spec = staticmethod(check_fn_match_arg_spec)
resolve_annotation = staticmethod(resolve_annotation) resolve_annotation = staticmethod(resolve_annotation)
parse_args_spec = staticmethod(parse_args_spec) parse_args_spec = staticmethod(parse_args_spec)
identity_event = staticmethod(identity_event) passthrough_event_spec = staticmethod(passthrough_event_spec)
input_event = staticmethod(input_event) input_event = staticmethod(input_event)
key_event = staticmethod(key_event) key_event = staticmethod(key_event)
empty_event = staticmethod(empty_event) no_args_event_spec = staticmethod(no_args_event_spec)
server_side = staticmethod(server_side) server_side = staticmethod(server_side)
redirect = staticmethod(redirect) redirect = staticmethod(redirect)
console_log = staticmethod(console_log) console_log = staticmethod(console_log)
noop = staticmethod(noop)
back = staticmethod(back) back = staticmethod(back)
window_alert = staticmethod(window_alert) window_alert = staticmethod(window_alert)
set_focus = staticmethod(set_focus) set_focus = staticmethod(set_focus)
@ -1842,6 +1877,8 @@ class EventNamespace(types.SimpleNamespace):
set_clipboard = staticmethod(set_clipboard) set_clipboard = staticmethod(set_clipboard)
download = staticmethod(download) download = staticmethod(download)
call_script = staticmethod(call_script) call_script = staticmethod(call_script)
call_function = staticmethod(call_function)
run_script = staticmethod(run_script)
event = EventNamespace() event = EventNamespace()

View File

@ -8,7 +8,7 @@ import sys
from typing import Any, Callable, Union from typing import Any, Callable, Union
from reflex import constants from reflex import constants
from reflex.event import EventChain, EventHandler, EventSpec, call_script from reflex.event import EventChain, EventHandler, EventSpec, run_script
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
from reflex.vars import ( from reflex.vars import (
VarData, VarData,
@ -227,7 +227,7 @@ class ClientStateVar(Var):
""" """
if not self._global_ref: if not self._global_ref:
raise ValueError("ClientStateVar must be global to retrieve the value.") raise ValueError("ClientStateVar must be global to retrieve the value.")
return call_script(_client_state_ref(self._getter_name), callback=callback) return run_script(_client_state_ref(self._getter_name), callback=callback)
def push(self, value: Any) -> EventSpec: def push(self, value: Any) -> EventSpec:
"""Push a value to the client state variable from the backend. """Push a value to the client state variable from the backend.
@ -245,4 +245,4 @@ class ClientStateVar(Var):
""" """
if not self._global_ref: if not self._global_ref:
raise ValueError("ClientStateVar must be global to push the value.") raise ValueError("ClientStateVar must be global to push the value.")
return call_script(f"{_client_state_ref(self._setter_name)}({value})") return run_script(f"{_client_state_ref(self._setter_name)}({value})")

View File

@ -12,7 +12,7 @@ from reflex.components.radix.themes.components.icon_button import IconButton
from reflex.components.radix.themes.layout.box import Box from reflex.components.radix.themes.layout.box import Box
from reflex.components.radix.themes.layout.container import Container from reflex.components.radix.themes.layout.container import Container
from reflex.components.radix.themes.layout.stack import HStack from reflex.components.radix.themes.layout.stack import HStack
from reflex.event import call_script from reflex.event import run_script
from reflex.experimental import hooks from reflex.experimental import hooks
from reflex.state import ComponentState from reflex.state import ComponentState
from reflex.style import Style from reflex.style import Style
@ -173,7 +173,7 @@ class SidebarTrigger(Fragment):
else: else:
open, toggle = ( open, toggle = (
Var(_js_expr="open"), Var(_js_expr="open"),
call_script(Var(_js_expr="setOpen(!open)")), run_script("setOpen(!open)"),
) )
trigger_props["left"] = cond(open, f"calc({sidebar_width} - 32px)", "0") trigger_props["left"] = cond(open, f"calc({sidebar_width} - 32px)", "0")

View File

@ -0,0 +1 @@
"""This module will provide interfaces for the state."""

33
reflex/istate/proxy.py Normal file
View File

@ -0,0 +1,33 @@
"""A module to hold state proxy classes."""
from typing import Any
from reflex.state import StateProxy
class ReadOnlyStateProxy(StateProxy):
"""A read-only proxy for a state."""
def __setattr__(self, name: str, value: Any) -> None:
"""Prevent setting attributes on the state for read-only proxy.
Args:
name: The attribute name.
value: The attribute value.
Raises:
NotImplementedError: Always raised when trying to set an attribute on proxied state.
"""
if name.startswith("_self_"):
# Special case attributes of the proxy itself, not applied to the wrapped object.
super().__setattr__(name, value)
return
raise NotImplementedError("This is a read-only state proxy.")
def mark_dirty(self):
"""Mark the state as dirty.
Raises:
NotImplementedError: Always raised when trying to mark the proxied state as dirty.
"""
raise NotImplementedError("This is a read-only state proxy.")

31
reflex/istate/wrappers.py Normal file
View File

@ -0,0 +1,31 @@
"""Wrappers for the state manager."""
from typing import Any
from reflex.istate.proxy import ReadOnlyStateProxy
from reflex.state import (
_split_substate_key,
_substate_key,
get_state_manager,
)
async def get_state(token, state_cls: Any | None = None) -> ReadOnlyStateProxy:
"""Get the instance of a state for a token.
Args:
token: The token for the state.
state_cls: The class of the state.
Returns:
A read-only proxy of the state instance.
"""
mng = get_state_manager()
if state_cls is not None:
root_state = await mng.get_state(_substate_key(token, state_cls))
else:
root_state = await mng.get_state(token)
_, state_path = _split_substate_key(token)
state_cls = root_state.get_class_substate(tuple(state_path.split(".")))
instance = await root_state.get_state(state_cls)
return ReadOnlyStateProxy(instance)

View File

@ -38,12 +38,12 @@ def get_engine(url: str | None = None) -> sqlalchemy.engine.Engine:
url = url or conf.db_url url = url or conf.db_url
if url is None: if url is None:
raise ValueError("No database url configured") raise ValueError("No database url configured")
if not environment.ALEMBIC_CONFIG.exists(): if not environment.ALEMBIC_CONFIG.get().exists():
console.warn( console.warn(
"Database is not initialized, run [bold]reflex db init[/bold] first." "Database is not initialized, run [bold]reflex db init[/bold] first."
) )
# Print the SQL queries if the log level is INFO or lower. # Print the SQL queries if the log level is INFO or lower.
echo_db_query = environment.SQLALCHEMY_ECHO echo_db_query = environment.SQLALCHEMY_ECHO.get()
# Needed for the admin dash on sqlite. # Needed for the admin dash on sqlite.
connect_args = {"check_same_thread": False} if url.startswith("sqlite") else {} connect_args = {"check_same_thread": False} if url.startswith("sqlite") else {}
return sqlmodel.create_engine(url, echo=echo_db_query, connect_args=connect_args) return sqlmodel.create_engine(url, echo=echo_db_query, connect_args=connect_args)
@ -231,7 +231,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
Returns: Returns:
tuple of (config, script_directory) tuple of (config, script_directory)
""" """
config = alembic.config.Config(environment.ALEMBIC_CONFIG) config = alembic.config.Config(environment.ALEMBIC_CONFIG.get())
return config, alembic.script.ScriptDirectory( return config, alembic.script.ScriptDirectory(
config.get_main_option("script_location", default="version"), config.get_main_option("script_location", default="version"),
) )
@ -266,8 +266,8 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
def alembic_init(cls): def alembic_init(cls):
"""Initialize alembic for the project.""" """Initialize alembic for the project."""
alembic.command.init( alembic.command.init(
config=alembic.config.Config(environment.ALEMBIC_CONFIG), config=alembic.config.Config(environment.ALEMBIC_CONFIG.get()),
directory=str(environment.ALEMBIC_CONFIG.parent / "alembic"), directory=str(environment.ALEMBIC_CONFIG.get().parent / "alembic"),
) )
@classmethod @classmethod
@ -287,7 +287,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
Returns: Returns:
True when changes have been detected. True when changes have been detected.
""" """
if not environment.ALEMBIC_CONFIG.exists(): if not environment.ALEMBIC_CONFIG.get().exists():
return False return False
config, script_directory = cls._alembic_config() config, script_directory = cls._alembic_config()
@ -388,7 +388,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
True - indicating the process was successful. True - indicating the process was successful.
None - indicating the process was skipped. None - indicating the process was skipped.
""" """
if not environment.ALEMBIC_CONFIG.exists(): if not environment.ALEMBIC_CONFIG.get().exists():
return return
with cls.get_db_engine().connect() as connection: with cls.get_db_engine().connect() as connection:

View File

@ -160,7 +160,7 @@ def _run(
console.set_log_level(loglevel) console.set_log_level(loglevel)
# Set env mode in the environment # Set env mode in the environment
os.environ[constants.ENV_MODE_ENV_VAR] = env.value environment.REFLEX_ENV_MODE.set(env)
# Show system info # Show system info
exec.output_system_info() exec.output_system_info()
@ -277,13 +277,13 @@ def run(
False, False,
"--frontend-only", "--frontend-only",
help="Execute only frontend.", help="Execute only frontend.",
envvar=constants.ENV_FRONTEND_ONLY_ENV_VAR, envvar=environment.REFLEX_FRONTEND_ONLY.name,
), ),
backend: bool = typer.Option( backend: bool = typer.Option(
False, False,
"--backend-only", "--backend-only",
help="Execute only backend.", help="Execute only backend.",
envvar=constants.ENV_BACKEND_ONLY_ENV_VAR, envvar=environment.REFLEX_BACKEND_ONLY.name,
), ),
frontend_port: str = typer.Option( frontend_port: str = typer.Option(
config.frontend_port, help="Specify a different frontend port." config.frontend_port, help="Specify a different frontend port."
@ -302,8 +302,8 @@ def run(
if frontend and backend: if frontend and backend:
console.error("Cannot use both --frontend-only and --backend-only options.") console.error("Cannot use both --frontend-only and --backend-only options.")
raise typer.Exit(1) raise typer.Exit(1)
os.environ[constants.ENV_BACKEND_ONLY_ENV_VAR] = str(backend).lower() environment.REFLEX_BACKEND_ONLY.set(backend)
os.environ[constants.ENV_FRONTEND_ONLY_ENV_VAR] = str(frontend).lower() environment.REFLEX_FRONTEND_ONLY.set(frontend)
_run(env, frontend, backend, frontend_port, backend_port, backend_host, loglevel) _run(env, frontend, backend, frontend_port, backend_port, backend_host, loglevel)
@ -405,7 +405,7 @@ script_cli = typer.Typer()
def _skip_compile(): def _skip_compile():
"""Skip the compile step.""" """Skip the compile step."""
os.environ[constants.SKIP_COMPILE_ENV_VAR] = "yes" environment.REFLEX_SKIP_COMPILE.set(True)
@db_cli.command(name="init") @db_cli.command(name="init")
@ -420,7 +420,7 @@ def db_init():
return return
# Check the alembic config. # Check the alembic config.
if environment.ALEMBIC_CONFIG.exists(): if environment.ALEMBIC_CONFIG.get().exists():
console.error( console.error(
"Database is already initialized. Use " "Database is already initialized. Use "
"[bold]reflex db makemigrations[/bold] to create schema change " "[bold]reflex db makemigrations[/bold] to create schema change "

View File

@ -8,6 +8,7 @@ import copy
import dataclasses import dataclasses
import functools import functools
import inspect import inspect
import json
import pickle import pickle
import sys import sys
import uuid import uuid
@ -104,6 +105,8 @@ var = computed_var
# If the state is this large, it's considered a performance issue. # If the state is this large, it's considered a performance issue.
TOO_LARGE_SERIALIZED_STATE = 100 * 1024 # 100kb TOO_LARGE_SERIALIZED_STATE = 100 * 1024 # 100kb
# Only warn about each state class size once.
_WARNED_ABOUT_STATE_SIZE: Set[str] = set()
# Errors caught during pickling of state # Errors caught during pickling of state
HANDLED_PICKLE_ERRORS = ( HANDLED_PICKLE_ERRORS = (
@ -353,7 +356,6 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
def __init__( def __init__(
self, self,
*args,
parent_state: BaseState | None = None, parent_state: BaseState | None = None,
init_substates: bool = True, init_substates: bool = True,
_reflex_internal_init: bool = False, _reflex_internal_init: bool = False,
@ -364,11 +366,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
DO NOT INSTANTIATE STATE CLASSES DIRECTLY! Use StateManager.get_state() instead. DO NOT INSTANTIATE STATE CLASSES DIRECTLY! Use StateManager.get_state() instead.
Args: Args:
*args: The args to pass to the Pydantic init method.
parent_state: The parent state. parent_state: The parent state.
init_substates: Whether to initialize the substates in this instance. init_substates: Whether to initialize the substates in this instance.
_reflex_internal_init: A flag to indicate that the state is being initialized by the framework. _reflex_internal_init: A flag to indicate that the state is being initialized by the framework.
**kwargs: The kwargs to pass to the Pydantic init method. **kwargs: The kwargs to set as attributes on the state.
Raises: Raises:
ReflexRuntimeError: If the state is instantiated directly by end user. ReflexRuntimeError: If the state is instantiated directly by end user.
@ -381,7 +382,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
"See https://reflex.dev/docs/state/ for further information." "See https://reflex.dev/docs/state/ for further information."
) )
kwargs["parent_state"] = parent_state kwargs["parent_state"] = parent_state
super().__init__(*args, **kwargs) super().__init__()
for name, value in kwargs.items():
setattr(self, name, value)
# Setup the substates (for memory state manager only). # Setup the substates (for memory state manager only).
if init_substates: if init_substates:
@ -1285,16 +1288,19 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
fields = self.get_fields() fields = self.get_fields()
if name in fields and not _isinstance( if name in fields:
value, (field_type := fields[name].outer_type_) field = fields[name]
): field_type = field.outer_type_
console.deprecate( if field.allow_none:
"mismatched-type-assignment", field_type = Union[field_type, None]
f"Tried to assign value {value} of type {type(value)} to field {type(self).__name__}.{name} of type {field_type}." if not _isinstance(value, field_type):
" This might lead to unexpected behavior.", console.deprecate(
"0.6.5", "mismatched-type-assignment",
"0.7.0", f"Tried to assign value {value} of type {type(value)} to field {type(self).__name__}.{name} of type {field_type}."
) " This might lead to unexpected behavior.",
"0.6.5",
"0.7.0",
)
# Set the attribute. # Set the attribute.
super().__setattr__(name, value) super().__setattr__(name, value)
@ -2046,6 +2052,27 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
state["__dict__"].pop(inherited_var_name, None) state["__dict__"].pop(inherited_var_name, None)
return state return state
def _warn_if_too_large(
self,
pickle_state_size: int,
):
"""Print a warning when the state is too large.
Args:
pickle_state_size: The size of the pickled state.
"""
state_full_name = self.get_full_name()
if (
state_full_name not in _WARNED_ABOUT_STATE_SIZE
and pickle_state_size > TOO_LARGE_SERIALIZED_STATE
and self.substates
):
console.warn(
f"State {state_full_name} serializes to {pickle_state_size} bytes "
"which may present performance issues. Consider reducing the size of this state."
)
_WARNED_ABOUT_STATE_SIZE.add(state_full_name)
@classmethod @classmethod
@functools.lru_cache() @functools.lru_cache()
def _to_schema(cls) -> str: def _to_schema(cls) -> str:
@ -2084,7 +2111,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
The serialized state. The serialized state.
""" """
try: try:
return pickle.dumps((self._to_schema(), self)) pickle_state = pickle.dumps((self._to_schema(), self))
self._warn_if_too_large(len(pickle_state))
return pickle_state
except HANDLED_PICKLE_ERRORS as og_pickle_error: except HANDLED_PICKLE_ERRORS as og_pickle_error:
error = ( error = (
f"Failed to serialize state {self.get_full_name()} due to unpicklable object. " f"Failed to serialize state {self.get_full_name()} due to unpicklable object. "
@ -3075,9 +3104,6 @@ class StateManagerRedis(StateManager):
b"evicted", b"evicted",
} }
# Only warn about each state class size once.
_warned_about_state_size: ClassVar[Set[str]] = set()
async def _get_parent_state( async def _get_parent_state(
self, token: str, state: BaseState | None = None self, token: str, state: BaseState | None = None
) -> BaseState | None: ) -> BaseState | None:
@ -3221,29 +3247,6 @@ class StateManagerRedis(StateManager):
return state._get_root_state() return state._get_root_state()
return state return state
def _warn_if_too_large(
self,
state: BaseState,
pickle_state_size: int,
):
"""Print a warning when the state is too large.
Args:
state: The state to check.
pickle_state_size: The size of the pickled state.
"""
state_full_name = state.get_full_name()
if (
state_full_name not in self._warned_about_state_size
and pickle_state_size > TOO_LARGE_SERIALIZED_STATE
and state.substates
):
console.warn(
f"State {state_full_name} serializes to {pickle_state_size} bytes "
"which may present performance issues. Consider reducing the size of this state."
)
self._warned_about_state_size.add(state_full_name)
@override @override
async def set_state( async def set_state(
self, self,
@ -3294,7 +3297,6 @@ class StateManagerRedis(StateManager):
# Persist only the given state (parents or substates are excluded by BaseState.__getstate__). # Persist only the given state (parents or substates are excluded by BaseState.__getstate__).
if state._get_was_touched(): if state._get_was_touched():
pickle_state = state._serialize() pickle_state = state._serialize()
self._warn_if_too_large(state, len(pickle_state))
if pickle_state: if pickle_state:
await self.redis.set( await self.redis.set(
_substate_key(client_token, state), _substate_key(client_token, state),
@ -3375,7 +3377,7 @@ class StateManagerRedis(StateManager):
) )
except ResponseError: except ResponseError:
# Some redis servers only allow out-of-band configuration, so ignore errors here. # Some redis servers only allow out-of-band configuration, so ignore errors here.
if not environment.REFLEX_IGNORE_REDIS_CONFIG_ERROR: if not environment.REFLEX_IGNORE_REDIS_CONFIG_ERROR.get():
raise raise
async with self.redis.pubsub() as pubsub: async with self.redis.pubsub() as pubsub:
await pubsub.psubscribe(lock_key_channel) await pubsub.psubscribe(lock_key_channel)
@ -3715,6 +3717,29 @@ def serialize_mutable_proxy(mp: MutableProxy):
return mp.__wrapped__ return mp.__wrapped__
_orig_json_JSONEncoder_default = json.JSONEncoder.default
def _json_JSONEncoder_default_wrapper(self: json.JSONEncoder, o: Any) -> Any:
"""Wrap JSONEncoder.default to handle MutableProxy objects.
Args:
self: the JSONEncoder instance.
o: the object to serialize.
Returns:
A JSON-able object.
"""
try:
return o.__wrapped__
except AttributeError:
pass
return _orig_json_JSONEncoder_default(self, o)
json.JSONEncoder.default = _json_JSONEncoder_default_wrapper
class ImmutableMutableProxy(MutableProxy): class ImmutableMutableProxy(MutableProxy):
"""A proxy for a mutable object that tracks changes. """A proxy for a mutable object that tracks changes.

View File

@ -43,6 +43,7 @@ import reflex.utils.exec
import reflex.utils.format import reflex.utils.format
import reflex.utils.prerequisites import reflex.utils.prerequisites
import reflex.utils.processes import reflex.utils.processes
from reflex.config import environment
from reflex.state import ( from reflex.state import (
BaseState, BaseState,
StateManager, StateManager,
@ -250,6 +251,7 @@ class AppHarness:
def _initialize_app(self): def _initialize_app(self):
# disable telemetry reporting for tests # disable telemetry reporting for tests
os.environ["TELEMETRY_ENABLED"] = "false" os.environ["TELEMETRY_ENABLED"] = "false"
self.app_path.mkdir(parents=True, exist_ok=True) self.app_path.mkdir(parents=True, exist_ok=True)
if self.app_source is not None: if self.app_source is not None:
@ -615,10 +617,10 @@ class AppHarness:
if self.frontend_url is None: if self.frontend_url is None:
raise RuntimeError("Frontend is not running.") raise RuntimeError("Frontend is not running.")
want_headless = False want_headless = False
if os.environ.get("APP_HARNESS_HEADLESS"): if environment.APP_HARNESS_HEADLESS.get():
want_headless = True want_headless = True
if driver_clz is None: if driver_clz is None:
requested_driver = os.environ.get("APP_HARNESS_DRIVER", "Chrome") requested_driver = environment.APP_HARNESS_DRIVER.get()
driver_clz = getattr(webdriver, requested_driver) driver_clz = getattr(webdriver, requested_driver)
if driver_options is None: if driver_options is None:
driver_options = getattr(webdriver, f"{requested_driver}Options")() driver_options = getattr(webdriver, f"{requested_driver}Options")()
@ -640,7 +642,7 @@ class AppHarness:
driver_options.add_argument("headless") driver_options.add_argument("headless")
if driver_options is None: if driver_options is None:
raise RuntimeError(f"Could not determine options for {driver_clz}") raise RuntimeError(f"Could not determine options for {driver_clz}")
if args := os.environ.get("APP_HARNESS_DRIVER_ARGS"): if args := environment.APP_HARNESS_DRIVER_ARGS.get():
for arg in args.split(","): for arg in args.split(","):
driver_options.add_argument(arg) driver_options.add_argument(arg)
if driver_option_args is not None: if driver_option_args is not None:
@ -944,7 +946,7 @@ class AppHarnessProd(AppHarness):
def _start_backend(self): def _start_backend(self):
if self.app_instance is None: if self.app_instance is None:
raise RuntimeError("App was not initialized.") raise RuntimeError("App was not initialized.")
os.environ[reflex.constants.SKIP_COMPILE_ENV_VAR] = "yes" environment.REFLEX_SKIP_COMPILE.set(True)
self.backend = uvicorn.Server( self.backend = uvicorn.Server(
uvicorn.Config( uvicorn.Config(
app=self.app_instance, app=self.app_instance,
@ -961,7 +963,7 @@ class AppHarnessProd(AppHarness):
try: try:
return super()._poll_for_servers(timeout) return super()._poll_for_servers(timeout)
finally: finally:
os.environ.pop(reflex.constants.SKIP_COMPILE_ENV_VAR, None) environment.REFLEX_SKIP_COMPILE.set(None)
def stop(self): def stop(self):
"""Stop the frontend python webserver.""" """Stop the frontend python webserver."""

View File

@ -184,7 +184,7 @@ def should_use_granian():
Returns: Returns:
True if Granian should be used. True if Granian should be used.
""" """
return environment.REFLEX_USE_GRANIAN return environment.REFLEX_USE_GRANIAN.get()
def get_app_module(): def get_app_module():
@ -369,7 +369,9 @@ def run_uvicorn_backend_prod(host, port, loglevel):
command, command,
run=True, run=True,
show_logs=True, show_logs=True,
env={constants.SKIP_COMPILE_ENV_VAR: "yes"}, # skip compile for prod backend env={
environment.REFLEX_SKIP_COMPILE.name: "true"
}, # skip compile for prod backend
) )
@ -405,7 +407,7 @@ def run_granian_backend_prod(host, port, loglevel):
run=True, run=True,
show_logs=True, show_logs=True,
env={ env={
constants.SKIP_COMPILE_ENV_VAR: "yes" environment.REFLEX_SKIP_COMPILE.name: "true"
}, # skip compile for prod backend }, # skip compile for prod backend
) )
except ImportError: except ImportError:
@ -467,9 +469,11 @@ def output_system_info():
console.debug(f"{dep}") console.debug(f"{dep}")
console.debug( console.debug(
f"Using package installer at: {prerequisites.get_install_package_manager()}" # type: ignore f"Using package installer at: {prerequisites.get_install_package_manager(on_failure_return_none=True)}" # type: ignore
) )
console.debug(f"Using package executer at: {prerequisites.get_package_manager()}") # type: ignore console.debug(
f"Using package executer at: {prerequisites.get_package_manager(on_failure_return_none=True)}"
) # type: ignore
if system != "Windows": if system != "Windows":
console.debug(f"Unzip path: {path_ops.which('unzip')}") console.debug(f"Unzip path: {path_ops.which('unzip')}")
@ -489,11 +493,8 @@ def is_prod_mode() -> bool:
Returns: Returns:
True if the app is running in production mode or False if running in dev mode. True if the app is running in production mode or False if running in dev mode.
""" """
current_mode = os.environ.get( current_mode = environment.REFLEX_ENV_MODE.get()
constants.ENV_MODE_ENV_VAR, return current_mode == constants.Env.PROD
constants.Env.DEV.value,
)
return current_mode == constants.Env.PROD.value
def is_frontend_only() -> bool: def is_frontend_only() -> bool:
@ -502,7 +503,13 @@ def is_frontend_only() -> bool:
Returns: Returns:
True if the app is running in frontend-only mode. True if the app is running in frontend-only mode.
""" """
return os.environ.get(constants.ENV_FRONTEND_ONLY_ENV_VAR, "").lower() == "true" console.deprecate(
"is_frontend_only() is deprecated and will be removed in a future release.",
reason="Use `environment.REFLEX_FRONTEND_ONLY.get()` instead.",
deprecation_version="0.6.5",
removal_version="0.7.0",
)
return environment.REFLEX_FRONTEND_ONLY.get()
def is_backend_only() -> bool: def is_backend_only() -> bool:
@ -511,7 +518,13 @@ def is_backend_only() -> bool:
Returns: Returns:
True if the app is running in backend-only mode. True if the app is running in backend-only mode.
""" """
return os.environ.get(constants.ENV_BACKEND_ONLY_ENV_VAR, "").lower() == "true" console.deprecate(
"is_backend_only() is deprecated and will be removed in a future release.",
reason="Use `environment.REFLEX_BACKEND_ONLY.get()` instead.",
deprecation_version="0.6.5",
removal_version="0.7.0",
)
return environment.REFLEX_BACKEND_ONLY.get()
def should_skip_compile() -> bool: def should_skip_compile() -> bool:
@ -520,4 +533,10 @@ def should_skip_compile() -> bool:
Returns: Returns:
True if the app should skip compile. True if the app should skip compile.
""" """
return os.environ.get(constants.SKIP_COMPILE_ENV_VAR) == "yes" console.deprecate(
"should_skip_compile() is deprecated and will be removed in a future release.",
reason="Use `environment.REFLEX_SKIP_COMPILE.get()` instead.",
deprecation_version="0.6.5",
removal_version="0.7.0",
)
return environment.REFLEX_SKIP_COMPILE.get()

View File

@ -6,7 +6,7 @@ import inspect
import json import json
import os import os
import re import re
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Union from typing import TYPE_CHECKING, Any, List, Optional, Union
from reflex import constants from reflex import constants
from reflex.utils import exceptions from reflex.utils import exceptions
@ -14,7 +14,7 @@ from reflex.utils.console import deprecate
if TYPE_CHECKING: if TYPE_CHECKING:
from reflex.components.component import ComponentStyle from reflex.components.component import ComponentStyle
from reflex.event import ArgsSpec, EventChain, EventHandler, EventSpec from reflex.event import ArgsSpec, EventChain, EventHandler, EventSpec, EventType
WRAP_MAP = { WRAP_MAP = {
"{": "}", "{": "}",
@ -533,13 +533,7 @@ def format_event_chain(
def format_queue_events( def format_queue_events(
events: ( events: EventType | None = None,
EventSpec
| EventHandler
| Callable
| List[EventSpec | EventHandler | Callable]
| None
) = None,
args_spec: Optional[ArgsSpec] = None, args_spec: Optional[ArgsSpec] = None,
) -> Var[EventChain]: ) -> Var[EventChain]:
"""Format a list of event handler / event spec as a javascript callback. """Format a list of event handler / event spec as a javascript callback.

View File

@ -12,7 +12,7 @@ def _httpx_verify_kwarg() -> bool:
Returns: Returns:
True if SSL verification is enabled, False otherwise True if SSL verification is enabled, False otherwise
""" """
return not environment.SSL_NO_VERIFY return not environment.SSL_NO_VERIFY.get()
def get(url: str, **kwargs) -> httpx.Response: def get(url: str, **kwargs) -> httpx.Response:

View File

@ -136,7 +136,7 @@ def use_system_node() -> bool:
Returns: Returns:
Whether the system node should be used. Whether the system node should be used.
""" """
return environment.REFLEX_USE_SYSTEM_NODE return environment.REFLEX_USE_SYSTEM_NODE.get()
def use_system_bun() -> bool: def use_system_bun() -> bool:
@ -145,7 +145,7 @@ def use_system_bun() -> bool:
Returns: Returns:
Whether the system bun should be used. Whether the system bun should be used.
""" """
return environment.REFLEX_USE_SYSTEM_BUN return environment.REFLEX_USE_SYSTEM_BUN.get()
def get_node_bin_path() -> Path | None: def get_node_bin_path() -> Path | None:

View File

@ -69,7 +69,7 @@ def get_web_dir() -> Path:
Returns: Returns:
The working directory. The working directory.
""" """
return environment.REFLEX_WEB_WORKDIR return environment.REFLEX_WEB_WORKDIR.get()
def _python_version_check(): def _python_version_check():
@ -204,10 +204,13 @@ def get_bun_version() -> version.Version | None:
return None return None
def get_install_package_manager() -> str | None: def get_install_package_manager(on_failure_return_none: bool = False) -> str | None:
"""Get the package manager executable for installation. """Get the package manager executable for installation.
Currently, bun is used for installation only. Currently, bun is used for installation only.
Args:
on_failure_return_none: Whether to return None on failure.
Returns: Returns:
The path to the package manager. The path to the package manager.
""" """
@ -217,21 +220,29 @@ def get_install_package_manager() -> str | None:
or windows_check_onedrive_in_path() or windows_check_onedrive_in_path()
or windows_npm_escape_hatch() or windows_npm_escape_hatch()
): ):
return get_package_manager() return get_package_manager(on_failure_return_none)
return str(get_config().bun_path) return str(get_config().bun_path)
def get_package_manager() -> str | None: def get_package_manager(on_failure_return_none: bool = False) -> str | None:
"""Get the package manager executable for running app. """Get the package manager executable for running app.
Currently on unix systems, npm is used for running the app only. Currently on unix systems, npm is used for running the app only.
Args:
on_failure_return_none: Whether to return None on failure.
Returns: Returns:
The path to the package manager. The path to the package manager.
Raises:
FileNotFoundError: If the package manager is not found.
""" """
npm_path = path_ops.get_npm_path() npm_path = path_ops.get_npm_path()
if npm_path is not None: if npm_path is not None:
npm_path = str(Path(npm_path).resolve()) return str(Path(npm_path).resolve())
return npm_path if on_failure_return_none:
return None
raise FileNotFoundError("NPM not found. You may need to run `reflex init`.")
def windows_check_onedrive_in_path() -> bool: def windows_check_onedrive_in_path() -> bool:
@ -249,7 +260,7 @@ def windows_npm_escape_hatch() -> bool:
Returns: Returns:
If the user has set REFLEX_USE_NPM. If the user has set REFLEX_USE_NPM.
""" """
return environment.REFLEX_USE_NPM return environment.REFLEX_USE_NPM.get()
def get_app(reload: bool = False) -> ModuleType: def get_app(reload: bool = False) -> ModuleType:
@ -267,7 +278,7 @@ def get_app(reload: bool = False) -> ModuleType:
from reflex.utils import telemetry from reflex.utils import telemetry
try: try:
os.environ[constants.RELOAD_CONFIG] = str(reload) environment.RELOAD_CONFIG.set(reload)
config = get_config() config = get_config()
if not config.app_name: if not config.app_name:
raise RuntimeError( raise RuntimeError(
@ -430,7 +441,7 @@ def create_config(app_name: str):
def initialize_gitignore( def initialize_gitignore(
gitignore_file: Path = constants.GitIgnore.FILE, gitignore_file: Path = constants.GitIgnore.FILE,
files_to_ignore: set[str] = constants.GitIgnore.DEFAULTS, files_to_ignore: set[str] | list[str] = constants.GitIgnore.DEFAULTS,
): ):
"""Initialize the template .gitignore file. """Initialize the template .gitignore file.
@ -439,23 +450,20 @@ def initialize_gitignore(
files_to_ignore: The files to add to the .gitignore file. files_to_ignore: The files to add to the .gitignore file.
""" """
# Combine with the current ignored files. # Combine with the current ignored files.
current_ignore: set[str] = set() current_ignore: list[str] = []
if gitignore_file.exists(): if gitignore_file.exists():
current_ignore |= set( current_ignore = [ln.strip() for ln in gitignore_file.read_text().splitlines()]
line.strip() for line in gitignore_file.read_text().splitlines()
)
if files_to_ignore == current_ignore: if files_to_ignore == current_ignore:
console.debug(f"{gitignore_file} already up to date.") console.debug(f"{gitignore_file} already up to date.")
return return
files_to_ignore |= current_ignore files_to_ignore = [ln for ln in files_to_ignore if ln not in current_ignore]
files_to_ignore += current_ignore
# Write files to the .gitignore file. # Write files to the .gitignore file.
gitignore_file.touch(exist_ok=True) gitignore_file.touch(exist_ok=True)
console.debug(f"Creating {gitignore_file}") console.debug(f"Creating {gitignore_file}")
gitignore_file.write_text( gitignore_file.write_text("\n".join(files_to_ignore) + "\n")
"\n".join(sorted(files_to_ignore)) + "\n",
)
def initialize_requirements_txt(): def initialize_requirements_txt():
@ -920,20 +928,39 @@ def install_frontend_packages(packages: set[str], config: Config):
packages: A list of package names to be installed. packages: A list of package names to be installed.
config: The config object. config: The config object.
Raises:
FileNotFoundError: If the package manager is not found.
Example: Example:
>>> install_frontend_packages(["react", "react-dom"], get_config()) >>> install_frontend_packages(["react", "react-dom"], get_config())
""" """
# unsupported archs(arm and 32bit machines) will use npm anyway. so we dont have to run npm twice # unsupported archs(arm and 32bit machines) will use npm anyway. so we dont have to run npm twice
fallback_command = ( fallback_command = (
get_package_manager() get_package_manager(on_failure_return_none=True)
if not constants.IS_WINDOWS if (
or constants.IS_WINDOWS not constants.IS_WINDOWS
and is_windows_bun_supported() or constants.IS_WINDOWS
and not windows_check_onedrive_in_path() and is_windows_bun_supported()
and not windows_check_onedrive_in_path()
)
else None else None
) )
install_package_manager = (
get_install_package_manager(on_failure_return_none=True) or fallback_command
)
if install_package_manager is None:
raise FileNotFoundError(
"Could not find a package manager to install frontend packages. You may need to run `reflex init`."
)
fallback_command = (
fallback_command if fallback_command is not install_package_manager else None
)
processes.run_process_with_fallback( processes.run_process_with_fallback(
[get_install_package_manager(), "install"], # type: ignore [install_package_manager, "install"], # type: ignore
fallback=fallback_command, fallback=fallback_command,
analytics_enabled=True, analytics_enabled=True,
show_status_message="Installing base frontend packages", show_status_message="Installing base frontend packages",
@ -944,7 +971,7 @@ def install_frontend_packages(packages: set[str], config: Config):
if config.tailwind is not None: if config.tailwind is not None:
processes.run_process_with_fallback( processes.run_process_with_fallback(
[ [
get_install_package_manager(), install_package_manager,
"add", "add",
"-d", "-d",
constants.Tailwind.VERSION, constants.Tailwind.VERSION,
@ -960,7 +987,7 @@ def install_frontend_packages(packages: set[str], config: Config):
# Install custom packages defined in frontend_packages # Install custom packages defined in frontend_packages
if len(packages) > 0: if len(packages) > 0:
processes.run_process_with_fallback( processes.run_process_with_fallback(
[get_install_package_manager(), "add", *packages], [install_package_manager, "add", *packages],
fallback=fallback_command, fallback=fallback_command,
analytics_enabled=True, analytics_enabled=True,
show_status_message="Installing frontend packages from config and components", show_status_message="Installing frontend packages from config and components",
@ -992,7 +1019,7 @@ def needs_reinit(frontend: bool = True) -> bool:
return False return False
# Make sure the .reflex directory exists. # Make sure the .reflex directory exists.
if not environment.REFLEX_DIR.exists(): if not environment.REFLEX_DIR.get().exists():
return True return True
# Make sure the .web directory exists in frontend mode. # Make sure the .web directory exists in frontend mode.
@ -1097,7 +1124,7 @@ def ensure_reflex_installation_id() -> Optional[int]:
""" """
try: try:
initialize_reflex_user_directory() initialize_reflex_user_directory()
installation_id_file = environment.REFLEX_DIR / "installation_id" installation_id_file = environment.REFLEX_DIR.get() / "installation_id"
installation_id = None installation_id = None
if installation_id_file.exists(): if installation_id_file.exists():
@ -1122,7 +1149,7 @@ def ensure_reflex_installation_id() -> Optional[int]:
def initialize_reflex_user_directory(): def initialize_reflex_user_directory():
"""Initialize the reflex user directory.""" """Initialize the reflex user directory."""
# Create the reflex directory. # Create the reflex directory.
path_ops.mkdir(environment.REFLEX_DIR) path_ops.mkdir(environment.REFLEX_DIR.get())
def initialize_frontend_dependencies(): def initialize_frontend_dependencies():
@ -1145,7 +1172,10 @@ def check_db_initialized() -> bool:
Returns: Returns:
True if alembic is initialized (or if database is not used). True if alembic is initialized (or if database is not used).
""" """
if get_config().db_url is not None and not environment.ALEMBIC_CONFIG.exists(): if (
get_config().db_url is not None
and not environment.ALEMBIC_CONFIG.get().exists()
):
console.error( console.error(
"Database is not initialized. Run [bold]reflex db init[/bold] first." "Database is not initialized. Run [bold]reflex db init[/bold] first."
) )
@ -1155,7 +1185,7 @@ def check_db_initialized() -> bool:
def check_schema_up_to_date(): def check_schema_up_to_date():
"""Check if the sqlmodel metadata matches the current database schema.""" """Check if the sqlmodel metadata matches the current database schema."""
if get_config().db_url is None or not environment.ALEMBIC_CONFIG.exists(): if get_config().db_url is None or not environment.ALEMBIC_CONFIG.get().exists():
return return
with model.Model.get_db_engine().connect() as connection: with model.Model.get_db_engine().connect() as connection:
try: try:

View File

@ -55,4 +55,4 @@ def _get_npm_registry() -> str:
Returns: Returns:
str: str:
""" """
return environment.NPM_CONFIG_REGISTRY or get_best_registry() return environment.NPM_CONFIG_REGISTRY.get() or get_best_registry()

View File

@ -78,7 +78,7 @@ def serializer(
) )
# Apply type transformation if requested # Apply type transformation if requested
if to is not None: if to is not None or ((to := type_hints.get("return")) is not None):
SERIALIZER_TYPES[type_] = to SERIALIZER_TYPES[type_] = to
get_serializer_type.cache_clear() get_serializer_type.cache_clear()
@ -189,16 +189,37 @@ def get_serializer_type(type_: Type) -> Optional[Type]:
return None return None
def has_serializer(type_: Type) -> bool: def has_serializer(type_: Type, into_type: Type | None = None) -> bool:
"""Check if there is a serializer for the type. """Check if there is a serializer for the type.
Args: Args:
type_: The type to check. type_: The type to check.
into_type: The type to serialize into.
Returns: Returns:
Whether there is a serializer for the type. Whether there is a serializer for the type.
""" """
return get_serializer(type_) is not None serializer_for_type = get_serializer(type_)
return serializer_for_type is not None and (
into_type is None or get_serializer_type(type_) == into_type
)
def can_serialize(type_: Type, into_type: Type | None = None) -> bool:
"""Check if there is a serializer for the type.
Args:
type_: The type to check.
into_type: The type to serialize into.
Returns:
Whether there is a serializer for the type.
"""
return has_serializer(type_, into_type) or (
isinstance(type_, type)
and dataclasses.is_dataclass(type_)
and (into_type is None or into_type is dict)
)
@serializer(to=str) @serializer(to=str)
@ -214,7 +235,7 @@ def serialize_type(value: type) -> str:
return value.__name__ return value.__name__
@serializer @serializer(to=dict)
def serialize_base(value: Base) -> dict: def serialize_base(value: Base) -> dict:
"""Serialize a Base instance. """Serialize a Base instance.

View File

@ -8,6 +8,8 @@ import multiprocessing
import platform import platform
import warnings import warnings
from reflex.config import environment
try: try:
from datetime import UTC, datetime from datetime import UTC, datetime
except ImportError: except ImportError:
@ -20,7 +22,6 @@ import psutil
from reflex import constants from reflex import constants
from reflex.utils import console from reflex.utils import console
from reflex.utils.exec import should_skip_compile
from reflex.utils.prerequisites import ensure_reflex_installation_id, get_project_hash from reflex.utils.prerequisites import ensure_reflex_installation_id, get_project_hash
POSTHOG_API_URL: str = "https://app.posthog.com/capture/" POSTHOG_API_URL: str = "https://app.posthog.com/capture/"
@ -94,7 +95,7 @@ def _raise_on_missing_project_hash() -> bool:
False when compilation should be skipped (i.e. no .web directory is required). False when compilation should be skipped (i.e. no .web directory is required).
Otherwise return True. Otherwise return True.
""" """
return not should_skip_compile() return not environment.REFLEX_SKIP_COMPILE.get()
def _prepare_event(event: str, **kwargs) -> dict: def _prepare_event(event: str, **kwargs) -> dict:

View File

@ -570,6 +570,12 @@ def _isinstance(obj: Any, cls: GenericType, nested: bool = False) -> bool:
_isinstance(item, args[0]) for item in obj _isinstance(item, args[0]) for item in obj
) )
if args:
from reflex.vars import Field
if origin is Field:
return _isinstance(obj, args[0])
return isinstance(obj, get_base_class(cls)) return isinstance(obj, get_base_class(cls))

View File

@ -75,7 +75,6 @@ from reflex.utils.types import (
if TYPE_CHECKING: if TYPE_CHECKING:
from reflex.state import BaseState from reflex.state import BaseState
from .function import FunctionVar
from .number import ( from .number import (
BooleanVar, BooleanVar,
NumberVar, NumberVar,
@ -279,6 +278,24 @@ def _decode_var_immutable(value: str) -> tuple[VarData | None, str]:
return VarData.merge(*var_datas) if var_datas else None, value return VarData.merge(*var_datas) if var_datas else None, value
def can_use_in_object_var(cls: GenericType) -> bool:
"""Check if the class can be used in an ObjectVar.
Args:
cls: The class to check.
Returns:
Whether the class can be used in an ObjectVar.
"""
if types.is_union(cls):
return all(can_use_in_object_var(t) for t in types.get_args(cls))
return (
inspect.isclass(cls)
and not issubclass(cls, Var)
and serializers.can_serialize(cls, dict)
)
@dataclasses.dataclass( @dataclasses.dataclass(
eq=False, eq=False,
frozen=True, frozen=True,
@ -565,36 +582,33 @@ class Var(Generic[VAR_TYPE]):
# Encode the _var_data into the formatted output for tracking purposes. # Encode the _var_data into the formatted output for tracking purposes.
return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._js_expr}" return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._js_expr}"
@overload
def to(self, output: Type[StringVar]) -> StringVar: ...
@overload @overload
def to(self, output: Type[str]) -> StringVar: ... def to(self, output: Type[str]) -> StringVar: ...
@overload @overload
def to(self, output: Type[BooleanVar]) -> BooleanVar: ... def to(self, output: Type[bool]) -> BooleanVar: ...
@overload @overload
def to( def to(self, output: type[int] | type[float]) -> NumberVar: ...
self, output: Type[NumberVar], var_type: type[int] | type[float] = float
) -> NumberVar: ...
@overload @overload
def to( def to(
self, self,
output: Type[ArrayVar], output: type[list] | type[tuple] | type[set],
var_type: type[list] | type[tuple] | type[set] = list,
) -> ArrayVar: ... ) -> ArrayVar: ...
@overload @overload
def to( def to(
self, output: Type[ObjectVar], var_type: types.GenericType = dict self, output: Type[ObjectVar], var_type: Type[VAR_INSIDE]
) -> ObjectVar: ... ) -> ObjectVar[VAR_INSIDE]: ...
@overload @overload
def to( def to(
self, output: Type[FunctionVar], var_type: Type[Callable] = Callable self, output: Type[ObjectVar], var_type: None = None
) -> FunctionVar: ... ) -> ObjectVar[VAR_TYPE]: ...
@overload
def to(self, output: VAR_SUBCLASS, var_type: None = None) -> VAR_SUBCLASS: ...
@overload @overload
def to( def to(
@ -630,21 +644,19 @@ class Var(Generic[VAR_TYPE]):
return get_to_operation(NoneVar).create(self) # type: ignore return get_to_operation(NoneVar).create(self) # type: ignore
# Handle fixed_output_type being Base or a dataclass. # Handle fixed_output_type being Base or a dataclass.
try: if can_use_in_object_var(fixed_output_type):
if issubclass(fixed_output_type, Base):
return self.to(ObjectVar, output)
except TypeError:
pass
if dataclasses.is_dataclass(fixed_output_type) and not issubclass(
fixed_output_type, Var
):
return self.to(ObjectVar, output) return self.to(ObjectVar, output)
if inspect.isclass(output): if inspect.isclass(output):
for var_subclass in _var_subclasses[::-1]: for var_subclass in _var_subclasses[::-1]:
if issubclass(output, var_subclass.var_subclass): if issubclass(output, var_subclass.var_subclass):
current_var_type = self._var_type
if current_var_type is Any:
new_var_type = var_type
else:
new_var_type = var_type or current_var_type
to_operation_return = var_subclass.to_var_subclass.create( to_operation_return = var_subclass.to_var_subclass.create(
value=self, _var_type=var_type value=self, _var_type=new_var_type
) )
return to_operation_return # type: ignore return to_operation_return # type: ignore
@ -707,11 +719,7 @@ class Var(Generic[VAR_TYPE]):
): ):
return self.to(NumberVar, self._var_type) return self.to(NumberVar, self._var_type)
if all( if can_use_in_object_var(var_type):
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 return self
@ -730,13 +738,9 @@ class Var(Generic[VAR_TYPE]):
if issubclass(fixed_type, var_subclass.python_types): if issubclass(fixed_type, var_subclass.python_types):
return self.to(var_subclass.var_subclass, self._var_type) return self.to(var_subclass.var_subclass, self._var_type)
try: if can_use_in_object_var(fixed_type):
if issubclass(fixed_type, Base):
return self.to(ObjectVar, self._var_type)
except TypeError:
pass
if dataclasses.is_dataclass(fixed_type):
return self.to(ObjectVar, self._var_type) return self.to(ObjectVar, self._var_type)
return self return self
def get_default_value(self) -> Any: def get_default_value(self) -> Any:
@ -1181,6 +1185,9 @@ class Var(Generic[VAR_TYPE]):
OUTPUT = TypeVar("OUTPUT", bound=Var) OUTPUT = TypeVar("OUTPUT", bound=Var)
VAR_SUBCLASS = TypeVar("VAR_SUBCLASS", bound=Var)
VAR_INSIDE = TypeVar("VAR_INSIDE")
class ToOperation: class ToOperation:
"""A var operation that converts a var to another type.""" """A var operation that converts a var to another type."""
@ -2888,6 +2895,8 @@ def dispatch(
V = TypeVar("V") V = TypeVar("V")
BASE_TYPE = TypeVar("BASE_TYPE", bound=Base)
class Field(Generic[T]): class Field(Generic[T]):
"""Shadow class for Var to allow for type hinting in the IDE.""" """Shadow class for Var to allow for type hinting in the IDE."""
@ -2924,6 +2933,11 @@ class Field(Generic[T]):
self: Field[Dict[str, V]], instance: None, owner self: Field[Dict[str, V]], instance: None, owner
) -> ObjectVar[Dict[str, V]]: ... ) -> ObjectVar[Dict[str, V]]: ...
@overload
def __get__(
self: Field[BASE_TYPE], instance: None, owner
) -> ObjectVar[BASE_TYPE]: ...
@overload @overload
def __get__(self, instance: None, owner) -> Var[T]: ... def __get__(self, instance: None, owner) -> Var[T]: ...

View File

@ -1116,7 +1116,9 @@ U = TypeVar("U")
@var_operation @var_operation
def ternary_operation(condition: BooleanVar, if_true: Var[T], if_false: Var[U]): def ternary_operation(
condition: BooleanVar, if_true: Var[T], if_false: Var[U]
) -> CustomVarOperationReturn[Union[T, U]]:
"""Create a ternary operation. """Create a ternary operation.
Args: Args:

View File

@ -36,7 +36,7 @@ from .base import (
from .number import BooleanVar, NumberVar, raise_unsupported_operand_types from .number import BooleanVar, NumberVar, raise_unsupported_operand_types
from .sequence import ArrayVar, StringVar from .sequence import ArrayVar, StringVar
OBJECT_TYPE = TypeVar("OBJECT_TYPE", bound=Dict) OBJECT_TYPE = TypeVar("OBJECT_TYPE")
KEY_TYPE = TypeVar("KEY_TYPE") KEY_TYPE = TypeVar("KEY_TYPE")
VALUE_TYPE = TypeVar("VALUE_TYPE") VALUE_TYPE = TypeVar("VALUE_TYPE")
@ -59,7 +59,7 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict):
@overload @overload
def _value_type( def _value_type(
self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]], self: ObjectVar[Dict[Any, VALUE_TYPE]],
) -> Type[VALUE_TYPE]: ... ) -> Type[VALUE_TYPE]: ...
@overload @overload
@ -87,7 +87,7 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict):
@overload @overload
def values( def values(
self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]], self: ObjectVar[Dict[Any, VALUE_TYPE]],
) -> ArrayVar[List[VALUE_TYPE]]: ... ) -> ArrayVar[List[VALUE_TYPE]]: ...
@overload @overload
@ -103,7 +103,7 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict):
@overload @overload
def entries( def entries(
self: ObjectVar[Dict[KEY_TYPE, VALUE_TYPE]], self: ObjectVar[Dict[Any, VALUE_TYPE]],
) -> ArrayVar[List[Tuple[str, VALUE_TYPE]]]: ... ) -> ArrayVar[List[Tuple[str, VALUE_TYPE]]]: ...
@overload @overload
@ -133,47 +133,47 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict):
# NoReturn is used here to catch when key value is Any # NoReturn is used here to catch when key value is Any
@overload @overload
def __getitem__( def __getitem__(
self: ObjectVar[Dict[KEY_TYPE, NoReturn]], self: ObjectVar[Dict[Any, NoReturn]],
key: Var | Any, key: Var | Any,
) -> Var: ... ) -> Var: ...
@overload @overload
def __getitem__( def __getitem__(
self: ( self: (
ObjectVar[Dict[KEY_TYPE, int]] ObjectVar[Dict[Any, int]]
| ObjectVar[Dict[KEY_TYPE, float]] | ObjectVar[Dict[Any, float]]
| ObjectVar[Dict[KEY_TYPE, int | float]] | ObjectVar[Dict[Any, int | float]]
), ),
key: Var | Any, key: Var | Any,
) -> NumberVar: ... ) -> NumberVar: ...
@overload @overload
def __getitem__( def __getitem__(
self: ObjectVar[Dict[KEY_TYPE, str]], self: ObjectVar[Dict[Any, str]],
key: Var | Any, key: Var | Any,
) -> StringVar: ... ) -> StringVar: ...
@overload @overload
def __getitem__( def __getitem__(
self: ObjectVar[Dict[KEY_TYPE, list[ARRAY_INNER_TYPE]]], self: ObjectVar[Dict[Any, list[ARRAY_INNER_TYPE]]],
key: Var | Any, key: Var | Any,
) -> ArrayVar[list[ARRAY_INNER_TYPE]]: ... ) -> ArrayVar[list[ARRAY_INNER_TYPE]]: ...
@overload @overload
def __getitem__( def __getitem__(
self: ObjectVar[Dict[KEY_TYPE, set[ARRAY_INNER_TYPE]]], self: ObjectVar[Dict[Any, set[ARRAY_INNER_TYPE]]],
key: Var | Any, key: Var | Any,
) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ... ) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ...
@overload @overload
def __getitem__( def __getitem__(
self: ObjectVar[Dict[KEY_TYPE, tuple[ARRAY_INNER_TYPE, ...]]], self: ObjectVar[Dict[Any, tuple[ARRAY_INNER_TYPE, ...]]],
key: Var | Any, key: Var | Any,
) -> ArrayVar[tuple[ARRAY_INNER_TYPE, ...]]: ... ) -> ArrayVar[tuple[ARRAY_INNER_TYPE, ...]]: ...
@overload @overload
def __getitem__( def __getitem__(
self: ObjectVar[Dict[KEY_TYPE, dict[OTHER_KEY_TYPE, VALUE_TYPE]]], self: ObjectVar[Dict[Any, dict[OTHER_KEY_TYPE, VALUE_TYPE]]],
key: Var | Any, key: Var | Any,
) -> ObjectVar[dict[OTHER_KEY_TYPE, VALUE_TYPE]]: ... ) -> ObjectVar[dict[OTHER_KEY_TYPE, VALUE_TYPE]]: ...
@ -195,50 +195,56 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict):
# NoReturn is used here to catch when key value is Any # NoReturn is used here to catch when key value is Any
@overload @overload
def __getattr__( def __getattr__(
self: ObjectVar[Dict[KEY_TYPE, NoReturn]], self: ObjectVar[Dict[Any, NoReturn]],
name: str, name: str,
) -> Var: ... ) -> Var: ...
@overload @overload
def __getattr__( def __getattr__(
self: ( self: (
ObjectVar[Dict[KEY_TYPE, int]] ObjectVar[Dict[Any, int]]
| ObjectVar[Dict[KEY_TYPE, float]] | ObjectVar[Dict[Any, float]]
| ObjectVar[Dict[KEY_TYPE, int | float]] | ObjectVar[Dict[Any, int | float]]
), ),
name: str, name: str,
) -> NumberVar: ... ) -> NumberVar: ...
@overload @overload
def __getattr__( def __getattr__(
self: ObjectVar[Dict[KEY_TYPE, str]], self: ObjectVar[Dict[Any, str]],
name: str, name: str,
) -> StringVar: ... ) -> StringVar: ...
@overload @overload
def __getattr__( def __getattr__(
self: ObjectVar[Dict[KEY_TYPE, list[ARRAY_INNER_TYPE]]], self: ObjectVar[Dict[Any, list[ARRAY_INNER_TYPE]]],
name: str, name: str,
) -> ArrayVar[list[ARRAY_INNER_TYPE]]: ... ) -> ArrayVar[list[ARRAY_INNER_TYPE]]: ...
@overload @overload
def __getattr__( def __getattr__(
self: ObjectVar[Dict[KEY_TYPE, set[ARRAY_INNER_TYPE]]], self: ObjectVar[Dict[Any, set[ARRAY_INNER_TYPE]]],
name: str, name: str,
) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ... ) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ...
@overload @overload
def __getattr__( def __getattr__(
self: ObjectVar[Dict[KEY_TYPE, tuple[ARRAY_INNER_TYPE, ...]]], self: ObjectVar[Dict[Any, tuple[ARRAY_INNER_TYPE, ...]]],
name: str, name: str,
) -> ArrayVar[tuple[ARRAY_INNER_TYPE, ...]]: ... ) -> ArrayVar[tuple[ARRAY_INNER_TYPE, ...]]: ...
@overload @overload
def __getattr__( def __getattr__(
self: ObjectVar[Dict[KEY_TYPE, dict[OTHER_KEY_TYPE, VALUE_TYPE]]], self: ObjectVar[Dict[Any, dict[OTHER_KEY_TYPE, VALUE_TYPE]]],
name: str, name: str,
) -> ObjectVar[dict[OTHER_KEY_TYPE, VALUE_TYPE]]: ... ) -> ObjectVar[dict[OTHER_KEY_TYPE, VALUE_TYPE]]: ...
@overload
def __getattr__(
self: ObjectVar,
name: str,
) -> ObjectItemOperation: ...
def __getattr__(self, name) -> Var: def __getattr__(self, name) -> Var:
"""Get an attribute of the var. """Get an attribute of the var.
@ -377,8 +383,8 @@ class LiteralObjectVar(CachedVarOperation, ObjectVar[OBJECT_TYPE], LiteralVar):
@classmethod @classmethod
def create( def create(
cls, cls,
_var_value: OBJECT_TYPE, _var_value: dict,
_var_type: GenericType | None = None, _var_type: Type[OBJECT_TYPE] | None = None,
_var_data: VarData | None = None, _var_data: VarData | None = None,
) -> LiteralObjectVar[OBJECT_TYPE]: ) -> LiteralObjectVar[OBJECT_TYPE]:
"""Create the literal object var. """Create the literal object var.

View File

@ -853,31 +853,31 @@ class ArrayVar(Var[ARRAY_VAR_TYPE], python_types=(list, tuple, set)):
@overload @overload
def __getitem__( def __getitem__(
self: ( self: (
ArrayVar[Tuple[OTHER_TUPLE, int]] ArrayVar[Tuple[Any, int]]
| ArrayVar[Tuple[OTHER_TUPLE, float]] | ArrayVar[Tuple[Any, float]]
| ArrayVar[Tuple[OTHER_TUPLE, int | float]] | ArrayVar[Tuple[Any, int | float]]
), ),
i: Literal[1, -1], i: Literal[1, -1],
) -> NumberVar: ... ) -> NumberVar: ...
@overload @overload
def __getitem__( def __getitem__(
self: ArrayVar[Tuple[str, OTHER_TUPLE]], i: Literal[0, -2] self: ArrayVar[Tuple[str, Any]], i: Literal[0, -2]
) -> StringVar: ... ) -> StringVar: ...
@overload @overload
def __getitem__( def __getitem__(
self: ArrayVar[Tuple[OTHER_TUPLE, str]], i: Literal[1, -1] self: ArrayVar[Tuple[Any, str]], i: Literal[1, -1]
) -> StringVar: ... ) -> StringVar: ...
@overload @overload
def __getitem__( def __getitem__(
self: ArrayVar[Tuple[bool, OTHER_TUPLE]], i: Literal[0, -2] self: ArrayVar[Tuple[bool, Any]], i: Literal[0, -2]
) -> BooleanVar: ... ) -> BooleanVar: ...
@overload @overload
def __getitem__( def __getitem__(
self: ArrayVar[Tuple[OTHER_TUPLE, bool]], i: Literal[1, -1] self: ArrayVar[Tuple[Any, bool]], i: Literal[1, -1]
) -> BooleanVar: ... ) -> BooleanVar: ...
@overload @overload

View File

@ -6,6 +6,7 @@ from pathlib import Path
import pytest import pytest
from reflex.config import environment
from reflex.testing import AppHarness, AppHarnessProd from reflex.testing import AppHarness, AppHarnessProd
DISPLAY = None DISPLAY = None
@ -21,7 +22,7 @@ def xvfb():
Yields: Yields:
the pyvirtualdisplay object that the browser will be open on the pyvirtualdisplay object that the browser will be open on
""" """
if os.environ.get("GITHUB_ACTIONS") and not os.environ.get("APP_HARNESS_HEADLESS"): if os.environ.get("GITHUB_ACTIONS") and not environment.APP_HARNESS_HEADLESS.get():
from pyvirtualdisplay.smartdisplay import ( # pyright: ignore [reportMissingImports] from pyvirtualdisplay.smartdisplay import ( # pyright: ignore [reportMissingImports]
SmartDisplay, SmartDisplay,
) )
@ -42,7 +43,7 @@ def pytest_exception_interact(node, call, report):
call: The pytest call describing when/where the test was invoked. call: The pytest call describing when/where the test was invoked.
report: The pytest log report object. report: The pytest log report object.
""" """
screenshot_dir = os.environ.get("SCREENSHOT_DIR") screenshot_dir = environment.SCREENSHOT_DIR.get()
if DISPLAY is None or screenshot_dir is None: if DISPLAY is None or screenshot_dir is None:
return return

View File

@ -4,6 +4,7 @@ from __future__ import annotations
import asyncio import asyncio
import time import time
from pathlib import Path
from typing import Generator from typing import Generator
import pytest import pytest
@ -205,11 +206,12 @@ async def test_upload_file(
file_data = await AppHarness._poll_for_async(get_file_data) file_data = await AppHarness._poll_for_async(get_file_data)
assert isinstance(file_data, dict) assert isinstance(file_data, dict)
assert file_data[exp_name] == exp_contents normalized_file_data = {Path(k).name: v for k, v in file_data.items()}
assert normalized_file_data[Path(exp_name).name] == exp_contents
# check that the selected files are displayed # check that the selected files are displayed
selected_files = driver.find_element(By.ID, f"selected_files{suffix}") selected_files = driver.find_element(By.ID, f"selected_files{suffix}")
assert selected_files.text == exp_name assert Path(selected_files.text).name == Path(exp_name).name
state = await upload_file.get_state(substate_token) state = await upload_file.get_state(substate_token)
if secondary: if secondary:
@ -256,7 +258,9 @@ async def test_upload_file_multiple(tmp_path, upload_file: AppHarness, driver):
# check that the selected files are displayed # check that the selected files are displayed
selected_files = driver.find_element(By.ID, "selected_files") selected_files = driver.find_element(By.ID, "selected_files")
assert selected_files.text == "\n".join(exp_files) assert [Path(name).name for name in selected_files.text.split("\n")] == [
Path(name).name for name in exp_files
]
# do the upload # do the upload
upload_button.click() upload_button.click()
@ -271,8 +275,9 @@ async def test_upload_file_multiple(tmp_path, upload_file: AppHarness, driver):
file_data = await AppHarness._poll_for_async(get_file_data) file_data = await AppHarness._poll_for_async(get_file_data)
assert isinstance(file_data, dict) assert isinstance(file_data, dict)
normalized_file_data = {Path(k).name: v for k, v in file_data.items()}
for exp_name, exp_contents in exp_files.items(): for exp_name, exp_contents in exp_files.items():
assert file_data[exp_name] == exp_contents assert normalized_file_data[Path(exp_name).name] == exp_contents
@pytest.mark.parametrize("secondary", [False, True]) @pytest.mark.parametrize("secondary", [False, True])
@ -317,7 +322,9 @@ def test_clear_files(
# check that the selected files are displayed # check that the selected files are displayed
selected_files = driver.find_element(By.ID, f"selected_files{suffix}") selected_files = driver.find_element(By.ID, f"selected_files{suffix}")
assert selected_files.text == "\n".join(exp_files) assert [Path(name).name for name in selected_files.text.split("\n")] == [
Path(name).name for name in exp_files
]
clear_button = driver.find_element(By.ID, f"clear_button{suffix}") clear_button = driver.find_element(By.ID, f"clear_button{suffix}")
assert clear_button assert clear_button
@ -369,6 +376,9 @@ async def test_cancel_upload(tmp_path, upload_file: AppHarness, driver: WebDrive
# look up the backend state and assert on progress # look up the backend state and assert on progress
state = await upload_file.get_state(substate_token) state = await upload_file.get_state(substate_token)
assert state.substates[state_name].progress_dicts assert state.substates[state_name].progress_dicts
assert exp_name not in state.substates[state_name]._file_data file_data = state.substates[state_name]._file_data
assert isinstance(file_data, dict)
normalized_file_data = {Path(k).name: v for k, v in file_data.items()}
assert Path(exp_name).name not in normalized_file_data
target_file.unlink() target_file.unlink()

View File

@ -19,10 +19,10 @@ from reflex.constants import EventTriggers
from reflex.event import ( from reflex.event import (
EventChain, EventChain,
EventHandler, EventHandler,
empty_event,
identity_event,
input_event, input_event,
no_args_event_spec,
parse_args_spec, parse_args_spec,
passthrough_event_spec,
) )
from reflex.state import BaseState from reflex.state import BaseState
from reflex.style import Style from reflex.style import Style
@ -111,10 +111,10 @@ def component2() -> Type[Component]:
""" """
return { return {
**super().get_event_triggers(), **super().get_event_triggers(),
"on_open": identity_event(bool), "on_open": passthrough_event_spec(bool),
"on_close": identity_event(bool), "on_close": passthrough_event_spec(bool),
"on_user_visited_count_changed": identity_event(int), "on_user_visited_count_changed": passthrough_event_spec(int),
"on_user_list_changed": identity_event(List[str]), "on_user_list_changed": passthrough_event_spec(List[str]),
} }
def _get_imports(self) -> ParsedImportDict: def _get_imports(self) -> ParsedImportDict:
@ -1821,8 +1821,8 @@ def test_custom_component_declare_event_handlers_in_fields():
class TestComponent(Component): class TestComponent(Component):
on_a: EventHandler[lambda e0: [e0]] on_a: EventHandler[lambda e0: [e0]]
on_b: EventHandler[input_event] on_b: EventHandler[input_event]
on_c: EventHandler[empty_event] on_c: EventHandler[no_args_event_spec]
on_d: EventHandler[empty_event] on_d: EventHandler[no_args_event_spec]
on_e: EventHandler on_e: EventHandler
on_f: EventHandler[lambda a, b, c: [c, b, a]] on_f: EventHandler[lambda a, b, c: [c, b, a]]

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from typing import Any from typing import Any
from reflex.components.component import Component from reflex.components.component import Component
from reflex.event import EventHandler, empty_event, input_event from reflex.event import EventHandler, input_event, no_args_event_spec
# This is a repeat of its namesake in test_component.py. # This is a repeat of its namesake in test_component.py.
@ -26,8 +26,8 @@ def test_custom_component_declare_event_handlers_in_fields():
class TestComponent(Component): class TestComponent(Component):
on_a: EventHandler[lambda e0: [e0]] on_a: EventHandler[lambda e0: [e0]]
on_b: EventHandler[input_event] on_b: EventHandler[input_event]
on_c: EventHandler[empty_event] on_c: EventHandler[no_args_event_spec]
on_d: EventHandler[empty_event] on_d: EventHandler[no_args_event_spec]
custom_component = ReferenceComponent.create() custom_component = ReferenceComponent.create()
test_component = TestComponent.create() test_component = TestComponent.create()

View File

@ -8,6 +8,8 @@ import pytest
import reflex as rx import reflex as rx
import reflex.config import reflex.config
from reflex.config import ( from reflex.config import (
EnvVar,
env_var,
environment, environment,
interpret_boolean_env, interpret_boolean_env,
interpret_enum_env, interpret_enum_env,
@ -214,7 +216,7 @@ def test_replace_defaults(
def reflex_dir_constant() -> Path: def reflex_dir_constant() -> Path:
return environment.REFLEX_DIR return environment.REFLEX_DIR.get()
def test_reflex_dir_env_var(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: def test_reflex_dir_env_var(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
@ -227,6 +229,7 @@ def test_reflex_dir_env_var(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) ->
monkeypatch.setenv("REFLEX_DIR", str(tmp_path)) monkeypatch.setenv("REFLEX_DIR", str(tmp_path))
mp_ctx = multiprocessing.get_context(method="spawn") mp_ctx = multiprocessing.get_context(method="spawn")
assert reflex_dir_constant() == tmp_path
with mp_ctx.Pool(processes=1) as pool: with mp_ctx.Pool(processes=1) as pool:
assert pool.apply(reflex_dir_constant) == tmp_path assert pool.apply(reflex_dir_constant) == tmp_path
@ -242,3 +245,38 @@ def test_interpret_int_env() -> None:
@pytest.mark.parametrize("value, expected", [("true", True), ("false", False)]) @pytest.mark.parametrize("value, expected", [("true", True), ("false", False)])
def test_interpret_bool_env(value: str, expected: bool) -> None: def test_interpret_bool_env(value: str, expected: bool) -> None:
assert interpret_boolean_env(value, "TELEMETRY_ENABLED") == expected assert interpret_boolean_env(value, "TELEMETRY_ENABLED") == expected
def test_env_var():
class TestEnv:
BLUBB: EnvVar[str] = env_var("default")
INTERNAL: EnvVar[str] = env_var("default", internal=True)
BOOLEAN: EnvVar[bool] = env_var(False)
assert TestEnv.BLUBB.get() == "default"
assert TestEnv.BLUBB.name == "BLUBB"
TestEnv.BLUBB.set("new")
assert os.environ.get("BLUBB") == "new"
assert TestEnv.BLUBB.get() == "new"
TestEnv.BLUBB.set(None)
assert "BLUBB" not in os.environ
assert TestEnv.INTERNAL.get() == "default"
assert TestEnv.INTERNAL.name == "__INTERNAL"
TestEnv.INTERNAL.set("new")
assert os.environ.get("__INTERNAL") == "new"
assert TestEnv.INTERNAL.get() == "new"
assert TestEnv.INTERNAL.getenv() == "new"
TestEnv.INTERNAL.set(None)
assert "__INTERNAL" not in os.environ
assert TestEnv.BOOLEAN.get() is False
assert TestEnv.BOOLEAN.name == "BOOLEAN"
TestEnv.BOOLEAN.set(True)
assert os.environ.get("BOOLEAN") == "True"
assert TestEnv.BOOLEAN.get() is True
TestEnv.BOOLEAN.set(False)
assert os.environ.get("BOOLEAN") == "False"
assert TestEnv.BOOLEAN.get() is False
TestEnv.BOOLEAN.set(None)
assert "BOOLEAN" not in os.environ

View File

@ -1,4 +1,4 @@
from typing import List from typing import Callable, List
import pytest import pytest
@ -216,24 +216,40 @@ def test_event_console_log():
"""Test the event console log function.""" """Test the event console log function."""
spec = event.console_log("message") spec = event.console_log("message")
assert isinstance(spec, EventSpec) assert isinstance(spec, EventSpec)
assert spec.handler.fn.__qualname__ == "_console" assert spec.handler.fn.__qualname__ == "_call_function"
assert spec.args[0][0].equals(Var(_js_expr="message")) assert spec.args[0][0].equals(Var(_js_expr="function"))
assert spec.args[0][1].equals(LiteralVar.create("message")) assert spec.args[0][1].equals(
assert format.format_event(spec) == 'Event("_console", {message:"message"})' Var('(() => ((console["log"]("message"))))', _var_type=Callable)
)
assert (
format.format_event(spec)
== 'Event("_call_function", {function:(() => ((console["log"]("message"))))})'
)
spec = event.console_log(Var(_js_expr="message")) spec = event.console_log(Var(_js_expr="message"))
assert format.format_event(spec) == 'Event("_console", {message:message})' assert (
format.format_event(spec)
== 'Event("_call_function", {function:(() => ((console["log"](message))))})'
)
def test_event_window_alert(): def test_event_window_alert():
"""Test the event window alert function.""" """Test the event window alert function."""
spec = event.window_alert("message") spec = event.window_alert("message")
assert isinstance(spec, EventSpec) assert isinstance(spec, EventSpec)
assert spec.handler.fn.__qualname__ == "_alert" assert spec.handler.fn.__qualname__ == "_call_function"
assert spec.args[0][0].equals(Var(_js_expr="message")) assert spec.args[0][0].equals(Var(_js_expr="function"))
assert spec.args[0][1].equals(LiteralVar.create("message")) assert spec.args[0][1].equals(
assert format.format_event(spec) == 'Event("_alert", {message:"message"})' Var('(() => ((window["alert"]("message"))))', _var_type=Callable)
)
assert (
format.format_event(spec)
== 'Event("_call_function", {function:(() => ((window["alert"]("message"))))})'
)
spec = event.window_alert(Var(_js_expr="message")) spec = event.window_alert(Var(_js_expr="message"))
assert format.format_event(spec) == 'Event("_alert", {message:message})' assert (
format.format_event(spec)
== 'Event("_call_function", {function:(() => ((window["alert"](message))))})'
)
def test_set_focus(): def test_set_focus():

View File

@ -45,7 +45,7 @@ from reflex.testing import chdir
from reflex.utils import format, prerequisites, types from reflex.utils import format, prerequisites, types
from reflex.utils.exceptions import SetUndefinedStateVarError from reflex.utils.exceptions import SetUndefinedStateVarError
from reflex.utils.format import json_dumps from reflex.utils.format import json_dumps
from reflex.vars.base import ComputedVar, Var from reflex.vars.base import Var, computed_var
from tests.units.states.mutation import MutableSQLAModel, MutableTestState from tests.units.states.mutation import MutableSQLAModel, MutableTestState
from .states import GenState from .states import GenState
@ -109,7 +109,7 @@ class TestState(BaseState):
_backend: int = 0 _backend: int = 0
asynctest: int = 0 asynctest: int = 0
@ComputedVar @computed_var
def sum(self) -> float: def sum(self) -> float:
"""Dynamically sum the numbers. """Dynamically sum the numbers.
@ -118,7 +118,7 @@ class TestState(BaseState):
""" """
return self.num1 + self.num2 return self.num1 + self.num2
@ComputedVar @computed_var
def upper(self) -> str: def upper(self) -> str:
"""Uppercase the key. """Uppercase the key.
@ -1124,7 +1124,7 @@ def test_child_state():
v: int = 2 v: int = 2
class ChildState(MainState): class ChildState(MainState):
@ComputedVar @computed_var
def rendered_var(self): def rendered_var(self):
return self.v return self.v
@ -1143,7 +1143,7 @@ def test_conditional_computed_vars():
t1: str = "a" t1: str = "a"
t2: str = "b" t2: str = "b"
@ComputedVar @computed_var
def rendered_var(self) -> str: def rendered_var(self) -> str:
if self.flag: if self.flag:
return self.t1 return self.t1
@ -3095,12 +3095,12 @@ def test_potentially_dirty_substates():
""" """
class State(RxState): class State(RxState):
@ComputedVar @computed_var
def foo(self) -> str: def foo(self) -> str:
return "" return ""
class C1(State): class C1(State):
@ComputedVar @computed_var
def bar(self) -> str: def bar(self) -> str:
return "" return ""
@ -3404,3 +3404,10 @@ def test_fallback_pickle():
state3._g = (i for i in range(10)) state3._g = (i for i in range(10))
pk3 = state3._serialize() pk3 = state3._serialize()
assert len(pk3) == 0 assert len(pk3) == 0
def test_typed_state() -> None:
class TypedState(rx.State):
field: rx.Field[str] = rx.field("")
_ = TypedState(field="str")

View File

@ -10,6 +10,7 @@ from packaging import version
from reflex import constants from reflex import constants
from reflex.base import Base from reflex.base import Base
from reflex.config import environment
from reflex.event import EventHandler from reflex.event import EventHandler
from reflex.state import BaseState from reflex.state import BaseState
from reflex.utils import ( from reflex.utils import (
@ -593,3 +594,11 @@ def test_style_prop_with_event_handler_value(callable):
rx.box( rx.box(
style=style, # type: ignore style=style, # type: ignore
) )
def test_is_prod_mode() -> None:
"""Test that the prod mode is correctly determined."""
environment.REFLEX_ENV_MODE.set(constants.Env.PROD)
assert utils_exec.is_prod_mode()
environment.REFLEX_ENV_MODE.set(None)
assert not utils_exec.is_prod_mode()

View File

@ -0,0 +1,102 @@
import pytest
from typing_extensions import assert_type
import reflex as rx
from reflex.utils.types import GenericType
from reflex.vars.base import Var
from reflex.vars.object import LiteralObjectVar, ObjectVar
class Bare:
"""A bare class with a single attribute."""
quantity: int = 0
@rx.serializer
def serialize_bare(obj: Bare) -> dict:
"""A serializer for the bare class.
Args:
obj: The object to serialize.
Returns:
A dictionary with the quantity attribute.
"""
return {"quantity": obj.quantity}
class Base(rx.Base):
"""A reflex base class with a single attribute."""
quantity: int = 0
class ObjectState(rx.State):
"""A reflex state with bare and base objects."""
bare: rx.Field[Bare] = rx.field(Bare())
base: rx.Field[Base] = rx.field(Base())
@pytest.mark.parametrize("type_", [Base, Bare])
def test_var_create(type_: GenericType) -> None:
my_object = type_()
var = Var.create(my_object)
assert var._var_type is type_
quantity = var.quantity
assert quantity._var_type is int
@pytest.mark.parametrize("type_", [Base, Bare])
def test_literal_create(type_: GenericType) -> None:
my_object = type_()
var = LiteralObjectVar.create(my_object)
assert var._var_type is type_
quantity = var.quantity
assert quantity._var_type is int
@pytest.mark.parametrize("type_", [Base, Bare])
def test_guess(type_: GenericType) -> None:
my_object = type_()
var = Var.create(my_object)
var = var.guess_type()
assert var._var_type is type_
quantity = var.quantity
assert quantity._var_type is int
@pytest.mark.parametrize("type_", [Base, Bare])
def test_state(type_: GenericType) -> None:
attr_name = type_.__name__.lower()
var = getattr(ObjectState, attr_name)
assert var._var_type is type_
quantity = var.quantity
assert quantity._var_type is int
@pytest.mark.parametrize("type_", [Base, Bare])
def test_state_to_operation(type_: GenericType) -> None:
attr_name = type_.__name__.lower()
original_var = getattr(ObjectState, attr_name)
var = original_var.to(ObjectVar, type_)
assert var._var_type is type_
var = original_var.to(ObjectVar)
assert var._var_type is type_
def test_typing() -> None:
# Bare
var = ObjectState.bare.to(ObjectVar)
_ = assert_type(var, ObjectVar[Bare])
# Base
var = ObjectState.base
_ = assert_type(var, ObjectVar[Base])