reflex 0.8.8a2__py3-none-any.whl → 0.8.9a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of reflex might be problematic. Click here for more details.
- reflex/app.py +15 -5
- reflex/compiler/templates.py +19 -3
- reflex/compiler/utils.py +7 -4
- reflex/components/component.py +64 -34
- reflex/components/el/elements/media.py +518 -0
- reflex/components/lucide/icon.py +3 -0
- reflex/components/radix/primitives/drawer.pyi +4 -0
- reflex/config.py +1 -1
- reflex/constants/installer.py +4 -4
- reflex/environment.py +8 -3
- reflex/istate/data.py +15 -0
- reflex/plugins/sitemap.py +3 -6
- reflex/reflex.py +8 -2
- reflex/state.py +46 -6
- reflex/testing.py +1 -1
- reflex/utils/console.py +67 -7
- reflex/utils/frontend_skeleton.py +6 -1
- reflex/utils/imports.py +1 -1
- reflex/utils/prerequisites.py +3 -1
- reflex/utils/pyi_generator.py +2 -2
- reflex/utils/serializers.py +4 -4
- reflex/utils/types.py +7 -10
- reflex/vars/base.py +24 -17
- reflex/vars/object.py +4 -2
- {reflex-0.8.8a2.dist-info → reflex-0.8.9a1.dist-info}/METADATA +1 -1
- {reflex-0.8.8a2.dist-info → reflex-0.8.9a1.dist-info}/RECORD +29 -29
- {reflex-0.8.8a2.dist-info → reflex-0.8.9a1.dist-info}/WHEEL +0 -0
- {reflex-0.8.8a2.dist-info → reflex-0.8.9a1.dist-info}/entry_points.txt +0 -0
- {reflex-0.8.8a2.dist-info → reflex-0.8.9a1.dist-info}/licenses/LICENSE +0 -0
reflex/istate/data.py
CHANGED
|
@@ -83,6 +83,11 @@ class HeaderData(_HeaderData):
|
|
|
83
83
|
)
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
@serializer(to=dict)
|
|
87
|
+
def _serialize_header_data(obj: HeaderData) -> dict:
|
|
88
|
+
return {k.name: getattr(obj, k.name) for k in dataclasses.fields(obj)}
|
|
89
|
+
|
|
90
|
+
|
|
86
91
|
@serializer(to=dict)
|
|
87
92
|
def serialize_frozen_dict_str_str(obj: _FrozenDictStrStr) -> dict:
|
|
88
93
|
"""Serialize a _FrozenDictStrStr object to a dict.
|
|
@@ -165,6 +170,11 @@ class PageData:
|
|
|
165
170
|
)
|
|
166
171
|
|
|
167
172
|
|
|
173
|
+
@serializer(to=dict)
|
|
174
|
+
def _serialize_page_data(obj: PageData) -> dict:
|
|
175
|
+
return dataclasses.asdict(obj)
|
|
176
|
+
|
|
177
|
+
|
|
168
178
|
@dataclasses.dataclass(frozen=True)
|
|
169
179
|
class SessionData:
|
|
170
180
|
"""An object containing session data."""
|
|
@@ -190,6 +200,11 @@ class SessionData:
|
|
|
190
200
|
)
|
|
191
201
|
|
|
192
202
|
|
|
203
|
+
@serializer(to=dict)
|
|
204
|
+
def _serialize_session_data(obj: SessionData) -> dict:
|
|
205
|
+
return dataclasses.asdict(obj)
|
|
206
|
+
|
|
207
|
+
|
|
193
208
|
@dataclasses.dataclass(frozen=True)
|
|
194
209
|
class RouterData:
|
|
195
210
|
"""An object containing RouterData."""
|
reflex/plugins/sitemap.py
CHANGED
|
@@ -5,8 +5,7 @@ from collections.abc import Sequence
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from types import SimpleNamespace
|
|
7
7
|
from typing import TYPE_CHECKING, Literal, TypedDict
|
|
8
|
-
from xml.
|
|
9
|
-
from xml.etree.ElementTree import Element, SubElement, tostring
|
|
8
|
+
from xml.etree.ElementTree import Element, SubElement, indent, tostring
|
|
10
9
|
|
|
11
10
|
from typing_extensions import NotRequired
|
|
12
11
|
|
|
@@ -104,10 +103,8 @@ def generate_xml(links: Sequence[SitemapLink]) -> str:
|
|
|
104
103
|
if (priority := link.get("priority")) is not None:
|
|
105
104
|
priority_element = SubElement(url, "priority")
|
|
106
105
|
priority_element.text = str(priority)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
reparsed = minidom.parseString(rough_string)
|
|
110
|
-
return reparsed.toprettyxml(indent=" ")
|
|
106
|
+
indent(urlset, " ")
|
|
107
|
+
return tostring(urlset, encoding="utf-8", xml_declaration=True).decode("utf-8")
|
|
111
108
|
|
|
112
109
|
|
|
113
110
|
def is_route_dynamic(route: str) -> bool:
|
reflex/reflex.py
CHANGED
|
@@ -360,7 +360,13 @@ def run(
|
|
|
360
360
|
default=False,
|
|
361
361
|
help="Run the command without making any changes.",
|
|
362
362
|
)
|
|
363
|
-
|
|
363
|
+
@click.option(
|
|
364
|
+
"--rich/--no-rich",
|
|
365
|
+
default=True,
|
|
366
|
+
is_flag=True,
|
|
367
|
+
help="Whether to use rich progress bars.",
|
|
368
|
+
)
|
|
369
|
+
def compile(dry: bool, rich: bool):
|
|
364
370
|
"""Compile the app in the current directory."""
|
|
365
371
|
import time
|
|
366
372
|
|
|
@@ -371,7 +377,7 @@ def compile(dry: bool):
|
|
|
371
377
|
_init(name=get_config().app_name)
|
|
372
378
|
get_config(reload=True)
|
|
373
379
|
starting_time = time.monotonic()
|
|
374
|
-
prerequisites.get_compiled_app(dry_run=dry)
|
|
380
|
+
prerequisites.get_compiled_app(dry_run=dry, use_rich=rich)
|
|
375
381
|
elapsed_time = time.monotonic() - starting_time
|
|
376
382
|
console.success(f"App compiled successfully in {elapsed_time:.3f} seconds.")
|
|
377
383
|
|
reflex/state.py
CHANGED
|
@@ -225,8 +225,20 @@ class EventHandlerSetVar(EventHandler):
|
|
|
225
225
|
EventHandlerValueError: If the given Var name is not a str
|
|
226
226
|
NotImplementedError: If the setter for the given Var is async
|
|
227
227
|
"""
|
|
228
|
+
from reflex.config import get_config
|
|
228
229
|
from reflex.utils.exceptions import EventHandlerValueError
|
|
229
230
|
|
|
231
|
+
config = get_config()
|
|
232
|
+
if config.state_auto_setters is None:
|
|
233
|
+
console.deprecate(
|
|
234
|
+
feature_name="state_auto_setters defaulting to True",
|
|
235
|
+
reason="The default value will be changed to False in a future release. Set state_auto_setters explicitly or define setters explicitly. "
|
|
236
|
+
f"Used {self.state_cls.__name__}.setvar without defining it.",
|
|
237
|
+
deprecation_version="0.8.9",
|
|
238
|
+
removal_version="0.9.0",
|
|
239
|
+
dedupe=True,
|
|
240
|
+
)
|
|
241
|
+
|
|
230
242
|
if args:
|
|
231
243
|
if not isinstance(args[0], str):
|
|
232
244
|
msg = f"Var name must be passed as a string, got {args[0]!r}"
|
|
@@ -1036,7 +1048,7 @@ class BaseState(EvenMoreBasicBaseState):
|
|
|
1036
1048
|
)
|
|
1037
1049
|
raise VarTypeError(msg)
|
|
1038
1050
|
cls._set_var(name, prop)
|
|
1039
|
-
if cls.is_user_defined() and get_config().state_auto_setters:
|
|
1051
|
+
if cls.is_user_defined() and get_config().state_auto_setters is not False:
|
|
1040
1052
|
cls._create_setter(name, prop)
|
|
1041
1053
|
cls._set_default_value(name, prop)
|
|
1042
1054
|
|
|
@@ -1096,11 +1108,14 @@ class BaseState(EvenMoreBasicBaseState):
|
|
|
1096
1108
|
setattr(cls, name, prop)
|
|
1097
1109
|
|
|
1098
1110
|
@classmethod
|
|
1099
|
-
def _create_event_handler(
|
|
1111
|
+
def _create_event_handler(
|
|
1112
|
+
cls, fn: Any, event_handler_cls: type[EventHandler] = EventHandler
|
|
1113
|
+
):
|
|
1100
1114
|
"""Create an event handler for the given function.
|
|
1101
1115
|
|
|
1102
1116
|
Args:
|
|
1103
1117
|
fn: The function to create an event handler for.
|
|
1118
|
+
event_handler_cls: The event handler class to use.
|
|
1104
1119
|
|
|
1105
1120
|
Returns:
|
|
1106
1121
|
The event handler.
|
|
@@ -1108,7 +1123,7 @@ class BaseState(EvenMoreBasicBaseState):
|
|
|
1108
1123
|
# Check if function has stored event_actions from decorator
|
|
1109
1124
|
event_actions = getattr(fn, "_rx_event_actions", {})
|
|
1110
1125
|
|
|
1111
|
-
return
|
|
1126
|
+
return event_handler_cls(
|
|
1112
1127
|
fn=fn, state_full_name=cls.get_full_name(), event_actions=event_actions
|
|
1113
1128
|
)
|
|
1114
1129
|
|
|
@@ -1125,9 +1140,34 @@ class BaseState(EvenMoreBasicBaseState):
|
|
|
1125
1140
|
name: The name of the var.
|
|
1126
1141
|
prop: The var to create a setter for.
|
|
1127
1142
|
"""
|
|
1143
|
+
from reflex.config import get_config
|
|
1144
|
+
|
|
1145
|
+
config = get_config()
|
|
1146
|
+
_create_event_handler_kwargs = {}
|
|
1147
|
+
|
|
1148
|
+
if config.state_auto_setters is None:
|
|
1149
|
+
|
|
1150
|
+
class EventHandlerDeprecatedSetter(EventHandler):
|
|
1151
|
+
def __call__(self, *args, **kwargs):
|
|
1152
|
+
console.deprecate(
|
|
1153
|
+
feature_name="state_auto_setters defaulting to True",
|
|
1154
|
+
reason="The default value will be changed to False in a future release. Set state_auto_setters explicitly or define setters explicitly. "
|
|
1155
|
+
f"Used {setter_name} in {cls.__name__} without defining it.",
|
|
1156
|
+
deprecation_version="0.8.9",
|
|
1157
|
+
removal_version="0.9.0",
|
|
1158
|
+
dedupe=True,
|
|
1159
|
+
)
|
|
1160
|
+
return super().__call__(*args, **kwargs)
|
|
1161
|
+
|
|
1162
|
+
_create_event_handler_kwargs["event_handler_cls"] = (
|
|
1163
|
+
EventHandlerDeprecatedSetter
|
|
1164
|
+
)
|
|
1165
|
+
|
|
1128
1166
|
setter_name = Var._get_setter_name_for_name(name)
|
|
1129
1167
|
if setter_name not in cls.__dict__:
|
|
1130
|
-
event_handler = cls._create_event_handler(
|
|
1168
|
+
event_handler = cls._create_event_handler(
|
|
1169
|
+
prop._get_setter(name), **_create_event_handler_kwargs
|
|
1170
|
+
)
|
|
1131
1171
|
cls.event_handlers[setter_name] = event_handler
|
|
1132
1172
|
setattr(cls, setter_name, event_handler)
|
|
1133
1173
|
|
|
@@ -1810,7 +1850,7 @@ class BaseState(EvenMoreBasicBaseState):
|
|
|
1810
1850
|
hinted_args = value_inside_optional(hinted_args)
|
|
1811
1851
|
if (
|
|
1812
1852
|
isinstance(value, dict)
|
|
1813
|
-
and
|
|
1853
|
+
and isinstance(hinted_args, type)
|
|
1814
1854
|
and not types.is_generic_alias(hinted_args) # py3.10
|
|
1815
1855
|
):
|
|
1816
1856
|
if issubclass(hinted_args, Model):
|
|
@@ -2341,7 +2381,7 @@ def _serialize_type(type_: Any) -> str:
|
|
|
2341
2381
|
Returns:
|
|
2342
2382
|
The serialized type.
|
|
2343
2383
|
"""
|
|
2344
|
-
if not
|
|
2384
|
+
if not isinstance(type_, type):
|
|
2345
2385
|
return f"{type_}"
|
|
2346
2386
|
return f"{type_.__module__}.{type_.__qualname__}"
|
|
2347
2387
|
|
reflex/testing.py
CHANGED
|
@@ -472,7 +472,7 @@ class AppHarness:
|
|
|
472
472
|
Returns:
|
|
473
473
|
The rendered app global code.
|
|
474
474
|
"""
|
|
475
|
-
if not
|
|
475
|
+
if not isinstance(value, type) and not inspect.isfunction(value):
|
|
476
476
|
return f"{key} = {value!r}"
|
|
477
477
|
return inspect.getsource(value)
|
|
478
478
|
|
reflex/utils/console.py
CHANGED
|
@@ -7,12 +7,13 @@ import datetime
|
|
|
7
7
|
import inspect
|
|
8
8
|
import os
|
|
9
9
|
import shutil
|
|
10
|
+
import sys
|
|
10
11
|
import time
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
from types import FrameType
|
|
13
14
|
|
|
14
15
|
from rich.console import Console
|
|
15
|
-
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
|
|
16
|
+
from rich.progress import MofNCompleteColumn, Progress, TaskID, TimeElapsedColumn
|
|
16
17
|
from rich.prompt import Prompt
|
|
17
18
|
|
|
18
19
|
from reflex.constants import LogLevel
|
|
@@ -244,23 +245,38 @@ def warn(msg: str, *, dedupe: bool = False, **kwargs):
|
|
|
244
245
|
print_to_log_file(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
|
|
245
246
|
|
|
246
247
|
|
|
247
|
-
|
|
248
|
+
@once
|
|
249
|
+
def _exclude_paths_from_frame_info() -> list[Path]:
|
|
250
|
+
import importlib.util
|
|
251
|
+
|
|
248
252
|
import click
|
|
253
|
+
import granian
|
|
254
|
+
import socketio
|
|
249
255
|
import typing_extensions
|
|
250
256
|
|
|
251
257
|
import reflex as rx
|
|
252
258
|
|
|
253
259
|
# Exclude utility modules that should never be the source of deprecated reflex usage.
|
|
254
|
-
exclude_modules = [click, rx, typing_extensions]
|
|
260
|
+
exclude_modules = [click, rx, typing_extensions, socketio, granian]
|
|
261
|
+
modules_paths = [file for m in exclude_modules if (file := m.__file__)] + [
|
|
262
|
+
spec.origin
|
|
263
|
+
for m in [*sys.builtin_module_names, *sys.stdlib_module_names]
|
|
264
|
+
if (spec := importlib.util.find_spec(m)) and spec.origin
|
|
265
|
+
]
|
|
255
266
|
exclude_roots = [
|
|
256
267
|
p.parent.resolve() if (p := Path(file)).name == "__init__.py" else p.resolve()
|
|
257
|
-
for
|
|
258
|
-
if (file := m.__file__)
|
|
268
|
+
for file in modules_paths
|
|
259
269
|
]
|
|
260
270
|
# Specifically exclude the reflex cli module.
|
|
261
271
|
if reflex_bin := shutil.which(b"reflex"):
|
|
262
272
|
exclude_roots.append(Path(reflex_bin.decode()))
|
|
263
273
|
|
|
274
|
+
return exclude_roots
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _get_first_non_framework_frame() -> FrameType | None:
|
|
278
|
+
exclude_roots = _exclude_paths_from_frame_info()
|
|
279
|
+
|
|
264
280
|
frame = inspect.currentframe()
|
|
265
281
|
while frame := frame and frame.f_back:
|
|
266
282
|
frame_path = Path(inspect.getfile(frame)).resolve()
|
|
@@ -297,13 +313,13 @@ def deprecate(
|
|
|
297
313
|
filename = Path(origin_frame.f_code.co_filename)
|
|
298
314
|
if filename.is_relative_to(Path.cwd()):
|
|
299
315
|
filename = filename.relative_to(Path.cwd())
|
|
300
|
-
loc = f"{filename}:{origin_frame.f_lineno}"
|
|
316
|
+
loc = f" ({filename}:{origin_frame.f_lineno})"
|
|
301
317
|
dedupe_key = f"{dedupe_key} {loc}"
|
|
302
318
|
|
|
303
319
|
if dedupe_key not in _EMITTED_DEPRECATION_WARNINGS:
|
|
304
320
|
msg = (
|
|
305
321
|
f"{feature_name} has been deprecated in version {deprecation_version}. {reason.rstrip('.').lstrip('. ')}. It will be completely "
|
|
306
|
-
f"removed in {removal_version}.
|
|
322
|
+
f"removed in {removal_version}.{loc}"
|
|
307
323
|
)
|
|
308
324
|
if _LOG_LEVEL <= LogLevel.WARNING:
|
|
309
325
|
print(f"[yellow]DeprecationWarning: {msg}[/yellow]", **kwargs)
|
|
@@ -395,3 +411,47 @@ def timing(msg: str):
|
|
|
395
411
|
yield
|
|
396
412
|
finally:
|
|
397
413
|
debug(f"[white]\\[timing] {msg}: {time.time() - start:.2f}s[/white]")
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
class PoorProgress:
|
|
417
|
+
"""A poor man's progress bar."""
|
|
418
|
+
|
|
419
|
+
def __init__(self):
|
|
420
|
+
"""Initialize the progress bar."""
|
|
421
|
+
super().__init__()
|
|
422
|
+
self.tasks = {}
|
|
423
|
+
self.progress = 0
|
|
424
|
+
self.total = 0
|
|
425
|
+
|
|
426
|
+
def add_task(self, task: str, total: int):
|
|
427
|
+
"""Add a task to the progress bar.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
task: The task name.
|
|
431
|
+
total: The total number of steps for the task.
|
|
432
|
+
|
|
433
|
+
Returns:
|
|
434
|
+
The task ID.
|
|
435
|
+
"""
|
|
436
|
+
self.total += total
|
|
437
|
+
task_id = TaskID(len(self.tasks))
|
|
438
|
+
self.tasks[task_id] = {"total": total, "current": 0}
|
|
439
|
+
return task_id
|
|
440
|
+
|
|
441
|
+
def advance(self, task: TaskID, advance: int = 1):
|
|
442
|
+
"""Advance the progress of a task.
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
task: The task ID.
|
|
446
|
+
advance: The number of steps to advance.
|
|
447
|
+
"""
|
|
448
|
+
if task in self.tasks:
|
|
449
|
+
self.tasks[task]["current"] += advance
|
|
450
|
+
self.progress += advance
|
|
451
|
+
_console.print(f"Progress: {self.progress}/{self.total}")
|
|
452
|
+
|
|
453
|
+
def start(self):
|
|
454
|
+
"""Start the progress bar."""
|
|
455
|
+
|
|
456
|
+
def stop(self):
|
|
457
|
+
"""Stop the progress bar."""
|
|
@@ -10,6 +10,7 @@ import click
|
|
|
10
10
|
from reflex import constants
|
|
11
11
|
from reflex.compiler import templates
|
|
12
12
|
from reflex.config import Config, get_config
|
|
13
|
+
from reflex.environment import environment
|
|
13
14
|
from reflex.utils import console, path_ops
|
|
14
15
|
from reflex.utils.prerequisites import get_project_hash, get_web_dir
|
|
15
16
|
from reflex.utils.registry import get_npm_registry
|
|
@@ -192,7 +193,11 @@ def _compile_vite_config(config: Config):
|
|
|
192
193
|
base = "/"
|
|
193
194
|
if frontend_path := config.frontend_path.strip("/"):
|
|
194
195
|
base += frontend_path + "/"
|
|
195
|
-
return templates.vite_config_template(
|
|
196
|
+
return templates.vite_config_template(
|
|
197
|
+
base=base,
|
|
198
|
+
hmr=environment.VITE_HMR.get(),
|
|
199
|
+
force_full_reload=environment.VITE_FORCE_FULL_RELOAD.get(),
|
|
200
|
+
)
|
|
196
201
|
|
|
197
202
|
|
|
198
203
|
def initialize_vite_config():
|
reflex/utils/imports.py
CHANGED
reflex/utils/prerequisites.py
CHANGED
|
@@ -248,6 +248,7 @@ def get_compiled_app(
|
|
|
248
248
|
prerender_routes: bool = False,
|
|
249
249
|
dry_run: bool = False,
|
|
250
250
|
check_if_schema_up_to_date: bool = False,
|
|
251
|
+
use_rich: bool = True,
|
|
251
252
|
) -> ModuleType:
|
|
252
253
|
"""Get the app module based on the default config after first compiling it.
|
|
253
254
|
|
|
@@ -256,6 +257,7 @@ def get_compiled_app(
|
|
|
256
257
|
prerender_routes: Whether to prerender routes.
|
|
257
258
|
dry_run: If True, do not write the compiled app to disk.
|
|
258
259
|
check_if_schema_up_to_date: If True, check if the schema is up to date.
|
|
260
|
+
use_rich: Whether to use rich progress bars.
|
|
259
261
|
|
|
260
262
|
Returns:
|
|
261
263
|
The compiled app based on the default config.
|
|
@@ -263,7 +265,7 @@ def get_compiled_app(
|
|
|
263
265
|
app, app_module = get_and_validate_app(
|
|
264
266
|
reload=reload, check_if_schema_up_to_date=check_if_schema_up_to_date
|
|
265
267
|
)
|
|
266
|
-
app._compile(prerender_routes=prerender_routes, dry_run=dry_run)
|
|
268
|
+
app._compile(prerender_routes=prerender_routes, dry_run=dry_run, use_rich=use_rich)
|
|
267
269
|
return app_module
|
|
268
270
|
|
|
269
271
|
|
reflex/utils/pyi_generator.py
CHANGED
|
@@ -547,7 +547,7 @@ def _generate_component_create_functiondef(
|
|
|
547
547
|
kwargs.extend(prop_kwargs)
|
|
548
548
|
|
|
549
549
|
def figure_out_return_type(annotation: Any):
|
|
550
|
-
if
|
|
550
|
+
if isinstance(annotation, type) and issubclass(annotation, inspect._empty):
|
|
551
551
|
return ast.Name(id="EventType[Any]")
|
|
552
552
|
|
|
553
553
|
if not isinstance(annotation, str) and get_origin(annotation) is tuple:
|
|
@@ -1181,7 +1181,7 @@ class PyiGenerator:
|
|
|
1181
1181
|
class_names = {
|
|
1182
1182
|
name: obj
|
|
1183
1183
|
for name, obj in vars(module).items()
|
|
1184
|
-
if
|
|
1184
|
+
if isinstance(obj, type)
|
|
1185
1185
|
and (
|
|
1186
1186
|
rx_types.safe_issubclass(obj, Component)
|
|
1187
1187
|
or rx_types.safe_issubclass(obj, SimpleNamespace)
|
reflex/utils/serializers.py
CHANGED
|
@@ -187,7 +187,7 @@ def get_serializer(type_: type) -> Serializer | None:
|
|
|
187
187
|
|
|
188
188
|
# If the type is not registered, check if it is a subclass of a registered type.
|
|
189
189
|
for registered_type, serializer in reversed(SERIALIZERS.items()):
|
|
190
|
-
if
|
|
190
|
+
if issubclass(type_, registered_type):
|
|
191
191
|
return serializer
|
|
192
192
|
|
|
193
193
|
# If there is no serializer, return None.
|
|
@@ -211,7 +211,7 @@ def get_serializer_type(type_: type) -> type | None:
|
|
|
211
211
|
|
|
212
212
|
# If the type is not registered, check if it is a subclass of a registered type.
|
|
213
213
|
for registered_type, serializer in reversed(SERIALIZER_TYPES.items()):
|
|
214
|
-
if
|
|
214
|
+
if issubclass(type_, registered_type):
|
|
215
215
|
return serializer
|
|
216
216
|
|
|
217
217
|
# If there is no serializer, return None.
|
|
@@ -244,11 +244,11 @@ def can_serialize(type_: type, into_type: type | None = None) -> bool:
|
|
|
244
244
|
Returns:
|
|
245
245
|
Whether there is a serializer for the type.
|
|
246
246
|
"""
|
|
247
|
-
return
|
|
247
|
+
return (
|
|
248
248
|
isinstance(type_, type)
|
|
249
249
|
and dataclasses.is_dataclass(type_)
|
|
250
250
|
and (into_type is None or into_type is dict)
|
|
251
|
-
)
|
|
251
|
+
) or has_serializer(type_, into_type)
|
|
252
252
|
|
|
253
253
|
|
|
254
254
|
@serializer(to=str)
|
reflex/utils/types.py
CHANGED
|
@@ -52,11 +52,8 @@ UnionTypes = (Union, types.UnionType)
|
|
|
52
52
|
GenericType = type | _GenericAlias
|
|
53
53
|
|
|
54
54
|
# Valid state var types.
|
|
55
|
-
JSONType = {str, int, float, bool}
|
|
56
|
-
PrimitiveType = int | float | bool | str | list | dict | set | tuple
|
|
57
55
|
PrimitiveTypes = (int, float, bool, str, list, dict, set, tuple)
|
|
58
|
-
|
|
59
|
-
StateIterVar = list | set | tuple
|
|
56
|
+
StateVarTypes = (*PrimitiveTypes, Base, type(None))
|
|
60
57
|
|
|
61
58
|
if TYPE_CHECKING:
|
|
62
59
|
from reflex.vars.base import Var
|
|
@@ -401,6 +398,8 @@ def get_field_type(cls: GenericType, field_name: str) -> GenericType | None:
|
|
|
401
398
|
Returns:
|
|
402
399
|
The type of the field, if it exists, else None.
|
|
403
400
|
"""
|
|
401
|
+
if (fields := getattr(cls, "_fields", None)) is not None and field_name in fields:
|
|
402
|
+
return fields[field_name].annotated_type
|
|
404
403
|
if (
|
|
405
404
|
hasattr(cls, "__fields__")
|
|
406
405
|
and field_name in cls.__fields__
|
|
@@ -857,8 +856,11 @@ def is_valid_var_type(type_: type) -> bool:
|
|
|
857
856
|
|
|
858
857
|
if is_union(type_):
|
|
859
858
|
return all(is_valid_var_type(arg) for arg in get_args(type_))
|
|
859
|
+
|
|
860
|
+
type_ = origin if (origin := get_origin(type_)) is not None else type_
|
|
861
|
+
|
|
860
862
|
return (
|
|
861
|
-
|
|
863
|
+
issubclass(type_, StateVarTypes)
|
|
862
864
|
or serializers.has_serializer(type_)
|
|
863
865
|
or dataclasses.is_dataclass(type_)
|
|
864
866
|
)
|
|
@@ -993,11 +995,6 @@ def validate_literal(key: str, value: Any, expected_type: type, comp_name: str):
|
|
|
993
995
|
raise ValueError(msg)
|
|
994
996
|
|
|
995
997
|
|
|
996
|
-
# Store this here for performance.
|
|
997
|
-
StateBases = get_base_class(StateVar)
|
|
998
|
-
StateIterBases = get_base_class(StateIterVar)
|
|
999
|
-
|
|
1000
|
-
|
|
1001
998
|
def safe_issubclass(cls: Any, cls_check: Any | tuple[Any, ...]):
|
|
1002
999
|
"""Check if a class is a subclass of another class. Returns False if internal error occurs.
|
|
1003
1000
|
|
reflex/vars/base.py
CHANGED
|
@@ -366,7 +366,7 @@ def can_use_in_object_var(cls: GenericType) -> bool:
|
|
|
366
366
|
if types.is_union(cls):
|
|
367
367
|
return all(can_use_in_object_var(t) for t in types.get_args(cls))
|
|
368
368
|
return (
|
|
369
|
-
|
|
369
|
+
isinstance(cls, type)
|
|
370
370
|
and not safe_issubclass(cls, Var)
|
|
371
371
|
and serializers.can_serialize(cls, dict)
|
|
372
372
|
)
|
|
@@ -516,6 +516,17 @@ class Var(Generic[VAR_TYPE], metaclass=MetaclassVar):
|
|
|
516
516
|
"""
|
|
517
517
|
return self._var_data
|
|
518
518
|
|
|
519
|
+
def __deepcopy__(self, memo: dict[int, Any]) -> Self:
|
|
520
|
+
"""Deepcopy the var.
|
|
521
|
+
|
|
522
|
+
Args:
|
|
523
|
+
memo: The memo dictionary to use for the deepcopy.
|
|
524
|
+
|
|
525
|
+
Returns:
|
|
526
|
+
A deepcopy of the var.
|
|
527
|
+
"""
|
|
528
|
+
return self
|
|
529
|
+
|
|
519
530
|
def equals(self, other: Var) -> bool:
|
|
520
531
|
"""Check if two vars are equal.
|
|
521
532
|
|
|
@@ -795,7 +806,7 @@ class Var(Generic[VAR_TYPE], metaclass=MetaclassVar):
|
|
|
795
806
|
if can_use_in_object_var(output):
|
|
796
807
|
return self.to(ObjectVar, output)
|
|
797
808
|
|
|
798
|
-
if
|
|
809
|
+
if isinstance(output, type):
|
|
799
810
|
for var_subclass in _var_subclasses[::-1]:
|
|
800
811
|
if safe_issubclass(output, var_subclass.var_subclass):
|
|
801
812
|
current_var_type = self._var_type
|
|
@@ -891,7 +902,7 @@ class Var(Generic[VAR_TYPE], metaclass=MetaclassVar):
|
|
|
891
902
|
args = get_args(var_type)
|
|
892
903
|
fixed_type = unionize(*(type(arg) for arg in args))
|
|
893
904
|
|
|
894
|
-
if not
|
|
905
|
+
if not isinstance(fixed_type, type):
|
|
895
906
|
msg = f"Unsupported type {var_type} for guess_type."
|
|
896
907
|
raise TypeError(msg)
|
|
897
908
|
|
|
@@ -930,6 +941,7 @@ class Var(Generic[VAR_TYPE], metaclass=MetaclassVar):
|
|
|
930
941
|
Returns:
|
|
931
942
|
A function that that creates a setter for the var.
|
|
932
943
|
"""
|
|
944
|
+
setter_name = Var._get_setter_name_for_name(name)
|
|
933
945
|
|
|
934
946
|
def setter(state: Any, value: Any):
|
|
935
947
|
"""Get the setter for the var.
|
|
@@ -951,7 +963,7 @@ class Var(Generic[VAR_TYPE], metaclass=MetaclassVar):
|
|
|
951
963
|
|
|
952
964
|
setter.__annotations__["value"] = self._var_type
|
|
953
965
|
|
|
954
|
-
setter.__qualname__ =
|
|
966
|
+
setter.__qualname__ = setter_name
|
|
955
967
|
|
|
956
968
|
return setter
|
|
957
969
|
|
|
@@ -1153,18 +1165,6 @@ class Var(Generic[VAR_TYPE], metaclass=MetaclassVar):
|
|
|
1153
1165
|
"""
|
|
1154
1166
|
return dataclasses.replace(self, _var_data=None)
|
|
1155
1167
|
|
|
1156
|
-
def __get__(self, instance: Any, owner: Any):
|
|
1157
|
-
"""Get the var.
|
|
1158
|
-
|
|
1159
|
-
Args:
|
|
1160
|
-
instance: The instance to get the var from.
|
|
1161
|
-
owner: The owner of the var.
|
|
1162
|
-
|
|
1163
|
-
Returns:
|
|
1164
|
-
The var.
|
|
1165
|
-
"""
|
|
1166
|
-
return self
|
|
1167
|
-
|
|
1168
1168
|
def _decode(self) -> Any:
|
|
1169
1169
|
"""Decode Var as a python value.
|
|
1170
1170
|
|
|
@@ -1409,7 +1409,7 @@ class LiteralVar(Var):
|
|
|
1409
1409
|
bases = cls.__bases__
|
|
1410
1410
|
|
|
1411
1411
|
bases_normalized = [
|
|
1412
|
-
base if
|
|
1412
|
+
base if isinstance(base, type) else get_origin(base) for base in bases
|
|
1413
1413
|
]
|
|
1414
1414
|
|
|
1415
1415
|
possible_bases = [
|
|
@@ -1476,6 +1476,13 @@ class LiteralVar(Var):
|
|
|
1476
1476
|
if isinstance(value, var_subclass.python_types):
|
|
1477
1477
|
return literal_subclass.create(value, _var_data=_var_data)
|
|
1478
1478
|
|
|
1479
|
+
if (
|
|
1480
|
+
(as_var_method := getattr(value, "_as_var", None)) is not None
|
|
1481
|
+
and callable(as_var_method)
|
|
1482
|
+
and isinstance((resulting_var := as_var_method()), Var)
|
|
1483
|
+
):
|
|
1484
|
+
return resulting_var
|
|
1485
|
+
|
|
1479
1486
|
from reflex.event import EventHandler
|
|
1480
1487
|
from reflex.utils.format import get_event_handler_parts
|
|
1481
1488
|
|
reflex/vars/object.py
CHANGED
|
@@ -6,7 +6,6 @@ import collections.abc
|
|
|
6
6
|
import dataclasses
|
|
7
7
|
import typing
|
|
8
8
|
from collections.abc import Mapping
|
|
9
|
-
from inspect import isclass
|
|
10
9
|
from typing import (
|
|
11
10
|
Any,
|
|
12
11
|
NoReturn,
|
|
@@ -328,7 +327,10 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=Mapping):
|
|
|
328
327
|
|
|
329
328
|
if (
|
|
330
329
|
is_typeddict(fixed_type)
|
|
331
|
-
or (
|
|
330
|
+
or (
|
|
331
|
+
isinstance(fixed_type, type)
|
|
332
|
+
and not safe_issubclass(fixed_type, Mapping)
|
|
333
|
+
)
|
|
332
334
|
or (fixed_type in types.UnionTypes)
|
|
333
335
|
):
|
|
334
336
|
attribute_type = get_attribute_access_type(var_type, name)
|