reflex 0.6.6.post2__py3-none-any.whl → 0.6.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of reflex might be problematic. Click here for more details.

Files changed (141) hide show
  1. reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +5 -1
  2. reflex/.templates/web/utils/state.js +36 -28
  3. reflex/__init__.py +1 -1
  4. reflex/__init__.pyi +1 -0
  5. reflex/app.py +41 -16
  6. reflex/assets.py +2 -2
  7. reflex/base.py +8 -7
  8. reflex/compiler/templates.py +1 -0
  9. reflex/compiler/utils.py +2 -3
  10. reflex/components/base/bare.py +2 -2
  11. reflex/components/component.py +54 -29
  12. reflex/components/core/banner.py +2 -2
  13. reflex/components/core/banner.pyi +1 -1
  14. reflex/components/core/client_side_routing.py +2 -2
  15. reflex/components/core/client_side_routing.pyi +1 -1
  16. reflex/components/core/clipboard.py +11 -9
  17. reflex/components/core/clipboard.pyi +1 -1
  18. reflex/components/core/cond.py +3 -3
  19. reflex/components/core/foreach.py +1 -1
  20. reflex/components/core/html.pyi +1 -1
  21. reflex/components/core/upload.py +8 -8
  22. reflex/components/datadisplay/code.py +5 -5
  23. reflex/components/datadisplay/dataeditor.py +8 -28
  24. reflex/components/datadisplay/dataeditor.pyi +1 -1
  25. reflex/components/datadisplay/shiki_code_block.py +7 -7
  26. reflex/components/dynamic.py +2 -2
  27. reflex/components/el/elements/__init__.py +1 -1
  28. reflex/components/el/elements/__init__.pyi +1 -1
  29. reflex/components/el/elements/base.py +2 -2
  30. reflex/components/el/elements/base.pyi +1 -1
  31. reflex/components/el/elements/forms.py +40 -10
  32. reflex/components/el/elements/forms.pyi +17 -15
  33. reflex/components/el/elements/inline.py +1 -1
  34. reflex/components/el/elements/inline.pyi +28 -28
  35. reflex/components/el/elements/media.py +1 -4
  36. reflex/components/el/elements/media.pyi +25 -26
  37. reflex/components/el/elements/metadata.py +6 -6
  38. reflex/components/el/elements/metadata.pyi +4 -4
  39. reflex/components/el/elements/other.py +17 -9
  40. reflex/components/el/elements/other.pyi +7 -7
  41. reflex/components/el/elements/scripts.py +1 -2
  42. reflex/components/el/elements/scripts.pyi +3 -3
  43. reflex/components/el/elements/sectioning.py +16 -16
  44. reflex/components/el/elements/sectioning.pyi +15 -15
  45. reflex/components/el/elements/tables.py +1 -1
  46. reflex/components/el/elements/tables.pyi +10 -10
  47. reflex/components/el/elements/typography.py +1 -1
  48. reflex/components/el/elements/typography.pyi +15 -15
  49. reflex/components/markdown/markdown.py +3 -3
  50. reflex/components/next/image.py +1 -1
  51. reflex/components/next/image.pyi +1 -1
  52. reflex/components/plotly/plotly.py +2 -2
  53. reflex/components/radix/primitives/accordion.py +2 -1
  54. reflex/components/radix/primitives/form.pyi +3 -3
  55. reflex/components/radix/primitives/slider.py +1 -1
  56. reflex/components/radix/themes/base.py +4 -10
  57. reflex/components/radix/themes/color_mode.pyi +2 -2
  58. reflex/components/radix/themes/components/alert_dialog.pyi +1 -1
  59. reflex/components/radix/themes/components/badge.pyi +1 -1
  60. reflex/components/radix/themes/components/button.pyi +1 -1
  61. reflex/components/radix/themes/components/callout.pyi +5 -5
  62. reflex/components/radix/themes/components/card.pyi +1 -1
  63. reflex/components/radix/themes/components/checkbox.pyi +3 -3
  64. reflex/components/radix/themes/components/context_menu.py +11 -0
  65. reflex/components/radix/themes/components/context_menu.pyi +155 -0
  66. reflex/components/radix/themes/components/dialog.pyi +1 -1
  67. reflex/components/radix/themes/components/hover_card.pyi +1 -1
  68. reflex/components/radix/themes/components/icon_button.py +1 -1
  69. reflex/components/radix/themes/components/icon_button.pyi +1 -1
  70. reflex/components/radix/themes/components/inset.pyi +1 -1
  71. reflex/components/radix/themes/components/popover.pyi +1 -1
  72. reflex/components/radix/themes/components/radio_group.py +2 -4
  73. reflex/components/radix/themes/components/radio_group.pyi +1 -1
  74. reflex/components/radix/themes/components/select.pyi +3 -3
  75. reflex/components/radix/themes/components/slider.pyi +1 -1
  76. reflex/components/radix/themes/components/switch.pyi +1 -1
  77. reflex/components/radix/themes/components/table.pyi +7 -7
  78. reflex/components/radix/themes/components/tabs.pyi +2 -2
  79. reflex/components/radix/themes/components/text_area.py +3 -0
  80. reflex/components/radix/themes/components/text_area.pyi +3 -1
  81. reflex/components/radix/themes/components/text_field.py +16 -1
  82. reflex/components/radix/themes/components/text_field.pyi +105 -17
  83. reflex/components/radix/themes/layout/box.pyi +1 -1
  84. reflex/components/radix/themes/layout/center.pyi +1 -1
  85. reflex/components/radix/themes/layout/flex.pyi +1 -1
  86. reflex/components/radix/themes/layout/grid.pyi +1 -1
  87. reflex/components/radix/themes/layout/list.py +0 -4
  88. reflex/components/radix/themes/layout/list.pyi +3 -8
  89. reflex/components/radix/themes/layout/section.pyi +1 -1
  90. reflex/components/radix/themes/layout/spacer.pyi +1 -1
  91. reflex/components/radix/themes/layout/stack.pyi +3 -3
  92. reflex/components/radix/themes/typography/blockquote.pyi +1 -1
  93. reflex/components/radix/themes/typography/code.pyi +1 -1
  94. reflex/components/radix/themes/typography/heading.pyi +1 -1
  95. reflex/components/radix/themes/typography/link.py +5 -1
  96. reflex/components/radix/themes/typography/link.pyi +1 -1
  97. reflex/components/radix/themes/typography/text.pyi +7 -7
  98. reflex/components/recharts/cartesian.py +1 -1
  99. reflex/components/recharts/charts.py +4 -4
  100. reflex/components/recharts/polar.py +1 -1
  101. reflex/components/recharts/polar.pyi +1 -1
  102. reflex/components/sonner/toast.py +4 -7
  103. reflex/components/suneditor/editor.py +6 -6
  104. reflex/components/suneditor/editor.pyi +6 -6
  105. reflex/config.py +25 -10
  106. reflex/constants/compiler.py +6 -0
  107. reflex/constants/config.py +2 -0
  108. reflex/constants/custom_components.py +1 -1
  109. reflex/constants/route.py +1 -1
  110. reflex/custom_components/custom_components.py +21 -21
  111. reflex/event.py +57 -22
  112. reflex/experimental/client_state.py +2 -1
  113. reflex/experimental/layout.py +0 -6
  114. reflex/model.py +125 -9
  115. reflex/reflex.py +12 -8
  116. reflex/state.py +200 -88
  117. reflex/style.py +1 -4
  118. reflex/testing.py +10 -11
  119. reflex/utils/build.py +1 -1
  120. reflex/utils/console.py +75 -6
  121. reflex/utils/exceptions.py +12 -0
  122. reflex/utils/exec.py +10 -10
  123. reflex/utils/export.py +1 -2
  124. reflex/utils/format.py +11 -8
  125. reflex/utils/path_ops.py +2 -2
  126. reflex/utils/prerequisites.py +31 -28
  127. reflex/utils/processes.py +4 -4
  128. reflex/utils/pyi_generator.py +12 -11
  129. reflex/utils/types.py +6 -3
  130. reflex/vars/__init__.py +1 -0
  131. reflex/vars/base.py +75 -38
  132. reflex/vars/datetime.py +222 -0
  133. reflex/vars/function.py +3 -3
  134. reflex/vars/number.py +3 -3
  135. reflex/vars/object.py +5 -5
  136. reflex/vars/sequence.py +7 -7
  137. {reflex-0.6.6.post2.dist-info → reflex-0.6.7.dist-info}/METADATA +2 -2
  138. {reflex-0.6.6.post2.dist-info → reflex-0.6.7.dist-info}/RECORD +141 -140
  139. {reflex-0.6.6.post2.dist-info → reflex-0.6.7.dist-info}/LICENSE +0 -0
  140. {reflex-0.6.6.post2.dist-info → reflex-0.6.7.dist-info}/WHEEL +0 -0
  141. {reflex-0.6.6.post2.dist-info → reflex-0.6.7.dist-info}/entry_points.txt +0 -0
@@ -264,7 +264,7 @@ class Text(elements.Span, RadixThemesComponent, MarkdownComponentMap):
264
264
  trim: Removes the leading trim space: "normal" | "start" | "end" | "both"
265
265
  color_scheme: Overrides the accent color inherited from the Theme.
266
266
  high_contrast: Whether to render the text with higher contrast color
267
- access_key: Provides a hint for generating a keyboard shortcut for the current element.
267
+ access_key: Provides a hint for generating a keyboard shortcut for the current element.
268
268
  auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
269
269
  content_editable: Indicates whether the element's content is editable.
270
270
  context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
@@ -521,7 +521,7 @@ class Span(Text):
521
521
  trim: Removes the leading trim space: "normal" | "start" | "end" | "both"
522
522
  color_scheme: Overrides the accent color inherited from the Theme.
523
523
  high_contrast: Whether to render the text with higher contrast color
524
- access_key: Provides a hint for generating a keyboard shortcut for the current element.
524
+ access_key: Provides a hint for generating a keyboard shortcut for the current element.
525
525
  auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
526
526
  content_editable: Indicates whether the element's content is editable.
527
527
  context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
@@ -610,7 +610,7 @@ class Em(elements.Em, RadixThemesComponent):
610
610
 
611
611
  Args:
612
612
  *children: Child components.
613
- access_key: Provides a hint for generating a keyboard shortcut for the current element.
613
+ access_key: Provides a hint for generating a keyboard shortcut for the current element.
614
614
  auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
615
615
  content_editable: Indicates whether the element's content is editable.
616
616
  context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
@@ -706,7 +706,7 @@ class Kbd(elements.Kbd, RadixThemesComponent):
706
706
  Args:
707
707
  *children: Child components.
708
708
  size: Text size: "1" - "9"
709
- access_key: Provides a hint for generating a keyboard shortcut for the current element.
709
+ access_key: Provides a hint for generating a keyboard shortcut for the current element.
710
710
  auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
711
711
  content_editable: Indicates whether the element's content is editable.
712
712
  context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
@@ -797,7 +797,7 @@ class Quote(elements.Q, RadixThemesComponent):
797
797
  Args:
798
798
  *children: Child components.
799
799
  cite: Specifies the source URL of the quote.
800
- access_key: Provides a hint for generating a keyboard shortcut for the current element.
800
+ access_key: Provides a hint for generating a keyboard shortcut for the current element.
801
801
  auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
802
802
  content_editable: Indicates whether the element's content is editable.
803
803
  context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
@@ -886,7 +886,7 @@ class Strong(elements.Strong, RadixThemesComponent):
886
886
 
887
887
  Args:
888
888
  *children: Child components.
889
- access_key: Provides a hint for generating a keyboard shortcut for the current element.
889
+ access_key: Provides a hint for generating a keyboard shortcut for the current element.
890
890
  auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
891
891
  content_editable: Indicates whether the element's content is editable.
892
892
  context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
@@ -1147,7 +1147,7 @@ class TextNamespace(ComponentNamespace):
1147
1147
  trim: Removes the leading trim space: "normal" | "start" | "end" | "both"
1148
1148
  color_scheme: Overrides the accent color inherited from the Theme.
1149
1149
  high_contrast: Whether to render the text with higher contrast color
1150
- access_key: Provides a hint for generating a keyboard shortcut for the current element.
1150
+ access_key: Provides a hint for generating a keyboard shortcut for the current element.
1151
1151
  auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
1152
1152
  content_editable: Indicates whether the element's content is editable.
1153
1153
  context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
@@ -416,7 +416,7 @@ class Bar(Cartesian):
416
416
  radius: Var[Union[int, List[int]]]
417
417
 
418
418
  # The active bar is shown when a user enters a bar chart and this chart has tooltip. If set to false, no active bar will be drawn. If set to true, active bar will be drawn with the props calculated internally. If passed an object, active bar will be drawn, and the internally calculated props will be merged with the key value pairs of the passed object.
419
- # active_bar: Var[Union[bool, Dict[str, Any]]]
419
+ # active_bar: Var[Union[bool, Dict[str, Any]]] #noqa: ERA001
420
420
 
421
421
  # Valid children components
422
422
  _valid_children: List[str] = ["Cell", "LabelList", "ErrorBar"]
@@ -84,10 +84,10 @@ class ChartBase(RechartsCharts):
84
84
  cls._ensure_valid_dimension("width", width)
85
85
  cls._ensure_valid_dimension("height", height)
86
86
 
87
- dim_props = dict(
88
- width=width or "100%",
89
- height=height or "100%",
90
- )
87
+ dim_props = {
88
+ "width": width or "100%",
89
+ "height": height or "100%",
90
+ }
91
91
  # Provide min dimensions so the graph always appears, even if the outer container is zero-size.
92
92
  if width is None:
93
93
  dim_props["min_width"] = 200
@@ -136,7 +136,7 @@ class Radar(Recharts):
136
136
  # Fill color. Default: rx.color("accent", 3)
137
137
  fill: Var[str] = LiteralVar.create(Color("accent", 3))
138
138
 
139
- # opacity. Default: 0.6
139
+ # The opacity to fill the chart. Default: 0.6
140
140
  fill_opacity: Var[float] = LiteralVar.create(0.6)
141
141
 
142
142
  # The type of icon in legend. If set to 'none', no legend item will be rendered. Default: "rect"
@@ -204,7 +204,7 @@ class Radar(Recharts):
204
204
  dot: If false set, dots will not be drawn. Default: True
205
205
  stroke: Stoke color. Default: rx.color("accent", 9)
206
206
  fill: Fill color. Default: rx.color("accent", 3)
207
- fill_opacity: opacity. Default: 0.6
207
+ fill_opacity: The opacity to fill the chart. Default: 0.6
208
208
  legend_type: The type of icon in legend. If set to 'none', no legend item will be rendered. Default: "rect"
209
209
  label: If false set, labels will not be drawn. Default: True
210
210
  is_animation_active: If set false, animation of polygon will be disabled. Default: True in CSR, and False in SSR
@@ -64,7 +64,7 @@ def _toast_callback_signature(toast: Var) -> list[Var]:
64
64
  """
65
65
  return [
66
66
  Var(
67
- _js_expr=f"(() => {{let {{action, cancel, onDismiss, onAutoClose, ...rest}} = {str(toast)}; return rest}})()"
67
+ _js_expr=f"(() => {{let {{action, cancel, onDismiss, onAutoClose, ...rest}} = {toast!s}; return rest}})()"
68
68
  )
69
69
  ]
70
70
 
@@ -98,7 +98,7 @@ class ToastProps(PropsBase, NoExtrasAllowedProps):
98
98
 
99
99
  # TODO: fix serialization of icons for toast? (might not be possible yet)
100
100
  # Icon displayed in front of toast's text, aligned vertically.
101
- # icon: Optional[Icon] = None
101
+ # icon: Optional[Icon] = None # noqa: ERA001
102
102
 
103
103
  # TODO: fix implementation for action / cancel buttons
104
104
  # Renders a primary button, clicking it will close the toast.
@@ -338,7 +338,7 @@ class Toaster(Component):
338
338
  dismiss_var_data = None
339
339
 
340
340
  if isinstance(id, Var):
341
- dismiss = f"{toast_ref}.dismiss({str(id)})"
341
+ dismiss = f"{toast_ref}.dismiss({id!s})"
342
342
  dismiss_var_data = id._get_all_var_data()
343
343
  elif isinstance(id, str):
344
344
  dismiss = f"{toast_ref}.dismiss('{id}')"
@@ -364,9 +364,7 @@ class Toaster(Component):
364
364
  return super().create(*children, **props)
365
365
 
366
366
 
367
- # TODO: figure out why loading toast stay open forever
368
- # def toast_loading(message: str, **kwargs):
369
- # return _toast(message, level="loading", **kwargs)
367
+ # TODO: figure out why loading toast stay open forever when using level="loading" in toast()
370
368
 
371
369
 
372
370
  class ToastNamespace(ComponentNamespace):
@@ -379,7 +377,6 @@ class ToastNamespace(ComponentNamespace):
379
377
  error = staticmethod(Toaster.toast_error)
380
378
  success = staticmethod(Toaster.toast_success)
381
379
  dismiss = staticmethod(Toaster.toast_dismiss)
382
- # loading = staticmethod(toast_loading)
383
380
  __call__ = staticmethod(Toaster.send_toast)
384
381
 
385
382
 
@@ -116,7 +116,7 @@ class Editor(NoSSRComponent):
116
116
  # Please refer to the library docs for this.
117
117
  # options: "en" | "da" | "de" | "es" | "fr" | "ja" | "ko" | "pt_br" |
118
118
  # "ru" | "zh_cn" | "ro" | "pl" | "ckb" | "lv" | "se" | "ua" | "he" | "it"
119
- # default : "en"
119
+ # default: "en".
120
120
  lang: Var[
121
121
  Union[
122
122
  Literal[
@@ -172,7 +172,7 @@ class Editor(NoSSRComponent):
172
172
  set_options: Var[Dict]
173
173
 
174
174
  # Whether all SunEditor plugins should be loaded.
175
- # default: True
175
+ # default: True.
176
176
  set_all_plugins: Var[bool]
177
177
 
178
178
  # Set the content of the editor.
@@ -191,19 +191,19 @@ class Editor(NoSSRComponent):
191
191
  set_default_style: Var[str]
192
192
 
193
193
  # Disable the editor
194
- # default: False
194
+ # default: False.
195
195
  disable: Var[bool]
196
196
 
197
197
  # Hide the editor
198
- # default: False
198
+ # default: False.
199
199
  hide: Var[bool]
200
200
 
201
201
  # Hide the editor toolbar
202
- # default: False
202
+ # default: False.
203
203
  hide_toolbar: Var[bool]
204
204
 
205
205
  # Disable the editor toolbar
206
- # default: False
206
+ # default: False.
207
207
  disable_toolbar: Var[bool]
208
208
 
209
209
  # Fired when the editor content changes.
@@ -172,7 +172,7 @@ class Editor(NoSSRComponent):
172
172
 
173
173
  Args:
174
174
  set_options(Optional[EditorOptions]): Configuration object to further configure the instance.
175
- lang: Language of the editor. Alternatively to a string, a dict of your language can be passed to this prop. Please refer to the library docs for this. options: "en" | "da" | "de" | "es" | "fr" | "ja" | "ko" | "pt_br" | "ru" | "zh_cn" | "ro" | "pl" | "ckb" | "lv" | "se" | "ua" | "he" | "it" default : "en"
175
+ lang: Language of the editor. Alternatively to a string, a dict of your language can be passed to this prop. Please refer to the library docs for this. options: "en" | "da" | "de" | "es" | "fr" | "ja" | "ko" | "pt_br" | "ru" | "zh_cn" | "ro" | "pl" | "ckb" | "lv" | "se" | "ua" | "he" | "it" default: "en".
176
176
  name: This is used to set the HTML form name of the editor. This means on HTML form submission, it will be submitted together with contents of the editor by the name provided.
177
177
  default_value: Sets the default value of the editor. This is useful if you don't want the on_change method to be called on render. If you want the on_change method to be called on render please use the set_contents prop
178
178
  width: Sets the width of the editor. px and percentage values are accepted, eg width="100%" or width="500px" default: 100%
@@ -180,14 +180,14 @@ class Editor(NoSSRComponent):
180
180
  placeholder: Sets the placeholder of the editor.
181
181
  auto_focus: Should the editor receive focus when initialized?
182
182
  set_options: Pass an EditorOptions instance to modify the behaviour of Editor even more.
183
- set_all_plugins: Whether all SunEditor plugins should be loaded. default: True
183
+ set_all_plugins: Whether all SunEditor plugins should be loaded. default: True.
184
184
  set_contents: Set the content of the editor. Note: To set the initial contents of the editor without calling the on_change event, please use the default_value prop. set_contents is used to set the contents of the editor programmatically. You must be aware that, when the set_contents's prop changes, the on_change event is triggered.
185
185
  append_contents: Append editor content
186
186
  set_default_style: Sets the default style of the editor's edit area
187
- disable: Disable the editor default: False
188
- hide: Hide the editor default: False
189
- hide_toolbar: Hide the editor toolbar default: False
190
- disable_toolbar: Disable the editor toolbar default: False
187
+ disable: Disable the editor default: False.
188
+ hide: Hide the editor default: False.
189
+ hide_toolbar: Hide the editor toolbar default: False.
190
+ disable_toolbar: Disable the editor toolbar default: False.
191
191
  on_change: Fired when the editor content changes.
192
192
  on_input: Fired when the something is inputted in the editor.
193
193
  on_blur: Fired when the editor loses focus.
reflex/config.py CHANGED
@@ -82,7 +82,7 @@ class DBConfig(Base):
82
82
  )
83
83
 
84
84
  @classmethod
85
- def postgresql_psycopg2(
85
+ def postgresql_psycopg(
86
86
  cls,
87
87
  database: str,
88
88
  username: str,
@@ -90,7 +90,7 @@ class DBConfig(Base):
90
90
  host: str | None = None,
91
91
  port: int | None = 5432,
92
92
  ) -> DBConfig:
93
- """Create an instance with postgresql+psycopg2 engine.
93
+ """Create an instance with postgresql+psycopg engine.
94
94
 
95
95
  Args:
96
96
  database: Database name.
@@ -103,7 +103,7 @@ class DBConfig(Base):
103
103
  DBConfig instance.
104
104
  """
105
105
  return cls(
106
- engine="postgresql+psycopg2",
106
+ engine="postgresql+psycopg",
107
107
  username=username,
108
108
  password=password,
109
109
  host=host,
@@ -512,6 +512,9 @@ class EnvironmentVariables:
512
512
  # Whether to print the SQL queries if the log level is INFO or lower.
513
513
  SQLALCHEMY_ECHO: EnvVar[bool] = env_var(False)
514
514
 
515
+ # Whether to check db connections before using them.
516
+ SQLALCHEMY_POOL_PRE_PING: EnvVar[bool] = env_var(True)
517
+
515
518
  # Whether to ignore the redis config error. Some redis servers only allow out-of-band configuration.
516
519
  REFLEX_IGNORE_REDIS_CONFIG_ERROR: EnvVar[bool] = env_var(False)
517
520
 
@@ -568,6 +571,10 @@ class EnvironmentVariables:
568
571
  environment = EnvironmentVariables()
569
572
 
570
573
 
574
+ # These vars are not logged because they may contain sensitive information.
575
+ _sensitive_env_vars = {"DB_URL", "ASYNC_DB_URL", "REDIS_URL"}
576
+
577
+
571
578
  class Config(Base):
572
579
  """The config defines runtime settings for the app.
573
580
 
@@ -621,6 +628,9 @@ class Config(Base):
621
628
  # The database url used by rx.Model.
622
629
  db_url: Optional[str] = "sqlite:///reflex.db"
623
630
 
631
+ # The async database url used by rx.Model.
632
+ async_db_url: Optional[str] = None
633
+
624
634
  # The redis url
625
635
  redis_url: Optional[str] = None
626
636
 
@@ -674,6 +684,9 @@ class Config(Base):
674
684
  # Maximum expiration lock time for redis state manager
675
685
  redis_lock_expiration: int = constants.Expiration.LOCK
676
686
 
687
+ # Maximum lock time before warning for redis state manager.
688
+ redis_lock_warning_threshold: int = constants.Expiration.LOCK_WARNING_THRESHOLD
689
+
677
690
  # Token expiration time for redis state manager
678
691
  redis_token_expiration: int = constants.Expiration.TOKEN
679
692
 
@@ -748,18 +761,20 @@ class Config(Base):
748
761
 
749
762
  # If the env var is set, override the config value.
750
763
  if env_var is not None:
751
- if key.upper() != "DB_URL":
752
- console.info(
753
- f"Overriding config value {key} with env var {key.upper()}={env_var}",
754
- dedupe=True,
755
- )
756
-
757
764
  # Interpret the value.
758
765
  value = interpret_env_var_value(env_var, field.outer_type_, field.name)
759
766
 
760
767
  # Set the value.
761
768
  updated_values[key] = value
762
769
 
770
+ if key.upper() in _sensitive_env_vars:
771
+ env_var = "***"
772
+
773
+ console.info(
774
+ f"Overriding config value {key} with env var {key.upper()}={env_var}",
775
+ dedupe=True,
776
+ )
777
+
763
778
  return updated_values
764
779
 
765
780
  def get_event_namespace(self) -> str:
@@ -858,7 +873,7 @@ def get_config(reload: bool = False) -> Config:
858
873
  with _config_lock:
859
874
  sys_path = sys.path.copy()
860
875
  sys.path.clear()
861
- sys.path.append(os.getcwd())
876
+ sys.path.append(str(Path.cwd()))
862
877
  try:
863
878
  # Try to import the module with only the current directory in the path.
864
879
  return _get_config()
@@ -132,6 +132,12 @@ class Hooks(SimpleNamespace):
132
132
  }
133
133
  })"""
134
134
 
135
+ class HookPosition(enum.Enum):
136
+ """The position of the hook in the component."""
137
+
138
+ PRE_TRIGGER = "pre_trigger"
139
+ POST_TRIGGER = "post_trigger"
140
+
135
141
 
136
142
  class MemoizationDisposition(enum.Enum):
137
143
  """The conditions under which a component should be memoized."""
@@ -29,6 +29,8 @@ class Expiration(SimpleNamespace):
29
29
  LOCK = 10000
30
30
  # The PING timeout
31
31
  PING = 120
32
+ # The maximum time in milliseconds to hold a lock before throwing a warning.
33
+ LOCK_WARNING_THRESHOLD = 1000
32
34
 
33
35
 
34
36
  class GitIgnore(SimpleNamespace):
@@ -10,7 +10,7 @@ class CustomComponents(SimpleNamespace):
10
10
  """Constants for the custom components."""
11
11
 
12
12
  # The name of the custom components source directory.
13
- SRC_DIR = "custom_components"
13
+ SRC_DIR = Path("custom_components")
14
14
  # The name of the custom components pyproject.toml file.
15
15
  PYPROJECT_TOML = Path("pyproject.toml")
16
16
  # The name of the custom components package README file.
reflex/constants/route.py CHANGED
@@ -31,7 +31,7 @@ class RouteVar(SimpleNamespace):
31
31
 
32
32
 
33
33
  # This subset of router_data is included in chained on_load events.
34
- ROUTER_DATA_INCLUDE = set((RouteVar.PATH, RouteVar.ORIGIN, RouteVar.QUERY))
34
+ ROUTER_DATA_INCLUDE = {RouteVar.PATH, RouteVar.ORIGIN, RouteVar.QUERY}
35
35
 
36
36
 
37
37
  class RouteRegex(SimpleNamespace):
@@ -150,27 +150,27 @@ def _populate_demo_app(name_variants: NameVariants):
150
150
  from reflex.compiler import templates
151
151
  from reflex.reflex import _init
152
152
 
153
- demo_app_dir = name_variants.demo_app_dir
153
+ demo_app_dir = Path(name_variants.demo_app_dir)
154
154
  demo_app_name = name_variants.demo_app_name
155
155
 
156
- console.info(f"Creating app for testing: {demo_app_dir}")
156
+ console.info(f"Creating app for testing: {demo_app_dir!s}")
157
157
 
158
- os.makedirs(demo_app_dir)
158
+ demo_app_dir.mkdir(exist_ok=True)
159
159
 
160
160
  with set_directory(demo_app_dir):
161
161
  # We start with the blank template as basis.
162
162
  _init(name=demo_app_name, template=constants.Templates.DEFAULT)
163
163
  # Then overwrite the app source file with the one we want for testing custom components.
164
164
  # This source file is rendered using jinja template file.
165
- with open(f"{demo_app_name}/{demo_app_name}.py", "w") as f:
166
- f.write(
167
- templates.CUSTOM_COMPONENTS_DEMO_APP.render(
168
- custom_component_module_dir=name_variants.custom_component_module_dir,
169
- module_name=name_variants.module_name,
170
- )
165
+ demo_file = Path(f"{demo_app_name}/{demo_app_name}.py")
166
+ demo_file.write_text(
167
+ templates.CUSTOM_COMPONENTS_DEMO_APP.render(
168
+ custom_component_module_dir=name_variants.custom_component_module_dir,
169
+ module_name=name_variants.module_name,
171
170
  )
171
+ )
172
172
  # Append the custom component package to the requirements.txt file.
173
- with open(f"{constants.RequirementsTxt.FILE}", "a") as f:
173
+ with Path(f"{constants.RequirementsTxt.FILE}").open(mode="a") as f:
174
174
  f.write(f"{name_variants.package_name}\n")
175
175
 
176
176
 
@@ -296,13 +296,14 @@ def _populate_custom_component_project(name_variants: NameVariants):
296
296
  )
297
297
 
298
298
  console.info(
299
- f"Initializing the component directory: {CustomComponents.SRC_DIR}/{name_variants.custom_component_module_dir}"
299
+ f"Initializing the component directory: {CustomComponents.SRC_DIR / name_variants.custom_component_module_dir}"
300
300
  )
301
- os.makedirs(CustomComponents.SRC_DIR)
301
+ CustomComponents.SRC_DIR.mkdir(exist_ok=True)
302
302
  with set_directory(CustomComponents.SRC_DIR):
303
- os.makedirs(name_variants.custom_component_module_dir)
303
+ module_dir = Path(name_variants.custom_component_module_dir)
304
+ module_dir.mkdir(exist_ok=True, parents=True)
304
305
  _write_source_and_init_py(
305
- custom_component_src_dir=name_variants.custom_component_module_dir,
306
+ custom_component_src_dir=module_dir,
306
307
  component_class_name=name_variants.component_class_name,
307
308
  module_name=name_variants.module_name,
308
309
  )
@@ -814,7 +815,7 @@ def _validate_project_info():
814
815
  )
815
816
  pyproject_toml["project"] = project
816
817
  try:
817
- with open(CustomComponents.PYPROJECT_TOML, "w") as f:
818
+ with CustomComponents.PYPROJECT_TOML.open("w") as f:
818
819
  tomlkit.dump(pyproject_toml, f)
819
820
  except (OSError, TOMLKitError) as ex:
820
821
  console.error(f"Unable to write to pyproject.toml due to {ex}")
@@ -922,16 +923,15 @@ def _validate_url_with_protocol_prefix(url: str | None) -> bool:
922
923
  def _get_file_from_prompt_in_loop() -> Tuple[bytes, str] | None:
923
924
  image_file = file_extension = None
924
925
  while image_file is None:
925
- image_filepath = console.ask(
926
- "Upload a preview image of your demo app (enter to skip)"
926
+ image_filepath = Path(
927
+ console.ask("Upload a preview image of your demo app (enter to skip)")
927
928
  )
928
929
  if not image_filepath:
929
930
  break
930
- file_extension = image_filepath.split(".")[-1]
931
+ file_extension = image_filepath.suffix
931
932
  try:
932
- with open(image_filepath, "rb") as f:
933
- image_file = f.read()
934
- return image_file, file_extension
933
+ image_file = image_filepath.read_bytes()
934
+ return image_file, file_extension
935
935
  except OSError as ose:
936
936
  console.error(f"Unable to read the {file_extension} file due to {ose}")
937
937
  raise typer.Exit(code=1) from ose
reflex/event.py CHANGED
@@ -25,6 +25,7 @@ from typing import (
25
25
  overload,
26
26
  )
27
27
 
28
+ import typing_extensions
28
29
  from typing_extensions import (
29
30
  Concatenate,
30
31
  ParamSpec,
@@ -296,7 +297,7 @@ class EventSpec(EventActionsMixin):
296
297
  handler: EventHandler,
297
298
  event_actions: Dict[str, Union[bool, int]] | None = None,
298
299
  client_handler_name: str = "",
299
- args: Tuple[Tuple[Var, Var], ...] = tuple(),
300
+ args: Tuple[Tuple[Var, Var], ...] = (),
300
301
  ):
301
302
  """Initialize an EventSpec.
302
303
 
@@ -311,7 +312,7 @@ class EventSpec(EventActionsMixin):
311
312
  object.__setattr__(self, "event_actions", event_actions)
312
313
  object.__setattr__(self, "handler", handler)
313
314
  object.__setattr__(self, "client_handler_name", client_handler_name)
314
- object.__setattr__(self, "args", args or tuple())
315
+ object.__setattr__(self, "args", args or ())
315
316
 
316
317
  def with_args(self, args: Tuple[Tuple[Var, Var], ...]) -> EventSpec:
317
318
  """Copy the event spec, with updated args.
@@ -349,13 +350,14 @@ class EventSpec(EventActionsMixin):
349
350
 
350
351
  # Construct the payload.
351
352
  values = []
352
- for arg in args:
353
- try:
354
- values.append(LiteralVar.create(arg))
355
- except TypeError as e:
356
- raise EventHandlerTypeError(
357
- f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
358
- ) from e
353
+ arg = None
354
+ try:
355
+ for arg in args:
356
+ values.append(LiteralVar.create(value=arg)) # noqa: PERF401
357
+ except TypeError as e:
358
+ raise EventHandlerTypeError(
359
+ f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
360
+ ) from e
359
361
  new_payload = tuple(zip(fn_args, values))
360
362
  return self.with_args(self.args + new_payload)
361
363
 
@@ -447,7 +449,7 @@ class JavascriptHTMLInputElement:
447
449
  class JavascriptInputEvent:
448
450
  """Interface for a Javascript InputEvent https://developer.mozilla.org/en-US/docs/Web/API/InputEvent."""
449
451
 
450
- target: JavascriptHTMLInputElement = JavascriptHTMLInputElement()
452
+ target: JavascriptHTMLInputElement = JavascriptHTMLInputElement() # noqa: RUF009
451
453
 
452
454
 
453
455
  @dataclasses.dataclass(
@@ -513,7 +515,7 @@ def no_args_event_spec() -> Tuple[()]:
513
515
  Returns:
514
516
  An empty tuple.
515
517
  """
516
- return tuple() # type: ignore
518
+ return () # type: ignore
517
519
 
518
520
 
519
521
  # These chains can be used for their side effects when no other events are desired.
@@ -714,26 +716,61 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
714
716
  )
715
717
 
716
718
 
719
+ @overload
720
+ def redirect(
721
+ path: str | Var[str],
722
+ is_external: Optional[bool] = None,
723
+ replace: bool = False,
724
+ ) -> EventSpec: ...
725
+
726
+
727
+ @overload
728
+ @typing_extensions.deprecated("`external` is deprecated use `is_external` instead")
729
+ def redirect(
730
+ path: str | Var[str],
731
+ is_external: Optional[bool] = None,
732
+ replace: bool = False,
733
+ external: Optional[bool] = None,
734
+ ) -> EventSpec: ...
735
+
736
+
717
737
  def redirect(
718
738
  path: str | Var[str],
719
- external: Optional[bool] = False,
720
- replace: Optional[bool] = False,
739
+ is_external: Optional[bool] = None,
740
+ replace: bool = False,
741
+ external: Optional[bool] = None,
721
742
  ) -> EventSpec:
722
743
  """Redirect to a new path.
723
744
 
724
745
  Args:
725
746
  path: The path to redirect to.
726
- external: Whether to open in new tab or not.
747
+ is_external: Whether to open in new tab or not.
727
748
  replace: If True, the current page will not create a new history entry.
749
+ external(Deprecated): Whether to open in new tab or not.
728
750
 
729
751
  Returns:
730
752
  An event to redirect to the path.
731
753
  """
754
+ if external is not None:
755
+ console.deprecate(
756
+ "The `external` prop in `rx.redirect`",
757
+ "use `is_external` instead.",
758
+ "0.6.6",
759
+ "0.7.0",
760
+ )
761
+
762
+ # is_external should take precedence over external.
763
+ is_external = (
764
+ (False if external is None else external)
765
+ if is_external is None
766
+ else is_external
767
+ )
768
+
732
769
  return server_side(
733
770
  "_redirect",
734
771
  get_fn_signature(redirect),
735
772
  path=path,
736
- external=external,
773
+ external=is_external,
737
774
  replace=replace,
738
775
  )
739
776
 
@@ -1101,9 +1138,7 @@ def run_script(
1101
1138
  Var(javascript_code) if isinstance(javascript_code, str) else javascript_code
1102
1139
  )
1103
1140
 
1104
- return call_function(
1105
- ArgsFunctionOperation.create(tuple(), javascript_code), callback
1106
- )
1141
+ return call_function(ArgsFunctionOperation.create((), javascript_code), callback)
1107
1142
 
1108
1143
 
1109
1144
  def get_event(state, event):
@@ -1222,7 +1257,7 @@ def call_event_handler(
1222
1257
  except TypeError:
1223
1258
  # TODO: In 0.7.0, remove this block and raise the exception
1224
1259
  # raise TypeError(
1225
- # f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_handler.fn.__qualname__} provided for {key}."
1260
+ # f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_handler.fn.__qualname__} provided for {key}." # noqa: ERA001
1226
1261
  # ) from e
1227
1262
  console.warn(
1228
1263
  f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_callback.fn.__qualname__} provided for {key}."
@@ -1455,7 +1490,7 @@ def get_handler_args(
1455
1490
  """
1456
1491
  args = inspect.getfullargspec(event_spec.handler.fn).args
1457
1492
 
1458
- return event_spec.args if len(args) > 1 else tuple()
1493
+ return event_spec.args if len(args) > 1 else ()
1459
1494
 
1460
1495
 
1461
1496
  def fix_events(
@@ -1556,7 +1591,7 @@ class LiteralEventVar(VarOperationCall, LiteralVar, EventVar):
1556
1591
  Returns:
1557
1592
  The hash of the var.
1558
1593
  """
1559
- return hash((self.__class__.__name__, self._js_expr))
1594
+ return hash((type(self).__name__, self._js_expr))
1560
1595
 
1561
1596
  @classmethod
1562
1597
  def create(
@@ -1620,7 +1655,7 @@ class LiteralEventChainVar(ArgsFunctionOperationBuilder, LiteralVar, EventChainV
1620
1655
  Returns:
1621
1656
  The hash of the var.
1622
1657
  """
1623
- return hash((self.__class__.__name__, self._js_expr))
1658
+ return hash((type(self).__name__, self._js_expr))
1624
1659
 
1625
1660
  @classmethod
1626
1661
  def create(
@@ -106,7 +106,7 @@ class ClientStateVar(Var):
106
106
  default_var = default
107
107
  setter_name = f"set{var_name.capitalize()}"
108
108
  hooks = {
109
- f"const [{var_name}, {setter_name}] = useState({str(default_var)})": None,
109
+ f"const [{var_name}, {setter_name}] = useState({default_var!s})": None,
110
110
  }
111
111
  imports = {
112
112
  "react": [ImportVar(tag="useState")],
@@ -242,4 +242,5 @@ class ClientStateVar(Var):
242
242
  """
243
243
  if not self._global_ref:
244
244
  raise ValueError("ClientStateVar must be global to push the value.")
245
+ value = Var.create(value)
245
246
  return run_script(f"{_client_state_ref(self._setter_name)}({value})")