reflex 0.5.10a3__py3-none-any.whl → 0.6.0a1__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/utils.js.jinja2 +4 -4
- reflex/.templates/jinja/web/utils/context.js.jinja2 +1 -1
- reflex/.templates/jinja/web/utils/theme.js.jinja2 +1 -1
- reflex/__init__.py +3 -2
- reflex/__init__.pyi +2 -2
- reflex/app.py +43 -9
- reflex/base.py +3 -2
- reflex/compiler/compiler.py +6 -6
- reflex/compiler/utils.py +5 -3
- reflex/components/base/app_wrap.py +2 -4
- reflex/components/base/app_wrap.pyi +17 -17
- reflex/components/base/bare.py +7 -4
- reflex/components/base/body.pyi +17 -17
- reflex/components/base/document.pyi +81 -81
- reflex/components/base/error_boundary.py +10 -8
- reflex/components/base/error_boundary.pyi +20 -19
- reflex/components/base/fragment.pyi +17 -17
- reflex/components/base/head.pyi +33 -33
- reflex/components/base/link.pyi +34 -33
- reflex/components/base/meta.pyi +65 -65
- reflex/components/base/script.py +2 -1
- reflex/components/base/script.pyi +21 -20
- reflex/components/component.py +116 -145
- reflex/components/core/banner.py +59 -60
- reflex/components/core/banner.pyi +86 -150
- reflex/components/core/client_side_routing.py +2 -1
- reflex/components/core/client_side_routing.pyi +34 -33
- reflex/components/core/clipboard.py +2 -2
- reflex/components/core/clipboard.pyi +19 -18
- reflex/components/core/cond.py +21 -44
- reflex/components/core/debounce.py +6 -8
- reflex/components/core/debounce.pyi +19 -18
- reflex/components/core/foreach.py +5 -14
- reflex/components/core/html.pyi +18 -17
- reflex/components/core/match.py +36 -43
- reflex/components/core/upload.py +32 -25
- reflex/components/core/upload.pyi +84 -73
- reflex/components/datadisplay/code.py +55 -28
- reflex/components/datadisplay/code.pyi +20 -17
- reflex/components/datadisplay/dataeditor.py +17 -11
- reflex/components/datadisplay/dataeditor.pyi +34 -33
- reflex/components/el/__init__.py +0 -1
- reflex/components/el/__init__.pyi +0 -11
- reflex/components/el/element.pyi +17 -17
- reflex/components/el/elements/__init__.py +1 -7
- reflex/components/el/elements/__init__.pyi +1 -15
- reflex/components/el/elements/base.pyi +18 -17
- reflex/components/el/elements/forms.py +24 -31
- reflex/components/el/elements/forms.pyi +237 -236
- reflex/components/el/elements/inline.pyi +450 -449
- reflex/components/el/elements/media.py +0 -21
- reflex/components/el/elements/media.pyi +338 -337
- reflex/components/el/elements/metadata.py +3 -2
- reflex/components/el/elements/metadata.pyi +98 -97
- reflex/components/el/elements/other.pyi +114 -113
- reflex/components/el/elements/scripts.pyi +50 -49
- reflex/components/el/elements/sectioning.pyi +242 -241
- reflex/components/el/elements/tables.pyi +162 -161
- reflex/components/el/elements/typography.pyi +242 -241
- reflex/components/gridjs/datatable.py +13 -14
- reflex/components/gridjs/datatable.pyi +34 -33
- reflex/components/lucide/icon.py +2 -126
- reflex/components/lucide/icon.pyi +34 -142
- reflex/components/markdown/markdown.py +30 -35
- reflex/components/markdown/markdown.pyi +29 -32
- reflex/components/moment/moment.pyi +19 -18
- reflex/components/next/base.pyi +17 -17
- reflex/components/next/image.py +0 -4
- reflex/components/next/image.pyi +20 -19
- reflex/components/next/link.pyi +18 -17
- reflex/components/next/video.pyi +18 -17
- reflex/components/plotly/plotly.py +16 -28
- reflex/components/plotly/plotly.pyi +36 -35
- reflex/components/props.py +21 -10
- reflex/components/radix/__init__.pyi +1 -1
- reflex/components/radix/primitives/__init__.pyi +0 -1
- reflex/components/radix/primitives/accordion.py +7 -8
- reflex/components/radix/primitives/accordion.pyi +117 -116
- reflex/components/radix/primitives/base.pyi +34 -33
- reflex/components/radix/primitives/drawer.pyi +169 -168
- reflex/components/radix/primitives/form.pyi +168 -167
- reflex/components/radix/primitives/progress.pyi +82 -81
- reflex/components/radix/primitives/slider.pyi +84 -83
- reflex/components/radix/themes/base.py +8 -4
- reflex/components/radix/themes/base.pyi +114 -113
- reflex/components/radix/themes/color_mode.py +12 -21
- reflex/components/radix/themes/color_mode.pyi +67 -67
- reflex/components/radix/themes/components/__init__.pyi +1 -0
- reflex/components/radix/themes/components/alert_dialog.pyi +118 -117
- reflex/components/radix/themes/components/aspect_ratio.pyi +18 -17
- reflex/components/radix/themes/components/avatar.pyi +18 -17
- reflex/components/radix/themes/components/badge.pyi +18 -17
- reflex/components/radix/themes/components/button.pyi +18 -17
- reflex/components/radix/themes/components/callout.pyi +82 -81
- reflex/components/radix/themes/components/card.pyi +18 -17
- reflex/components/radix/themes/components/checkbox.py +2 -3
- reflex/components/radix/themes/components/checkbox.pyi +53 -52
- reflex/components/radix/themes/components/checkbox_cards.pyi +34 -33
- reflex/components/radix/themes/components/checkbox_group.pyi +34 -33
- reflex/components/radix/themes/components/context_menu.pyi +140 -139
- reflex/components/radix/themes/components/data_list.py +5 -0
- reflex/components/radix/themes/components/data_list.pyi +71 -65
- reflex/components/radix/themes/components/dialog.pyi +121 -120
- reflex/components/radix/themes/components/dropdown_menu.pyi +142 -141
- reflex/components/radix/themes/components/hover_card.pyi +68 -67
- reflex/components/radix/themes/components/icon_button.py +2 -1
- reflex/components/radix/themes/components/icon_button.pyi +18 -17
- reflex/components/radix/themes/components/inset.pyi +18 -17
- reflex/components/radix/themes/components/popover.pyi +73 -72
- reflex/components/radix/themes/components/progress.pyi +18 -17
- reflex/components/radix/themes/components/radio.pyi +18 -17
- reflex/components/radix/themes/components/radio_cards.pyi +35 -34
- reflex/components/radix/themes/components/radio_group.py +35 -31
- reflex/components/radix/themes/components/radio_group.pyi +73 -66
- reflex/components/radix/themes/components/scroll_area.pyi +18 -17
- reflex/components/radix/themes/components/segmented_control.pyi +35 -34
- reflex/components/radix/themes/components/select.py +2 -1
- reflex/components/radix/themes/components/select.pyi +155 -154
- reflex/components/radix/themes/components/separator.py +2 -3
- reflex/components/radix/themes/components/separator.pyi +18 -17
- reflex/components/radix/themes/components/skeleton.pyi +18 -17
- reflex/components/radix/themes/components/slider.py +2 -1
- reflex/components/radix/themes/components/slider.pyi +20 -19
- reflex/components/radix/themes/components/spinner.pyi +18 -17
- reflex/components/radix/themes/components/switch.pyi +19 -18
- reflex/components/radix/themes/components/table.pyi +114 -113
- reflex/components/radix/themes/components/tabs.pyi +84 -83
- reflex/components/radix/themes/components/text_area.pyi +21 -20
- reflex/components/radix/themes/components/text_field.py +0 -79
- reflex/components/radix/themes/components/text_field.pyi +57 -63
- reflex/components/radix/themes/components/tooltip.pyi +21 -20
- reflex/components/radix/themes/layout/base.pyi +18 -17
- reflex/components/radix/themes/layout/box.pyi +18 -17
- reflex/components/radix/themes/layout/center.pyi +18 -17
- reflex/components/radix/themes/layout/container.py +2 -3
- reflex/components/radix/themes/layout/container.pyi +18 -17
- reflex/components/radix/themes/layout/flex.pyi +18 -17
- reflex/components/radix/themes/layout/grid.pyi +18 -17
- reflex/components/radix/themes/layout/list.py +5 -4
- reflex/components/radix/themes/layout/list.pyi +86 -85
- reflex/components/radix/themes/layout/section.py +2 -3
- reflex/components/radix/themes/layout/section.pyi +18 -17
- reflex/components/radix/themes/layout/spacer.pyi +18 -17
- reflex/components/radix/themes/layout/stack.pyi +50 -49
- reflex/components/radix/themes/typography/blockquote.pyi +18 -17
- reflex/components/radix/themes/typography/code.pyi +18 -17
- reflex/components/radix/themes/typography/heading.pyi +18 -17
- reflex/components/radix/themes/typography/link.pyi +18 -17
- reflex/components/radix/themes/typography/text.pyi +114 -113
- reflex/components/react_player/audio.pyi +34 -33
- reflex/components/react_player/react_player.pyi +34 -33
- reflex/components/react_player/video.pyi +34 -33
- reflex/components/recharts/cartesian.py +23 -19
- reflex/components/recharts/cartesian.pyi +297 -296
- reflex/components/recharts/charts.py +6 -5
- reflex/components/recharts/charts.pyi +179 -178
- reflex/components/recharts/general.py +8 -7
- reflex/components/recharts/general.pyi +82 -81
- reflex/components/recharts/polar.py +14 -13
- reflex/components/recharts/polar.pyi +76 -75
- reflex/components/recharts/recharts.pyi +33 -33
- reflex/components/sonner/toast.py +30 -33
- reflex/components/sonner/toast.pyi +27 -25
- reflex/components/suneditor/editor.py +2 -1
- reflex/components/suneditor/editor.pyi +27 -26
- reflex/components/tags/iter_tag.py +16 -16
- reflex/components/tags/tag.py +8 -10
- reflex/constants/base.py +3 -1
- reflex/constants/event.py +1 -0
- reflex/event.py +89 -79
- reflex/experimental/__init__.py +25 -6
- reflex/experimental/client_state.py +34 -58
- reflex/experimental/hooks.py +13 -18
- reflex/experimental/layout.py +5 -5
- reflex/experimental/layout.pyi +84 -83
- reflex/{experimental/vars → ivars}/__init__.py +0 -1
- reflex/ivars/base.py +2180 -0
- reflex/ivars/function.py +200 -0
- reflex/ivars/number.py +1137 -0
- reflex/ivars/object.py +564 -0
- reflex/ivars/sequence.py +1601 -0
- reflex/model.py +22 -0
- reflex/reflex.py +4 -0
- reflex/state.py +388 -73
- reflex/style.py +52 -34
- reflex/testing.py +8 -3
- reflex/utils/exceptions.py +12 -0
- reflex/utils/exec.py +0 -14
- reflex/utils/format.py +74 -223
- reflex/utils/net.py +43 -0
- reflex/utils/path_ops.py +13 -1
- reflex/utils/prerequisites.py +46 -26
- reflex/utils/pyi_generator.py +5 -4
- reflex/utils/serializers.py +13 -31
- reflex/utils/types.py +44 -9
- reflex/vars.py +127 -2230
- {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/METADATA +4 -6
- reflex-0.6.0a1.dist-info/RECORD +384 -0
- reflex/.templates/apps/demo/.gitignore +0 -4
- reflex/.templates/apps/demo/assets/favicon.ico +0 -0
- reflex/.templates/apps/demo/assets/github.svg +0 -10
- reflex/.templates/apps/demo/assets/icon.svg +0 -37
- reflex/.templates/apps/demo/assets/logo.svg +0 -68
- reflex/.templates/apps/demo/assets/paneleft.svg +0 -13
- reflex/.templates/apps/demo/code/__init__.py +0 -1
- reflex/.templates/apps/demo/code/demo.py +0 -127
- reflex/.templates/apps/demo/code/pages/__init__.py +0 -7
- reflex/.templates/apps/demo/code/pages/chatapp.py +0 -31
- reflex/.templates/apps/demo/code/pages/datatable.py +0 -360
- reflex/.templates/apps/demo/code/pages/forms.py +0 -257
- reflex/.templates/apps/demo/code/pages/graphing.py +0 -253
- reflex/.templates/apps/demo/code/pages/home.py +0 -56
- reflex/.templates/apps/demo/code/sidebar.py +0 -178
- reflex/.templates/apps/demo/code/state.py +0 -22
- reflex/.templates/apps/demo/code/states/form_state.py +0 -40
- reflex/.templates/apps/demo/code/states/pie_state.py +0 -47
- reflex/.templates/apps/demo/code/styles.py +0 -68
- reflex/.templates/apps/demo/code/webui/__init__.py +0 -0
- reflex/.templates/apps/demo/code/webui/components/__init__.py +0 -4
- reflex/.templates/apps/demo/code/webui/components/chat.py +0 -118
- reflex/.templates/apps/demo/code/webui/components/loading_icon.py +0 -19
- reflex/.templates/apps/demo/code/webui/components/modal.py +0 -56
- reflex/.templates/apps/demo/code/webui/components/navbar.py +0 -70
- reflex/.templates/apps/demo/code/webui/components/sidebar.py +0 -66
- reflex/.templates/apps/demo/code/webui/state.py +0 -146
- reflex/.templates/apps/demo/code/webui/styles.py +0 -88
- reflex/experimental/vars/base.py +0 -583
- reflex/experimental/vars/function.py +0 -290
- reflex/experimental/vars/number.py +0 -1458
- reflex/experimental/vars/object.py +0 -804
- reflex/experimental/vars/sequence.py +0 -1764
- reflex/utils/watch.py +0 -96
- reflex/vars.pyi +0 -218
- reflex-0.5.10a3.dist-info/RECORD +0 -413
- {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/LICENSE +0 -0
- {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/WHEEL +0 -0
- {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/entry_points.txt +0 -0
reflex/utils/format.py
CHANGED
|
@@ -10,7 +10,7 @@ from typing import TYPE_CHECKING, Any, Callable, List, Optional, Union
|
|
|
10
10
|
|
|
11
11
|
from reflex import constants
|
|
12
12
|
from reflex.utils import exceptions, types
|
|
13
|
-
from reflex.vars import
|
|
13
|
+
from reflex.vars import Var
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
16
|
from reflex.components.component import ComponentStyle
|
|
@@ -263,32 +263,6 @@ def format_string(string: str) -> str:
|
|
|
263
263
|
return _wrap_js_string(_escape_js_string(string))
|
|
264
264
|
|
|
265
265
|
|
|
266
|
-
def format_f_string_prop(prop: BaseVar) -> str:
|
|
267
|
-
"""Format the string in a given prop as an f-string.
|
|
268
|
-
|
|
269
|
-
Args:
|
|
270
|
-
prop: The prop to format.
|
|
271
|
-
|
|
272
|
-
Returns:
|
|
273
|
-
The formatted string.
|
|
274
|
-
"""
|
|
275
|
-
s = prop._var_full_name
|
|
276
|
-
var_data = prop._var_data
|
|
277
|
-
interps = var_data.interpolations if var_data else []
|
|
278
|
-
parts: List[str] = []
|
|
279
|
-
|
|
280
|
-
if interps:
|
|
281
|
-
for i, (start, end) in enumerate(interps):
|
|
282
|
-
prev_end = interps[i - 1][1] if i > 0 else 0
|
|
283
|
-
parts.append(_escape_js_string(s[prev_end:start]))
|
|
284
|
-
parts.append(s[start:end])
|
|
285
|
-
parts.append(_escape_js_string(s[interps[-1][1] :]))
|
|
286
|
-
else:
|
|
287
|
-
parts.append(_escape_js_string(s))
|
|
288
|
-
|
|
289
|
-
return _wrap_js_string("".join(parts))
|
|
290
|
-
|
|
291
|
-
|
|
292
266
|
def format_var(var: Var) -> str:
|
|
293
267
|
"""Format the given Var as a javascript value.
|
|
294
268
|
|
|
@@ -298,13 +272,7 @@ def format_var(var: Var) -> str:
|
|
|
298
272
|
Returns:
|
|
299
273
|
The formatted Var.
|
|
300
274
|
"""
|
|
301
|
-
|
|
302
|
-
return str(var)
|
|
303
|
-
if types._issubclass(var._var_type, str):
|
|
304
|
-
return format_string(var._var_full_name)
|
|
305
|
-
if is_wrapped(var._var_full_name, "{"):
|
|
306
|
-
return var._var_full_name
|
|
307
|
-
return json_dumps(var._var_full_name)
|
|
275
|
+
return str(var)
|
|
308
276
|
|
|
309
277
|
|
|
310
278
|
def format_route(route: str, format_case=True) -> str:
|
|
@@ -329,46 +297,11 @@ def format_route(route: str, format_case=True) -> str:
|
|
|
329
297
|
return route
|
|
330
298
|
|
|
331
299
|
|
|
332
|
-
def
|
|
333
|
-
cond: str |
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
is_prop=False,
|
|
300
|
+
def format_match(
|
|
301
|
+
cond: str | ImmutableVar,
|
|
302
|
+
match_cases: List[List[ImmutableVar]],
|
|
303
|
+
default: ImmutableVar,
|
|
337
304
|
) -> str:
|
|
338
|
-
"""Format a conditional expression.
|
|
339
|
-
|
|
340
|
-
Args:
|
|
341
|
-
cond: The cond.
|
|
342
|
-
true_value: The value to return if the cond is true.
|
|
343
|
-
false_value: The value to return if the cond is false.
|
|
344
|
-
is_prop: Whether the cond is a prop
|
|
345
|
-
|
|
346
|
-
Returns:
|
|
347
|
-
The formatted conditional expression.
|
|
348
|
-
"""
|
|
349
|
-
# Use Python truthiness.
|
|
350
|
-
cond = f"isTrue({cond})"
|
|
351
|
-
|
|
352
|
-
def create_var(cond_part):
|
|
353
|
-
return Var.create_safe(cond_part, _var_is_string=isinstance(cond_part, str))
|
|
354
|
-
|
|
355
|
-
# Format prop conds.
|
|
356
|
-
if is_prop:
|
|
357
|
-
true_value = create_var(true_value)
|
|
358
|
-
prop1 = true_value._replace(
|
|
359
|
-
_var_is_local=True,
|
|
360
|
-
)
|
|
361
|
-
|
|
362
|
-
false_value = create_var(false_value)
|
|
363
|
-
prop2 = false_value._replace(_var_is_local=True)
|
|
364
|
-
# unwrap '{}' to avoid f-string semantics for Var
|
|
365
|
-
return f"{cond} ? {prop1._var_name_unwrapped} : {prop2._var_name_unwrapped}"
|
|
366
|
-
|
|
367
|
-
# Format component conds.
|
|
368
|
-
return wrap(f"{cond} ? {true_value} : {false_value}", "{")
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
def format_match(cond: str | Var, match_cases: List[BaseVar], default: Var) -> str:
|
|
372
305
|
"""Format a match expression whose return type is a Var.
|
|
373
306
|
|
|
374
307
|
Args:
|
|
@@ -387,24 +320,19 @@ def format_match(cond: str | Var, match_cases: List[BaseVar], default: Var) -> s
|
|
|
387
320
|
return_value = case[-1]
|
|
388
321
|
|
|
389
322
|
case_conditions = " ".join(
|
|
390
|
-
[
|
|
391
|
-
f"case JSON.stringify({condition._var_name_unwrapped}):"
|
|
392
|
-
for condition in conditions
|
|
393
|
-
]
|
|
394
|
-
)
|
|
395
|
-
case_code = (
|
|
396
|
-
f"{case_conditions} return ({return_value._var_name_unwrapped}); break;"
|
|
323
|
+
[f"case JSON.stringify({str(condition)}):" for condition in conditions]
|
|
397
324
|
)
|
|
325
|
+
case_code = f"{case_conditions} return ({str(return_value)}); break;"
|
|
398
326
|
switch_code += case_code
|
|
399
327
|
|
|
400
|
-
switch_code += f"default: return ({default
|
|
328
|
+
switch_code += f"default: return ({str(default)}); break;"
|
|
401
329
|
switch_code += "};})()"
|
|
402
330
|
|
|
403
331
|
return switch_code
|
|
404
332
|
|
|
405
333
|
|
|
406
334
|
def format_prop(
|
|
407
|
-
prop: Union[
|
|
335
|
+
prop: Union[ImmutableVar, EventChain, ComponentStyle, str],
|
|
408
336
|
) -> Union[int, float, str]:
|
|
409
337
|
"""Format a prop.
|
|
410
338
|
|
|
@@ -420,21 +348,16 @@ def format_prop(
|
|
|
420
348
|
"""
|
|
421
349
|
# import here to avoid circular import.
|
|
422
350
|
from reflex.event import EventChain
|
|
351
|
+
from reflex.ivars import ImmutableVar
|
|
423
352
|
from reflex.utils import serializers
|
|
424
353
|
|
|
425
354
|
try:
|
|
426
355
|
# Handle var props.
|
|
427
|
-
if isinstance(prop,
|
|
428
|
-
|
|
429
|
-
return str(prop)
|
|
430
|
-
if isinstance(prop, BaseVar) and types._issubclass(prop._var_type, str):
|
|
431
|
-
if prop._var_data and prop._var_data.interpolations:
|
|
432
|
-
return format_f_string_prop(prop)
|
|
433
|
-
return format_string(prop._var_full_name)
|
|
434
|
-
prop = prop._var_full_name
|
|
356
|
+
if isinstance(prop, ImmutableVar):
|
|
357
|
+
return str(prop)
|
|
435
358
|
|
|
436
359
|
# Handle event props.
|
|
437
|
-
|
|
360
|
+
if isinstance(prop, EventChain):
|
|
438
361
|
sig = inspect.signature(prop.args_spec) # type: ignore
|
|
439
362
|
if sig.parameters:
|
|
440
363
|
arg_def = ",".join(f"_{p}" for p in sig.parameters)
|
|
@@ -483,11 +406,26 @@ def format_props(*single_props, **key_value_props) -> list[str]:
|
|
|
483
406
|
The formatted props list.
|
|
484
407
|
"""
|
|
485
408
|
# Format all the props.
|
|
409
|
+
from reflex.ivars.base import ImmutableVar, LiteralVar
|
|
410
|
+
|
|
486
411
|
return [
|
|
487
|
-
|
|
412
|
+
(
|
|
413
|
+
f"{name}={format_prop(prop)}"
|
|
414
|
+
if isinstance(prop, ImmutableVar) and not isinstance(prop, ImmutableVar)
|
|
415
|
+
else (
|
|
416
|
+
f"{name}={{{format_prop(prop if isinstance(prop, ImmutableVar) else LiteralVar.create(prop))}}}"
|
|
417
|
+
)
|
|
418
|
+
)
|
|
488
419
|
for name, prop in sorted(key_value_props.items())
|
|
489
420
|
if prop is not None
|
|
490
|
-
] + [
|
|
421
|
+
] + [
|
|
422
|
+
(
|
|
423
|
+
str(prop)
|
|
424
|
+
if isinstance(prop, ImmutableVar) and not isinstance(prop, ImmutableVar)
|
|
425
|
+
else f"{str(LiteralVar.create(prop))}"
|
|
426
|
+
)
|
|
427
|
+
for prop in single_props
|
|
428
|
+
]
|
|
491
429
|
|
|
492
430
|
|
|
493
431
|
def get_event_handler_parts(handler: EventHandler) -> tuple[str, str]:
|
|
@@ -502,13 +440,13 @@ def get_event_handler_parts(handler: EventHandler) -> tuple[str, str]:
|
|
|
502
440
|
# Get the class that defines the event handler.
|
|
503
441
|
parts = handler.fn.__qualname__.split(".")
|
|
504
442
|
|
|
505
|
-
# If there's no enclosing class, just return the function name.
|
|
506
|
-
if len(parts) == 1:
|
|
507
|
-
return ("", parts[-1])
|
|
508
|
-
|
|
509
443
|
# Get the state full name
|
|
510
444
|
state_full_name = handler.state_full_name
|
|
511
445
|
|
|
446
|
+
# If there's no enclosing class, just return the function name.
|
|
447
|
+
if not state_full_name:
|
|
448
|
+
return ("", parts[-1])
|
|
449
|
+
|
|
512
450
|
# Get the function name
|
|
513
451
|
name = parts[-1]
|
|
514
452
|
|
|
@@ -555,7 +493,7 @@ def format_event(event_spec: EventSpec) -> str:
|
|
|
555
493
|
"`",
|
|
556
494
|
)
|
|
557
495
|
if val._var_is_string
|
|
558
|
-
else val
|
|
496
|
+
else str(val)
|
|
559
497
|
),
|
|
560
498
|
)
|
|
561
499
|
)
|
|
@@ -572,54 +510,20 @@ def format_event(event_spec: EventSpec) -> str:
|
|
|
572
510
|
return f"Event({', '.join(event_args)})"
|
|
573
511
|
|
|
574
512
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
event_arg: Var | None = None,
|
|
578
|
-
) -> str:
|
|
579
|
-
"""Format an event chain as a javascript invocation.
|
|
580
|
-
|
|
581
|
-
Args:
|
|
582
|
-
event_chain: The event chain to queue on the frontend.
|
|
583
|
-
event_arg: The browser-native event (only used to preventDefault).
|
|
584
|
-
|
|
585
|
-
Returns:
|
|
586
|
-
Compiled javascript code to queue the given event chain on the frontend.
|
|
587
|
-
|
|
588
|
-
Raises:
|
|
589
|
-
ValueError: When the given event chain is not a valid event chain.
|
|
590
|
-
"""
|
|
591
|
-
if isinstance(event_chain, Var):
|
|
592
|
-
from reflex.event import EventChain
|
|
593
|
-
|
|
594
|
-
if event_chain._var_type is not EventChain:
|
|
595
|
-
raise ValueError(f"Invalid event chain: {event_chain}")
|
|
596
|
-
return "".join(
|
|
597
|
-
[
|
|
598
|
-
"(() => {",
|
|
599
|
-
format_var(event_chain),
|
|
600
|
-
f"; preventDefault({format_var(event_arg)})" if event_arg else "",
|
|
601
|
-
"})()",
|
|
602
|
-
]
|
|
603
|
-
)
|
|
604
|
-
|
|
605
|
-
chain = ",".join([format_event(event) for event in event_chain.events])
|
|
606
|
-
return "".join(
|
|
607
|
-
[
|
|
608
|
-
f"addEvents([{chain}]",
|
|
609
|
-
f", {format_var(event_arg)}" if event_arg else "",
|
|
610
|
-
")",
|
|
611
|
-
]
|
|
612
|
-
)
|
|
513
|
+
if TYPE_CHECKING:
|
|
514
|
+
from reflex.ivars import ImmutableVar
|
|
613
515
|
|
|
614
516
|
|
|
615
517
|
def format_queue_events(
|
|
616
|
-
events:
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
518
|
+
events: (
|
|
519
|
+
EventSpec
|
|
520
|
+
| EventHandler
|
|
521
|
+
| Callable
|
|
522
|
+
| List[EventSpec | EventHandler | Callable]
|
|
523
|
+
| None
|
|
524
|
+
) = None,
|
|
621
525
|
args_spec: Optional[ArgsSpec] = None,
|
|
622
|
-
) ->
|
|
526
|
+
) -> ImmutableVar[EventChain]:
|
|
623
527
|
"""Format a list of event handler / event spec as a javascript callback.
|
|
624
528
|
|
|
625
529
|
The resulting code can be passed to interfaces that expect a callback
|
|
@@ -645,11 +549,10 @@ def format_queue_events(
|
|
|
645
549
|
call_event_fn,
|
|
646
550
|
call_event_handler,
|
|
647
551
|
)
|
|
552
|
+
from reflex.ivars import FunctionVar, ImmutableVar
|
|
648
553
|
|
|
649
554
|
if not events:
|
|
650
|
-
return
|
|
651
|
-
"() => null", _var_is_string=False, _var_is_local=False
|
|
652
|
-
).to(EventChain)
|
|
555
|
+
return ImmutableVar("(() => null)").to(FunctionVar, EventChain) # type: ignore
|
|
653
556
|
|
|
654
557
|
# If no spec is provided, the function will take no arguments.
|
|
655
558
|
def _default_args_spec():
|
|
@@ -674,7 +577,7 @@ def format_queue_events(
|
|
|
674
577
|
specs = [call_event_handler(spec, args_spec or _default_args_spec)]
|
|
675
578
|
elif isinstance(spec, type(lambda: None)):
|
|
676
579
|
specs = call_event_fn(spec, args_spec or _default_args_spec) # type: ignore
|
|
677
|
-
if isinstance(specs,
|
|
580
|
+
if isinstance(specs, ImmutableVar):
|
|
678
581
|
raise ValueError(
|
|
679
582
|
f"Invalid event spec: {specs}. Expected a list of EventSpecs."
|
|
680
583
|
)
|
|
@@ -682,12 +585,10 @@ def format_queue_events(
|
|
|
682
585
|
|
|
683
586
|
# Return the final code snippet, expecting queueEvents, processEvent, and socket to be in scope.
|
|
684
587
|
# Typically this snippet will _only_ run from within an rx.call_script eval context.
|
|
685
|
-
return
|
|
588
|
+
return ImmutableVar(
|
|
686
589
|
f"{arg_def} => {{queueEvents([{','.join(payloads)}], {constants.CompileVars.SOCKET}); "
|
|
687
590
|
f"processEvent({constants.CompileVars.SOCKET})}}",
|
|
688
|
-
|
|
689
|
-
_var_is_local=False,
|
|
690
|
-
).to(EventChain)
|
|
591
|
+
).to(FunctionVar, EventChain) # type: ignore
|
|
691
592
|
|
|
692
593
|
|
|
693
594
|
def format_query_params(router_data: dict[str, Any]) -> dict[str, str]:
|
|
@@ -774,41 +675,6 @@ def format_ref(ref: str) -> str:
|
|
|
774
675
|
return f"ref_{clean_ref}"
|
|
775
676
|
|
|
776
677
|
|
|
777
|
-
def format_array_ref(refs: str, idx: Var | None) -> str:
|
|
778
|
-
"""Format a ref accessed by array.
|
|
779
|
-
|
|
780
|
-
Args:
|
|
781
|
-
refs : The ref array to access.
|
|
782
|
-
idx : The index of the ref in the array.
|
|
783
|
-
|
|
784
|
-
Returns:
|
|
785
|
-
The formatted ref.
|
|
786
|
-
"""
|
|
787
|
-
clean_ref = re.sub(r"[^\w]+", "_", refs)
|
|
788
|
-
if idx is not None:
|
|
789
|
-
idx._var_is_local = True
|
|
790
|
-
return f"refs_{clean_ref}[{idx}]"
|
|
791
|
-
return f"refs_{clean_ref}"
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
def format_breadcrumbs(route: str) -> list[tuple[str, str]]:
|
|
795
|
-
"""Take a route and return a list of tuple for use in breadcrumb.
|
|
796
|
-
|
|
797
|
-
Args:
|
|
798
|
-
route: The route to transform.
|
|
799
|
-
|
|
800
|
-
Returns:
|
|
801
|
-
list[tuple[str, str]]: the list of tuples for the breadcrumb.
|
|
802
|
-
"""
|
|
803
|
-
route_parts = route.lstrip("/").split("/")
|
|
804
|
-
|
|
805
|
-
# create and return breadcrumbs
|
|
806
|
-
return [
|
|
807
|
-
(part, "/".join(["", *route_parts[: i + 1]]))
|
|
808
|
-
for i, part in enumerate(route_parts)
|
|
809
|
-
]
|
|
810
|
-
|
|
811
|
-
|
|
812
678
|
def format_library_name(library_fullname: str):
|
|
813
679
|
"""Format the name of a library.
|
|
814
680
|
|
|
@@ -839,42 +705,6 @@ def json_dumps(obj: Any) -> str:
|
|
|
839
705
|
return json.dumps(obj, ensure_ascii=False, default=serializers.serialize)
|
|
840
706
|
|
|
841
707
|
|
|
842
|
-
def unwrap_vars(value: str) -> str:
|
|
843
|
-
"""Unwrap var values from a JSON string.
|
|
844
|
-
|
|
845
|
-
For example, "{var}" will be unwrapped to "var".
|
|
846
|
-
|
|
847
|
-
Args:
|
|
848
|
-
value: The JSON string to unwrap.
|
|
849
|
-
|
|
850
|
-
Returns:
|
|
851
|
-
The unwrapped JSON string.
|
|
852
|
-
"""
|
|
853
|
-
|
|
854
|
-
def unescape_double_quotes_in_var(m: re.Match) -> str:
|
|
855
|
-
prefix = m.group(1) or ""
|
|
856
|
-
# Since the outer quotes are removed, the inner escaped quotes must be unescaped.
|
|
857
|
-
return prefix + re.sub('\\\\"', '"', m.group(2))
|
|
858
|
-
|
|
859
|
-
# This substitution is necessary to unwrap var values.
|
|
860
|
-
return (
|
|
861
|
-
re.sub(
|
|
862
|
-
pattern=r"""
|
|
863
|
-
(?<!\\) # must NOT start with a backslash
|
|
864
|
-
" # match opening double quote of JSON value
|
|
865
|
-
(<reflex.Var>.*?</reflex.Var>)? # Optional encoded VarData (non-greedy)
|
|
866
|
-
{(.*?)} # extract the value between curly braces (non-greedy)
|
|
867
|
-
" # match must end with an unescaped double quote
|
|
868
|
-
""",
|
|
869
|
-
repl=unescape_double_quotes_in_var,
|
|
870
|
-
string=value,
|
|
871
|
-
flags=re.VERBOSE,
|
|
872
|
-
)
|
|
873
|
-
.replace('"`', "`")
|
|
874
|
-
.replace('`"', "`")
|
|
875
|
-
)
|
|
876
|
-
|
|
877
|
-
|
|
878
708
|
def collect_form_dict_names(form_dict: dict[str, Any]) -> dict[str, Any]:
|
|
879
709
|
"""Collapse keys with consecutive suffixes into a single list value.
|
|
880
710
|
|
|
@@ -897,6 +727,23 @@ def collect_form_dict_names(form_dict: dict[str, Any]) -> dict[str, Any]:
|
|
|
897
727
|
return collapsed
|
|
898
728
|
|
|
899
729
|
|
|
730
|
+
def format_array_ref(refs: str, idx: ImmutableVar | None) -> str:
|
|
731
|
+
"""Format a ref accessed by array.
|
|
732
|
+
|
|
733
|
+
Args:
|
|
734
|
+
refs : The ref array to access.
|
|
735
|
+
idx : The index of the ref in the array.
|
|
736
|
+
|
|
737
|
+
Returns:
|
|
738
|
+
The formatted ref.
|
|
739
|
+
"""
|
|
740
|
+
clean_ref = re.sub(r"[^\w]+", "_", refs)
|
|
741
|
+
if idx is not None:
|
|
742
|
+
# idx._var_is_local = True
|
|
743
|
+
return f"refs_{clean_ref}[{str(idx)}]"
|
|
744
|
+
return f"refs_{clean_ref}"
|
|
745
|
+
|
|
746
|
+
|
|
900
747
|
def format_data_editor_column(col: str | dict):
|
|
901
748
|
"""Format a given column into the proper format.
|
|
902
749
|
|
|
@@ -909,6 +756,8 @@ def format_data_editor_column(col: str | dict):
|
|
|
909
756
|
Returns:
|
|
910
757
|
The formatted column.
|
|
911
758
|
"""
|
|
759
|
+
from reflex.ivars import ImmutableVar
|
|
760
|
+
|
|
912
761
|
if isinstance(col, str):
|
|
913
762
|
return {"title": col, "id": col.lower(), "type": "str"}
|
|
914
763
|
|
|
@@ -921,7 +770,7 @@ def format_data_editor_column(col: str | dict):
|
|
|
921
770
|
col["overlayIcon"] = None
|
|
922
771
|
return col
|
|
923
772
|
|
|
924
|
-
if isinstance(col,
|
|
773
|
+
if isinstance(col, ImmutableVar):
|
|
925
774
|
return col
|
|
926
775
|
|
|
927
776
|
raise ValueError(
|
|
@@ -938,7 +787,9 @@ def format_data_editor_cell(cell: Any):
|
|
|
938
787
|
Returns:
|
|
939
788
|
The formatted cell.
|
|
940
789
|
"""
|
|
790
|
+
from reflex.ivars.base import ImmutableVar
|
|
791
|
+
|
|
941
792
|
return {
|
|
942
|
-
"kind":
|
|
793
|
+
"kind": ImmutableVar.create("GridCellKind.Text"),
|
|
943
794
|
"data": cell,
|
|
944
795
|
}
|
reflex/utils/net.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Helpers for downloading files from the network."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from . import console
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _httpx_verify_kwarg() -> bool:
|
|
11
|
+
"""Get the value of the HTTPX verify keyword argument.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
True if SSL verification is enabled, False otherwise
|
|
15
|
+
"""
|
|
16
|
+
ssl_no_verify = os.environ.get("SSL_NO_VERIFY", "").lower() in ["true", "1", "yes"]
|
|
17
|
+
return not ssl_no_verify
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get(url: str, **kwargs) -> httpx.Response:
|
|
21
|
+
"""Make an HTTP GET request.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
url: The URL to request.
|
|
25
|
+
**kwargs: Additional keyword arguments to pass to httpx.get.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
The response object.
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
httpx.ConnectError: If the connection cannot be established.
|
|
32
|
+
"""
|
|
33
|
+
kwargs.setdefault("verify", _httpx_verify_kwarg())
|
|
34
|
+
try:
|
|
35
|
+
return httpx.get(url, **kwargs)
|
|
36
|
+
except httpx.ConnectError as err:
|
|
37
|
+
if "CERTIFICATE_VERIFY_FAILED" in str(err):
|
|
38
|
+
# If the error is a certificate verification error, recommend mitigating steps.
|
|
39
|
+
console.error(
|
|
40
|
+
f"Certificate verification failed for {url}. Set environment variable SSL_CERT_FILE to the "
|
|
41
|
+
"path of the certificate file or SSL_NO_VERIFY=1 to disable verification."
|
|
42
|
+
)
|
|
43
|
+
raise
|
reflex/utils/path_ops.py
CHANGED
|
@@ -81,6 +81,18 @@ def mkdir(path: str | Path):
|
|
|
81
81
|
Path(path).mkdir(parents=True, exist_ok=True)
|
|
82
82
|
|
|
83
83
|
|
|
84
|
+
def ls(path: str | Path) -> list[Path]:
|
|
85
|
+
"""List the contents of a directory.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
path: The path to the directory.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
A list of paths to the contents of the directory.
|
|
92
|
+
"""
|
|
93
|
+
return list(Path(path).iterdir())
|
|
94
|
+
|
|
95
|
+
|
|
84
96
|
def ln(src: str | Path, dest: str | Path, overwrite: bool = False) -> bool:
|
|
85
97
|
"""Create a symbolic link.
|
|
86
98
|
|
|
@@ -197,4 +209,4 @@ def find_replace(directory: str | Path, find: str, replace: str):
|
|
|
197
209
|
filepath = Path(root, file)
|
|
198
210
|
text = filepath.read_text(encoding="utf-8")
|
|
199
211
|
text = re.sub(find, replace, text)
|
|
200
|
-
filepath.write_text(text)
|
|
212
|
+
filepath.write_text(text, encoding="utf-8")
|
reflex/utils/prerequisites.py
CHANGED
|
@@ -28,13 +28,14 @@ import typer
|
|
|
28
28
|
from alembic.util.exc import CommandError
|
|
29
29
|
from packaging import version
|
|
30
30
|
from redis import Redis as RedisSync
|
|
31
|
+
from redis import exceptions
|
|
31
32
|
from redis.asyncio import Redis
|
|
32
33
|
|
|
33
34
|
from reflex import constants, model
|
|
34
35
|
from reflex.base import Base
|
|
35
36
|
from reflex.compiler import templates
|
|
36
37
|
from reflex.config import Config, get_config
|
|
37
|
-
from reflex.utils import console, path_ops, processes
|
|
38
|
+
from reflex.utils import console, net, path_ops, processes
|
|
38
39
|
from reflex.utils.format import format_library_name
|
|
39
40
|
from reflex.utils.registry import _get_best_registry
|
|
40
41
|
|
|
@@ -80,7 +81,7 @@ def check_latest_package_version(package_name: str):
|
|
|
80
81
|
# Get the latest version from PyPI
|
|
81
82
|
current_version = importlib.metadata.version(package_name)
|
|
82
83
|
url = f"https://pypi.org/pypi/{package_name}/json"
|
|
83
|
-
response =
|
|
84
|
+
response = net.get(url)
|
|
84
85
|
latest_version = response.json()["info"]["version"]
|
|
85
86
|
if (
|
|
86
87
|
version.parse(current_version) < version.parse(latest_version)
|
|
@@ -324,24 +325,43 @@ def parse_redis_url() -> str | dict | None:
|
|
|
324
325
|
"""Parse the REDIS_URL in config if applicable.
|
|
325
326
|
|
|
326
327
|
Returns:
|
|
327
|
-
If
|
|
328
|
+
If url is non-empty, return the URL as it is.
|
|
329
|
+
|
|
330
|
+
Raises:
|
|
331
|
+
ValueError: If the REDIS_URL is not a supported scheme.
|
|
328
332
|
"""
|
|
329
333
|
config = get_config()
|
|
330
334
|
if not config.redis_url:
|
|
331
335
|
return None
|
|
332
|
-
if config.redis_url.startswith(("redis://", "rediss://", "unix://")):
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
336
|
+
if not config.redis_url.startswith(("redis://", "rediss://", "unix://")):
|
|
337
|
+
raise ValueError(
|
|
338
|
+
"REDIS_URL must start with 'redis://', 'rediss://', or 'unix://'."
|
|
339
|
+
)
|
|
340
|
+
return config.redis_url
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
async def get_redis_status() -> bool | None:
|
|
344
|
+
"""Checks the status of the Redis connection.
|
|
345
|
+
|
|
346
|
+
Attempts to connect to Redis and send a ping command to verify connectivity.
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
bool or None: The status of the Redis connection:
|
|
350
|
+
- True: Redis is accessible and responding.
|
|
351
|
+
- False: Redis is not accessible due to a connection error.
|
|
352
|
+
- None: Redis not used i.e redis_url is not set in rxconfig.
|
|
353
|
+
"""
|
|
354
|
+
try:
|
|
355
|
+
status = True
|
|
356
|
+
redis_client = get_redis_sync()
|
|
357
|
+
if redis_client is not None:
|
|
358
|
+
redis_client.ping()
|
|
359
|
+
else:
|
|
360
|
+
status = None
|
|
361
|
+
except exceptions.RedisError:
|
|
362
|
+
status = False
|
|
363
|
+
|
|
364
|
+
return status
|
|
345
365
|
|
|
346
366
|
|
|
347
367
|
def validate_app_name(app_name: str | None = None) -> str:
|
|
@@ -670,7 +690,7 @@ def download_and_run(url: str, *args, show_status: bool = False, **env):
|
|
|
670
690
|
"""
|
|
671
691
|
# Download the script
|
|
672
692
|
console.debug(f"Downloading {url}")
|
|
673
|
-
response =
|
|
693
|
+
response = net.get(url)
|
|
674
694
|
if response.status_code != httpx.codes.OK:
|
|
675
695
|
response.raise_for_status()
|
|
676
696
|
|
|
@@ -700,11 +720,11 @@ def download_and_extract_fnm_zip():
|
|
|
700
720
|
try:
|
|
701
721
|
# Download the FNM zip release.
|
|
702
722
|
# TODO: show progress to improve UX
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
723
|
+
response = net.get(url, follow_redirects=True)
|
|
724
|
+
response.raise_for_status()
|
|
725
|
+
with open(fnm_zip_file, "wb") as output_file:
|
|
726
|
+
for chunk in response.iter_bytes():
|
|
727
|
+
output_file.write(chunk)
|
|
708
728
|
|
|
709
729
|
# Extract the downloaded zip file.
|
|
710
730
|
with zipfile.ZipFile(fnm_zip_file, "r") as zip_ref:
|
|
@@ -1222,7 +1242,7 @@ def fetch_app_templates(version: str) -> dict[str, Template]:
|
|
|
1222
1242
|
"""
|
|
1223
1243
|
|
|
1224
1244
|
def get_release_by_tag(tag: str) -> dict | None:
|
|
1225
|
-
response =
|
|
1245
|
+
response = net.get(constants.Reflex.RELEASES_URL)
|
|
1226
1246
|
response.raise_for_status()
|
|
1227
1247
|
releases = response.json()
|
|
1228
1248
|
for release in releases:
|
|
@@ -1243,7 +1263,7 @@ def fetch_app_templates(version: str) -> dict[str, Template]:
|
|
|
1243
1263
|
else:
|
|
1244
1264
|
templates_url = asset["browser_download_url"]
|
|
1245
1265
|
|
|
1246
|
-
templates_data =
|
|
1266
|
+
templates_data = net.get(templates_url, follow_redirects=True).json()["templates"]
|
|
1247
1267
|
|
|
1248
1268
|
for template in templates_data:
|
|
1249
1269
|
if template["name"] == "blank":
|
|
@@ -1286,7 +1306,7 @@ def create_config_init_app_from_remote_template(app_name: str, template_url: str
|
|
|
1286
1306
|
zip_file_path = Path(temp_dir) / "template.zip"
|
|
1287
1307
|
try:
|
|
1288
1308
|
# Note: following redirects can be risky. We only allow this for reflex built templates at the moment.
|
|
1289
|
-
response =
|
|
1309
|
+
response = net.get(template_url, follow_redirects=True)
|
|
1290
1310
|
console.debug(f"Server responded download request: {response}")
|
|
1291
1311
|
response.raise_for_status()
|
|
1292
1312
|
except httpx.HTTPError as he:
|
|
@@ -1417,7 +1437,7 @@ def initialize_main_module_index_from_generation(app_name: str, generation_hash:
|
|
|
1417
1437
|
generation_hash: The generation hash from reflex.build.
|
|
1418
1438
|
"""
|
|
1419
1439
|
# Download the reflex code for the generation.
|
|
1420
|
-
resp =
|
|
1440
|
+
resp = net.get(
|
|
1421
1441
|
constants.Templates.REFLEX_BUILD_CODE_URL.format(
|
|
1422
1442
|
generation_hash=generation_hash
|
|
1423
1443
|
)
|