reflex 0.5.5a2__py3-none-any.whl → 0.5.6a1__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/_app.js.jinja2 +0 -1
- reflex/.templates/jinja/web/utils/context.js.jinja2 +2 -0
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +20 -2
- reflex/.templates/web/utils/helpers/paste.js +59 -0
- reflex/__init__.py +2 -0
- reflex/__init__.pyi +86 -87
- reflex/app.py +64 -126
- reflex/app_mixins/__init__.py +5 -0
- reflex/app_mixins/lifespan.py +57 -0
- reflex/app_mixins/middleware.py +93 -0
- reflex/app_mixins/mixin.py +14 -0
- reflex/compiler/compiler.py +6 -1
- reflex/components/__init__.pyi +0 -2
- reflex/components/base/__init__.pyi +1 -3
- reflex/components/base/app_wrap.pyi +21 -22
- reflex/components/base/body.pyi +21 -20
- reflex/components/base/document.pyi +85 -85
- reflex/components/base/fragment.pyi +21 -20
- reflex/components/base/head.pyi +37 -36
- reflex/components/base/link.pyi +37 -37
- reflex/components/base/meta.pyi +69 -70
- reflex/components/base/script.py +6 -2
- reflex/components/base/script.pyi +31 -27
- reflex/components/chakra/base.pyi +54 -56
- reflex/components/chakra/datadisplay/badge.pyi +21 -21
- reflex/components/chakra/datadisplay/code.pyi +21 -20
- reflex/components/chakra/datadisplay/divider.pyi +21 -22
- reflex/components/chakra/datadisplay/keyboard_key.pyi +21 -20
- reflex/components/chakra/datadisplay/list.pyi +69 -71
- reflex/components/chakra/datadisplay/stat.pyi +101 -102
- reflex/components/chakra/datadisplay/table.pyi +149 -153
- reflex/components/chakra/datadisplay/tag.pyi +85 -89
- reflex/components/chakra/disclosure/accordion.pyi +87 -93
- reflex/components/chakra/disclosure/tabs.pyi +85 -90
- reflex/components/chakra/disclosure/transition.pyi +104 -105
- reflex/components/chakra/disclosure/visuallyhidden.pyi +21 -20
- reflex/components/chakra/feedback/alert.pyi +69 -70
- reflex/components/chakra/feedback/circularprogress.pyi +38 -40
- reflex/components/chakra/feedback/progress.pyi +22 -23
- reflex/components/chakra/feedback/skeleton.pyi +53 -53
- reflex/components/chakra/feedback/spinner.pyi +21 -21
- reflex/components/chakra/forms/button.pyi +37 -42
- reflex/components/chakra/forms/checkbox.pyi +38 -39
- reflex/components/chakra/forms/colormodeswitch.pyi +72 -71
- reflex/components/chakra/forms/date_picker.pyi +24 -24
- reflex/components/chakra/forms/date_time_picker.pyi +24 -24
- reflex/components/chakra/forms/editable.pyi +73 -74
- reflex/components/chakra/forms/email.pyi +24 -24
- reflex/components/chakra/forms/form.pyi +112 -141
- reflex/components/chakra/forms/iconbutton.pyi +21 -22
- reflex/components/chakra/forms/input.pyi +104 -111
- reflex/components/chakra/forms/numberinput.pyi +87 -91
- reflex/components/chakra/forms/password.pyi +24 -24
- reflex/components/chakra/forms/pininput.pyi +39 -45
- reflex/components/chakra/forms/radio.pyi +38 -43
- reflex/components/chakra/forms/rangeslider.pyi +72 -76
- reflex/components/chakra/forms/select.pyi +39 -44
- reflex/components/chakra/forms/slider.pyi +88 -91
- reflex/components/chakra/forms/switch.pyi +22 -23
- reflex/components/chakra/forms/textarea.pyi +24 -27
- reflex/components/chakra/forms/time_picker.pyi +24 -24
- reflex/components/chakra/layout/aspect_ratio.pyi +21 -21
- reflex/components/chakra/layout/box.pyi +21 -22
- reflex/components/chakra/layout/card.pyi +69 -73
- reflex/components/chakra/layout/center.pyi +53 -52
- reflex/components/chakra/layout/container.pyi +21 -21
- reflex/components/chakra/layout/flex.pyi +23 -26
- reflex/components/chakra/layout/grid.pyi +53 -54
- reflex/components/chakra/layout/spacer.pyi +21 -20
- reflex/components/chakra/layout/stack.pyi +62 -60
- reflex/components/chakra/layout/wrap.pyi +37 -38
- reflex/components/chakra/media/avatar.pyi +54 -55
- reflex/components/chakra/media/icon.pyi +37 -38
- reflex/components/chakra/media/image.pyi +24 -26
- reflex/components/chakra/navigation/breadcrumb.pyi +69 -71
- reflex/components/chakra/navigation/link.pyi +20 -21
- reflex/components/chakra/navigation/linkoverlay.pyi +37 -37
- reflex/components/chakra/navigation/stepper.pyi +149 -151
- reflex/components/chakra/overlay/alertdialog.pyi +121 -124
- reflex/components/chakra/overlay/drawer.pyi +121 -126
- reflex/components/chakra/overlay/menu.pyi +135 -141
- reflex/components/chakra/overlay/modal.pyi +121 -124
- reflex/components/chakra/overlay/popover.pyi +151 -156
- reflex/components/chakra/overlay/tooltip.pyi +23 -24
- reflex/components/chakra/typography/heading.pyi +21 -21
- reflex/components/chakra/typography/highlight.pyi +21 -23
- reflex/components/chakra/typography/span.pyi +21 -21
- reflex/components/chakra/typography/text.pyi +21 -21
- reflex/components/component.py +6 -2
- reflex/components/core/__init__.py +2 -0
- reflex/components/core/__init__.pyi +9 -7
- reflex/components/core/banner.pyi +118 -146
- reflex/components/core/breakpoints.py +95 -0
- reflex/components/core/client_side_routing.pyi +37 -39
- reflex/components/core/clipboard.py +95 -0
- reflex/components/core/clipboard.pyi +102 -0
- reflex/components/core/debounce.pyi +23 -28
- reflex/components/core/html.pyi +38 -55
- reflex/components/core/upload.pyi +74 -91
- reflex/components/datadisplay/__init__.pyi +2 -3
- reflex/components/datadisplay/code.py +3 -3
- reflex/components/datadisplay/code.pyi +22 -31
- reflex/components/datadisplay/dataeditor.pyi +41 -45
- reflex/components/el/__init__.pyi +131 -135
- reflex/components/el/element.pyi +21 -20
- reflex/components/el/elements/__init__.pyi +131 -132
- reflex/components/el/elements/base.pyi +38 -55
- reflex/components/el/elements/forms.pyi +558 -878
- reflex/components/el/elements/inline.pyi +941 -1403
- reflex/components/el/elements/media.pyi +645 -994
- reflex/components/el/elements/metadata.pyi +186 -268
- reflex/components/el/elements/other.pyi +239 -353
- reflex/components/el/elements/scripts.pyi +113 -171
- reflex/components/el/elements/sectioning.pyi +500 -739
- reflex/components/el/elements/tables.pyi +355 -551
- reflex/components/el/elements/typography.pyi +510 -760
- reflex/components/gridjs/datatable.pyi +38 -42
- reflex/components/lucide/icon.pyi +37 -38
- reflex/components/markdown/markdown.pyi +23 -36
- reflex/components/moment/moment.pyi +23 -25
- reflex/components/next/base.pyi +21 -20
- reflex/components/next/image.pyi +25 -27
- reflex/components/next/link.pyi +21 -21
- reflex/components/next/video.pyi +22 -22
- reflex/components/plotly/plotly.pyi +42 -45
- reflex/components/radix/__init__.pyi +26 -30
- reflex/components/radix/primitives/__init__.pyi +0 -2
- reflex/components/radix/primitives/accordion.pyi +119 -127
- reflex/components/radix/primitives/base.pyi +37 -40
- reflex/components/radix/primitives/drawer.pyi +175 -179
- reflex/components/radix/primitives/form.pyi +250 -336
- reflex/components/radix/primitives/progress.pyi +92 -96
- reflex/components/radix/primitives/slider.pyi +87 -89
- reflex/components/radix/themes/__init__.pyi +0 -2
- reflex/components/radix/themes/base.pyi +118 -121
- reflex/components/radix/themes/color_mode.pyi +103 -117
- reflex/components/radix/themes/components/__init__.pyi +12 -14
- reflex/components/radix/themes/components/alert_dialog.py +2 -1
- reflex/components/radix/themes/components/alert_dialog.pyi +150 -157
- reflex/components/radix/themes/components/aspect_ratio.pyi +22 -22
- reflex/components/radix/themes/components/avatar.py +2 -1
- reflex/components/radix/themes/components/avatar.pyi +32 -23
- reflex/components/radix/themes/components/badge.py +2 -1
- reflex/components/radix/themes/components/badge.pyi +50 -57
- reflex/components/radix/themes/components/button.py +2 -1
- reflex/components/radix/themes/components/button.pyi +60 -79
- reflex/components/radix/themes/components/callout.py +2 -1
- reflex/components/radix/themes/components/callout.pyi +201 -258
- reflex/components/radix/themes/components/card.py +2 -1
- reflex/components/radix/themes/components/card.pyi +48 -56
- reflex/components/radix/themes/components/checkbox.py +2 -1
- reflex/components/radix/themes/components/checkbox.pyi +68 -62
- reflex/components/radix/themes/components/checkbox_cards.py +8 -3
- reflex/components/radix/themes/components/checkbox_cards.pyi +87 -44
- reflex/components/radix/themes/components/checkbox_group.py +2 -1
- reflex/components/radix/themes/components/checkbox_group.pyi +49 -40
- reflex/components/radix/themes/components/context_menu.py +2 -1
- reflex/components/radix/themes/components/context_menu.pyi +153 -147
- reflex/components/radix/themes/components/data_list.py +8 -7
- reflex/components/radix/themes/components/data_list.pyi +116 -78
- reflex/components/radix/themes/components/dialog.py +2 -1
- reflex/components/radix/themes/components/dialog.pyi +154 -161
- reflex/components/radix/themes/components/dropdown_menu.py +2 -1
- reflex/components/radix/themes/components/dropdown_menu.pyi +169 -163
- reflex/components/radix/themes/components/hover_card.py +2 -1
- reflex/components/radix/themes/components/hover_card.pyi +97 -107
- reflex/components/radix/themes/components/icon_button.py +2 -1
- reflex/components/radix/themes/components/icon_button.pyi +59 -82
- reflex/components/radix/themes/components/inset.py +10 -9
- reflex/components/radix/themes/components/inset.pyi +109 -61
- reflex/components/radix/themes/components/popover.py +2 -1
- reflex/components/radix/themes/components/popover.pyi +105 -112
- reflex/components/radix/themes/components/progress.py +2 -1
- reflex/components/radix/themes/components/progress.pyi +32 -24
- reflex/components/radix/themes/components/radio.py +2 -1
- reflex/components/radix/themes/components/radio.pyi +32 -23
- reflex/components/radix/themes/components/radio_cards.py +51 -3
- reflex/components/radix/themes/components/radio_cards.pyi +120 -44
- reflex/components/radix/themes/components/radio_group.py +5 -2
- reflex/components/radix/themes/components/radio_group.pyi +82 -77
- reflex/components/radix/themes/components/scroll_area.pyi +21 -21
- reflex/components/radix/themes/components/segmented_control.py +2 -1
- reflex/components/radix/themes/components/segmented_control.pyi +52 -46
- reflex/components/radix/themes/components/select.py +2 -1
- reflex/components/radix/themes/components/select.pyi +188 -164
- reflex/components/radix/themes/components/separator.py +5 -2
- reflex/components/radix/themes/components/separator.pyi +40 -24
- reflex/components/radix/themes/components/skeleton.py +7 -6
- reflex/components/radix/themes/components/skeleton.pyi +40 -26
- reflex/components/radix/themes/components/slider.py +2 -1
- reflex/components/radix/themes/components/slider.pyi +40 -31
- reflex/components/radix/themes/components/spinner.py +2 -1
- reflex/components/radix/themes/components/spinner.pyi +31 -22
- reflex/components/radix/themes/components/switch.py +2 -1
- reflex/components/radix/themes/components/switch.pyi +33 -25
- reflex/components/radix/themes/components/table.py +2 -1
- reflex/components/radix/themes/components/table.pyi +265 -404
- reflex/components/radix/themes/components/tabs.py +14 -1
- reflex/components/radix/themes/components/tabs.pyi +113 -92
- reflex/components/radix/themes/components/text_area.py +3 -2
- reflex/components/radix/themes/components/text_area.pyi +64 -66
- reflex/components/radix/themes/components/text_field.py +2 -1
- reflex/components/radix/themes/components/text_field.pyi +116 -140
- reflex/components/radix/themes/components/tooltip.pyi +32 -37
- reflex/components/radix/themes/layout/__init__.pyi +4 -7
- reflex/components/radix/themes/layout/base.py +10 -9
- reflex/components/radix/themes/layout/base.pyi +121 -31
- reflex/components/radix/themes/layout/box.pyi +39 -53
- reflex/components/radix/themes/layout/center.pyi +89 -58
- reflex/components/radix/themes/layout/container.py +4 -1
- reflex/components/radix/themes/layout/container.pyi +51 -58
- reflex/components/radix/themes/layout/flex.py +6 -5
- reflex/components/radix/themes/layout/flex.pyi +91 -61
- reflex/components/radix/themes/layout/grid.py +9 -8
- reflex/components/radix/themes/layout/grid.pyi +116 -64
- reflex/components/radix/themes/layout/list.pyi +173 -233
- reflex/components/radix/themes/layout/section.py +4 -1
- reflex/components/radix/themes/layout/section.pyi +50 -57
- reflex/components/radix/themes/layout/spacer.pyi +89 -58
- reflex/components/radix/themes/layout/stack.pyi +160 -160
- reflex/components/radix/themes/typography/__init__.pyi +0 -2
- reflex/components/radix/themes/typography/blockquote.py +3 -2
- reflex/components/radix/themes/typography/blockquote.pyi +58 -59
- reflex/components/radix/themes/typography/code.py +3 -2
- reflex/components/radix/themes/typography/code.pyi +57 -58
- reflex/components/radix/themes/typography/heading.py +5 -4
- reflex/components/radix/themes/typography/heading.pyi +71 -60
- reflex/components/radix/themes/typography/link.py +4 -3
- reflex/components/radix/themes/typography/link.pyi +74 -82
- reflex/components/radix/themes/typography/text.py +5 -4
- reflex/components/radix/themes/typography/text.pyi +330 -364
- reflex/components/react_player/audio.pyi +37 -36
- reflex/components/react_player/react_player.pyi +37 -38
- reflex/components/react_player/video.pyi +37 -36
- reflex/components/recharts/__init__.pyi +41 -42
- reflex/components/recharts/cartesian.pyi +384 -400
- reflex/components/recharts/charts.pyi +224 -231
- reflex/components/recharts/general.pyi +89 -96
- reflex/components/recharts/polar.pyi +97 -104
- reflex/components/recharts/recharts.pyi +37 -37
- reflex/components/sonner/toast.pyi +22 -27
- reflex/components/suneditor/editor.pyi +53 -58
- reflex/config.py +6 -0
- reflex/constants/event.py +1 -0
- reflex/experimental/layout.pyi +140 -194
- reflex/model.py +14 -2
- reflex/state.py +38 -45
- reflex/style.py +24 -13
- reflex/utils/codespaces.py +94 -0
- reflex/utils/compat.py +21 -0
- reflex/utils/exceptions.py +4 -0
- reflex/utils/format.py +25 -4
- reflex/utils/prerequisites.py +0 -13
- reflex/utils/pyi_generator.py +88 -61
- reflex/utils/types.py +83 -5
- reflex/vars.py +62 -5
- reflex/vars.pyi +23 -11
- {reflex-0.5.5a2.dist-info → reflex-0.5.6a1.dist-info}/METADATA +4 -5
- {reflex-0.5.5a2.dist-info → reflex-0.5.6a1.dist-info}/RECORD +262 -253
- {reflex-0.5.5a2.dist-info → reflex-0.5.6a1.dist-info}/LICENSE +0 -0
- {reflex-0.5.5a2.dist-info → reflex-0.5.6a1.dist-info}/WHEEL +0 -0
- {reflex-0.5.5a2.dist-info → reflex-0.5.6a1.dist-info}/entry_points.txt +0 -0
reflex/state.py
CHANGED
|
@@ -40,6 +40,7 @@ from redis.exceptions import ResponseError
|
|
|
40
40
|
|
|
41
41
|
from reflex import constants
|
|
42
42
|
from reflex.base import Base
|
|
43
|
+
from reflex.config import get_config
|
|
43
44
|
from reflex.event import (
|
|
44
45
|
BACKGROUND_TASK_MARKER,
|
|
45
46
|
Event,
|
|
@@ -60,6 +61,7 @@ if TYPE_CHECKING:
|
|
|
60
61
|
|
|
61
62
|
Delta = Dict[str, Any]
|
|
62
63
|
var = computed_var
|
|
64
|
+
config = get_config()
|
|
63
65
|
|
|
64
66
|
|
|
65
67
|
# If the state is this large, it's considered a performance issue.
|
|
@@ -197,16 +199,6 @@ def _no_chain_background_task(
|
|
|
197
199
|
raise TypeError(f"{fn} is marked as a background task, but is not async.")
|
|
198
200
|
|
|
199
201
|
|
|
200
|
-
RESERVED_BACKEND_VAR_NAMES = {
|
|
201
|
-
"_backend_vars",
|
|
202
|
-
"_computed_var_dependencies",
|
|
203
|
-
"_substate_var_dependencies",
|
|
204
|
-
"_always_dirty_computed_vars",
|
|
205
|
-
"_always_dirty_substates",
|
|
206
|
-
"_was_touched",
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
202
|
def _substate_key(
|
|
211
203
|
token: str,
|
|
212
204
|
state_cls_or_name: BaseState | Type[BaseState] | str | list[str],
|
|
@@ -313,10 +305,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
313
305
|
# Vars inherited by the parent state.
|
|
314
306
|
inherited_vars: ClassVar[Dict[str, Var]] = {}
|
|
315
307
|
|
|
316
|
-
# Backend vars that are never sent to the client.
|
|
308
|
+
# Backend base vars that are never sent to the client.
|
|
317
309
|
backend_vars: ClassVar[Dict[str, Any]] = {}
|
|
318
310
|
|
|
319
|
-
# Backend vars inherited
|
|
311
|
+
# Backend base vars inherited
|
|
320
312
|
inherited_backend_vars: ClassVar[Dict[str, Any]] = {}
|
|
321
313
|
|
|
322
314
|
# The event handlers.
|
|
@@ -352,7 +344,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
352
344
|
# The routing path that triggered the state
|
|
353
345
|
router_data: Dict[str, Any] = {}
|
|
354
346
|
|
|
355
|
-
# Per-instance copy of backend variable values
|
|
347
|
+
# Per-instance copy of backend base variable values
|
|
356
348
|
_backend_vars: Dict[str, Any] = {}
|
|
357
349
|
|
|
358
350
|
# The router data for the current page
|
|
@@ -409,11 +401,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
409
401
|
|
|
410
402
|
# Create a fresh copy of the backend variables for this instance
|
|
411
403
|
self._backend_vars = copy.deepcopy(
|
|
412
|
-
{
|
|
413
|
-
name: item
|
|
414
|
-
for name, item in self.backend_vars.items()
|
|
415
|
-
if name not in self.computed_vars
|
|
416
|
-
}
|
|
404
|
+
{name: item for name, item in self.backend_vars.items()}
|
|
417
405
|
)
|
|
418
406
|
|
|
419
407
|
def __repr__(self) -> str:
|
|
@@ -500,25 +488,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
500
488
|
new_backend_vars = {
|
|
501
489
|
name: value
|
|
502
490
|
for name, value in cls.__dict__.items()
|
|
503
|
-
if types.
|
|
504
|
-
and name not in RESERVED_BACKEND_VAR_NAMES
|
|
505
|
-
and name not in cls.inherited_backend_vars
|
|
506
|
-
and not isinstance(value, FunctionType)
|
|
507
|
-
and not isinstance(value, ComputedVar)
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
# Get backend computed vars
|
|
511
|
-
backend_computed_vars = {
|
|
512
|
-
v._var_name: v._var_set_state(cls)
|
|
513
|
-
for v in computed_vars
|
|
514
|
-
if types.is_backend_variable(v._var_name, cls)
|
|
515
|
-
and v._var_name not in cls.inherited_backend_vars
|
|
491
|
+
if types.is_backend_base_variable(name, cls)
|
|
516
492
|
}
|
|
517
493
|
|
|
518
494
|
cls.backend_vars = {
|
|
519
495
|
**cls.inherited_backend_vars,
|
|
520
496
|
**new_backend_vars,
|
|
521
|
-
**backend_computed_vars,
|
|
522
497
|
}
|
|
523
498
|
|
|
524
499
|
# Set the base and computed vars.
|
|
@@ -560,6 +535,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
560
535
|
cls.computed_vars[newcv._var_name] = newcv
|
|
561
536
|
cls.vars[newcv._var_name] = newcv
|
|
562
537
|
continue
|
|
538
|
+
if types.is_backend_base_variable(name, mixin):
|
|
539
|
+
cls.backend_vars[name] = copy.deepcopy(value)
|
|
540
|
+
continue
|
|
563
541
|
if events.get(name) is not None:
|
|
564
542
|
continue
|
|
565
543
|
if not cls._item_is_event_handler(name, value):
|
|
@@ -743,7 +721,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
743
721
|
"dirty_substates",
|
|
744
722
|
"router_data",
|
|
745
723
|
}
|
|
746
|
-
| RESERVED_BACKEND_VAR_NAMES
|
|
724
|
+
| types.RESERVED_BACKEND_VAR_NAMES
|
|
747
725
|
)
|
|
748
726
|
|
|
749
727
|
@classmethod
|
|
@@ -1096,10 +1074,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1096
1074
|
setattr(self.parent_state, name, value)
|
|
1097
1075
|
return
|
|
1098
1076
|
|
|
1099
|
-
if
|
|
1100
|
-
types.is_backend_variable(name, self.__class__)
|
|
1101
|
-
and name not in RESERVED_BACKEND_VAR_NAMES
|
|
1102
|
-
):
|
|
1077
|
+
if name in self.backend_vars:
|
|
1103
1078
|
self._backend_vars.__setitem__(name, value)
|
|
1104
1079
|
self.dirty_vars.add(name)
|
|
1105
1080
|
self._mark_dirty()
|
|
@@ -1550,11 +1525,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1550
1525
|
if self.computed_vars[cvar].needs_update(instance=self)
|
|
1551
1526
|
)
|
|
1552
1527
|
|
|
1553
|
-
def _dirty_computed_vars(
|
|
1528
|
+
def _dirty_computed_vars(
|
|
1529
|
+
self, from_vars: set[str] | None = None, include_backend: bool = True
|
|
1530
|
+
) -> set[str]:
|
|
1554
1531
|
"""Determine ComputedVars that need to be recalculated based on the given vars.
|
|
1555
1532
|
|
|
1556
1533
|
Args:
|
|
1557
1534
|
from_vars: find ComputedVar that depend on this set of vars. If unspecified, will use the dirty_vars.
|
|
1535
|
+
include_backend: whether to include backend vars in the calculation.
|
|
1558
1536
|
|
|
1559
1537
|
Returns:
|
|
1560
1538
|
Set of computed vars to include in the delta.
|
|
@@ -1563,6 +1541,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1563
1541
|
cvar
|
|
1564
1542
|
for dirty_var in from_vars or self.dirty_vars
|
|
1565
1543
|
for cvar in self._computed_var_dependencies[dirty_var]
|
|
1544
|
+
if include_backend or not self.computed_vars[cvar]._backend
|
|
1566
1545
|
)
|
|
1567
1546
|
|
|
1568
1547
|
@classmethod
|
|
@@ -1598,19 +1577,23 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1598
1577
|
self.dirty_vars.update(self._always_dirty_computed_vars)
|
|
1599
1578
|
self._mark_dirty()
|
|
1600
1579
|
|
|
1580
|
+
frontend_computed_vars: set[str] = {
|
|
1581
|
+
name for name, cv in self.computed_vars.items() if not cv._backend
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1601
1584
|
# Return the dirty vars for this instance, any cached/dependent computed vars,
|
|
1602
1585
|
# and always dirty computed vars (cache=False)
|
|
1603
1586
|
delta_vars = (
|
|
1604
1587
|
self.dirty_vars.intersection(self.base_vars)
|
|
1605
|
-
.union(self.dirty_vars.intersection(
|
|
1606
|
-
.union(self._dirty_computed_vars())
|
|
1588
|
+
.union(self.dirty_vars.intersection(frontend_computed_vars))
|
|
1589
|
+
.union(self._dirty_computed_vars(include_backend=False))
|
|
1607
1590
|
.union(self._always_dirty_computed_vars)
|
|
1608
1591
|
)
|
|
1609
1592
|
|
|
1610
1593
|
subdelta = {
|
|
1611
1594
|
prop: getattr(self, prop)
|
|
1612
1595
|
for prop in delta_vars
|
|
1613
|
-
if not types.
|
|
1596
|
+
if not types.is_backend_base_variable(prop, type(self))
|
|
1614
1597
|
}
|
|
1615
1598
|
if len(subdelta) > 0:
|
|
1616
1599
|
delta[self.get_full_name()] = subdelta
|
|
@@ -1739,12 +1722,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1739
1722
|
else self.get_value(getattr(self, prop_name))
|
|
1740
1723
|
)
|
|
1741
1724
|
for prop_name, cv in self.computed_vars.items()
|
|
1725
|
+
if not cv._backend
|
|
1742
1726
|
}
|
|
1743
1727
|
elif include_computed:
|
|
1744
1728
|
computed_vars = {
|
|
1745
1729
|
# Include the computed vars.
|
|
1746
1730
|
prop_name: self.get_value(getattr(self, prop_name))
|
|
1747
|
-
for prop_name in self.computed_vars
|
|
1731
|
+
for prop_name, cv in self.computed_vars.items()
|
|
1732
|
+
if not cv._backend
|
|
1748
1733
|
}
|
|
1749
1734
|
else:
|
|
1750
1735
|
computed_vars = {}
|
|
@@ -1757,6 +1742,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1757
1742
|
for v in self.substates.values()
|
|
1758
1743
|
]:
|
|
1759
1744
|
d.update(substate_d)
|
|
1745
|
+
|
|
1760
1746
|
return d
|
|
1761
1747
|
|
|
1762
1748
|
async def __aenter__(self) -> BaseState:
|
|
@@ -2202,7 +2188,14 @@ class StateManager(Base, ABC):
|
|
|
2202
2188
|
"""
|
|
2203
2189
|
redis = prerequisites.get_redis()
|
|
2204
2190
|
if redis is not None:
|
|
2205
|
-
|
|
2191
|
+
# make sure expiration values are obtained only from the config object on creation
|
|
2192
|
+
config = get_config()
|
|
2193
|
+
return StateManagerRedis(
|
|
2194
|
+
state=state,
|
|
2195
|
+
redis=redis,
|
|
2196
|
+
token_expiration=config.redis_token_expiration,
|
|
2197
|
+
lock_expiration=config.redis_lock_expiration,
|
|
2198
|
+
)
|
|
2206
2199
|
return StateManagerMemory(state=state)
|
|
2207
2200
|
|
|
2208
2201
|
@abstractmethod
|
|
@@ -2333,10 +2326,10 @@ class StateManagerRedis(StateManager):
|
|
|
2333
2326
|
redis: Redis
|
|
2334
2327
|
|
|
2335
2328
|
# The token expiration time (s).
|
|
2336
|
-
token_expiration: int =
|
|
2329
|
+
token_expiration: int = config.redis_token_expiration
|
|
2337
2330
|
|
|
2338
2331
|
# The maximum time to hold a lock (ms).
|
|
2339
|
-
lock_expiration: int =
|
|
2332
|
+
lock_expiration: int = config.redis_lock_expiration
|
|
2340
2333
|
|
|
2341
2334
|
# The keyspace subscription string when redis is waiting for lock to be released
|
|
2342
2335
|
_redis_notify_keyspace_events: str = (
|
reflex/style.py
CHANGED
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
from typing import Any, Literal, Tuple, Type
|
|
6
6
|
|
|
7
7
|
from reflex import constants
|
|
8
|
+
from reflex.components.core.breakpoints import Breakpoints, breakpoints_values
|
|
8
9
|
from reflex.event import EventChain
|
|
9
10
|
from reflex.utils import format
|
|
10
11
|
from reflex.utils.imports import ImportVar
|
|
@@ -86,8 +87,6 @@ toggle_color_mode = _color_mode_var(
|
|
|
86
87
|
_var_type=EventChain,
|
|
87
88
|
)
|
|
88
89
|
|
|
89
|
-
breakpoints = ["0", "30em", "48em", "62em", "80em", "96em"]
|
|
90
|
-
|
|
91
90
|
STYLE_PROP_SHORTHAND_MAPPING = {
|
|
92
91
|
"paddingX": ("paddingInlineStart", "paddingInlineEnd"),
|
|
93
92
|
"paddingY": ("paddingTop", "paddingBottom"),
|
|
@@ -100,16 +99,16 @@ STYLE_PROP_SHORTHAND_MAPPING = {
|
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
|
|
103
|
-
def media_query(
|
|
102
|
+
def media_query(breakpoint_expr: str):
|
|
104
103
|
"""Create a media query selector.
|
|
105
104
|
|
|
106
105
|
Args:
|
|
107
|
-
|
|
106
|
+
breakpoint_expr: The CSS expression representing the breakpoint.
|
|
108
107
|
|
|
109
108
|
Returns:
|
|
110
109
|
The media query selector used as a key in emotion css dict.
|
|
111
110
|
"""
|
|
112
|
-
return f"@media screen and (min-width: {
|
|
111
|
+
return f"@media screen and (min-width: {breakpoint_expr})"
|
|
113
112
|
|
|
114
113
|
|
|
115
114
|
def convert_item(style_item: str | Var) -> tuple[str, VarData | None]:
|
|
@@ -189,6 +188,10 @@ def convert(style_dict):
|
|
|
189
188
|
update_out_dict(return_val, keys)
|
|
190
189
|
# Combine all the collected VarData instances.
|
|
191
190
|
var_data = VarData.merge(var_data, new_var_data)
|
|
191
|
+
|
|
192
|
+
if isinstance(style_dict, Breakpoints):
|
|
193
|
+
out = Breakpoints(out).factorize()
|
|
194
|
+
|
|
192
195
|
return out, var_data
|
|
193
196
|
|
|
194
197
|
|
|
@@ -295,14 +298,22 @@ def format_as_emotion(style_dict: dict[str, Any]) -> Style | None:
|
|
|
295
298
|
|
|
296
299
|
for orig_key, value in style_dict.items():
|
|
297
300
|
key = _format_emotion_style_pseudo_selector(orig_key)
|
|
298
|
-
if isinstance(value, list):
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
301
|
+
if isinstance(value, (Breakpoints, list)):
|
|
302
|
+
if isinstance(value, Breakpoints):
|
|
303
|
+
mbps = {
|
|
304
|
+
media_query(bp): (
|
|
305
|
+
bp_value if isinstance(bp_value, dict) else {key: bp_value}
|
|
306
|
+
)
|
|
307
|
+
for bp, bp_value in value.items()
|
|
308
|
+
}
|
|
309
|
+
else:
|
|
310
|
+
# Apply media queries from responsive value list.
|
|
311
|
+
mbps = {
|
|
312
|
+
media_query([0, *breakpoints_values][bp]): (
|
|
313
|
+
bp_value if isinstance(bp_value, dict) else {key: bp_value}
|
|
314
|
+
)
|
|
315
|
+
for bp, bp_value in enumerate(value)
|
|
316
|
+
}
|
|
306
317
|
if key.startswith("&:"):
|
|
307
318
|
emotion_style[key] = mbps
|
|
308
319
|
else:
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Utilities for working with Github Codespaces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from fastapi.responses import HTMLResponse
|
|
8
|
+
|
|
9
|
+
from reflex.components.base.script import Script
|
|
10
|
+
from reflex.components.component import Component
|
|
11
|
+
from reflex.components.core.banner import has_connection_errors
|
|
12
|
+
from reflex.components.core.cond import cond
|
|
13
|
+
from reflex.constants import Endpoint
|
|
14
|
+
|
|
15
|
+
redirect_script = """
|
|
16
|
+
const thisUrl = new URL(window.location.href);
|
|
17
|
+
const params = new URLSearchParams(thisUrl.search)
|
|
18
|
+
|
|
19
|
+
function doRedirect(url) {
|
|
20
|
+
if (!window.sessionStorage.getItem("authenticated_github_codespaces")) {
|
|
21
|
+
const a = document.createElement("a");
|
|
22
|
+
if (params.has("redirect_to")) {
|
|
23
|
+
a.href = params.get("redirect_to")
|
|
24
|
+
} else if (!window.location.href.startsWith(url)) {
|
|
25
|
+
a.href = url + `?redirect_to=${window.location.href}`
|
|
26
|
+
} else {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
a.hidden = true;
|
|
30
|
+
a.click();
|
|
31
|
+
a.remove();
|
|
32
|
+
window.sessionStorage.setItem("authenticated_github_codespaces", "true")
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
doRedirect("%s")
|
|
36
|
+
""" % Endpoint.AUTH_CODESPACE.get_url()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def codespaces_port_forwarding_domain() -> str | None:
|
|
40
|
+
"""Get the domain for port forwarding in Github Codespaces.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
The domain for port forwarding in Github Codespaces, or None if not running in Codespaces.
|
|
44
|
+
"""
|
|
45
|
+
GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN = os.getenv(
|
|
46
|
+
"GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN"
|
|
47
|
+
)
|
|
48
|
+
return GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def is_running_in_codespaces() -> bool:
|
|
52
|
+
"""Check if the app is running in Github Codespaces.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
True if running in Github Codespaces, False otherwise.
|
|
56
|
+
"""
|
|
57
|
+
return codespaces_port_forwarding_domain() is not None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def codespaces_auto_redirect() -> list[Component]:
|
|
61
|
+
"""Get the components for automatically redirecting back to the app after authenticating a codespace port forward.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
A list containing the conditional redirect component, or empty list.
|
|
65
|
+
"""
|
|
66
|
+
if is_running_in_codespaces():
|
|
67
|
+
return [cond(has_connection_errors, Script.create(redirect_script))]
|
|
68
|
+
return []
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async def auth_codespace() -> HTMLResponse:
|
|
72
|
+
"""Page automatically redirecting back to the app after authenticating a codespace port forward.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
An HTML response with an embedded script to redirect back to the app.
|
|
76
|
+
"""
|
|
77
|
+
return HTMLResponse(
|
|
78
|
+
"""
|
|
79
|
+
<html>
|
|
80
|
+
<head>
|
|
81
|
+
<title>Reflex Github Codespace Forward Successfully Authenticated</title>
|
|
82
|
+
</head>
|
|
83
|
+
<body>
|
|
84
|
+
<center>
|
|
85
|
+
<h2>Successfully Authenticated</h2>
|
|
86
|
+
</center>
|
|
87
|
+
<script language="javascript">
|
|
88
|
+
%s
|
|
89
|
+
</script>
|
|
90
|
+
</body>
|
|
91
|
+
</html>
|
|
92
|
+
"""
|
|
93
|
+
% redirect_script
|
|
94
|
+
)
|
reflex/utils/compat.py
CHANGED
|
@@ -4,6 +4,27 @@ import contextlib
|
|
|
4
4
|
import sys
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
async def windows_hot_reload_lifespan_hack():
|
|
8
|
+
"""[REF-3164] A hack to fix hot reload on Windows.
|
|
9
|
+
|
|
10
|
+
Uvicorn has an issue stopping itself on Windows after detecting changes in
|
|
11
|
+
the filesystem.
|
|
12
|
+
|
|
13
|
+
This workaround repeatedly prints and flushes null characters to stderr,
|
|
14
|
+
which seems to allow the uvicorn server to exit when the CTRL-C signal is
|
|
15
|
+
sent from the reloader process.
|
|
16
|
+
|
|
17
|
+
Don't ask me why this works, I discovered it by accident - masenf.
|
|
18
|
+
"""
|
|
19
|
+
import asyncio
|
|
20
|
+
import sys
|
|
21
|
+
|
|
22
|
+
while True:
|
|
23
|
+
sys.stderr.write("\0")
|
|
24
|
+
sys.stderr.flush()
|
|
25
|
+
await asyncio.sleep(0.5)
|
|
26
|
+
|
|
27
|
+
|
|
7
28
|
@contextlib.contextmanager
|
|
8
29
|
def pydantic_v1_patch():
|
|
9
30
|
"""A context manager that patches the Pydantic module to mimic v1 behaviour.
|
reflex/utils/exceptions.py
CHANGED
|
@@ -61,6 +61,10 @@ class VarOperationTypeError(ReflexError, TypeError):
|
|
|
61
61
|
"""Custom TypeError for when unsupported operations are performed on vars."""
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
class VarDependencyError(ReflexError, ValueError):
|
|
65
|
+
"""Custom ValueError for when a var depends on a non-existent var."""
|
|
66
|
+
|
|
67
|
+
|
|
64
68
|
class InvalidStylePropError(ReflexError, TypeError):
|
|
65
69
|
"""Custom Type Error when style props have invalid values."""
|
|
66
70
|
|
reflex/utils/format.py
CHANGED
|
@@ -210,10 +210,31 @@ def _escape_js_string(string: str) -> str:
|
|
|
210
210
|
Returns:
|
|
211
211
|
The escaped string.
|
|
212
212
|
"""
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
213
|
+
|
|
214
|
+
# TODO: we may need to re-vist this logic after new Var API is implemented.
|
|
215
|
+
def escape_outside_segments(segment):
|
|
216
|
+
"""Escape backticks in segments outside of `${}`.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
segment: The part of the string to escape.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
The escaped or unescaped segment.
|
|
223
|
+
"""
|
|
224
|
+
if segment.startswith("${") and segment.endswith("}"):
|
|
225
|
+
# Return the `${}` segment unchanged
|
|
226
|
+
return segment
|
|
227
|
+
else:
|
|
228
|
+
# Escape backticks in the segment
|
|
229
|
+
segment = segment.replace(r"\`", "`")
|
|
230
|
+
segment = segment.replace("`", r"\`")
|
|
231
|
+
return segment
|
|
232
|
+
|
|
233
|
+
# Split the string into parts, keeping the `${}` segments
|
|
234
|
+
parts = re.split(r"(\$\{.*?\})", string)
|
|
235
|
+
escaped_parts = [escape_outside_segments(part) for part in parts]
|
|
236
|
+
escaped_string = "".join(escaped_parts)
|
|
237
|
+
return escaped_string
|
|
217
238
|
|
|
218
239
|
|
|
219
240
|
def _wrap_js_string(string: str) -> str:
|
reflex/utils/prerequisites.py
CHANGED
|
@@ -949,22 +949,9 @@ def needs_reinit(frontend: bool = True) -> bool:
|
|
|
949
949
|
return True
|
|
950
950
|
|
|
951
951
|
if constants.IS_WINDOWS:
|
|
952
|
-
import uvicorn
|
|
953
|
-
|
|
954
|
-
uvi_ver = uvicorn.__version__
|
|
955
952
|
console.warn(
|
|
956
953
|
"""Windows Subsystem for Linux (WSL) is recommended for improving initial install times."""
|
|
957
954
|
)
|
|
958
|
-
if sys.version_info >= (3, 12):
|
|
959
|
-
console.warn(
|
|
960
|
-
"Python 3.12 on Windows has known issues with hot reload (reflex-dev/reflex#3536). "
|
|
961
|
-
"Python 3.11 is recommended with this release of Reflex."
|
|
962
|
-
)
|
|
963
|
-
|
|
964
|
-
if sys.version_info < (3, 12) and uvi_ver != "0.20.0":
|
|
965
|
-
console.warn(
|
|
966
|
-
f"""On Python < 3.12, `uvicorn==0.20.0` is recommended for improved hot reload times. Found {uvi_ver} instead."""
|
|
967
|
-
)
|
|
968
955
|
|
|
969
956
|
if windows_check_onedrive_in_path():
|
|
970
957
|
console.warn(
|