reflex 0.5.3a2__py3-none-any.whl → 0.5.4__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.

Files changed (69) hide show
  1. reflex/.templates/apps/demo/code/webui/state.py +3 -2
  2. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +19 -20
  3. reflex/.templates/web/utils/state.js +6 -0
  4. reflex/__init__.py +7 -1
  5. reflex/__init__.pyi +2 -0
  6. reflex/app.py +2 -5
  7. reflex/compiler/compiler.py +2 -2
  8. reflex/components/chakra/base.py +3 -1
  9. reflex/components/chakra/forms/checkbox.py +1 -1
  10. reflex/components/chakra/forms/pininput.py +1 -1
  11. reflex/components/chakra/forms/rangeslider.py +1 -1
  12. reflex/components/chakra/navigation/link.py +3 -1
  13. reflex/components/component.py +43 -15
  14. reflex/components/core/banner.py +2 -1
  15. reflex/components/core/client_side_routing.py +3 -3
  16. reflex/components/core/client_side_routing.pyi +1 -0
  17. reflex/components/core/debounce.py +3 -1
  18. reflex/components/core/foreach.py +1 -1
  19. reflex/components/core/upload.py +2 -1
  20. reflex/components/datadisplay/code.py +4 -1
  21. reflex/components/datadisplay/dataeditor.py +11 -8
  22. reflex/components/datadisplay/dataeditor.pyi +1 -0
  23. reflex/components/el/elements/forms.py +25 -14
  24. reflex/components/el/elements/forms.pyi +2 -1
  25. reflex/components/markdown/markdown.py +17 -11
  26. reflex/components/markdown/markdown.pyi +12 -8
  27. reflex/components/plotly/plotly.py +82 -14
  28. reflex/components/plotly/plotly.pyi +15 -82
  29. reflex/components/radix/primitives/accordion.py +1 -1
  30. reflex/components/radix/themes/base.py +10 -2
  31. reflex/components/radix/themes/base.pyi +1 -0
  32. reflex/components/radix/themes/color_mode.py +1 -1
  33. reflex/components/radix/themes/components/checkbox.py +3 -1
  34. reflex/components/radix/themes/components/radio_group.py +6 -4
  35. reflex/components/radix/themes/components/separator.py +1 -1
  36. reflex/components/radix/themes/layout/container.py +1 -1
  37. reflex/components/radix/themes/layout/section.py +1 -1
  38. reflex/components/recharts/cartesian.py +42 -14
  39. reflex/components/recharts/cartesian.pyi +81 -17
  40. reflex/components/recharts/charts.py +12 -21
  41. reflex/components/recharts/charts.pyi +53 -14
  42. reflex/components/sonner/toast.py +37 -17
  43. reflex/components/sonner/toast.pyi +9 -5
  44. reflex/components/tags/tag.py +2 -1
  45. reflex/config.py +22 -14
  46. reflex/constants/__init__.py +2 -0
  47. reflex/constants/config.py +7 -0
  48. reflex/event.py +24 -15
  49. reflex/experimental/__init__.py +22 -2
  50. reflex/experimental/client_state.py +81 -23
  51. reflex/experimental/hooks.py +35 -35
  52. reflex/experimental/layout.py +17 -5
  53. reflex/experimental/layout.pyi +536 -0
  54. reflex/reflex.py +9 -5
  55. reflex/style.py +3 -2
  56. reflex/testing.py +44 -13
  57. reflex/utils/compat.py +5 -0
  58. reflex/utils/format.py +12 -2
  59. reflex/utils/processes.py +27 -0
  60. reflex/utils/pyi_generator.py +11 -4
  61. reflex/utils/serializers.py +114 -15
  62. reflex/utils/types.py +6 -2
  63. reflex/vars.py +57 -20
  64. reflex/vars.pyi +2 -2
  65. {reflex-0.5.3a2.dist-info → reflex-0.5.4.dist-info}/METADATA +1 -1
  66. {reflex-0.5.3a2.dist-info → reflex-0.5.4.dist-info}/RECORD +69 -68
  67. {reflex-0.5.3a2.dist-info → reflex-0.5.4.dist-info}/LICENSE +0 -0
  68. {reflex-0.5.3a2.dist-info → reflex-0.5.4.dist-info}/WHEEL +0 -0
  69. {reflex-0.5.3a2.dist-info → reflex-0.5.4.dist-info}/entry_points.txt +0 -0
@@ -11,6 +11,7 @@ from typing import Any, Dict, List, Union
11
11
  from reflex.components.component import Component
12
12
  from reflex.components.recharts.general import ResponsiveContainer
13
13
  from reflex.constants import EventTriggers
14
+ from reflex.event import EventHandler
14
15
  from reflex.vars import Var
15
16
  from .recharts import (
16
17
  LiteralAnimationEasing,
@@ -105,12 +106,6 @@ class AreaChart(ChartBase):
105
106
  Union[int, Literal["dataMin", "dataMax", "auto"]],
106
107
  ]
107
108
  ] = None,
108
- stack_offset: Optional[
109
- Union[
110
- Var[Literal["expand", "none", "wiggle", "silhouette"]],
111
- Literal["expand", "none", "wiggle", "silhouette"],
112
- ]
113
- ] = None,
114
109
  data: Optional[Union[Var[List[Dict[str, Any]]], List[Dict[str, Any]]]] = None,
115
110
  sync_id: Optional[Union[Var[str], str]] = None,
116
111
  sync_method: Optional[
@@ -125,6 +120,12 @@ class AreaChart(ChartBase):
125
120
  ]
126
121
  ] = None,
127
122
  margin: Optional[Union[Var[Dict[str, Any]], Dict[str, Any]]] = None,
123
+ stack_offset: Optional[
124
+ Union[
125
+ Var[Literal["expand", "none", "wiggle", "silhouette"]],
126
+ Literal["expand", "none", "wiggle", "silhouette"],
127
+ ]
128
+ ] = None,
128
129
  style: Optional[Style] = None,
129
130
  key: Optional[Any] = None,
130
131
  id: Optional[Any] = None,
@@ -150,7 +151,6 @@ class AreaChart(ChartBase):
150
151
  Args:
151
152
  *children: The children of the chart component.
152
153
  base_value: The base value of area. Number | 'dataMin' | 'dataMax' | 'auto'
153
- stack_offset: The type of offset function used to generate the lower and upper values in the series array. The four types are built-in offsets in d3-shape. 'expand' | 'none' | 'wiggle' | 'silhouette'
154
154
  data: The source data, in which each element is an object.
155
155
  sync_id: If any two categorical charts(rx.line_chart, rx.area_chart, rx.bar_chart, rx.composed_chart) have the same sync_id, these two charts can sync the position GraphingTooltip, and the start_index, end_index of Brush.
156
156
  sync_method: When sync_id is provided, allows customisation of how the charts will synchronize GraphingTooltips and brushes. Using 'index' (default setting), other charts will reuse current datum's index within the data array. In cases where data does not have the same length, this might yield unexpected results. In that case use 'value' which will try to match other charts values, or a fully custom function which will receive tick, data as argument and should return an index. 'index' | 'value' | function
@@ -158,6 +158,7 @@ class AreaChart(ChartBase):
158
158
  height: The height of chart container.
159
159
  layout: The layout of area in the chart. 'horizontal' | 'vertical'
160
160
  margin: The sizes of whitespace around the chart.
161
+ stack_offset: The type of offset function used to generate the lower and upper values in the series array. The four types are built-in offsets in d3-shape. 'expand' | 'none' | 'wiggle' | 'silhouette'
161
162
  style: The style of the component.
162
163
  key: A unique key for the component.
163
164
  id: The id for the component.
@@ -526,9 +527,6 @@ class RadarChart(ChartBase):
526
527
  on_mouse_leave: Optional[
527
528
  Union[EventHandler, EventSpec, list, function, BaseVar]
528
529
  ] = None,
529
- on_mouse_move: Optional[
530
- Union[EventHandler, EventSpec, list, function, BaseVar]
531
- ] = None,
532
530
  **props
533
531
  ) -> "RadarChart":
534
532
  """Create a chart component.
@@ -613,9 +611,6 @@ class RadialBarChart(ChartBase):
613
611
  on_mouse_leave: Optional[
614
612
  Union[EventHandler, EventSpec, list, function, BaseVar]
615
613
  ] = None,
616
- on_mouse_move: Optional[
617
- Union[EventHandler, EventSpec, list, function, BaseVar]
618
- ] = None,
619
614
  **props
620
615
  ) -> "RadialBarChart":
621
616
  """Create a chart component.
@@ -688,6 +683,9 @@ class ScatterChart(ChartBase):
688
683
  on_click: Optional[
689
684
  Union[EventHandler, EventSpec, list, function, BaseVar]
690
685
  ] = None,
686
+ on_mouse_down: Optional[
687
+ Union[EventHandler, EventSpec, list, function, BaseVar]
688
+ ] = None,
691
689
  on_mouse_enter: Optional[
692
690
  Union[EventHandler, EventSpec, list, function, BaseVar]
693
691
  ] = None,
@@ -703,6 +701,9 @@ class ScatterChart(ChartBase):
703
701
  on_mouse_over: Optional[
704
702
  Union[EventHandler, EventSpec, list, function, BaseVar]
705
703
  ] = None,
704
+ on_mouse_up: Optional[
705
+ Union[EventHandler, EventSpec, list, function, BaseVar]
706
+ ] = None,
706
707
  **props
707
708
  ) -> "ScatterChart":
708
709
  """Create a chart component.
@@ -731,7 +732,6 @@ class ScatterChart(ChartBase):
731
732
  ...
732
733
 
733
734
  class FunnelChart(RechartsCharts):
734
- def get_event_triggers(self) -> dict[str, Union[Var, Any]]: ...
735
735
  @overload
736
736
  @classmethod
737
737
  def create( # type: ignore
@@ -756,9 +756,27 @@ class FunnelChart(RechartsCharts):
756
756
  class_name: Optional[Any] = None,
757
757
  autofocus: Optional[bool] = None,
758
758
  custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
759
+ on_blur: Optional[
760
+ Union[EventHandler, EventSpec, list, function, BaseVar]
761
+ ] = None,
759
762
  on_click: Optional[
760
763
  Union[EventHandler, EventSpec, list, function, BaseVar]
761
764
  ] = None,
765
+ on_context_menu: Optional[
766
+ Union[EventHandler, EventSpec, list, function, BaseVar]
767
+ ] = None,
768
+ on_double_click: Optional[
769
+ Union[EventHandler, EventSpec, list, function, BaseVar]
770
+ ] = None,
771
+ on_focus: Optional[
772
+ Union[EventHandler, EventSpec, list, function, BaseVar]
773
+ ] = None,
774
+ on_mount: Optional[
775
+ Union[EventHandler, EventSpec, list, function, BaseVar]
776
+ ] = None,
777
+ on_mouse_down: Optional[
778
+ Union[EventHandler, EventSpec, list, function, BaseVar]
779
+ ] = None,
762
780
  on_mouse_enter: Optional[
763
781
  Union[EventHandler, EventSpec, list, function, BaseVar]
764
782
  ] = None,
@@ -768,6 +786,21 @@ class FunnelChart(RechartsCharts):
768
786
  on_mouse_move: Optional[
769
787
  Union[EventHandler, EventSpec, list, function, BaseVar]
770
788
  ] = None,
789
+ on_mouse_out: Optional[
790
+ Union[EventHandler, EventSpec, list, function, BaseVar]
791
+ ] = None,
792
+ on_mouse_over: Optional[
793
+ Union[EventHandler, EventSpec, list, function, BaseVar]
794
+ ] = None,
795
+ on_mouse_up: Optional[
796
+ Union[EventHandler, EventSpec, list, function, BaseVar]
797
+ ] = None,
798
+ on_scroll: Optional[
799
+ Union[EventHandler, EventSpec, list, function, BaseVar]
800
+ ] = None,
801
+ on_unmount: Optional[
802
+ Union[EventHandler, EventSpec, list, function, BaseVar]
803
+ ] = None,
771
804
  **props
772
805
  ) -> "FunnelChart":
773
806
  """Create a new memoization leaf component.
@@ -821,6 +854,12 @@ class Treemap(RechartsCharts):
821
854
  class_name: Optional[Any] = None,
822
855
  autofocus: Optional[bool] = None,
823
856
  custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
857
+ on_animation_end: Optional[
858
+ Union[EventHandler, EventSpec, list, function, BaseVar]
859
+ ] = None,
860
+ on_animation_start: Optional[
861
+ Union[EventHandler, EventSpec, list, function, BaseVar]
862
+ ] = None,
824
863
  on_blur: Optional[
825
864
  Union[EventHandler, EventSpec, list, function, BaseVar]
826
865
  ] = None,
@@ -28,7 +28,7 @@ LiteralPosition = Literal[
28
28
  ]
29
29
 
30
30
 
31
- toast_ref = Var.create_safe("refs['__toast']")
31
+ toast_ref = Var.create_safe("refs['__toast']", _var_is_string=False)
32
32
 
33
33
 
34
34
  class ToastAction(Base):
@@ -65,7 +65,8 @@ def _toast_callback_signature(toast: Var) -> list[Var]:
65
65
  """
66
66
  return [
67
67
  Var.create_safe(
68
- f"(() => {{let {{action, cancel, onDismiss, onAutoClose, ...rest}} = {toast}; return rest}})()"
68
+ f"(() => {{let {{action, cancel, onDismiss, onAutoClose, ...rest}} = {toast}; return rest}})()",
69
+ _var_is_string=False,
69
70
  )
70
71
  ]
71
72
 
@@ -106,7 +107,7 @@ class ToastProps(PropsBase):
106
107
  cancel: Optional[ToastAction]
107
108
 
108
109
  # Custom id for the toast.
109
- id: Optional[str]
110
+ id: Optional[Union[str, Var]]
110
111
 
111
112
  # Removes the default styling, which allows for easier customization.
112
113
  unstyled: Optional[bool]
@@ -127,7 +128,7 @@ class ToastProps(PropsBase):
127
128
  # Function that gets called when the toast disappears automatically after it's timeout (duration` prop).
128
129
  on_auto_close: Optional[Any]
129
130
 
130
- def dict(self, *args, **kwargs) -> dict:
131
+ def dict(self, *args, **kwargs) -> dict[str, Any]:
131
132
  """Convert the object to a dictionary.
132
133
 
133
134
  Args:
@@ -137,7 +138,7 @@ class ToastProps(PropsBase):
137
138
  Returns:
138
139
  The object as a dictionary with ToastAction fields intact.
139
140
  """
140
- kwargs.setdefault("exclude_none", True)
141
+ kwargs.setdefault("exclude_none", True) # type: ignore
141
142
  d = super().dict(*args, **kwargs)
142
143
  # Keep these fields as ToastAction so they can be serialized specially
143
144
  if "action" in d:
@@ -179,7 +180,9 @@ class Toaster(Component):
179
180
  visible_toasts: Var[int]
180
181
 
181
182
  # the position of the toast
182
- position: Var[LiteralPosition] = Var.create_safe("bottom-right")
183
+ position: Var[LiteralPosition] = Var.create_safe(
184
+ "bottom-right", _var_is_string=True
185
+ )
183
186
 
184
187
  # whether to show the close button
185
188
  close_button: Var[bool] = Var.create_safe(False)
@@ -208,10 +211,16 @@ class Toaster(Component):
208
211
  # Pauses toast timers when the page is hidden, e.g., when the tab is backgrounded, the browser is minimized, or the OS is locked.
209
212
  pause_when_page_is_hidden: Var[bool]
210
213
 
211
- def _get_hooks(self) -> Var[str]:
214
+ def add_hooks(self) -> list[Var | str]:
215
+ """Add hooks for the toaster component.
216
+
217
+ Returns:
218
+ The hooks for the toaster component.
219
+ """
212
220
  hook = Var.create_safe(
213
221
  f"{toast_ref} = toast",
214
222
  _var_is_local=True,
223
+ _var_is_string=False,
215
224
  _var_data=VarData(
216
225
  imports={
217
226
  "/utils/state": [ImportVar(tag="refs")],
@@ -219,7 +228,7 @@ class Toaster(Component):
219
228
  }
220
229
  ),
221
230
  )
222
- return hook
231
+ return [hook]
223
232
 
224
233
  @staticmethod
225
234
  def send_toast(message: str, level: str | None = None, **props) -> EventSpec:
@@ -235,13 +244,13 @@ class Toaster(Component):
235
244
  """
236
245
  toast_command = f"{toast_ref}.{level}" if level is not None else toast_ref
237
246
  if props:
238
- args = serialize(ToastProps(**props))
247
+ args = serialize(ToastProps(**props)) # type: ignore
239
248
  toast = f"{toast_command}(`{message}`, {args})"
240
249
  else:
241
250
  toast = f"{toast_command}(`{message}`)"
242
251
 
243
- toast_action = Var.create(toast, _var_is_string=False, _var_is_local=True)
244
- return call_script(toast_action) # type: ignore
252
+ toast_action = Var.create_safe(toast, _var_is_string=False, _var_is_local=True)
253
+ return call_script(toast_action)
245
254
 
246
255
  @staticmethod
247
256
  def toast_info(message: str, **kwargs):
@@ -295,7 +304,8 @@ class Toaster(Component):
295
304
  """
296
305
  return Toaster.send_toast(message, level="success", **kwargs)
297
306
 
298
- def toast_dismiss(self, id: str | None):
307
+ @staticmethod
308
+ def toast_dismiss(id: Var | str | None = None):
299
309
  """Dismiss a toast.
300
310
 
301
311
  Args:
@@ -304,12 +314,22 @@ class Toaster(Component):
304
314
  Returns:
305
315
  The toast dismiss event.
306
316
  """
307
- if id is None:
308
- dismiss = f"{toast_ref}.dismiss()"
317
+ dismiss_var_data = None
318
+
319
+ if isinstance(id, Var):
320
+ dismiss = f"{toast_ref}.dismiss({id._var_name_unwrapped})"
321
+ dismiss_var_data = id._var_data
322
+ elif isinstance(id, str):
323
+ dismiss = f"{toast_ref}.dismiss('{id}')"
309
324
  else:
310
- dismiss = f"{toast_ref}.dismiss({id})"
311
- dismiss_action = Var.create(dismiss, _var_is_string=False, _var_is_local=True)
312
- return call_script(dismiss_action) # type: ignore
325
+ dismiss = f"{toast_ref}.dismiss()"
326
+ dismiss_action = Var.create_safe(
327
+ dismiss,
328
+ _var_is_string=False,
329
+ _var_is_local=True,
330
+ _var_data=dismiss_var_data,
331
+ )
332
+ return call_script(dismiss_action)
313
333
 
314
334
 
315
335
  # TODO: figure out why loading toast stay open forever
@@ -27,7 +27,7 @@ LiteralPosition = Literal[
27
27
  "bottom-center",
28
28
  "bottom-right",
29
29
  ]
30
- toast_ref = Var.create_safe("refs['__toast']")
30
+ toast_ref = Var.create_safe("refs['__toast']", _var_is_string=False)
31
31
 
32
32
  class ToastAction(Base):
33
33
  label: str
@@ -46,7 +46,7 @@ class ToastProps(PropsBase):
46
46
  dismissible: Optional[bool]
47
47
  action: Optional[ToastAction]
48
48
  cancel: Optional[ToastAction]
49
- id: Optional[str]
49
+ id: Optional[Union[str, Var]]
50
50
  unstyled: Optional[bool]
51
51
  style: Optional[Style]
52
52
  action_button_styles: Optional[Style]
@@ -54,9 +54,10 @@ class ToastProps(PropsBase):
54
54
  on_dismiss: Optional[Any]
55
55
  on_auto_close: Optional[Any]
56
56
 
57
- def dict(self, *args, **kwargs) -> dict: ...
57
+ def dict(self, *args, **kwargs) -> dict[str, Any]: ...
58
58
 
59
59
  class Toaster(Component):
60
+ def add_hooks(self) -> list[Var | str]: ...
60
61
  @staticmethod
61
62
  def send_toast(message: str, level: str | None = None, **props) -> EventSpec: ...
62
63
  @staticmethod
@@ -67,7 +68,8 @@ class Toaster(Component):
67
68
  def toast_error(message: str, **kwargs): ...
68
69
  @staticmethod
69
70
  def toast_success(message: str, **kwargs): ...
70
- def toast_dismiss(self, id: str | None): ...
71
+ @staticmethod
72
+ def toast_dismiss(id: Var | str | None = None): ...
71
73
  @overload
72
74
  @classmethod
73
75
  def create( # type: ignore
@@ -202,7 +204,9 @@ class ToastNamespace(ComponentNamespace):
202
204
  dismiss = staticmethod(Toaster.toast_dismiss)
203
205
 
204
206
  @staticmethod
205
- def __call__(message: str, level: Optional[str], **props) -> "Optional[EventSpec]":
207
+ def __call__(
208
+ message: str, level: Optional[str] = None, **props
209
+ ) -> "Optional[EventSpec]":
206
210
  """Send a toast message.
207
211
 
208
212
  Args:
@@ -41,7 +41,8 @@ class Tag(Base):
41
41
  # Convert any props to vars.
42
42
  if "props" in kwargs:
43
43
  kwargs["props"] = {
44
- name: Var.create(value) for name, value in kwargs["props"].items()
44
+ name: Var.create(value, _var_is_string=False)
45
+ for name, value in kwargs["props"].items()
45
46
  }
46
47
  super().__init__(*args, **kwargs)
47
48
 
reflex/config.py CHANGED
@@ -161,13 +161,13 @@ class Config(Base):
161
161
  loglevel: constants.LogLevel = constants.LogLevel.INFO
162
162
 
163
163
  # The port to run the frontend on. NOTE: When running in dev mode, the next available port will be used if this is taken.
164
- frontend_port: int = 3000
164
+ frontend_port: int = constants.DefaultPorts.FRONTEND_PORT
165
165
 
166
166
  # The path to run the frontend on. For example, "/app" will run the frontend on http://localhost:3000/app
167
167
  frontend_path: str = ""
168
168
 
169
169
  # The port to run the backend on. NOTE: When running in dev mode, the next available port will be used if this is taken.
170
- backend_port: int = 8000
170
+ backend_port: int = constants.DefaultPorts.BACKEND_PORT
171
171
 
172
172
  # The backend url the frontend will connect to. This must be updated if the backend is hosted elsewhere, or in production.
173
173
  api_url: str = f"http://localhost:{backend_port}"
@@ -316,17 +316,22 @@ class Config(Base):
316
316
  ):
317
317
  self.deploy_url = f"http://localhost:{kwargs['frontend_port']}"
318
318
 
319
- # If running in Github Codespaces, override API_URL
320
- codespace_name = os.getenv("CODESPACE_NAME")
321
- if "api_url" not in self._non_default_attributes and codespace_name:
319
+ if "api_url" not in self._non_default_attributes:
320
+ # If running in Github Codespaces, override API_URL
321
+ codespace_name = os.getenv("CODESPACE_NAME")
322
322
  GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN = os.getenv(
323
323
  "GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN"
324
324
  )
325
- if codespace_name:
325
+ # If running on Replit.com interactively, override API_URL to ensure we maintain the backend_port
326
+ replit_dev_domain = os.getenv("REPLIT_DEV_DOMAIN")
327
+ backend_port = kwargs.get("backend_port", self.backend_port)
328
+ if codespace_name and GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN:
326
329
  self.api_url = (
327
330
  f"https://{codespace_name}-{kwargs.get('backend_port', self.backend_port)}"
328
331
  f".{GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}"
329
332
  )
333
+ elif replit_dev_domain and backend_port:
334
+ self.api_url = f"https://{replit_dev_domain}:{backend_port}"
330
335
 
331
336
  def _set_persistent(self, **kwargs):
332
337
  """Set values in this config and in the environment so they persist into subprocess.
@@ -352,11 +357,14 @@ def get_config(reload: bool = False) -> Config:
352
357
  The app config.
353
358
  """
354
359
  sys.path.insert(0, os.getcwd())
355
- try:
356
- rxconfig = __import__(constants.Config.MODULE)
357
- if reload:
358
- importlib.reload(rxconfig)
359
- return rxconfig.config
360
-
361
- except ImportError:
362
- return Config(app_name="") # type: ignore
360
+ # only import the module if it exists. If a module spec exists then
361
+ # the module exists.
362
+ spec = importlib.util.find_spec(constants.Config.MODULE) # type: ignore
363
+ if not spec:
364
+ # we need this condition to ensure that a ModuleNotFound error is not thrown when
365
+ # running unit/integration tests.
366
+ return Config(app_name="")
367
+ rxconfig = importlib.import_module(constants.Config.MODULE)
368
+ if reload:
369
+ importlib.reload(rxconfig)
370
+ return rxconfig.config
@@ -36,6 +36,7 @@ from .compiler import (
36
36
  from .config import (
37
37
  ALEMBIC_CONFIG,
38
38
  Config,
39
+ DefaultPorts,
39
40
  Expiration,
40
41
  GitIgnore,
41
42
  RequirementsTxt,
@@ -72,6 +73,7 @@ __ALL__ = [
72
73
  ComponentName,
73
74
  CustomComponents,
74
75
  DefaultPage,
76
+ DefaultPorts,
75
77
  Dirs,
76
78
  Endpoint,
77
79
  Env,
@@ -50,5 +50,12 @@ class RequirementsTxt(SimpleNamespace):
50
50
  DEFAULTS_STUB = f"{Reflex.MODULE_NAME}=="
51
51
 
52
52
 
53
+ class DefaultPorts(SimpleNamespace):
54
+ """Default port constants."""
55
+
56
+ FRONTEND_PORT = 3000
57
+ BACKEND_PORT = 8000
58
+
59
+
53
60
  # The deployment URL.
54
61
  PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app"
reflex/event.py CHANGED
@@ -186,7 +186,7 @@ class EventHandler(EventActionsMixin):
186
186
 
187
187
  # Get the function args.
188
188
  fn_args = inspect.getfullargspec(self.fn).args[1:]
189
- fn_args = (Var.create_safe(arg) for arg in fn_args)
189
+ fn_args = (Var.create_safe(arg, _var_is_string=False) for arg in fn_args)
190
190
 
191
191
  # Construct the payload.
192
192
  values = []
@@ -264,7 +264,7 @@ class EventSpec(EventActionsMixin):
264
264
 
265
265
  # Get the remaining unfilled function args.
266
266
  fn_args = inspect.getfullargspec(self.handler.fn).args[1 + len(self.args) :]
267
- fn_args = (Var.create_safe(arg) for arg in fn_args)
267
+ fn_args = (Var.create_safe(arg, _var_is_string=False) for arg in fn_args)
268
268
 
269
269
  # Construct the payload.
270
270
  values = []
@@ -389,13 +389,13 @@ class FileUpload(Base):
389
389
 
390
390
  spec_args = [
391
391
  (
392
- Var.create_safe("files"),
393
- Var.create_safe(f"filesById.{upload_id}")._replace(
394
- _var_data=upload_files_context_var_data
395
- ),
392
+ Var.create_safe("files", _var_is_string=False),
393
+ Var.create_safe(
394
+ f"filesById.{upload_id}", _var_is_string=False
395
+ )._replace(_var_data=upload_files_context_var_data),
396
396
  ),
397
397
  (
398
- Var.create_safe("upload_id"),
398
+ Var.create_safe("upload_id", _var_is_string=False),
399
399
  Var.create_safe(upload_id, _var_is_string=True),
400
400
  ),
401
401
  ]
@@ -415,6 +415,8 @@ class FileUpload(Base):
415
415
  ) # type: ignore
416
416
  else:
417
417
  raise ValueError(f"{on_upload_progress} is not a valid event handler.")
418
+ if isinstance(events, Var):
419
+ raise ValueError(f"{on_upload_progress} cannot return a var {events}.")
418
420
  on_upload_progress_chain = EventChain(
419
421
  events=events,
420
422
  args_spec=self.on_upload_progress_args_spec,
@@ -422,7 +424,7 @@ class FileUpload(Base):
422
424
  formatted_chain = str(format.format_prop(on_upload_progress_chain))
423
425
  spec_args.append(
424
426
  (
425
- Var.create_safe("on_upload_progress"),
427
+ Var.create_safe("on_upload_progress", _var_is_string=False),
426
428
  BaseVar(
427
429
  _var_name=formatted_chain.strip("{}"),
428
430
  _var_type=EventChain,
@@ -462,7 +464,10 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
462
464
  return EventSpec(
463
465
  handler=EventHandler(fn=fn),
464
466
  args=tuple(
465
- (Var.create_safe(k), Var.create_safe(v, _var_is_string=isinstance(v, str)))
467
+ (
468
+ Var.create_safe(k, _var_is_string=False),
469
+ Var.create_safe(v, _var_is_string=isinstance(v, str)),
470
+ )
466
471
  for k, v in kwargs.items()
467
472
  ),
468
473
  )
@@ -714,7 +719,7 @@ def _callback_arg_spec(eval_result):
714
719
 
715
720
 
716
721
  def call_script(
717
- javascript_code: str,
722
+ javascript_code: str | Var[str],
718
723
  callback: EventSpec
719
724
  | EventHandler
720
725
  | Callable
@@ -831,19 +836,19 @@ def parse_args_spec(arg_spec: ArgsSpec):
831
836
  )
832
837
 
833
838
 
834
- def call_event_fn(fn: Callable, arg: Union[Var, ArgsSpec]) -> list[EventSpec]:
839
+ def call_event_fn(fn: Callable, arg: Union[Var, ArgsSpec]) -> list[EventSpec] | Var:
835
840
  """Call a function to a list of event specs.
836
841
 
837
- The function should return either a single EventSpec or a list of EventSpecs.
838
- If the function takes in an arg, the arg will be passed to the function.
839
- Otherwise, the function will be called with no args.
842
+ The function should return a single EventSpec, a list of EventSpecs, or a
843
+ single Var. If the function takes in an arg, the arg will be passed to the
844
+ function. Otherwise, the function will be called with no args.
840
845
 
841
846
  Args:
842
847
  fn: The function to call.
843
848
  arg: The argument to pass to the function.
844
849
 
845
850
  Returns:
846
- The event specs from calling the function.
851
+ The event specs from calling the function or a Var.
847
852
 
848
853
  Raises:
849
854
  EventHandlerValueError: If the lambda has an invalid signature.
@@ -866,6 +871,10 @@ def call_event_fn(fn: Callable, arg: Union[Var, ArgsSpec]) -> list[EventSpec]:
866
871
  else:
867
872
  raise EventHandlerValueError(f"Lambda {fn} must have 0 or 1 arguments.")
868
873
 
874
+ # If the function returns a Var, assume it's an EventChain and render it directly.
875
+ if isinstance(out, Var):
876
+ return out
877
+
869
878
  # Convert the output to a list.
870
879
  if not isinstance(out, List):
871
880
  out = [out]
@@ -17,7 +17,28 @@ warn(
17
17
  "`rx._x` contains experimental features and might be removed at any time in the future .",
18
18
  )
19
19
 
20
- _x = SimpleNamespace(
20
+ _EMITTED_PROMOTION_WARNINGS = set()
21
+
22
+
23
+ class ExperimentalNamespace(SimpleNamespace):
24
+ """Namespace for experimental features."""
25
+
26
+ @property
27
+ def toast(self):
28
+ """Temporary property returning the toast namespace.
29
+
30
+ Remove this property when toast is fully promoted.
31
+
32
+ Returns:
33
+ The toast namespace.
34
+ """
35
+ if "toast" not in _EMITTED_PROMOTION_WARNINGS:
36
+ _EMITTED_PROMOTION_WARNINGS.add("toast")
37
+ warn(f"`rx._x.toast` was promoted to `rx.toast`.")
38
+ return toast
39
+
40
+
41
+ _x = ExperimentalNamespace(
21
42
  asset=asset,
22
43
  client_state=ClientStateVar.create,
23
44
  hooks=hooks,
@@ -25,5 +46,4 @@ _x = SimpleNamespace(
25
46
  progress=progress,
26
47
  PropsBase=PropsBase,
27
48
  run_in_thread=run_in_thread,
28
- toast=toast,
29
49
  )