reflex 0.7.6a0__py3-none-any.whl → 0.7.7__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/web/utils/state.js +9 -0
- reflex/app.py +7 -0
- reflex/components/component.py +2 -1
- reflex/components/el/elements/forms.py +63 -18
- reflex/components/el/elements/forms.pyi +1132 -4
- reflex/components/recharts/__init__.py +1 -0
- reflex/components/recharts/__init__.pyi +1 -0
- reflex/components/recharts/general.py +1 -1
- reflex/components/recharts/general.pyi +1 -1
- reflex/event.py +37 -0
- reflex/reflex.py +17 -13
- reflex/state.py +3 -2
- reflex/utils/decorator.py +21 -0
- reflex/utils/export.py +2 -2
- reflex/utils/prerequisites.py +28 -19
- reflex/utils/pyi_generator.py +13 -7
- reflex/utils/telemetry.py +82 -21
- reflex/utils/types.py +12 -0
- reflex/vars/base.py +15 -1
- reflex/vars/number.py +21 -0
- {reflex-0.7.6a0.dist-info → reflex-0.7.7.dist-info}/METADATA +16 -16
- {reflex-0.7.6a0.dist-info → reflex-0.7.7.dist-info}/RECORD +25 -25
- {reflex-0.7.6a0.dist-info → reflex-0.7.7.dist-info}/WHEEL +0 -0
- {reflex-0.7.6a0.dist-info → reflex-0.7.7.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.6a0.dist-info → reflex-0.7.7.dist-info}/licenses/LICENSE +0 -0
|
@@ -65,6 +65,7 @@ from .general import label as label
|
|
|
65
65
|
from .general import label_list as label_list
|
|
66
66
|
from .general import legend as legend
|
|
67
67
|
from .general import responsive_container as responsive_container
|
|
68
|
+
from .general import tooltip as tooltip
|
|
68
69
|
from .polar import Pie as Pie
|
|
69
70
|
from .polar import PolarAngleAxis as PolarAngleAxis
|
|
70
71
|
from .polar import PolarGrid as PolarGrid
|
|
@@ -259,7 +259,7 @@ class Cell(Recharts):
|
|
|
259
259
|
|
|
260
260
|
responsive_container = ResponsiveContainer.create
|
|
261
261
|
legend = Legend.create
|
|
262
|
-
graphing_tooltip = GraphingTooltip.create
|
|
262
|
+
graphing_tooltip = tooltip = GraphingTooltip.create
|
|
263
263
|
label = Label.create
|
|
264
264
|
label_list = LabelList.create
|
|
265
265
|
cell = Cell.create
|
|
@@ -533,7 +533,7 @@ class Cell(Recharts):
|
|
|
533
533
|
|
|
534
534
|
responsive_container = ResponsiveContainer.create
|
|
535
535
|
legend = Legend.create
|
|
536
|
-
graphing_tooltip = GraphingTooltip.create
|
|
536
|
+
graphing_tooltip = tooltip = GraphingTooltip.create
|
|
537
537
|
label = Label.create
|
|
538
538
|
label_list = LabelList.create
|
|
539
539
|
cell = Cell.create
|
reflex/event.py
CHANGED
|
@@ -507,6 +507,7 @@ class JavascriptHTMLInputElement:
|
|
|
507
507
|
"""Interface for a Javascript HTMLInputElement https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement."""
|
|
508
508
|
|
|
509
509
|
value: str = ""
|
|
510
|
+
checked: bool = False
|
|
510
511
|
|
|
511
512
|
|
|
512
513
|
@dataclasses.dataclass(
|
|
@@ -545,6 +546,42 @@ def input_event(e: ObjectVar[JavascriptInputEvent]) -> tuple[Var[str]]:
|
|
|
545
546
|
return (e.target.value,)
|
|
546
547
|
|
|
547
548
|
|
|
549
|
+
def int_input_event(e: ObjectVar[JavascriptInputEvent]) -> tuple[Var[int]]:
|
|
550
|
+
"""Get the value from an input event as an int.
|
|
551
|
+
|
|
552
|
+
Args:
|
|
553
|
+
e: The input event.
|
|
554
|
+
|
|
555
|
+
Returns:
|
|
556
|
+
The value from the input event as an int.
|
|
557
|
+
"""
|
|
558
|
+
return (Var("Number").to(FunctionVar).call(e.target.value).to(int),)
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def float_input_event(e: ObjectVar[JavascriptInputEvent]) -> tuple[Var[float]]:
|
|
562
|
+
"""Get the value from an input event as a float.
|
|
563
|
+
|
|
564
|
+
Args:
|
|
565
|
+
e: The input event.
|
|
566
|
+
|
|
567
|
+
Returns:
|
|
568
|
+
The value from the input event as a float.
|
|
569
|
+
"""
|
|
570
|
+
return (Var("Number").to(FunctionVar).call(e.target.value).to(float),)
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
def checked_input_event(e: ObjectVar[JavascriptInputEvent]) -> tuple[Var[bool]]:
|
|
574
|
+
"""Get the checked state from an input event.
|
|
575
|
+
|
|
576
|
+
Args:
|
|
577
|
+
e: The input event.
|
|
578
|
+
|
|
579
|
+
Returns:
|
|
580
|
+
The checked state from the input event.
|
|
581
|
+
"""
|
|
582
|
+
return (e.target.checked,)
|
|
583
|
+
|
|
584
|
+
|
|
548
585
|
class KeyInputInfo(TypedDict):
|
|
549
586
|
"""Information about a key input event."""
|
|
550
587
|
|
reflex/reflex.py
CHANGED
|
@@ -624,19 +624,23 @@ def deploy(
|
|
|
624
624
|
hosting_cli.deploy(
|
|
625
625
|
app_name=app_name,
|
|
626
626
|
app_id=app_id,
|
|
627
|
-
export_fn=
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
627
|
+
export_fn=(
|
|
628
|
+
lambda zip_dest_dir,
|
|
629
|
+
api_url,
|
|
630
|
+
deploy_url,
|
|
631
|
+
frontend,
|
|
632
|
+
backend,
|
|
633
|
+
upload_db,
|
|
634
|
+
zipping: export_utils.export(
|
|
635
|
+
zip_dest_dir=zip_dest_dir,
|
|
636
|
+
api_url=api_url,
|
|
637
|
+
deploy_url=deploy_url,
|
|
638
|
+
frontend=frontend,
|
|
639
|
+
backend=backend,
|
|
640
|
+
zipping=zipping,
|
|
641
|
+
loglevel=loglevel.subprocess_level(),
|
|
642
|
+
upload_db_file=upload_db,
|
|
643
|
+
)
|
|
640
644
|
),
|
|
641
645
|
regions=regions,
|
|
642
646
|
envs=envs,
|
reflex/state.py
CHANGED
|
@@ -48,6 +48,7 @@ from pydantic.v1.fields import ModelField
|
|
|
48
48
|
from redis.asyncio import Redis
|
|
49
49
|
from redis.asyncio.client import PubSub
|
|
50
50
|
from redis.exceptions import ResponseError
|
|
51
|
+
from rich.markup import escape
|
|
51
52
|
from sqlalchemy.orm import DeclarativeBase
|
|
52
53
|
from typing_extensions import Self
|
|
53
54
|
|
|
@@ -698,7 +699,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
698
699
|
|
|
699
700
|
if not _isinstance(result, of_type, nested=1, treat_var_as_type=False):
|
|
700
701
|
console.warn(
|
|
701
|
-
f"Inline ComputedVar {f} expected type {of_type}, got {type(result)}. "
|
|
702
|
+
f"Inline ComputedVar {f} expected type {escape(str(of_type))}, got {type(result)}. "
|
|
702
703
|
"You can specify expected type with `of_type` argument."
|
|
703
704
|
)
|
|
704
705
|
|
|
@@ -1364,7 +1365,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1364
1365
|
field_type = _unwrap_field_type(true_type_for_pydantic_field(field))
|
|
1365
1366
|
if not _isinstance(value, field_type, nested=1, treat_var_as_type=False):
|
|
1366
1367
|
console.error(
|
|
1367
|
-
f"Expected field '{type(self).__name__}.{name}' to receive type '{field_type}',"
|
|
1368
|
+
f"Expected field '{type(self).__name__}.{name}' to receive type '{escape(str(field_type))}',"
|
|
1368
1369
|
f" but got '{value}' of type '{type(value)}'."
|
|
1369
1370
|
)
|
|
1370
1371
|
|
reflex/utils/decorator.py
CHANGED
|
@@ -18,6 +18,7 @@ def once(f: Callable[[], T]) -> Callable[[], T]:
|
|
|
18
18
|
unset = object()
|
|
19
19
|
value: object | T = unset
|
|
20
20
|
|
|
21
|
+
@functools.wraps(f)
|
|
21
22
|
def wrapper() -> T:
|
|
22
23
|
nonlocal value
|
|
23
24
|
value = f() if value is unset else value
|
|
@@ -26,6 +27,26 @@ def once(f: Callable[[], T]) -> Callable[[], T]:
|
|
|
26
27
|
return wrapper
|
|
27
28
|
|
|
28
29
|
|
|
30
|
+
def once_unless_none(f: Callable[[], T | None]) -> Callable[[], T | None]:
|
|
31
|
+
"""A decorator that calls the function once and caches the result unless it is None.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
f: The function to call.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
A function that calls the function once and caches the result unless it is None.
|
|
38
|
+
"""
|
|
39
|
+
value: T | None = None
|
|
40
|
+
|
|
41
|
+
@functools.wraps(f)
|
|
42
|
+
def wrapper() -> T | None:
|
|
43
|
+
nonlocal value
|
|
44
|
+
value = f() if value is None else value
|
|
45
|
+
return value
|
|
46
|
+
|
|
47
|
+
return wrapper
|
|
48
|
+
|
|
49
|
+
|
|
29
50
|
P = ParamSpec("P")
|
|
30
51
|
|
|
31
52
|
|
reflex/utils/export.py
CHANGED
|
@@ -41,10 +41,10 @@ def export(
|
|
|
41
41
|
|
|
42
42
|
# Override the config url values if provided.
|
|
43
43
|
if api_url is not None:
|
|
44
|
-
config.api_url
|
|
44
|
+
config._set_persistent(api_url=str(api_url))
|
|
45
45
|
console.debug(f"overriding API URL: {config.api_url}")
|
|
46
46
|
if deploy_url is not None:
|
|
47
|
-
config.deploy_url
|
|
47
|
+
config._set_persistent(deploy_url=str(deploy_url))
|
|
48
48
|
console.debug(f"overriding deploy URL: {config.deploy_url}")
|
|
49
49
|
|
|
50
50
|
# Show system info
|
reflex/utils/prerequisites.py
CHANGED
|
@@ -192,13 +192,16 @@ def get_node_version() -> version.Version | None:
|
|
|
192
192
|
return None
|
|
193
193
|
|
|
194
194
|
|
|
195
|
-
def get_bun_version() -> version.Version | None:
|
|
195
|
+
def get_bun_version(bun_path: Path | None = None) -> version.Version | None:
|
|
196
196
|
"""Get the version of bun.
|
|
197
197
|
|
|
198
|
+
Args:
|
|
199
|
+
bun_path: The path to the bun executable.
|
|
200
|
+
|
|
198
201
|
Returns:
|
|
199
202
|
The version of bun.
|
|
200
203
|
"""
|
|
201
|
-
bun_path = path_ops.get_bun_path()
|
|
204
|
+
bun_path = bun_path or path_ops.get_bun_path()
|
|
202
205
|
if bun_path is None:
|
|
203
206
|
return None
|
|
204
207
|
try:
|
|
@@ -1153,13 +1156,21 @@ def install_bun():
|
|
|
1153
1156
|
"Creating project directories in OneDrive is not recommended for bun usage on windows. This will fallback to npm."
|
|
1154
1157
|
)
|
|
1155
1158
|
|
|
1159
|
+
bun_path = path_ops.get_bun_path()
|
|
1160
|
+
|
|
1156
1161
|
# Skip if bun is already installed.
|
|
1157
|
-
if (
|
|
1158
|
-
|
|
1162
|
+
if (
|
|
1163
|
+
bun_path
|
|
1164
|
+
and (current_version := get_bun_version(bun_path=bun_path))
|
|
1165
|
+
and current_version >= version.parse(constants.Bun.MIN_VERSION)
|
|
1159
1166
|
):
|
|
1160
1167
|
console.debug("Skipping bun installation as it is already installed.")
|
|
1161
1168
|
return
|
|
1162
1169
|
|
|
1170
|
+
if bun_path and path_ops.use_system_bun():
|
|
1171
|
+
validate_bun(bun_path=bun_path)
|
|
1172
|
+
return
|
|
1173
|
+
|
|
1163
1174
|
# if unzip is installed
|
|
1164
1175
|
if constants.IS_WINDOWS:
|
|
1165
1176
|
processes.new_process(
|
|
@@ -1394,13 +1405,16 @@ def is_latest_template() -> bool:
|
|
|
1394
1405
|
return app_version == constants.Reflex.VERSION
|
|
1395
1406
|
|
|
1396
1407
|
|
|
1397
|
-
def validate_bun():
|
|
1408
|
+
def validate_bun(bun_path: Path | None = None):
|
|
1398
1409
|
"""Validate bun if a custom bun path is specified to ensure the bun version meets requirements.
|
|
1399
1410
|
|
|
1411
|
+
Args:
|
|
1412
|
+
bun_path: The path to the bun executable. If None, the default bun path is used.
|
|
1413
|
+
|
|
1400
1414
|
Raises:
|
|
1401
1415
|
Exit: If custom specified bun does not exist or does not meet requirements.
|
|
1402
1416
|
"""
|
|
1403
|
-
bun_path = path_ops.get_bun_path()
|
|
1417
|
+
bun_path = bun_path or path_ops.get_bun_path()
|
|
1404
1418
|
|
|
1405
1419
|
if bun_path is None:
|
|
1406
1420
|
return
|
|
@@ -1414,14 +1428,12 @@ def validate_bun():
|
|
|
1414
1428
|
)
|
|
1415
1429
|
raise typer.Exit(1)
|
|
1416
1430
|
elif bun_version < version.parse(constants.Bun.MIN_VERSION):
|
|
1417
|
-
console.
|
|
1431
|
+
console.warn(
|
|
1418
1432
|
f"Reflex requires bun version {constants.Bun.MIN_VERSION} or higher to run, but the detected version is "
|
|
1419
1433
|
f"{bun_version}. If you have specified a custom bun path in your config, make sure to provide one "
|
|
1420
|
-
f"that satisfies the minimum version requirement."
|
|
1434
|
+
f"that satisfies the minimum version requirement. You can upgrade bun by running [bold]bun upgrade[/bold]."
|
|
1421
1435
|
)
|
|
1422
1436
|
|
|
1423
|
-
raise typer.Exit(1)
|
|
1424
|
-
|
|
1425
1437
|
|
|
1426
1438
|
def validate_frontend_dependencies(init: bool = True):
|
|
1427
1439
|
"""Validate frontend dependencies to ensure they meet requirements.
|
|
@@ -1438,15 +1450,12 @@ def validate_frontend_dependencies(init: bool = True):
|
|
|
1438
1450
|
except FileNotFoundError as e:
|
|
1439
1451
|
raise typer.Exit(1) from e
|
|
1440
1452
|
|
|
1441
|
-
if prefer_npm_over_bun():
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
raise typer.Exit(1)
|
|
1448
|
-
else:
|
|
1449
|
-
validate_bun()
|
|
1453
|
+
if prefer_npm_over_bun() and not check_node_version():
|
|
1454
|
+
node_version = get_node_version()
|
|
1455
|
+
console.error(
|
|
1456
|
+
f"Reflex requires node version {constants.Node.MIN_VERSION} or higher to run, but the detected version is {node_version}",
|
|
1457
|
+
)
|
|
1458
|
+
raise typer.Exit(1)
|
|
1450
1459
|
|
|
1451
1460
|
|
|
1452
1461
|
def ensure_reflex_installation_id() -> int | None:
|
reflex/utils/pyi_generator.py
CHANGED
|
@@ -1250,12 +1250,20 @@ class PyiGenerator:
|
|
|
1250
1250
|
file_parent = file_parent.parent
|
|
1251
1251
|
top_dir = top_dir.parent
|
|
1252
1252
|
|
|
1253
|
-
|
|
1253
|
+
pyi_hashes_file = top_dir / "pyi_hashes.json"
|
|
1254
|
+
if not pyi_hashes_file.exists():
|
|
1255
|
+
while top_dir.parent and not (top_dir / "pyi_hashes.json").exists():
|
|
1256
|
+
top_dir = top_dir.parent
|
|
1257
|
+
another_pyi_hashes_file = top_dir / "pyi_hashes.json"
|
|
1258
|
+
if another_pyi_hashes_file.exists():
|
|
1259
|
+
pyi_hashes_file = another_pyi_hashes_file
|
|
1260
|
+
|
|
1261
|
+
pyi_hashes_file.write_text(
|
|
1254
1262
|
json.dumps(
|
|
1255
1263
|
dict(
|
|
1256
1264
|
zip(
|
|
1257
1265
|
[
|
|
1258
|
-
str(f.relative_to(
|
|
1266
|
+
str(f.relative_to(pyi_hashes_file.parent))
|
|
1259
1267
|
for f in file_paths
|
|
1260
1268
|
],
|
|
1261
1269
|
hashes,
|
|
@@ -1271,11 +1279,8 @@ class PyiGenerator:
|
|
|
1271
1279
|
file_paths = list(map(Path, file_paths))
|
|
1272
1280
|
pyi_hashes_parent = file_paths[0].parent
|
|
1273
1281
|
while (
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
for subfile in pyi_hashes_parent.iterdir()
|
|
1277
|
-
)
|
|
1278
|
-
and pyi_hashes_parent.parent
|
|
1282
|
+
pyi_hashes_parent.parent
|
|
1283
|
+
and not (pyi_hashes_parent / "pyi_hashes.json").exists()
|
|
1279
1284
|
):
|
|
1280
1285
|
pyi_hashes_parent = pyi_hashes_parent.parent
|
|
1281
1286
|
|
|
@@ -1288,6 +1293,7 @@ class PyiGenerator:
|
|
|
1288
1293
|
pyi_hashes[str(file_path.relative_to(pyi_hashes_parent))] = (
|
|
1289
1294
|
hashed_content
|
|
1290
1295
|
)
|
|
1296
|
+
|
|
1291
1297
|
pyi_hashes_file.write_text(
|
|
1292
1298
|
json.dumps(pyi_hashes, indent=2, sort_keys=True) + "\n"
|
|
1293
1299
|
)
|
reflex/utils/telemetry.py
CHANGED
|
@@ -9,6 +9,7 @@ import platform
|
|
|
9
9
|
import warnings
|
|
10
10
|
from contextlib import suppress
|
|
11
11
|
from datetime import datetime, timezone
|
|
12
|
+
from typing import TypedDict
|
|
12
13
|
|
|
13
14
|
import httpx
|
|
14
15
|
import psutil
|
|
@@ -16,6 +17,8 @@ import psutil
|
|
|
16
17
|
from reflex import constants
|
|
17
18
|
from reflex.config import environment
|
|
18
19
|
from reflex.utils import console
|
|
20
|
+
from reflex.utils.decorator import once_unless_none
|
|
21
|
+
from reflex.utils.exceptions import ReflexError
|
|
19
22
|
from reflex.utils.prerequisites import ensure_reflex_installation_id, get_project_hash
|
|
20
23
|
|
|
21
24
|
UTC = timezone.utc
|
|
@@ -94,15 +97,39 @@ def _raise_on_missing_project_hash() -> bool:
|
|
|
94
97
|
return not environment.REFLEX_SKIP_COMPILE.get()
|
|
95
98
|
|
|
96
99
|
|
|
97
|
-
|
|
98
|
-
"""
|
|
100
|
+
class _Properties(TypedDict):
|
|
101
|
+
"""Properties type for telemetry."""
|
|
99
102
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
distinct_id: int
|
|
104
|
+
distinct_app_id: int
|
|
105
|
+
user_os: str
|
|
106
|
+
user_os_detail: str
|
|
107
|
+
reflex_version: str
|
|
108
|
+
python_version: str
|
|
109
|
+
cpu_count: int
|
|
110
|
+
memory: int
|
|
111
|
+
cpu_info: dict
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class _DefaultEvent(TypedDict):
|
|
115
|
+
"""Default event type for telemetry."""
|
|
116
|
+
|
|
117
|
+
api_key: str
|
|
118
|
+
properties: _Properties
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class _Event(_DefaultEvent):
|
|
122
|
+
"""Event type for telemetry."""
|
|
123
|
+
|
|
124
|
+
event: str
|
|
125
|
+
timestamp: str
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _get_event_defaults() -> _DefaultEvent | None:
|
|
129
|
+
"""Get the default event data.
|
|
103
130
|
|
|
104
131
|
Returns:
|
|
105
|
-
The event data.
|
|
132
|
+
The default event data.
|
|
106
133
|
"""
|
|
107
134
|
from reflex.utils.prerequisites import get_cpu_info
|
|
108
135
|
|
|
@@ -113,19 +140,12 @@ def _prepare_event(event: str, **kwargs) -> dict:
|
|
|
113
140
|
console.debug(
|
|
114
141
|
f"Could not get installation_id or project_hash: {installation_id}, {project_hash}"
|
|
115
142
|
)
|
|
116
|
-
return
|
|
117
|
-
|
|
118
|
-
stamp = datetime.now(UTC).isoformat()
|
|
143
|
+
return None
|
|
119
144
|
|
|
120
145
|
cpuinfo = get_cpu_info()
|
|
121
146
|
|
|
122
|
-
additional_keys = ["template", "context", "detail", "user_uuid"]
|
|
123
|
-
additional_fields = {
|
|
124
|
-
key: value for key in additional_keys if (value := kwargs.get(key)) is not None
|
|
125
|
-
}
|
|
126
147
|
return {
|
|
127
148
|
"api_key": "phc_JoMo0fOyi0GQAooY3UyO9k0hebGkMyFJrrCw1Gt5SGb",
|
|
128
|
-
"event": event,
|
|
129
149
|
"properties": {
|
|
130
150
|
"distinct_id": installation_id,
|
|
131
151
|
"distinct_app_id": project_hash,
|
|
@@ -136,13 +156,55 @@ def _prepare_event(event: str, **kwargs) -> dict:
|
|
|
136
156
|
"cpu_count": get_cpu_count(),
|
|
137
157
|
"memory": get_memory(),
|
|
138
158
|
"cpu_info": dataclasses.asdict(cpuinfo) if cpuinfo else {},
|
|
139
|
-
**additional_fields,
|
|
140
159
|
},
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@once_unless_none
|
|
164
|
+
def get_event_defaults() -> _DefaultEvent | None:
|
|
165
|
+
"""Get the default event data.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
The default event data.
|
|
169
|
+
"""
|
|
170
|
+
return _get_event_defaults()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _prepare_event(event: str, **kwargs) -> _Event | None:
|
|
174
|
+
"""Prepare the event to be sent to the PostHog server.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
event: The event name.
|
|
178
|
+
kwargs: Additional data to send with the event.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
The event data.
|
|
182
|
+
"""
|
|
183
|
+
event_data = get_event_defaults()
|
|
184
|
+
if not event_data:
|
|
185
|
+
return None
|
|
186
|
+
|
|
187
|
+
additional_keys = ["template", "context", "detail", "user_uuid"]
|
|
188
|
+
|
|
189
|
+
properties = event_data["properties"]
|
|
190
|
+
|
|
191
|
+
for key in additional_keys:
|
|
192
|
+
if key in properties or key not in kwargs:
|
|
193
|
+
continue
|
|
194
|
+
|
|
195
|
+
properties[key] = kwargs[key]
|
|
196
|
+
|
|
197
|
+
stamp = datetime.now(UTC).isoformat()
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
"api_key": event_data["api_key"],
|
|
201
|
+
"event": event,
|
|
202
|
+
"properties": properties,
|
|
141
203
|
"timestamp": stamp,
|
|
142
204
|
}
|
|
143
205
|
|
|
144
206
|
|
|
145
|
-
def _send_event(event_data:
|
|
207
|
+
def _send_event(event_data: _Event) -> bool:
|
|
146
208
|
try:
|
|
147
209
|
httpx.post(POSTHOG_API_URL, json=event_data)
|
|
148
210
|
except Exception:
|
|
@@ -151,7 +213,7 @@ def _send_event(event_data: dict) -> bool:
|
|
|
151
213
|
return True
|
|
152
214
|
|
|
153
215
|
|
|
154
|
-
def _send(event: str, telemetry_enabled: bool | None, **kwargs):
|
|
216
|
+
def _send(event: str, telemetry_enabled: bool | None, **kwargs) -> bool:
|
|
155
217
|
from reflex.config import get_config
|
|
156
218
|
|
|
157
219
|
# Get the telemetry_enabled from the config if it is not specified.
|
|
@@ -167,6 +229,7 @@ def _send(event: str, telemetry_enabled: bool | None, **kwargs):
|
|
|
167
229
|
if not event_data:
|
|
168
230
|
return False
|
|
169
231
|
return _send_event(event_data)
|
|
232
|
+
return False
|
|
170
233
|
|
|
171
234
|
|
|
172
235
|
def send(event: str, telemetry_enabled: bool | None = None, **kwargs):
|
|
@@ -196,8 +259,6 @@ def send_error(error: Exception, context: str):
|
|
|
196
259
|
Args:
|
|
197
260
|
error: The error to send.
|
|
198
261
|
context: The context of the error (e.g. "frontend" or "backend")
|
|
199
|
-
|
|
200
|
-
Returns:
|
|
201
|
-
Whether the telemetry was sent successfully.
|
|
202
262
|
"""
|
|
203
|
-
|
|
263
|
+
if isinstance(error, ReflexError):
|
|
264
|
+
send("error", detail=type(error).__name__, context=context)
|
reflex/utils/types.py
CHANGED
|
@@ -996,6 +996,18 @@ def typehint_issubclass(
|
|
|
996
996
|
for arg in args
|
|
997
997
|
)
|
|
998
998
|
|
|
999
|
+
if is_literal(possible_subclass):
|
|
1000
|
+
args = get_args(possible_subclass)
|
|
1001
|
+
return all(
|
|
1002
|
+
_isinstance(
|
|
1003
|
+
arg,
|
|
1004
|
+
possible_superclass,
|
|
1005
|
+
treat_mutable_obj_as_immutable=treat_mutable_superclasss_as_immutable,
|
|
1006
|
+
nested=2,
|
|
1007
|
+
)
|
|
1008
|
+
for arg in args
|
|
1009
|
+
)
|
|
1010
|
+
|
|
999
1011
|
# Remove this check when Python 3.10 is the minimum supported version
|
|
1000
1012
|
if hasattr(types, "UnionType"):
|
|
1001
1013
|
provided_type_origin = (
|
reflex/vars/base.py
CHANGED
|
@@ -44,6 +44,7 @@ from typing import (
|
|
|
44
44
|
overload,
|
|
45
45
|
)
|
|
46
46
|
|
|
47
|
+
from rich.markup import escape
|
|
47
48
|
from sqlalchemy.orm import DeclarativeBase
|
|
48
49
|
from typing_extensions import deprecated, override
|
|
49
50
|
|
|
@@ -730,6 +731,9 @@ class Var(Generic[VAR_TYPE]):
|
|
|
730
731
|
@overload
|
|
731
732
|
def to(self, output: Type[bool]) -> BooleanVar: ...
|
|
732
733
|
|
|
734
|
+
@overload
|
|
735
|
+
def to(self, output: type[int]) -> NumberVar[int]: ...
|
|
736
|
+
|
|
733
737
|
@overload
|
|
734
738
|
def to(self, output: type[int] | type[float]) -> NumberVar: ...
|
|
735
739
|
|
|
@@ -1060,6 +1064,16 @@ class Var(Generic[VAR_TYPE]):
|
|
|
1060
1064
|
|
|
1061
1065
|
return boolify(self)
|
|
1062
1066
|
|
|
1067
|
+
def is_not_none(self) -> BooleanVar:
|
|
1068
|
+
"""Check if the var is not None.
|
|
1069
|
+
|
|
1070
|
+
Returns:
|
|
1071
|
+
A BooleanVar object representing the result of the check.
|
|
1072
|
+
"""
|
|
1073
|
+
from .number import is_not_none_operation
|
|
1074
|
+
|
|
1075
|
+
return is_not_none_operation(self)
|
|
1076
|
+
|
|
1063
1077
|
def __and__(
|
|
1064
1078
|
self, other: Var[OTHER_VAR_TYPE] | Any
|
|
1065
1079
|
) -> Var[VAR_TYPE | OTHER_VAR_TYPE]:
|
|
@@ -2371,7 +2385,7 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|
|
2371
2385
|
if not _isinstance(value, self._var_type, nested=1, treat_var_as_type=False):
|
|
2372
2386
|
console.error(
|
|
2373
2387
|
f"Computed var '{type(instance).__name__}.{self._js_expr}' must return"
|
|
2374
|
-
f" a value of type '{self._var_type}', got '{value!s}' of type {type(value)}."
|
|
2388
|
+
f" a value of type '{escape(str(self._var_type))}', got '{value!s}' of type {type(value)}."
|
|
2375
2389
|
)
|
|
2376
2390
|
|
|
2377
2391
|
def _deps(
|
reflex/vars/number.py
CHANGED
|
@@ -1057,6 +1057,10 @@ _IS_TRUE_IMPORT: ImportDict = {
|
|
|
1057
1057
|
f"$/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")],
|
|
1058
1058
|
}
|
|
1059
1059
|
|
|
1060
|
+
_IS_NOT_NULL_OR_UNDEFINED_IMPORT: ImportDict = {
|
|
1061
|
+
f"$/{Dirs.STATE_PATH}": [ImportVar(tag="isNotNullOrUndefined")],
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1060
1064
|
|
|
1061
1065
|
@var_operation
|
|
1062
1066
|
def boolify(value: Var):
|
|
@@ -1075,6 +1079,23 @@ def boolify(value: Var):
|
|
|
1075
1079
|
)
|
|
1076
1080
|
|
|
1077
1081
|
|
|
1082
|
+
@var_operation
|
|
1083
|
+
def is_not_none_operation(value: Var):
|
|
1084
|
+
"""Check if the value is not None.
|
|
1085
|
+
|
|
1086
|
+
Args:
|
|
1087
|
+
value: The value.
|
|
1088
|
+
|
|
1089
|
+
Returns:
|
|
1090
|
+
The boolean value.
|
|
1091
|
+
"""
|
|
1092
|
+
return var_operation_return(
|
|
1093
|
+
js_expression=f"isNotNullOrUndefined({value})",
|
|
1094
|
+
var_type=bool,
|
|
1095
|
+
var_data=VarData(imports=_IS_NOT_NULL_OR_UNDEFINED_IMPORT),
|
|
1096
|
+
)
|
|
1097
|
+
|
|
1098
|
+
|
|
1078
1099
|
T = TypeVar("T")
|
|
1079
1100
|
U = TypeVar("U")
|
|
1080
1101
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reflex
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.7
|
|
4
4
|
Summary: Web apps in pure Python.
|
|
5
5
|
Project-URL: homepage, https://reflex.dev
|
|
6
6
|
Project-URL: repository, https://github.com/reflex-dev/reflex
|
|
@@ -18,26 +18,26 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
20
|
Requires-Python: <4.0,>=3.10
|
|
21
|
-
Requires-Dist: alembic<2.0,>=1.
|
|
22
|
-
Requires-Dist: distro<2.0,>=1.
|
|
23
|
-
Requires-Dist: fastapi
|
|
24
|
-
Requires-Dist: granian[reload]>=2.2.
|
|
21
|
+
Requires-Dist: alembic<2.0,>=1.15.2
|
|
22
|
+
Requires-Dist: distro<2.0,>=1.9.0; platform_system == 'Linux'
|
|
23
|
+
Requires-Dist: fastapi>=0.115.0
|
|
24
|
+
Requires-Dist: granian[reload]>=2.2.3
|
|
25
25
|
Requires-Dist: gunicorn<24.0.0,>=23.0.0
|
|
26
|
-
Requires-Dist: httpx<1.0,>=0.
|
|
26
|
+
Requires-Dist: httpx<1.0,>=0.28.0
|
|
27
27
|
Requires-Dist: jinja2<4.0,>=3.1.2
|
|
28
|
-
Requires-Dist: packaging<25.0,>=
|
|
29
|
-
Requires-Dist: platformdirs<5.0,>=3.
|
|
30
|
-
Requires-Dist: psutil<8.0,>=
|
|
28
|
+
Requires-Dist: packaging<25.0,>=24.2
|
|
29
|
+
Requires-Dist: platformdirs<5.0,>=4.3.7
|
|
30
|
+
Requires-Dist: psutil<8.0,>=7.0.0
|
|
31
31
|
Requires-Dist: pydantic<3.0,>=1.10.21
|
|
32
32
|
Requires-Dist: python-multipart<1.0,>=0.0.20
|
|
33
|
-
Requires-Dist: python-socketio<6.0,>=5.
|
|
34
|
-
Requires-Dist: redis<6.0,>=
|
|
35
|
-
Requires-Dist: reflex-hosting-cli>=0.1.
|
|
33
|
+
Requires-Dist: python-socketio<6.0,>=5.12.0
|
|
34
|
+
Requires-Dist: redis<6.0,>=5.2.1
|
|
35
|
+
Requires-Dist: reflex-hosting-cli>=0.1.38
|
|
36
36
|
Requires-Dist: rich<14.0,>=13.0.0
|
|
37
|
-
Requires-Dist: sqlmodel<0.1,>=0.0.
|
|
38
|
-
Requires-Dist: typer<1.0,>=0.15.
|
|
39
|
-
Requires-Dist: typing-extensions>=4.
|
|
40
|
-
Requires-Dist: uvicorn>=0.
|
|
37
|
+
Requires-Dist: sqlmodel<0.1,>=0.0.24
|
|
38
|
+
Requires-Dist: typer<1.0,>=0.15.2
|
|
39
|
+
Requires-Dist: typing-extensions>=4.13.0
|
|
40
|
+
Requires-Dist: uvicorn>=0.34.0
|
|
41
41
|
Requires-Dist: wrapt<2.0,>=1.17.0
|
|
42
42
|
Description-Content-Type: text/markdown
|
|
43
43
|
|