Merge branch 'main' into remove-some-benchmarks-from-CI
This commit is contained in:
commit
29f3a77a48
@ -3,7 +3,7 @@ fail_fast: true
|
||||
repos:
|
||||
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.9.3
|
||||
rev: v0.9.6
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
args: [reflex, tests]
|
||||
|
40
poetry.lock
generated
40
poetry.lock
generated
@ -2415,31 +2415,31 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.9.3"
|
||||
version = "0.9.6"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["dev"]
|
||||
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
|
||||
files = [
|
||||
{file = "ruff-0.9.3-py3-none-linux_armv6l.whl", hash = "sha256:7f39b879064c7d9670197d91124a75d118d00b0990586549949aae80cdc16624"},
|
||||
{file = "ruff-0.9.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a187171e7c09efa4b4cc30ee5d0d55a8d6c5311b3e1b74ac5cb96cc89bafc43c"},
|
||||
{file = "ruff-0.9.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c59ab92f8e92d6725b7ded9d4a31be3ef42688a115c6d3da9457a5bda140e2b4"},
|
||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc153c25e715be41bb228bc651c1e9b1a88d5c6e5ed0194fa0dfea02b026439"},
|
||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:646909a1e25e0dc28fbc529eab8eb7bb583079628e8cbe738192853dbbe43af5"},
|
||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a5a46e09355695fbdbb30ed9889d6cf1c61b77b700a9fafc21b41f097bfbba4"},
|
||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c4bb09d2bbb394e3730d0918c00276e79b2de70ec2a5231cd4ebb51a57df9ba1"},
|
||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96a87ec31dc1044d8c2da2ebbed1c456d9b561e7d087734336518181b26b3aa5"},
|
||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb7554aca6f842645022fe2d301c264e6925baa708b392867b7a62645304df4"},
|
||||
{file = "ruff-0.9.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cabc332b7075a914ecea912cd1f3d4370489c8018f2c945a30bcc934e3bc06a6"},
|
||||
{file = "ruff-0.9.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:33866c3cc2a575cbd546f2cd02bdd466fed65118e4365ee538a3deffd6fcb730"},
|
||||
{file = "ruff-0.9.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:006e5de2621304c8810bcd2ee101587712fa93b4f955ed0985907a36c427e0c2"},
|
||||
{file = "ruff-0.9.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ba6eea4459dbd6b1be4e6bfc766079fb9b8dd2e5a35aff6baee4d9b1514ea519"},
|
||||
{file = "ruff-0.9.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:90230a6b8055ad47d3325e9ee8f8a9ae7e273078a66401ac66df68943ced029b"},
|
||||
{file = "ruff-0.9.3-py3-none-win32.whl", hash = "sha256:eabe5eb2c19a42f4808c03b82bd313fc84d4e395133fb3fc1b1516170a31213c"},
|
||||
{file = "ruff-0.9.3-py3-none-win_amd64.whl", hash = "sha256:040ceb7f20791dfa0e78b4230ee9dce23da3b64dd5848e40e3bf3ab76468dcf4"},
|
||||
{file = "ruff-0.9.3-py3-none-win_arm64.whl", hash = "sha256:800d773f6d4d33b0a3c60e2c6ae8f4c202ea2de056365acfa519aa48acf28e0b"},
|
||||
{file = "ruff-0.9.3.tar.gz", hash = "sha256:8293f89985a090ebc3ed1064df31f3b4b56320cdfcec8b60d3295bddb955c22a"},
|
||||
{file = "ruff-0.9.6-py3-none-linux_armv6l.whl", hash = "sha256:2f218f356dd2d995839f1941322ff021c72a492c470f0b26a34f844c29cdf5ba"},
|
||||
{file = "ruff-0.9.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b908ff4df65dad7b251c9968a2e4560836d8f5487c2f0cc238321ed951ea0504"},
|
||||
{file = "ruff-0.9.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b109c0ad2ececf42e75fa99dc4043ff72a357436bb171900714a9ea581ddef83"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de4367cca3dac99bcbd15c161404e849bb0bfd543664db39232648dc00112dc"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3ee4d7c2c92ddfdaedf0bf31b2b176fa7aa8950efc454628d477394d35638b"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc1edd1775270e6aa2386119aea692039781429f0be1e0949ea5884e011aa8e"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4a091729086dffa4bd070aa5dab7e39cc6b9d62eb2bef8f3d91172d30d599666"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1bbc6808bf7b15796cef0815e1dfb796fbd383e7dbd4334709642649625e7c5"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:589d1d9f25b5754ff230dce914a174a7c951a85a4e9270613a2b74231fdac2f5"},
|
||||
{file = "ruff-0.9.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc61dd5131742e21103fbbdcad683a8813be0e3c204472d520d9a5021ca8b217"},
|
||||
{file = "ruff-0.9.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5e2d9126161d0357e5c8f30b0bd6168d2c3872372f14481136d13de9937f79b6"},
|
||||
{file = "ruff-0.9.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:68660eab1a8e65babb5229a1f97b46e3120923757a68b5413d8561f8a85d4897"},
|
||||
{file = "ruff-0.9.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4cae6c4cc7b9b4017c71114115db0445b00a16de3bcde0946273e8392856f08"},
|
||||
{file = "ruff-0.9.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19f505b643228b417c1111a2a536424ddde0db4ef9023b9e04a46ed8a1cb4656"},
|
||||
{file = "ruff-0.9.6-py3-none-win32.whl", hash = "sha256:194d8402bceef1b31164909540a597e0d913c0e4952015a5b40e28c146121b5d"},
|
||||
{file = "ruff-0.9.6-py3-none-win_amd64.whl", hash = "sha256:03482d5c09d90d4ee3f40d97578423698ad895c87314c4de39ed2af945633caa"},
|
||||
{file = "ruff-0.9.6-py3-none-win_arm64.whl", hash = "sha256:0e2bb706a2be7ddfea4a4af918562fdc1bcb16df255e5fa595bbd800ce322a5a"},
|
||||
{file = "ruff-0.9.6.tar.gz", hash = "sha256:81761592f72b620ec8fa1068a6fd00e98a5ebee342a3642efd84454f3031dca9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3188,4 +3188,4 @@ type = ["pytest-mypy"]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.10, <4.0"
|
||||
content-hash = "3b7e6e6e872c68f951f191d85a7d76fe1dd86caf32e2143a53a3152a3686fc7f"
|
||||
content-hash = "7ae644e1c5b910f4fd0d8ab0b530818077a96e5d329b2be1269e967c6b0b3d25"
|
||||
|
@ -61,7 +61,7 @@ dill = ">=0.3.8"
|
||||
toml = ">=0.10.2,<1.0"
|
||||
pytest-asyncio = ">=0.24.0"
|
||||
pytest-cov = ">=4.0.0,<7.0"
|
||||
ruff = "0.9.3"
|
||||
ruff = "0.9.6"
|
||||
pandas = ">=2.1.1,<3.0"
|
||||
pillow = ">=10.0.0,<12.0"
|
||||
plotly = ">=5.13.0,<6.0"
|
||||
|
@ -22,6 +22,7 @@ from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
AsyncIterator,
|
||||
BinaryIO,
|
||||
Callable,
|
||||
Coroutine,
|
||||
Dict,
|
||||
@ -35,12 +36,15 @@ from typing import (
|
||||
get_type_hints,
|
||||
)
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Request, UploadFile
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from fastapi import UploadFile as FastAPIUploadFile
|
||||
from fastapi.middleware import cors
|
||||
from fastapi.responses import JSONResponse, StreamingResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
|
||||
from socketio import ASGIApp, AsyncNamespace, AsyncServer
|
||||
from starlette.datastructures import Headers
|
||||
from starlette.datastructures import UploadFile as StarletteUploadFile
|
||||
from starlette_admin.contrib.sqla.admin import Admin
|
||||
from starlette_admin.contrib.sqla.view import ModelView
|
||||
|
||||
@ -231,6 +235,53 @@ class OverlayFragment(Fragment):
|
||||
pass
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class UploadFile(StarletteUploadFile):
|
||||
"""A file uploaded to the server.
|
||||
|
||||
Args:
|
||||
file: The standard Python file object (non-async).
|
||||
filename: The original file name.
|
||||
size: The size of the file in bytes.
|
||||
headers: The headers of the request.
|
||||
"""
|
||||
|
||||
file: BinaryIO
|
||||
|
||||
path: Optional[Path] = dataclasses.field(default=None)
|
||||
|
||||
_deprecated_filename: Optional[str] = dataclasses.field(default=None)
|
||||
|
||||
size: Optional[int] = dataclasses.field(default=None)
|
||||
|
||||
headers: Headers = dataclasses.field(default_factory=Headers)
|
||||
|
||||
@property
|
||||
def name(self) -> Optional[str]:
|
||||
"""Get the name of the uploaded file.
|
||||
|
||||
Returns:
|
||||
The name of the uploaded file.
|
||||
"""
|
||||
if self.path:
|
||||
return self.path.name
|
||||
|
||||
@property
|
||||
def filename(self) -> Optional[str]:
|
||||
"""Get the filename of the uploaded file.
|
||||
|
||||
Returns:
|
||||
The filename of the uploaded file.
|
||||
"""
|
||||
console.deprecate(
|
||||
feature_name="UploadFile.filename",
|
||||
reason="Use UploadFile.name instead.",
|
||||
deprecation_version="0.7.1",
|
||||
removal_version="0.8.0",
|
||||
)
|
||||
return self._deprecated_filename
|
||||
|
||||
|
||||
@dataclasses.dataclass(
|
||||
frozen=True,
|
||||
)
|
||||
@ -591,7 +642,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
Returns:
|
||||
The generated component.
|
||||
"""
|
||||
return component if isinstance(component, Component) else component()
|
||||
from reflex.compiler.compiler import into_component
|
||||
|
||||
return into_component(component)
|
||||
|
||||
def add_page(
|
||||
self,
|
||||
@ -1583,7 +1636,7 @@ def upload(app: App):
|
||||
The upload function.
|
||||
"""
|
||||
|
||||
async def upload_file(request: Request, files: List[UploadFile]):
|
||||
async def upload_file(request: Request, files: List[FastAPIUploadFile]):
|
||||
"""Upload a file.
|
||||
|
||||
Args:
|
||||
@ -1659,7 +1712,8 @@ def upload(app: App):
|
||||
file_copies.append(
|
||||
UploadFile(
|
||||
file=content_copy,
|
||||
filename=file.filename,
|
||||
path=Path(file.filename.lstrip("/")) if file.filename else None,
|
||||
_deprecated_filename=file.filename,
|
||||
size=file.size,
|
||||
headers=file.headers,
|
||||
)
|
||||
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Dict, Iterable, Optional, Tuple, Type, Union
|
||||
from typing import TYPE_CHECKING, Dict, Iterable, Optional, Sequence, Tuple, Type, Union
|
||||
|
||||
from reflex import constants
|
||||
from reflex.compiler import templates, utils
|
||||
@ -545,7 +545,47 @@ def purge_web_pages_dir():
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from reflex.app import UnevaluatedPage
|
||||
from reflex.app import ComponentCallable, UnevaluatedPage
|
||||
|
||||
|
||||
def _into_component_once(component: Component | ComponentCallable) -> Component | None:
|
||||
"""Convert a component to a Component.
|
||||
|
||||
Args:
|
||||
component: The component to convert.
|
||||
|
||||
Returns:
|
||||
The converted component.
|
||||
"""
|
||||
if isinstance(component, Component):
|
||||
return component
|
||||
if isinstance(component, (Var, int, float, str)):
|
||||
return Fragment.create(component)
|
||||
if isinstance(component, Sequence):
|
||||
return Fragment.create(*component)
|
||||
return None
|
||||
|
||||
|
||||
def into_component(component: Component | ComponentCallable) -> Component:
|
||||
"""Convert a component to a Component.
|
||||
|
||||
Args:
|
||||
component: The component to convert.
|
||||
|
||||
Returns:
|
||||
The converted component.
|
||||
|
||||
Raises:
|
||||
TypeError: If the component is not a Component.
|
||||
"""
|
||||
if (converted := _into_component_once(component)) is not None:
|
||||
return converted
|
||||
if (
|
||||
callable(component)
|
||||
and (converted := _into_component_once(component())) is not None
|
||||
):
|
||||
return converted
|
||||
raise TypeError(f"Expected a Component, got {type(component)}")
|
||||
|
||||
|
||||
def compile_unevaluated_page(
|
||||
@ -568,12 +608,7 @@ def compile_unevaluated_page(
|
||||
The compiled component and whether state should be enabled.
|
||||
"""
|
||||
# Generate the component if it is a callable.
|
||||
component = page.component
|
||||
component = component if isinstance(component, Component) else component()
|
||||
|
||||
# unpack components that return tuples in an rx.fragment.
|
||||
if isinstance(component, tuple):
|
||||
component = Fragment.create(*component)
|
||||
component = into_component(page.component)
|
||||
|
||||
component._add_style_recursive(style or {}, theme)
|
||||
|
||||
@ -678,10 +713,8 @@ class ExecutorSafeFunctions:
|
||||
The route, compiled component, and compiled page.
|
||||
"""
|
||||
component, enable_state = compile_unevaluated_page(
|
||||
route, cls.UNCOMPILED_PAGES[route]
|
||||
route, cls.UNCOMPILED_PAGES[route], cls.STATE, style, theme
|
||||
)
|
||||
component = component if isinstance(component, Component) else component()
|
||||
component._add_style_recursive(style, theme)
|
||||
return route, component, compile_page(route, component, cls.STATE)
|
||||
|
||||
@classmethod
|
||||
|
@ -51,13 +51,7 @@ from reflex.event import (
|
||||
)
|
||||
from reflex.style import Style, format_as_emotion
|
||||
from reflex.utils import format, imports, types
|
||||
from reflex.utils.imports import (
|
||||
ImmutableParsedImportDict,
|
||||
ImportDict,
|
||||
ImportVar,
|
||||
ParsedImportDict,
|
||||
parse_imports,
|
||||
)
|
||||
from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
|
||||
from reflex.vars import VarData
|
||||
from reflex.vars.base import (
|
||||
CachedVarOperation,
|
||||
@ -1208,7 +1202,7 @@ class Component(BaseComponent, ABC):
|
||||
Returns:
|
||||
True if the dependency should be transpiled.
|
||||
"""
|
||||
return (
|
||||
return bool(self.transpile_packages) and (
|
||||
dep in self.transpile_packages
|
||||
or format.format_library_name(dep or "") in self.transpile_packages
|
||||
)
|
||||
@ -1291,9 +1285,10 @@ class Component(BaseComponent, ABC):
|
||||
event_imports = Imports.EVENTS if self.event_triggers else {}
|
||||
|
||||
# Collect imports from Vars used directly by this component.
|
||||
var_datas = [var._get_all_var_data() for var in self._get_vars()]
|
||||
var_imports: List[ImmutableParsedImportDict] = [
|
||||
var_data.imports for var_data in var_datas if var_data is not None
|
||||
var_imports = [
|
||||
var_data.imports
|
||||
for var in self._get_vars()
|
||||
if (var_data := var._get_all_var_data()) is not None
|
||||
]
|
||||
|
||||
added_import_dicts: list[ParsedImportDict] = []
|
||||
|
@ -29,7 +29,7 @@ from reflex.event import (
|
||||
from reflex.utils import format
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.vars import VarData
|
||||
from reflex.vars.base import CallableVar, Var, get_unique_variable_name
|
||||
from reflex.vars.base import Var, get_unique_variable_name
|
||||
from reflex.vars.sequence import LiteralStringVar
|
||||
|
||||
DEFAULT_UPLOAD_ID: str = "default"
|
||||
@ -45,7 +45,6 @@ upload_files_context_var_data: VarData = VarData(
|
||||
)
|
||||
|
||||
|
||||
@CallableVar
|
||||
def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> Var:
|
||||
"""Get the file upload drop trigger.
|
||||
|
||||
@ -75,7 +74,6 @@ def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> Var:
|
||||
)
|
||||
|
||||
|
||||
@CallableVar
|
||||
def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> Var:
|
||||
"""Get the list of selected files.
|
||||
|
||||
|
@ -13,14 +13,12 @@ from reflex.event import CallableEventSpec, EventSpec, EventType
|
||||
from reflex.style import Style
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.vars import VarData
|
||||
from reflex.vars.base import CallableVar, Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
DEFAULT_UPLOAD_ID: str
|
||||
upload_files_context_var_data: VarData
|
||||
|
||||
@CallableVar
|
||||
def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> Var: ...
|
||||
@CallableVar
|
||||
def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> Var: ...
|
||||
@CallableEventSpec
|
||||
def clear_selected_files(id_: str = DEFAULT_UPLOAD_ID) -> EventSpec: ...
|
||||
|
@ -144,7 +144,7 @@ class ColorModeIconButton(IconButton):
|
||||
|
||||
if allow_system:
|
||||
|
||||
def color_mode_item(_color_mode: str):
|
||||
def color_mode_item(_color_mode: Literal["light", "dark", "system"]):
|
||||
return dropdown_menu.item(
|
||||
_color_mode.title(), on_click=set_color_mode(_color_mode)
|
||||
)
|
||||
|
@ -10,6 +10,7 @@ import os
|
||||
import sys
|
||||
import threading
|
||||
import urllib.parse
|
||||
from functools import lru_cache
|
||||
from importlib.util import find_spec
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
@ -408,6 +409,19 @@ class EnvVar(Generic[T]):
|
||||
os.environ[self.name] = str_value
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def get_type_hints_environment(cls: type) -> dict[str, Any]:
|
||||
"""Get the type hints for the environment variables.
|
||||
|
||||
Args:
|
||||
cls: The class.
|
||||
|
||||
Returns:
|
||||
The type hints.
|
||||
"""
|
||||
return get_type_hints(cls)
|
||||
|
||||
|
||||
class env_var: # noqa: N801 # pyright: ignore [reportRedeclaration]
|
||||
"""Descriptor for environment variables."""
|
||||
|
||||
@ -434,7 +448,9 @@ class env_var: # noqa: N801 # pyright: ignore [reportRedeclaration]
|
||||
"""
|
||||
self.name = name
|
||||
|
||||
def __get__(self, instance: Any, owner: Any):
|
||||
def __get__(
|
||||
self, instance: EnvironmentVariables, owner: type[EnvironmentVariables]
|
||||
):
|
||||
"""Get the EnvVar instance.
|
||||
|
||||
Args:
|
||||
@ -444,7 +460,7 @@ class env_var: # noqa: N801 # pyright: ignore [reportRedeclaration]
|
||||
Returns:
|
||||
The EnvVar instance.
|
||||
"""
|
||||
type_ = get_args(get_type_hints(owner)[self.name])[0]
|
||||
type_ = get_args(get_type_hints_environment(owner)[self.name])[0]
|
||||
env_name = self.name
|
||||
if self.internal:
|
||||
env_name = f"__{env_name}"
|
||||
|
@ -1742,6 +1742,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
|
||||
Yields:
|
||||
StateUpdate object
|
||||
|
||||
Raises:
|
||||
ValueError: If a string value is received for an int or float type and cannot be converted.
|
||||
"""
|
||||
from reflex.utils import telemetry
|
||||
|
||||
@ -1779,12 +1782,25 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
hinted_args, (Base, BaseModelV1, BaseModelV2)
|
||||
):
|
||||
payload[arg] = hinted_args(**value)
|
||||
if isinstance(value, list) and (hinted_args is set or hinted_args is Set):
|
||||
elif isinstance(value, list) and (hinted_args is set or hinted_args is Set):
|
||||
payload[arg] = set(value)
|
||||
if isinstance(value, list) and (
|
||||
elif isinstance(value, list) and (
|
||||
hinted_args is tuple or hinted_args is Tuple
|
||||
):
|
||||
payload[arg] = tuple(value)
|
||||
elif isinstance(value, str) and (
|
||||
hinted_args is int or hinted_args is float
|
||||
):
|
||||
try:
|
||||
payload[arg] = hinted_args(value)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
f"Received a string value ({value}) for {arg} but expected a {hinted_args}"
|
||||
) from None
|
||||
else:
|
||||
console.warn(
|
||||
f"Received a string value ({value}) for {arg} but expected a {hinted_args}. A simple conversion was successful."
|
||||
)
|
||||
|
||||
# Wrap the function in a try/except block.
|
||||
try:
|
||||
@ -2459,6 +2475,8 @@ class ComponentState(State, mixin=True):
|
||||
Returns:
|
||||
A new instance of the Component with an independent copy of the State.
|
||||
"""
|
||||
from reflex.compiler.compiler import into_component
|
||||
|
||||
cls._per_component_state_instance_count += 1
|
||||
state_cls_name = f"{cls.__name__}_n{cls._per_component_state_instance_count}"
|
||||
component_state = type(
|
||||
@ -2470,6 +2488,7 @@ class ComponentState(State, mixin=True):
|
||||
# Save a reference to the dynamic state for pickle/unpickle.
|
||||
setattr(reflex.istate.dynamic, state_cls_name, component_state)
|
||||
component = component_state.get_component(*children, **props)
|
||||
component = into_component(component)
|
||||
component.State = component_state
|
||||
return component
|
||||
|
||||
|
@ -12,7 +12,7 @@ from reflex.utils.exceptions import ReflexError
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.utils.types import get_origin
|
||||
from reflex.vars import VarData
|
||||
from reflex.vars.base import CallableVar, LiteralVar, Var
|
||||
from reflex.vars.base import LiteralVar, Var
|
||||
from reflex.vars.function import FunctionVar
|
||||
from reflex.vars.object import ObjectVar
|
||||
|
||||
@ -48,7 +48,6 @@ def _color_mode_var(_js_expr: str, _var_type: Type = str) -> Var:
|
||||
).guess_type()
|
||||
|
||||
|
||||
@CallableVar
|
||||
def set_color_mode(
|
||||
new_color_mode: LiteralColorMode | Var[LiteralColorMode] | None = None,
|
||||
) -> Var[EventChain]:
|
||||
|
@ -951,7 +951,7 @@ class Var(Generic[VAR_TYPE]):
|
||||
"""
|
||||
actual_name = self._var_field_name
|
||||
|
||||
def setter(state: BaseState, value: Any):
|
||||
def setter(state: Any, value: Any):
|
||||
"""Get the setter for the var.
|
||||
|
||||
Args:
|
||||
@ -969,6 +969,8 @@ class Var(Generic[VAR_TYPE]):
|
||||
else:
|
||||
setattr(state, actual_name, value)
|
||||
|
||||
setter.__annotations__["value"] = self._var_type
|
||||
|
||||
setter.__qualname__ = self._get_setter_name()
|
||||
|
||||
return setter
|
||||
@ -1901,61 +1903,6 @@ def _or_operation(a: Var, b: Var):
|
||||
)
|
||||
|
||||
|
||||
@dataclasses.dataclass(
|
||||
eq=False,
|
||||
frozen=True,
|
||||
slots=True,
|
||||
)
|
||||
class CallableVar(Var):
|
||||
"""Decorate a Var-returning function to act as both a Var and a function.
|
||||
|
||||
This is used as a compatibility shim for replacing Var objects in the
|
||||
API with functions that return a family of Var.
|
||||
"""
|
||||
|
||||
fn: Callable[..., Var] = dataclasses.field(
|
||||
default_factory=lambda: lambda: Var(_js_expr="undefined")
|
||||
)
|
||||
original_var: Var = dataclasses.field(
|
||||
default_factory=lambda: Var(_js_expr="undefined")
|
||||
)
|
||||
|
||||
def __init__(self, fn: Callable[..., Var]):
|
||||
"""Initialize a CallableVar.
|
||||
|
||||
Args:
|
||||
fn: The function to decorate (must return Var)
|
||||
"""
|
||||
original_var = fn()
|
||||
super(CallableVar, self).__init__(
|
||||
_js_expr=original_var._js_expr,
|
||||
_var_type=original_var._var_type,
|
||||
_var_data=VarData.merge(original_var._get_all_var_data()),
|
||||
)
|
||||
object.__setattr__(self, "fn", fn)
|
||||
object.__setattr__(self, "original_var", original_var)
|
||||
|
||||
def __call__(self, *args: Any, **kwargs: Any) -> Var:
|
||||
"""Call the decorated function.
|
||||
|
||||
Args:
|
||||
*args: The args to pass to the function.
|
||||
**kwargs: The kwargs to pass to the function.
|
||||
|
||||
Returns:
|
||||
The Var returned from calling the function.
|
||||
"""
|
||||
return self.fn(*args, **kwargs)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
"""Calculate the hash of the object.
|
||||
|
||||
Returns:
|
||||
The hash of the object.
|
||||
"""
|
||||
return hash((type(self).__name__, self.original_var))
|
||||
|
||||
|
||||
RETURN_TYPE = TypeVar("RETURN_TYPE")
|
||||
|
||||
DICT_KEY = TypeVar("DICT_KEY")
|
||||
|
@ -20,7 +20,11 @@ def BackgroundTask():
|
||||
class State(rx.State):
|
||||
counter: int = 0
|
||||
_task_id: int = 0
|
||||
iterations: int = 10
|
||||
iterations: rx.Field[int] = rx.field(10)
|
||||
|
||||
@rx.event
|
||||
def set_iterations(self, value: str):
|
||||
self.iterations = int(value)
|
||||
|
||||
@rx.event(background=True)
|
||||
async def handle_event(self):
|
||||
@ -125,8 +129,8 @@ def BackgroundTask():
|
||||
rx.input(
|
||||
id="iterations",
|
||||
placeholder="Iterations",
|
||||
value=State.iterations.to_string(), # pyright: ignore [reportAttributeAccessIssue]
|
||||
on_change=State.set_iterations, # pyright: ignore [reportAttributeAccessIssue]
|
||||
value=State.iterations.to_string(),
|
||||
on_change=State.set_iterations,
|
||||
),
|
||||
rx.button(
|
||||
"Delayed Increment",
|
||||
|
@ -87,7 +87,7 @@ def UploadFile():
|
||||
),
|
||||
rx.box(
|
||||
rx.foreach(
|
||||
rx.selected_files,
|
||||
rx.selected_files(),
|
||||
lambda f: rx.text(f, as_="p"),
|
||||
),
|
||||
id="selected_files",
|
||||
|
@ -61,7 +61,7 @@ def ColorToggleApp():
|
||||
rx.icon(tag="moon", size=20),
|
||||
value="dark",
|
||||
),
|
||||
on_change=set_color_mode,
|
||||
on_change=set_color_mode(),
|
||||
variant="classic",
|
||||
radius="large",
|
||||
value=color_mode,
|
||||
|
Loading…
Reference in New Issue
Block a user