reflex 0.7.2.dev1__py3-none-any.whl → 0.7.3__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/.templates/jinja/web/pages/custom_component.js.jinja2 +6 -3
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +1 -1
- reflex/.templates/web/components/shiki/code.js +26 -21
- reflex/.templates/web/postcss.config.js +1 -1
- reflex/.templates/web/utils/client_side_routing.js +18 -16
- reflex/.templates/web/utils/helpers/dataeditor.js +1 -1
- reflex/.templates/web/utils/helpers/range.js +30 -30
- reflex/.templates/web/utils/state.js +44 -22
- reflex/app_mixins/middleware.py +9 -10
- reflex/compiler/compiler.py +7 -0
- reflex/components/core/banner.py +1 -1
- reflex/components/core/foreach.py +3 -2
- reflex/components/datadisplay/logo.py +1 -1
- reflex/components/el/__init__.pyi +22 -0
- reflex/components/el/elements/__init__.py +20 -1
- reflex/components/el/elements/__init__.pyi +42 -1
- reflex/components/el/elements/media.py +11 -0
- reflex/components/el/elements/media.pyi +11 -0
- reflex/components/lucide/icon.py +8 -6
- reflex/components/lucide/icon.pyi +0 -1
- reflex/components/radix/themes/components/slider.py +2 -1
- reflex/config.py +13 -7
- reflex/custom_components/custom_components.py +23 -25
- reflex/event.py +7 -2
- reflex/istate/data.py +59 -7
- reflex/reflex.py +69 -30
- reflex/state.py +3 -3
- reflex/testing.py +4 -10
- reflex/utils/exceptions.py +31 -1
- reflex/utils/exec.py +4 -8
- reflex/utils/export.py +2 -2
- reflex/vars/base.py +23 -1
- reflex/vars/number.py +22 -21
- reflex/vars/object.py +29 -0
- reflex/vars/sequence.py +37 -0
- {reflex-0.7.2.dev1.dist-info → reflex-0.7.3.dist-info}/METADATA +42 -42
- {reflex-0.7.2.dev1.dist-info → reflex-0.7.3.dist-info}/RECORD +61 -70
- {reflex-0.7.2.dev1.dist-info → reflex-0.7.3.dist-info}/WHEEL +1 -1
- {reflex-0.7.2.dev1.dist-info → reflex-0.7.3.dist-info}/entry_points.txt +0 -3
- benchmarks/__init__.py +0 -3
- benchmarks/benchmark_compile_times.py +0 -147
- benchmarks/benchmark_imports.py +0 -128
- benchmarks/benchmark_lighthouse.py +0 -75
- benchmarks/benchmark_package_size.py +0 -135
- benchmarks/benchmark_web_size.py +0 -106
- benchmarks/conftest.py +0 -20
- benchmarks/lighthouse.sh +0 -77
- benchmarks/utils.py +0 -74
- {reflex-0.7.2.dev1.dist-info → reflex-0.7.3.dist-info}/licenses/LICENSE +0 -0
reflex/reflex.py
CHANGED
|
@@ -22,10 +22,6 @@ typer.core.rich = None # pyright: ignore [reportPrivateImportUsage]
|
|
|
22
22
|
cli = typer.Typer(add_completion=False, pretty_exceptions_enable=False)
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
# Get the config.
|
|
26
|
-
config = get_config()
|
|
27
|
-
|
|
28
|
-
|
|
29
25
|
def version(value: bool):
|
|
30
26
|
"""Get the Reflex version.
|
|
31
27
|
|
|
@@ -58,13 +54,19 @@ def main(
|
|
|
58
54
|
def _init(
|
|
59
55
|
name: str,
|
|
60
56
|
template: str | None = None,
|
|
61
|
-
loglevel: constants.LogLevel =
|
|
57
|
+
loglevel: constants.LogLevel | None = None,
|
|
62
58
|
ai: bool = False,
|
|
63
59
|
):
|
|
64
60
|
"""Initialize a new Reflex app in the given directory."""
|
|
65
61
|
from reflex.utils import exec, prerequisites
|
|
66
62
|
|
|
63
|
+
if loglevel is not None:
|
|
64
|
+
console.set_log_level(loglevel)
|
|
65
|
+
|
|
66
|
+
config = get_config()
|
|
67
|
+
|
|
67
68
|
# Set the log level.
|
|
69
|
+
loglevel = loglevel or config.loglevel
|
|
68
70
|
console.set_log_level(loglevel)
|
|
69
71
|
|
|
70
72
|
# Show system info
|
|
@@ -109,8 +111,8 @@ def init(
|
|
|
109
111
|
None,
|
|
110
112
|
help="The template to initialize the app with.",
|
|
111
113
|
),
|
|
112
|
-
loglevel: constants.LogLevel = typer.Option(
|
|
113
|
-
|
|
114
|
+
loglevel: constants.LogLevel | None = typer.Option(
|
|
115
|
+
None, help="The log level to use."
|
|
114
116
|
),
|
|
115
117
|
ai: bool = typer.Option(
|
|
116
118
|
False,
|
|
@@ -127,12 +129,20 @@ def _run(
|
|
|
127
129
|
backend: bool = True,
|
|
128
130
|
frontend_port: int | None = None,
|
|
129
131
|
backend_port: int | None = None,
|
|
130
|
-
backend_host: str =
|
|
131
|
-
loglevel: constants.LogLevel =
|
|
132
|
+
backend_host: str | None = None,
|
|
133
|
+
loglevel: constants.LogLevel | None = None,
|
|
132
134
|
):
|
|
133
135
|
"""Run the app in the given directory."""
|
|
134
136
|
from reflex.utils import build, exec, prerequisites, processes
|
|
135
137
|
|
|
138
|
+
if loglevel is not None:
|
|
139
|
+
console.set_log_level(loglevel)
|
|
140
|
+
|
|
141
|
+
config = get_config()
|
|
142
|
+
|
|
143
|
+
loglevel = loglevel or config.loglevel
|
|
144
|
+
backend_host = backend_host or config.backend_host
|
|
145
|
+
|
|
136
146
|
# Set the log level.
|
|
137
147
|
console.set_log_level(loglevel)
|
|
138
148
|
|
|
@@ -274,21 +284,19 @@ def run(
|
|
|
274
284
|
help="Execute only backend.",
|
|
275
285
|
envvar=environment.REFLEX_BACKEND_ONLY.name,
|
|
276
286
|
),
|
|
277
|
-
frontend_port: int = typer.Option(
|
|
278
|
-
|
|
287
|
+
frontend_port: int | None = typer.Option(
|
|
288
|
+
None,
|
|
279
289
|
help="Specify a different frontend port.",
|
|
280
290
|
envvar=environment.REFLEX_FRONTEND_PORT.name,
|
|
281
291
|
),
|
|
282
|
-
backend_port: int = typer.Option(
|
|
283
|
-
|
|
292
|
+
backend_port: int | None = typer.Option(
|
|
293
|
+
None,
|
|
284
294
|
help="Specify a different backend port.",
|
|
285
295
|
envvar=environment.REFLEX_BACKEND_PORT.name,
|
|
286
296
|
),
|
|
287
|
-
backend_host: str = typer.Option(
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
loglevel: constants.LogLevel = typer.Option(
|
|
291
|
-
config.loglevel, help="The log level to use."
|
|
297
|
+
backend_host: str | None = typer.Option(None, help="Specify the backend host."),
|
|
298
|
+
loglevel: constants.LogLevel | None = typer.Option(
|
|
299
|
+
None, help="The log level to use."
|
|
292
300
|
),
|
|
293
301
|
):
|
|
294
302
|
"""Run the app in the current directory."""
|
|
@@ -296,6 +304,16 @@ def run(
|
|
|
296
304
|
console.error("Cannot use both --frontend-only and --backend-only options.")
|
|
297
305
|
raise typer.Exit(1)
|
|
298
306
|
|
|
307
|
+
if loglevel is not None:
|
|
308
|
+
console.set_log_level(loglevel)
|
|
309
|
+
|
|
310
|
+
config = get_config()
|
|
311
|
+
|
|
312
|
+
frontend_port = frontend_port or config.frontend_port
|
|
313
|
+
backend_port = backend_port or config.backend_port
|
|
314
|
+
backend_host = backend_host or config.backend_host
|
|
315
|
+
loglevel = loglevel or config.loglevel
|
|
316
|
+
|
|
299
317
|
environment.REFLEX_COMPILE_CONTEXT.set(constants.CompileContext.RUN)
|
|
300
318
|
environment.REFLEX_BACKEND_ONLY.set(backend)
|
|
301
319
|
environment.REFLEX_FRONTEND_ONLY.set(frontend)
|
|
@@ -335,8 +353,8 @@ def export(
|
|
|
335
353
|
env: constants.Env = typer.Option(
|
|
336
354
|
constants.Env.PROD, help="The environment to export the app in."
|
|
337
355
|
),
|
|
338
|
-
loglevel: constants.LogLevel = typer.Option(
|
|
339
|
-
|
|
356
|
+
loglevel: constants.LogLevel | None = typer.Option(
|
|
357
|
+
None, help="The log level to use."
|
|
340
358
|
),
|
|
341
359
|
):
|
|
342
360
|
"""Export the app to a zip file."""
|
|
@@ -347,8 +365,11 @@ def export(
|
|
|
347
365
|
|
|
348
366
|
frontend, backend = prerequisites.check_running_mode(frontend, backend)
|
|
349
367
|
|
|
368
|
+
loglevel = loglevel or get_config().loglevel
|
|
369
|
+
console.set_log_level(loglevel)
|
|
370
|
+
|
|
350
371
|
if prerequisites.needs_reinit(frontend=frontend or not backend):
|
|
351
|
-
_init(name=
|
|
372
|
+
_init(name=get_config().app_name, loglevel=loglevel)
|
|
352
373
|
|
|
353
374
|
export_utils.export(
|
|
354
375
|
zipping=zipping,
|
|
@@ -362,10 +383,14 @@ def export(
|
|
|
362
383
|
|
|
363
384
|
|
|
364
385
|
@cli.command()
|
|
365
|
-
def login(loglevel: constants.LogLevel = typer.Option(
|
|
386
|
+
def login(loglevel: constants.LogLevel | None = typer.Option(None)):
|
|
366
387
|
"""Authenticate with experimental Reflex hosting service."""
|
|
367
388
|
from reflex_cli.v2 import cli as hosting_cli
|
|
368
389
|
|
|
390
|
+
loglevel = loglevel or get_config().loglevel
|
|
391
|
+
|
|
392
|
+
console.set_log_level(loglevel)
|
|
393
|
+
|
|
369
394
|
check_version()
|
|
370
395
|
|
|
371
396
|
validated_info = hosting_cli.login()
|
|
@@ -376,8 +401,8 @@ def login(loglevel: constants.LogLevel = typer.Option(config.loglevel)):
|
|
|
376
401
|
|
|
377
402
|
@cli.command()
|
|
378
403
|
def logout(
|
|
379
|
-
loglevel: constants.LogLevel = typer.Option(
|
|
380
|
-
|
|
404
|
+
loglevel: constants.LogLevel | None = typer.Option(
|
|
405
|
+
None, help="The log level to use."
|
|
381
406
|
),
|
|
382
407
|
):
|
|
383
408
|
"""Log out of access to Reflex hosting service."""
|
|
@@ -385,6 +410,8 @@ def logout(
|
|
|
385
410
|
|
|
386
411
|
check_version()
|
|
387
412
|
|
|
413
|
+
loglevel = loglevel or get_config().loglevel
|
|
414
|
+
|
|
388
415
|
logout(loglevel) # pyright: ignore [reportArgumentType]
|
|
389
416
|
|
|
390
417
|
|
|
@@ -403,6 +430,8 @@ def db_init():
|
|
|
403
430
|
from reflex import model
|
|
404
431
|
from reflex.utils import prerequisites
|
|
405
432
|
|
|
433
|
+
config = get_config()
|
|
434
|
+
|
|
406
435
|
# Check the database url.
|
|
407
436
|
if config.db_url is None:
|
|
408
437
|
console.error("db_url is not configured, cannot initialize.")
|
|
@@ -470,8 +499,8 @@ def makemigrations(
|
|
|
470
499
|
|
|
471
500
|
@cli.command()
|
|
472
501
|
def deploy(
|
|
473
|
-
app_name: str = typer.Option(
|
|
474
|
-
|
|
502
|
+
app_name: str | None = typer.Option(
|
|
503
|
+
None,
|
|
475
504
|
"--app-name",
|
|
476
505
|
help="The name of the App to deploy under.",
|
|
477
506
|
),
|
|
@@ -510,8 +539,8 @@ def deploy(
|
|
|
510
539
|
"--envfile",
|
|
511
540
|
help="The path to an env file to use. Will override any envs set manually.",
|
|
512
541
|
),
|
|
513
|
-
loglevel: constants.LogLevel = typer.Option(
|
|
514
|
-
|
|
542
|
+
loglevel: constants.LogLevel | None = typer.Option(
|
|
543
|
+
None, help="The log level to use."
|
|
515
544
|
),
|
|
516
545
|
project: str | None = typer.Option(
|
|
517
546
|
None,
|
|
@@ -542,6 +571,14 @@ def deploy(
|
|
|
542
571
|
from reflex.utils import export as export_utils
|
|
543
572
|
from reflex.utils import prerequisites
|
|
544
573
|
|
|
574
|
+
if loglevel is not None:
|
|
575
|
+
console.set_log_level(loglevel)
|
|
576
|
+
|
|
577
|
+
config = get_config()
|
|
578
|
+
|
|
579
|
+
loglevel = loglevel or config.loglevel
|
|
580
|
+
app_name = app_name or config.app_name
|
|
581
|
+
|
|
545
582
|
check_version()
|
|
546
583
|
|
|
547
584
|
environment.REFLEX_COMPILE_CONTEXT.set(constants.CompileContext.DEPLOY)
|
|
@@ -608,13 +645,15 @@ def deploy(
|
|
|
608
645
|
@cli.command()
|
|
609
646
|
def rename(
|
|
610
647
|
new_name: str = typer.Argument(..., help="The new name for the app."),
|
|
611
|
-
loglevel: constants.LogLevel = typer.Option(
|
|
612
|
-
|
|
648
|
+
loglevel: constants.LogLevel | None = typer.Option(
|
|
649
|
+
None, help="The log level to use."
|
|
613
650
|
),
|
|
614
651
|
):
|
|
615
652
|
"""Rename the app in the current directory."""
|
|
616
653
|
from reflex.utils import prerequisites
|
|
617
654
|
|
|
655
|
+
loglevel = loglevel or get_config().loglevel
|
|
656
|
+
|
|
618
657
|
prerequisites.validate_app_name(new_name)
|
|
619
658
|
prerequisites.rename_app(new_name, loglevel)
|
|
620
659
|
|
reflex/state.py
CHANGED
|
@@ -1013,9 +1013,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1013
1013
|
|
|
1014
1014
|
if not types.is_valid_var_type(prop._var_type):
|
|
1015
1015
|
raise VarTypeError(
|
|
1016
|
-
"State vars must be
|
|
1017
|
-
"
|
|
1018
|
-
"
|
|
1016
|
+
"State vars must be of a serializable type. "
|
|
1017
|
+
"Valid types include strings, numbers, booleans, lists, "
|
|
1018
|
+
"dictionaries, dataclasses, datetime objects, and pydantic models. "
|
|
1019
1019
|
f'Found var "{prop._js_expr}" with type {prop._var_type}.'
|
|
1020
1020
|
)
|
|
1021
1021
|
cls._set_var(prop)
|
reflex/testing.py
CHANGED
|
@@ -54,18 +54,12 @@ from reflex.state import (
|
|
|
54
54
|
from reflex.utils import console
|
|
55
55
|
|
|
56
56
|
try:
|
|
57
|
-
from selenium import webdriver
|
|
58
|
-
from selenium.webdriver.remote.webdriver import
|
|
59
|
-
WebDriver,
|
|
60
|
-
)
|
|
57
|
+
from selenium import webdriver
|
|
58
|
+
from selenium.webdriver.remote.webdriver import WebDriver
|
|
61
59
|
|
|
62
60
|
if TYPE_CHECKING:
|
|
63
|
-
from selenium.webdriver.common.options import
|
|
64
|
-
|
|
65
|
-
)
|
|
66
|
-
from selenium.webdriver.remote.webelement import ( # pyright: ignore [reportMissingImports]
|
|
67
|
-
WebElement,
|
|
68
|
-
)
|
|
61
|
+
from selenium.webdriver.common.options import ArgOptions
|
|
62
|
+
from selenium.webdriver.remote.webelement import WebElement
|
|
69
63
|
|
|
70
64
|
has_selenium = True
|
|
71
65
|
except ImportError:
|
reflex/utils/exceptions.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"""Custom Exceptions."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from reflex.vars import Var
|
|
4
9
|
|
|
5
10
|
|
|
6
11
|
class ReflexError(Exception):
|
|
@@ -78,6 +83,31 @@ class VarAttributeError(ReflexError, AttributeError):
|
|
|
78
83
|
class UntypedVarError(ReflexError, TypeError):
|
|
79
84
|
"""Custom TypeError for untyped var errors."""
|
|
80
85
|
|
|
86
|
+
def __init__(self, var: Var, action: str, doc_link: str = ""):
|
|
87
|
+
"""Create an UntypedVarError from a var.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
var: The var.
|
|
91
|
+
action: The action that caused the error.
|
|
92
|
+
doc_link: The link to the documentation.
|
|
93
|
+
"""
|
|
94
|
+
var_data = var._get_all_var_data()
|
|
95
|
+
is_state_var = (
|
|
96
|
+
var_data
|
|
97
|
+
and var_data.state
|
|
98
|
+
and var_data.field_name
|
|
99
|
+
and var_data.state + "." + var_data.field_name == str(var)
|
|
100
|
+
)
|
|
101
|
+
super().__init__(
|
|
102
|
+
f"Cannot {action} on untyped var '{var!s}' of type '{var._var_type!s}'."
|
|
103
|
+
+ (
|
|
104
|
+
" Please add a type annotation to the var in the state class."
|
|
105
|
+
if is_state_var
|
|
106
|
+
else " You can call the var's .to(desired_type) method to convert it to the desired type."
|
|
107
|
+
)
|
|
108
|
+
+ (f" See {doc_link}" if doc_link else "")
|
|
109
|
+
)
|
|
110
|
+
|
|
81
111
|
|
|
82
112
|
class UntypedComputedVarError(ReflexError, TypeError):
|
|
83
113
|
"""Custom TypeError for untyped computed var errors."""
|
reflex/utils/exec.py
CHANGED
|
@@ -332,11 +332,9 @@ def run_granian_backend(host: str, port: int, loglevel: LogLevel):
|
|
|
332
332
|
"""
|
|
333
333
|
console.debug("Using Granian for backend")
|
|
334
334
|
try:
|
|
335
|
-
from granian import
|
|
336
|
-
from granian.
|
|
337
|
-
|
|
338
|
-
)
|
|
339
|
-
from granian.log import LogLevels # pyright: ignore [reportMissingImports]
|
|
335
|
+
from granian.constants import Interfaces
|
|
336
|
+
from granian.log import LogLevels
|
|
337
|
+
from granian.server import Server as Granian
|
|
340
338
|
|
|
341
339
|
Granian(
|
|
342
340
|
target=get_granian_target(),
|
|
@@ -466,9 +464,7 @@ def run_granian_backend_prod(host: str, port: int, loglevel: LogLevel):
|
|
|
466
464
|
from reflex.utils import processes
|
|
467
465
|
|
|
468
466
|
try:
|
|
469
|
-
from granian.constants import
|
|
470
|
-
Interfaces,
|
|
471
|
-
)
|
|
467
|
+
from granian.constants import Interfaces
|
|
472
468
|
|
|
473
469
|
command = [
|
|
474
470
|
"granian",
|
reflex/utils/export.py
CHANGED
|
@@ -6,8 +6,6 @@ from reflex import constants
|
|
|
6
6
|
from reflex.config import environment, get_config
|
|
7
7
|
from reflex.utils import build, console, exec, prerequisites, telemetry
|
|
8
8
|
|
|
9
|
-
config = get_config()
|
|
10
|
-
|
|
11
9
|
|
|
12
10
|
def export(
|
|
13
11
|
zipping: bool = True,
|
|
@@ -33,6 +31,8 @@ def export(
|
|
|
33
31
|
env: The environment to use. Defaults to constants.Env.PROD.
|
|
34
32
|
loglevel: The log level to use. Defaults to console._LOG_LEVEL.
|
|
35
33
|
"""
|
|
34
|
+
config = get_config()
|
|
35
|
+
|
|
36
36
|
# Set the log level.
|
|
37
37
|
console.set_log_level(loglevel)
|
|
38
38
|
|
reflex/vars/base.py
CHANGED
|
@@ -1256,6 +1256,27 @@ class Var(Generic[VAR_TYPE]):
|
|
|
1256
1256
|
|
|
1257
1257
|
if not TYPE_CHECKING:
|
|
1258
1258
|
|
|
1259
|
+
def __getitem__(self, key: Any) -> Var:
|
|
1260
|
+
"""Get the item from the var.
|
|
1261
|
+
|
|
1262
|
+
Args:
|
|
1263
|
+
key: The key to get.
|
|
1264
|
+
|
|
1265
|
+
Raises:
|
|
1266
|
+
UntypedVarError: If the var type is Any.
|
|
1267
|
+
TypeError: If the var type is Any.
|
|
1268
|
+
|
|
1269
|
+
# noqa: DAR101 self
|
|
1270
|
+
"""
|
|
1271
|
+
if self._var_type is Any:
|
|
1272
|
+
raise exceptions.UntypedVarError(
|
|
1273
|
+
self,
|
|
1274
|
+
f"access the item '{key}'",
|
|
1275
|
+
)
|
|
1276
|
+
raise TypeError(
|
|
1277
|
+
f"Var of type {self._var_type} does not support item access."
|
|
1278
|
+
)
|
|
1279
|
+
|
|
1259
1280
|
def __getattr__(self, name: str):
|
|
1260
1281
|
"""Get an attribute of the var.
|
|
1261
1282
|
|
|
@@ -1281,7 +1302,8 @@ class Var(Generic[VAR_TYPE]):
|
|
|
1281
1302
|
|
|
1282
1303
|
if self._var_type is Any:
|
|
1283
1304
|
raise exceptions.UntypedVarError(
|
|
1284
|
-
|
|
1305
|
+
self,
|
|
1306
|
+
f"access the attribute '{name}'",
|
|
1285
1307
|
)
|
|
1286
1308
|
|
|
1287
1309
|
raise VarAttributeError(
|
reflex/vars/number.py
CHANGED
|
@@ -23,6 +23,7 @@ from reflex.utils.exceptions import (
|
|
|
23
23
|
VarValueError,
|
|
24
24
|
)
|
|
25
25
|
from reflex.utils.imports import ImportDict, ImportVar
|
|
26
|
+
from reflex.utils.types import safe_issubclass
|
|
26
27
|
|
|
27
28
|
from .base import (
|
|
28
29
|
CustomVarOperationReturn,
|
|
@@ -524,7 +525,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)):
|
|
|
524
525
|
Returns:
|
|
525
526
|
bool: True if the number is a float.
|
|
526
527
|
"""
|
|
527
|
-
return
|
|
528
|
+
return safe_issubclass(self._var_type, float)
|
|
528
529
|
|
|
529
530
|
def _is_strict_int(self) -> bool:
|
|
530
531
|
"""Check if the number is an int.
|
|
@@ -532,7 +533,7 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)):
|
|
|
532
533
|
Returns:
|
|
533
534
|
bool: True if the number is an int.
|
|
534
535
|
"""
|
|
535
|
-
return
|
|
536
|
+
return safe_issubclass(self._var_type, int)
|
|
536
537
|
|
|
537
538
|
def __format__(self, format_spec: str) -> str:
|
|
538
539
|
"""Format the number.
|
|
@@ -546,6 +547,20 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)):
|
|
|
546
547
|
Raises:
|
|
547
548
|
VarValueError: If the format specifier is not supported.
|
|
548
549
|
"""
|
|
550
|
+
from .sequence import (
|
|
551
|
+
get_decimal_string_operation,
|
|
552
|
+
get_decimal_string_separator_operation,
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
separator = ""
|
|
556
|
+
|
|
557
|
+
if format_spec and format_spec[:1] == ",":
|
|
558
|
+
separator = ","
|
|
559
|
+
format_spec = format_spec[1:]
|
|
560
|
+
elif format_spec and format_spec[:1] == "_":
|
|
561
|
+
separator = "_"
|
|
562
|
+
format_spec = format_spec[1:]
|
|
563
|
+
|
|
549
564
|
if (
|
|
550
565
|
format_spec
|
|
551
566
|
and format_spec[-1] == "f"
|
|
@@ -553,14 +568,17 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)):
|
|
|
553
568
|
and format_spec[1:-1].isdigit()
|
|
554
569
|
):
|
|
555
570
|
how_many_decimals = int(format_spec[1:-1])
|
|
571
|
+
return f"{get_decimal_string_operation(self, Var.create(how_many_decimals), Var.create(separator))}"
|
|
572
|
+
|
|
573
|
+
if not format_spec and separator:
|
|
556
574
|
return (
|
|
557
|
-
f"{
|
|
575
|
+
f"{get_decimal_string_separator_operation(self, Var.create(separator))}"
|
|
558
576
|
)
|
|
559
577
|
|
|
560
578
|
if format_spec:
|
|
561
579
|
raise VarValueError(
|
|
562
580
|
(
|
|
563
|
-
"Unknown format code '{}' for object of type 'NumberVar'. It is only supported to use '.f' for float numbers."
|
|
581
|
+
"Unknown format code '{}' for object of type 'NumberVar'. It is only supported to use ',', '_', and '.f' for float numbers."
|
|
564
582
|
"If possible, use computed variables instead: https://reflex.dev/docs/vars/computed-vars/"
|
|
565
583
|
).format(format_spec)
|
|
566
584
|
)
|
|
@@ -568,23 +586,6 @@ class NumberVar(Var[NUMBER_T], python_types=(int, float)):
|
|
|
568
586
|
return super().__format__(format_spec)
|
|
569
587
|
|
|
570
588
|
|
|
571
|
-
@var_operation
|
|
572
|
-
def get_decimal_string_operation(value: NumberVar, decimals: NumberVar):
|
|
573
|
-
"""Get the decimal string of the number.
|
|
574
|
-
|
|
575
|
-
Args:
|
|
576
|
-
value: The number.
|
|
577
|
-
decimals: The number of decimals.
|
|
578
|
-
|
|
579
|
-
Returns:
|
|
580
|
-
The decimal string of the number.
|
|
581
|
-
"""
|
|
582
|
-
return var_operation_return(
|
|
583
|
-
js_expression=f"({value}.toFixed({decimals}))",
|
|
584
|
-
var_type=str,
|
|
585
|
-
)
|
|
586
|
-
|
|
587
|
-
|
|
588
589
|
def binary_number_operation(
|
|
589
590
|
func: Callable[[NumberVar, NumberVar], str],
|
|
590
591
|
) -> Callable[[number_types, number_types], NumberVar]:
|
reflex/vars/object.py
CHANGED
|
@@ -202,6 +202,12 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=Mapping):
|
|
|
202
202
|
key: Var | Any,
|
|
203
203
|
) -> ObjectVar[Mapping[OTHER_KEY_TYPE, VALUE_TYPE]]: ...
|
|
204
204
|
|
|
205
|
+
@overload
|
|
206
|
+
def __getitem__(
|
|
207
|
+
self: ObjectVar[Mapping[Any, VALUE_TYPE]],
|
|
208
|
+
key: Var | Any,
|
|
209
|
+
) -> Var[VALUE_TYPE]: ...
|
|
210
|
+
|
|
205
211
|
def __getitem__(self, key: Var | Any) -> Var:
|
|
206
212
|
"""Get an item from the object.
|
|
207
213
|
|
|
@@ -221,6 +227,29 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=Mapping):
|
|
|
221
227
|
return self.__getattr__(key)
|
|
222
228
|
return ObjectItemOperation.create(self, key).guess_type()
|
|
223
229
|
|
|
230
|
+
def get(self, key: Var | Any, default: Var | Any | None = None) -> Var:
|
|
231
|
+
"""Get an item from the object.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
key: The key to get from the object.
|
|
235
|
+
default: The default value if the key is not found.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
The item from the object.
|
|
239
|
+
"""
|
|
240
|
+
from reflex.components.core.cond import cond
|
|
241
|
+
|
|
242
|
+
if default is None:
|
|
243
|
+
default = Var.create(None)
|
|
244
|
+
|
|
245
|
+
value = self.__getitem__(key) # pyright: ignore[reportUnknownVariableType,reportAttributeAccessIssue,reportUnknownMemberType]
|
|
246
|
+
|
|
247
|
+
return cond( # pyright: ignore[reportUnknownVariableType]
|
|
248
|
+
value,
|
|
249
|
+
value,
|
|
250
|
+
default,
|
|
251
|
+
)
|
|
252
|
+
|
|
224
253
|
# NoReturn is used here to catch when key value is Any
|
|
225
254
|
@overload
|
|
226
255
|
def __getattr__( # pyright: ignore [reportOverlappingOverload]
|
reflex/vars/sequence.py
CHANGED
|
@@ -1202,6 +1202,43 @@ def string_replace_operation(
|
|
|
1202
1202
|
)
|
|
1203
1203
|
|
|
1204
1204
|
|
|
1205
|
+
@var_operation
|
|
1206
|
+
def get_decimal_string_separator_operation(value: NumberVar, separator: StringVar):
|
|
1207
|
+
"""Get the decimal string separator.
|
|
1208
|
+
|
|
1209
|
+
Args:
|
|
1210
|
+
value: The number.
|
|
1211
|
+
separator: The separator.
|
|
1212
|
+
|
|
1213
|
+
Returns:
|
|
1214
|
+
The decimal string separator.
|
|
1215
|
+
"""
|
|
1216
|
+
return var_operation_return(
|
|
1217
|
+
js_expression=f"({value}.toLocaleString('en-US').replaceAll(',', {separator}))",
|
|
1218
|
+
var_type=str,
|
|
1219
|
+
)
|
|
1220
|
+
|
|
1221
|
+
|
|
1222
|
+
@var_operation
|
|
1223
|
+
def get_decimal_string_operation(
|
|
1224
|
+
value: NumberVar, decimals: NumberVar, separator: StringVar
|
|
1225
|
+
):
|
|
1226
|
+
"""Get the decimal string of the number.
|
|
1227
|
+
|
|
1228
|
+
Args:
|
|
1229
|
+
value: The number.
|
|
1230
|
+
decimals: The number of decimals.
|
|
1231
|
+
separator: The separator.
|
|
1232
|
+
|
|
1233
|
+
Returns:
|
|
1234
|
+
The decimal string of the number.
|
|
1235
|
+
"""
|
|
1236
|
+
return var_operation_return(
|
|
1237
|
+
js_expression=f"({value}.toLocaleString('en-US', ((decimals) => ({{minimumFractionDigits: decimals, maximumFractionDigits: decimals}}))({decimals})).replaceAll(',', {separator}))",
|
|
1238
|
+
var_type=str,
|
|
1239
|
+
)
|
|
1240
|
+
|
|
1241
|
+
|
|
1205
1242
|
# Compile regex for finding reflex var tags.
|
|
1206
1243
|
_decode_var_pattern_re = (
|
|
1207
1244
|
rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"
|