reflex 0.6.8a2__py3-none-any.whl → 0.7.0a1__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (154) hide show
  1. reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +1 -1
  2. reflex/.templates/jinja/web/pages/_app.js.jinja2 +7 -7
  3. reflex/.templates/jinja/web/pages/utils.js.jinja2 +2 -2
  4. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +1 -4
  5. reflex/.templates/web/utils/state.js +65 -36
  6. reflex/__init__.py +4 -17
  7. reflex/__init__.pyi +1 -2
  8. reflex/app.py +244 -109
  9. reflex/app_mixins/lifespan.py +9 -9
  10. reflex/app_mixins/middleware.py +6 -6
  11. reflex/app_module_for_backend.py +3 -7
  12. reflex/base.py +7 -7
  13. reflex/compiler/compiler.py +8 -0
  14. reflex/compiler/utils.py +35 -6
  15. reflex/components/base/bare.py +1 -1
  16. reflex/components/base/error_boundary.py +2 -1
  17. reflex/components/base/error_boundary.pyi +2 -1
  18. reflex/components/base/meta.py +2 -2
  19. reflex/components/base/strict_mode.py +10 -0
  20. reflex/components/base/strict_mode.pyi +57 -0
  21. reflex/components/component.py +38 -77
  22. reflex/components/core/banner.py +83 -4
  23. reflex/components/core/banner.pyi +86 -0
  24. reflex/components/core/breakpoints.py +3 -1
  25. reflex/components/core/client_side_routing.py +1 -1
  26. reflex/components/core/client_side_routing.pyi +1 -1
  27. reflex/components/core/cond.py +9 -10
  28. reflex/components/core/debounce.py +1 -1
  29. reflex/components/core/foreach.py +23 -3
  30. reflex/components/core/html.py +1 -1
  31. reflex/components/core/match.py +5 -5
  32. reflex/components/core/sticky.py +160 -0
  33. reflex/components/core/sticky.pyi +449 -0
  34. reflex/components/core/upload.py +2 -2
  35. reflex/components/datadisplay/code.py +5 -14
  36. reflex/components/datadisplay/dataeditor.py +7 -4
  37. reflex/components/datadisplay/logo.py +13 -8
  38. reflex/components/datadisplay/shiki_code_block.py +14 -9
  39. reflex/components/dynamic.py +22 -3
  40. reflex/components/el/constants/reflex.py +1 -1
  41. reflex/components/el/element.py +1 -1
  42. reflex/components/el/elements/forms.py +4 -4
  43. reflex/components/el/elements/forms.pyi +4 -4
  44. reflex/components/lucide/icon.py +46 -8
  45. reflex/components/lucide/icon.pyi +54 -0
  46. reflex/components/markdown/markdown.py +10 -8
  47. reflex/components/moment/moment.py +2 -2
  48. reflex/components/next/image.py +16 -4
  49. reflex/components/next/image.pyi +4 -2
  50. reflex/components/next/link.py +1 -1
  51. reflex/components/plotly/plotly.py +5 -5
  52. reflex/components/props.py +3 -3
  53. reflex/components/radix/__init__.pyi +1 -1
  54. reflex/components/radix/primitives/accordion.py +9 -5
  55. reflex/components/radix/primitives/accordion.pyi +3 -1
  56. reflex/components/radix/primitives/drawer.py +5 -2
  57. reflex/components/radix/primitives/drawer.pyi +4 -4
  58. reflex/components/radix/primitives/form.pyi +6 -6
  59. reflex/components/radix/primitives/progress.py +1 -1
  60. reflex/components/radix/primitives/slider.py +1 -1
  61. reflex/components/radix/themes/color_mode.py +11 -9
  62. reflex/components/radix/themes/components/alert_dialog.py +3 -0
  63. reflex/components/radix/themes/components/card.py +1 -1
  64. reflex/components/radix/themes/components/card.pyi +1 -1
  65. reflex/components/radix/themes/components/context_menu.py +5 -0
  66. reflex/components/radix/themes/components/dialog.py +3 -0
  67. reflex/components/radix/themes/components/dropdown_menu.py +5 -0
  68. reflex/components/radix/themes/components/hover_card.py +3 -0
  69. reflex/components/radix/themes/components/icon_button.py +2 -2
  70. reflex/components/radix/themes/components/icon_button.pyi +1 -0
  71. reflex/components/radix/themes/components/popover.py +3 -0
  72. reflex/components/radix/themes/components/radio_cards.py +2 -0
  73. reflex/components/radix/themes/components/radio_group.py +1 -1
  74. reflex/components/radix/themes/components/select.py +3 -0
  75. reflex/components/radix/themes/components/tabs.py +3 -0
  76. reflex/components/radix/themes/components/text_area.py +12 -0
  77. reflex/components/radix/themes/components/text_area.pyi +2 -0
  78. reflex/components/radix/themes/components/text_field.py +1 -1
  79. reflex/components/radix/themes/components/tooltip.py +3 -1
  80. reflex/components/radix/themes/components/tooltip.pyi +1 -0
  81. reflex/components/radix/themes/layout/__init__.pyi +1 -1
  82. reflex/components/radix/themes/layout/list.py +2 -2
  83. reflex/components/radix/themes/layout/stack.py +2 -2
  84. reflex/components/radix/themes/typography/link.py +1 -1
  85. reflex/components/radix/themes/typography/text.py +2 -2
  86. reflex/components/react_player/react_player.py +1 -1
  87. reflex/components/recharts/__init__.py +2 -0
  88. reflex/components/recharts/__init__.pyi +2 -0
  89. reflex/components/recharts/charts.py +15 -15
  90. reflex/components/recharts/general.py +19 -4
  91. reflex/components/recharts/general.pyi +55 -4
  92. reflex/components/recharts/polar.py +2 -2
  93. reflex/components/recharts/recharts.py +4 -4
  94. reflex/components/sonner/toast.py +15 -13
  95. reflex/components/sonner/toast.pyi +6 -6
  96. reflex/components/suneditor/editor.py +6 -4
  97. reflex/components/suneditor/editor.pyi +2 -2
  98. reflex/components/tags/iter_tag.py +3 -3
  99. reflex/components/tags/tag.py +25 -3
  100. reflex/config.py +48 -15
  101. reflex/constants/__init__.py +1 -0
  102. reflex/constants/base.py +4 -1
  103. reflex/constants/compiler.py +5 -2
  104. reflex/constants/config.py +8 -1
  105. reflex/constants/installer.py +9 -9
  106. reflex/constants/style.py +1 -1
  107. reflex/custom_components/custom_components.py +9 -7
  108. reflex/event.py +130 -161
  109. reflex/experimental/__init__.py +19 -11
  110. reflex/experimental/client_state.py +53 -28
  111. reflex/experimental/hooks.py +5 -5
  112. reflex/experimental/layout.py +8 -5
  113. reflex/experimental/layout.pyi +1 -1
  114. reflex/experimental/misc.py +3 -3
  115. reflex/istate/wrappers.py +1 -1
  116. reflex/middleware/hydrate_middleware.py +2 -2
  117. reflex/model.py +11 -6
  118. reflex/page.py +3 -3
  119. reflex/reflex.py +90 -19
  120. reflex/route.py +1 -1
  121. reflex/state.py +358 -401
  122. reflex/style.py +27 -3
  123. reflex/testing.py +29 -23
  124. reflex/utils/build.py +6 -2
  125. reflex/utils/codespaces.py +1 -4
  126. reflex/utils/compat.py +6 -5
  127. reflex/utils/console.py +52 -16
  128. reflex/utils/exceptions.py +76 -26
  129. reflex/utils/exec.py +69 -74
  130. reflex/utils/export.py +6 -1
  131. reflex/utils/format.py +7 -39
  132. reflex/utils/imports.py +2 -2
  133. reflex/utils/lazy_loader.py +7 -1
  134. reflex/utils/path_ops.py +28 -14
  135. reflex/utils/prerequisites.py +324 -65
  136. reflex/utils/processes.py +45 -32
  137. reflex/utils/pyi_generator.py +30 -25
  138. reflex/utils/registry.py +4 -4
  139. reflex/utils/serializers.py +1 -1
  140. reflex/utils/telemetry.py +5 -4
  141. reflex/utils/types.py +42 -18
  142. reflex/vars/base.py +650 -333
  143. reflex/vars/datetime.py +6 -7
  144. reflex/vars/dep_tracking.py +344 -0
  145. reflex/vars/function.py +11 -5
  146. reflex/vars/number.py +31 -43
  147. reflex/vars/object.py +63 -62
  148. reflex/vars/sequence.py +79 -67
  149. {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/METADATA +7 -8
  150. {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/RECORD +153 -149
  151. {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/WHEEL +1 -1
  152. reflex/experimental/assets.py +0 -37
  153. {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/LICENSE +0 -0
  154. {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/entry_points.txt +0 -0
@@ -9,21 +9,29 @@ from reflex.components.sonner.toast import toast as toast
9
9
 
10
10
  from ..utils.console import warn
11
11
  from . import hooks as hooks
12
- from .assets import asset as asset
13
12
  from .client_state import ClientStateVar as ClientStateVar
14
13
  from .layout import layout as layout
15
14
  from .misc import run_in_thread as run_in_thread
16
15
 
17
- warn(
18
- "`rx._x` contains experimental features and might be removed at any time in the future .",
19
- )
20
-
21
- _EMITTED_PROMOTION_WARNINGS = set()
22
-
23
16
 
24
17
  class ExperimentalNamespace(SimpleNamespace):
25
18
  """Namespace for experimental features."""
26
19
 
20
+ def __getattribute__(self, item: str):
21
+ """Get attribute from the namespace.
22
+
23
+ Args:
24
+ item: attribute name.
25
+
26
+ Returns:
27
+ The attribute.
28
+ """
29
+ warn(
30
+ "`rx._x` contains experimental features and might be removed at any time in the future.",
31
+ dedupe=True,
32
+ )
33
+ return super().__getattribute__(item)
34
+
27
35
  @property
28
36
  def toast(self):
29
37
  """Temporary property returning the toast namespace.
@@ -56,13 +64,13 @@ class ExperimentalNamespace(SimpleNamespace):
56
64
  Args:
57
65
  component_name: name of the component.
58
66
  """
59
- if component_name not in _EMITTED_PROMOTION_WARNINGS:
60
- _EMITTED_PROMOTION_WARNINGS.add(component_name)
61
- warn(f"`rx._x.{component_name}` was promoted to `rx.{component_name}`.")
67
+ warn(
68
+ f"`rx._x.{component_name}` was promoted to `rx.{component_name}`.",
69
+ dedupe=True,
70
+ )
62
71
 
63
72
 
64
73
  _x = ExperimentalNamespace(
65
- asset=asset,
66
74
  client_state=ClientStateVar.create,
67
75
  hooks=hooks,
68
76
  layout=layout,
@@ -4,7 +4,6 @@ from __future__ import annotations
4
4
 
5
5
  import dataclasses
6
6
  import re
7
- import sys
8
7
  from typing import Any, Callable, Union
9
8
 
10
9
  from reflex import constants
@@ -34,10 +33,22 @@ def _client_state_ref(var_name: str) -> str:
34
33
  return f"refs['_client_state_{var_name}']"
35
34
 
36
35
 
36
+ def _client_state_ref_dict(var_name: str) -> str:
37
+ """Get the ref path for a ClientStateVar.
38
+
39
+ Args:
40
+ var_name: The name of the variable.
41
+
42
+ Returns:
43
+ An accessor for ClientStateVar ref as a string.
44
+ """
45
+ return f"refs['_client_state_dict_{var_name}']"
46
+
47
+
37
48
  @dataclasses.dataclass(
38
49
  eq=False,
39
50
  frozen=True,
40
- **{"slots": True} if sys.version_info >= (3, 10) else {},
51
+ slots=True,
41
52
  )
42
53
  class ClientStateVar(Var):
43
54
  """A Var that exists on the client via useState."""
@@ -115,10 +126,41 @@ class ClientStateVar(Var):
115
126
  "react": [ImportVar(tag="useState"), ImportVar(tag="useId")],
116
127
  }
117
128
  if global_ref:
118
- hooks[f"{_client_state_ref(var_name)} ??= {{}}"] = None
119
- hooks[f"{_client_state_ref(setter_name)} ??= {{}}"] = None
120
- hooks[f"{_client_state_ref(var_name)}[{id_name}] = {var_name}"] = None
121
- hooks[f"{_client_state_ref(setter_name)}[{id_name}] = {setter_name}"] = None
129
+ arg_name = get_unique_variable_name()
130
+ func = ArgsFunctionOperationBuilder.create(
131
+ args_names=(arg_name,),
132
+ return_expr=Var("Array.prototype.forEach.call")
133
+ .to(FunctionVar)
134
+ .call(
135
+ (
136
+ Var("Object.values")
137
+ .to(FunctionVar)
138
+ .call(Var(_client_state_ref_dict(setter_name)))
139
+ .to(list)
140
+ .to(list)
141
+ )
142
+ + Var.create(
143
+ [
144
+ Var(
145
+ f"(value) => {{ {_client_state_ref(var_name)} = value; }}"
146
+ )
147
+ ]
148
+ ).to(list),
149
+ ArgsFunctionOperationBuilder.create(
150
+ args_names=("setter",),
151
+ return_expr=Var("setter").to(FunctionVar).call(Var(arg_name)),
152
+ ),
153
+ ),
154
+ )
155
+
156
+ hooks[f"{_client_state_ref(setter_name)} = {func!s}"] = None
157
+ hooks[f"{_client_state_ref(var_name)} ??= {var_name!s}"] = None
158
+ hooks[f"{_client_state_ref_dict(var_name)} ??= {{}}"] = None
159
+ hooks[f"{_client_state_ref_dict(setter_name)} ??= {{}}"] = None
160
+ hooks[f"{_client_state_ref_dict(var_name)}[{id_name}] = {var_name}"] = None
161
+ hooks[
162
+ f"{_client_state_ref_dict(setter_name)}[{id_name}] = {setter_name}"
163
+ ] = None
122
164
  imports.update(_refs_import)
123
165
  return cls(
124
166
  _js_expr="",
@@ -150,7 +192,7 @@ class ClientStateVar(Var):
150
192
  return (
151
193
  Var(
152
194
  _js_expr=(
153
- _client_state_ref(self._getter_name) + f"[{self._id_name}]"
195
+ _client_state_ref_dict(self._getter_name) + f"[{self._id_name}]"
154
196
  if self._global_ref
155
197
  else self._getter_name
156
198
  ),
@@ -158,9 +200,7 @@ class ClientStateVar(Var):
158
200
  )
159
201
  .to(self._var_type)
160
202
  ._replace(
161
- merge_var_data=VarData( # type: ignore
162
- imports=_refs_import if self._global_ref else {}
163
- )
203
+ merge_var_data=VarData(imports=_refs_import if self._global_ref else {})
164
204
  )
165
205
  )
166
206
 
@@ -179,26 +219,11 @@ class ClientStateVar(Var):
179
219
  """
180
220
  _var_data = VarData(imports=_refs_import if self._global_ref else {})
181
221
 
182
- arg_name = get_unique_variable_name()
183
222
  setter = (
184
- ArgsFunctionOperationBuilder.create(
185
- args_names=(arg_name,),
186
- return_expr=Var("Array.prototype.forEach.call")
187
- .to(FunctionVar)
188
- .call(
189
- Var("Object.values")
190
- .to(FunctionVar)
191
- .call(Var(_client_state_ref(self._setter_name))),
192
- ArgsFunctionOperationBuilder.create(
193
- args_names=("setter",),
194
- return_expr=Var("setter").to(FunctionVar).call(Var(arg_name)),
195
- ),
196
- ),
197
- _var_data=_var_data,
198
- )
223
+ Var(_client_state_ref(self._setter_name))
199
224
  if self._global_ref
200
- else Var(self._setter_name, _var_data=_var_data).to(FunctionVar)
201
- )
225
+ else Var(self._setter_name, _var_data=_var_data)
226
+ ).to(FunctionVar)
202
227
 
203
228
  if value is not NoValue:
204
229
  # This is a hack to make it work like an EventSpec taking an arg
@@ -11,7 +11,7 @@ def _compose_react_imports(tags: list[str]) -> dict[str, list[ImportVar]]:
11
11
  return {"react": [ImportVar(tag=tag) for tag in tags]}
12
12
 
13
13
 
14
- def const(name, value) -> Var:
14
+ def const(name: str | list[str], value: str | Var) -> Var:
15
15
  """Create a constant Var.
16
16
 
17
17
  Args:
@@ -26,7 +26,7 @@ def const(name, value) -> Var:
26
26
  return Var(_js_expr=f"const {name} = {value}")
27
27
 
28
28
 
29
- def useCallback(func, deps) -> Var:
29
+ def useCallback(func: str, deps: list) -> Var: # noqa: N802
30
30
  """Create a useCallback hook with a function and dependencies.
31
31
 
32
32
  Args:
@@ -42,7 +42,7 @@ def useCallback(func, deps) -> Var:
42
42
  )
43
43
 
44
44
 
45
- def useContext(context) -> Var:
45
+ def useContext(context: str) -> Var: # noqa: N802
46
46
  """Create a useContext hook with a context.
47
47
 
48
48
  Args:
@@ -57,7 +57,7 @@ def useContext(context) -> Var:
57
57
  )
58
58
 
59
59
 
60
- def useRef(default) -> Var:
60
+ def useRef(default: str) -> Var: # noqa: N802
61
61
  """Create a useRef hook with a default value.
62
62
 
63
63
  Args:
@@ -72,7 +72,7 @@ def useRef(default) -> Var:
72
72
  )
73
73
 
74
74
 
75
- def useState(var_name, default=None) -> Var:
75
+ def useState(var_name: str, default: str | None = None) -> Var: # noqa: N802
76
76
  """Create a useState hook with a variable name and setter name.
77
77
 
78
78
  Args:
@@ -12,6 +12,7 @@ from reflex.components.radix.themes.components.icon_button import IconButton
12
12
  from reflex.components.radix.themes.layout.box import Box
13
13
  from reflex.components.radix.themes.layout.container import Container
14
14
  from reflex.components.radix.themes.layout.stack import HStack
15
+ from reflex.constants.compiler import MemoizationMode
15
16
  from reflex.event import run_script
16
17
  from reflex.experimental import hooks
17
18
  from reflex.state import ComponentState
@@ -44,10 +45,10 @@ class Sidebar(Box, MemoizationLeaf):
44
45
  Returns:
45
46
  The style of the component.
46
47
  """
47
- sidebar: Component = self.children[-2] # type: ignore
48
- spacer: Component = self.children[-1] # type: ignore
48
+ sidebar: Component = self.children[-2] # pyright: ignore [reportAssignmentType]
49
+ spacer: Component = self.children[-1] # pyright: ignore [reportAssignmentType]
49
50
  open = (
50
- self.State.open # type: ignore
51
+ self.State.open # pyright: ignore [reportAttributeAccessIssue]
51
52
  if self.State
52
53
  else Var(_js_expr="open")
53
54
  )
@@ -146,6 +147,8 @@ sidebar_trigger_style = {
146
147
  class SidebarTrigger(Fragment):
147
148
  """A component that renders the sidebar trigger."""
148
149
 
150
+ _memoization_mode = MemoizationMode(recursive=False)
151
+
149
152
  @classmethod
150
153
  def create(cls, sidebar: Component, **props):
151
154
  """Create the sidebar trigger component.
@@ -159,11 +162,11 @@ class SidebarTrigger(Fragment):
159
162
  """
160
163
  trigger_props = {**props, **sidebar_trigger_style}
161
164
 
162
- inner_sidebar: Component = sidebar.children[0] # type: ignore
165
+ inner_sidebar: Component = sidebar.children[0] # pyright: ignore [reportAssignmentType]
163
166
  sidebar_width = inner_sidebar.style.get("width")
164
167
 
165
168
  if sidebar.State:
166
- open, toggle = sidebar.State.open, sidebar.State.toggle # type: ignore
169
+ open, toggle = sidebar.State.open, sidebar.State.toggle # pyright: ignore [reportAttributeAccessIssue]
167
170
  else:
168
171
  open, toggle = (
169
172
  Var(_js_expr="open"),
@@ -109,7 +109,7 @@ class DrawerSidebar(DrawerRoot):
109
109
  snap_points: Optional[List[Union[float, str]]] = None,
110
110
  fade_from_index: Optional[Union[Var[int], int]] = None,
111
111
  scroll_lock_timeout: Optional[Union[Var[int], int]] = None,
112
- preventScrollRestoration: Optional[Union[Var[bool], bool]] = None,
112
+ prevent_scroll_restoration: Optional[Union[Var[bool], bool]] = None,
113
113
  should_scale_background: Optional[Union[Var[bool], bool]] = None,
114
114
  close_threshold: Optional[Union[Var[float], float]] = None,
115
115
  as_child: Optional[Union[Var[bool], bool]] = None,
@@ -1,16 +1,16 @@
1
1
  """Miscellaneous functions for the experimental package."""
2
2
 
3
3
  import asyncio
4
- from typing import Any
4
+ from typing import Any, Callable
5
5
 
6
6
 
7
- async def run_in_thread(func) -> Any:
7
+ async def run_in_thread(func: Callable) -> Any:
8
8
  """Run a function in a separate thread.
9
9
 
10
10
  To not block the UI event queue, run_in_thread must be inside inside a rx.event(background=True) decorated method.
11
11
 
12
12
  Args:
13
- func (callable): The non-async function to run.
13
+ func: The non-async function to run.
14
14
 
15
15
  Raises:
16
16
  ValueError: If the function is an async function.
reflex/istate/wrappers.py CHANGED
@@ -6,7 +6,7 @@ from reflex.istate.proxy import ReadOnlyStateProxy
6
6
  from reflex.state import _split_substate_key, _substate_key, get_state_manager
7
7
 
8
8
 
9
- async def get_state(token, state_cls: Any | None = None) -> ReadOnlyStateProxy:
9
+ async def get_state(token: str, state_cls: Any | None = None) -> ReadOnlyStateProxy:
10
10
  """Get the instance of a state for a token.
11
11
 
12
12
  Args:
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Optional
8
8
  from reflex import constants
9
9
  from reflex.event import Event, get_hydrate_event
10
10
  from reflex.middleware.middleware import Middleware
11
- from reflex.state import BaseState, StateUpdate
11
+ from reflex.state import BaseState, StateUpdate, _resolve_delta
12
12
 
13
13
  if TYPE_CHECKING:
14
14
  from reflex.app import App
@@ -42,7 +42,7 @@ class HydrateMiddleware(Middleware):
42
42
  setattr(state, constants.CompileVars.IS_HYDRATED, False)
43
43
 
44
44
  # Get the initial state.
45
- delta = state.dict()
45
+ delta = await _resolve_delta(state.dict())
46
46
  # since a full dict was captured, clean any dirtiness
47
47
  state._clean()
48
48
 
reflex/model.py CHANGED
@@ -18,6 +18,7 @@ import sqlalchemy
18
18
  import sqlalchemy.exc
19
19
  import sqlalchemy.ext.asyncio
20
20
  import sqlalchemy.orm
21
+ from alembic.runtime.migration import MigrationContext
21
22
 
22
23
  from reflex.base import Base
23
24
  from reflex.config import environment, get_config
@@ -242,7 +243,7 @@ class ModelRegistry:
242
243
  return metadata
243
244
 
244
245
 
245
- class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssues]
246
+ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssues,reportIncompatibleVariableOverride]
246
247
  """Base class to define a table in the database."""
247
248
 
248
249
  # The primary key for the table.
@@ -261,7 +262,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
261
262
  super().__init_subclass__()
262
263
 
263
264
  @classmethod
264
- def _dict_recursive(cls, value):
265
+ def _dict_recursive(cls, value: Any):
265
266
  """Recursively serialize the relationship object(s).
266
267
 
267
268
  Args:
@@ -393,7 +394,11 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
393
394
  writer = alembic.autogenerate.rewriter.Rewriter()
394
395
 
395
396
  @writer.rewrites(alembic.operations.ops.AddColumnOp)
396
- def render_add_column_with_server_default(context, revision, op):
397
+ def render_add_column_with_server_default(
398
+ context: MigrationContext,
399
+ revision: str | None,
400
+ op: Any,
401
+ ):
397
402
  # Carry the sqlmodel default as server_default so that newly added
398
403
  # columns get the desired default value in existing rows.
399
404
  if op.column.default is not None and op.column.server_default is None:
@@ -402,7 +407,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
402
407
  )
403
408
  return op
404
409
 
405
- def run_autogenerate(rev, context):
410
+ def run_autogenerate(rev: str, context: MigrationContext):
406
411
  revision_context.run_autogenerate(rev, context)
407
412
  return []
408
413
 
@@ -415,7 +420,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
415
420
  connection=connection,
416
421
  target_metadata=ModelRegistry.get_metadata(),
417
422
  render_item=cls._alembic_render_item,
418
- process_revision_directives=writer, # type: ignore
423
+ process_revision_directives=writer,
419
424
  compare_type=False,
420
425
  render_as_batch=True, # for sqlite compatibility
421
426
  )
@@ -444,7 +449,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
444
449
  """
445
450
  config, script_directory = cls._alembic_config()
446
451
 
447
- def run_upgrade(rev, context):
452
+ def run_upgrade(rev: str, context: MigrationContext):
448
453
  return script_directory._upgrade_revs(to_rev, rev)
449
454
 
450
455
  with alembic.runtime.environment.EnvironmentContext(
reflex/page.py CHANGED
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from collections import defaultdict
6
- from typing import Any, Dict, List
6
+ from typing import Any, Callable, Dict, List
7
7
 
8
8
  from reflex.config import get_config
9
9
  from reflex.event import BASE_STATE, EventType
@@ -42,7 +42,7 @@ def page(
42
42
  The decorated function.
43
43
  """
44
44
 
45
- def decorator(render_fn):
45
+ def decorator(render_fn: Callable):
46
46
  kwargs = {}
47
47
  if route:
48
48
  kwargs["route"] = route
@@ -66,7 +66,7 @@ def page(
66
66
  return decorator
67
67
 
68
68
 
69
- def get_decorated_pages(omit_implicit_routes=True) -> list[dict[str, Any]]:
69
+ def get_decorated_pages(omit_implicit_routes: bool = True) -> list[dict[str, Any]]:
70
70
  """Get the decorated pages.
71
71
 
72
72
  Args:
reflex/reflex.py CHANGED
@@ -17,7 +17,7 @@ from reflex.state import reset_disk_state_manager
17
17
  from reflex.utils import console, telemetry
18
18
 
19
19
  # Disable typer+rich integration for help panels
20
- typer.core.rich = None # type: ignore
20
+ typer.core.rich = None # pyright: ignore [reportPrivateImportUsage]
21
21
 
22
22
  # Create the app.
23
23
  try:
@@ -26,6 +26,8 @@ except TypeError:
26
26
  # Fallback for older typer versions.
27
27
  cli = typer.Typer(add_completion=False)
28
28
 
29
+ SHOW_BUILT_WITH_REFLEX_INFO = "https://reflex.dev/docs/hosting/reflex-branding/"
30
+
29
31
  # Get the config.
30
32
  config = get_config()
31
33
 
@@ -125,8 +127,8 @@ def _run(
125
127
  env: constants.Env = constants.Env.DEV,
126
128
  frontend: bool = True,
127
129
  backend: bool = True,
128
- frontend_port: str = str(config.frontend_port),
129
- backend_port: str = str(config.backend_port),
130
+ frontend_port: int = config.frontend_port,
131
+ backend_port: int = config.backend_port,
130
132
  backend_host: str = config.backend_host,
131
133
  loglevel: constants.LogLevel = config.loglevel,
132
134
  ):
@@ -160,18 +162,22 @@ def _run(
160
162
  # Find the next available open port if applicable.
161
163
  if frontend:
162
164
  frontend_port = processes.handle_port(
163
- "frontend", frontend_port, str(constants.DefaultPorts.FRONTEND_PORT)
165
+ "frontend",
166
+ frontend_port,
167
+ constants.DefaultPorts.FRONTEND_PORT,
164
168
  )
165
169
 
166
170
  if backend:
167
171
  backend_port = processes.handle_port(
168
- "backend", backend_port, str(constants.DefaultPorts.BACKEND_PORT)
172
+ "backend",
173
+ backend_port,
174
+ constants.DefaultPorts.BACKEND_PORT,
169
175
  )
170
176
 
171
177
  # Apply the new ports to the config.
172
- if frontend_port != str(config.frontend_port):
178
+ if frontend_port != config.frontend_port:
173
179
  config._set_persistent(frontend_port=frontend_port)
174
- if backend_port != str(config.backend_port):
180
+ if backend_port != config.backend_port:
175
181
  config._set_persistent(backend_port=backend_port)
176
182
 
177
183
  # Reload the config to make sure the env vars are persistent.
@@ -182,6 +188,15 @@ def _run(
182
188
  prerequisites.check_latest_package_version(constants.Reflex.MODULE_NAME)
183
189
 
184
190
  if frontend:
191
+ if not config.show_built_with_reflex:
192
+ # The sticky badge may be disabled at runtime for team/enterprise tiers.
193
+ prerequisites.check_config_option_in_tier(
194
+ option_name="show_built_with_reflex",
195
+ allowed_tiers=["team", "enterprise"],
196
+ fallback_value=True,
197
+ help_link=SHOW_BUILT_WITH_REFLEX_INFO,
198
+ )
199
+
185
200
  # Get the app module.
186
201
  prerequisites.get_compiled_app()
187
202
 
@@ -262,10 +277,10 @@ def run(
262
277
  help="Execute only backend.",
263
278
  envvar=environment.REFLEX_BACKEND_ONLY.name,
264
279
  ),
265
- frontend_port: str = typer.Option(
280
+ frontend_port: int = typer.Option(
266
281
  config.frontend_port, help="Specify a different frontend port."
267
282
  ),
268
- backend_port: str = typer.Option(
283
+ backend_port: int = typer.Option(
269
284
  config.backend_port, help="Specify a different backend port."
270
285
  ),
271
286
  backend_host: str = typer.Option(
@@ -306,6 +321,9 @@ def export(
306
321
  help="Whether to exclude sqlite db files when exporting backend.",
307
322
  hidden=True,
308
323
  ),
324
+ env: constants.Env = typer.Option(
325
+ constants.Env.PROD, help="The environment to export the app in."
326
+ ),
309
327
  loglevel: constants.LogLevel = typer.Option(
310
328
  config.loglevel, help="The log level to use."
311
329
  ),
@@ -317,12 +335,22 @@ def export(
317
335
  if prerequisites.needs_reinit(frontend=True):
318
336
  _init(name=config.app_name, loglevel=loglevel)
319
337
 
338
+ if frontend and not config.show_built_with_reflex:
339
+ # The sticky badge may be disabled on export for team/enterprise tiers.
340
+ prerequisites.check_config_option_in_tier(
341
+ option_name="show_built_with_reflex",
342
+ allowed_tiers=["team", "enterprise"],
343
+ fallback_value=False,
344
+ help_link=SHOW_BUILT_WITH_REFLEX_INFO,
345
+ )
346
+
320
347
  export_utils.export(
321
348
  zipping=zipping,
322
349
  frontend=frontend,
323
350
  backend=backend,
324
351
  zip_dest_dir=zip_dest_dir,
325
352
  upload_db_file=upload_db_file,
353
+ env=env,
326
354
  loglevel=loglevel.subprocess_level(),
327
355
  )
328
356
 
@@ -351,7 +379,7 @@ def logout(
351
379
 
352
380
  check_version()
353
381
 
354
- logout(loglevel) # type: ignore
382
+ logout(loglevel) # pyright: ignore [reportArgumentType]
355
383
 
356
384
 
357
385
  db_cli = typer.Typer()
@@ -440,7 +468,11 @@ def deploy(
440
468
  config.app_name,
441
469
  "--app-name",
442
470
  help="The name of the App to deploy under.",
443
- hidden=True,
471
+ ),
472
+ app_id: str = typer.Option(
473
+ None,
474
+ "--app-id",
475
+ help="The ID of the App to deploy over.",
444
476
  ),
445
477
  regions: List[str] = typer.Option(
446
478
  [],
@@ -480,6 +512,11 @@ def deploy(
480
512
  "--project",
481
513
  help="project id to deploy to",
482
514
  ),
515
+ project_name: Optional[str] = typer.Option(
516
+ None,
517
+ "--project-name",
518
+ help="The name of the project to deploy to.",
519
+ ),
483
520
  token: Optional[str] = typer.Option(
484
521
  None,
485
522
  "--token",
@@ -492,6 +529,7 @@ def deploy(
492
529
  ),
493
530
  ):
494
531
  """Deploy the app to the Reflex hosting service."""
532
+ from reflex_cli.constants.base import LogLevel as HostingLogLevel
495
533
  from reflex_cli.utils import dependency
496
534
  from reflex_cli.v2 import cli as hosting_cli
497
535
 
@@ -500,15 +538,32 @@ def deploy(
500
538
 
501
539
  check_version()
502
540
 
541
+ if not config.show_built_with_reflex:
542
+ # The sticky badge may be disabled on deploy for pro/team/enterprise tiers.
543
+ prerequisites.check_config_option_in_tier(
544
+ option_name="show_built_with_reflex",
545
+ allowed_tiers=["pro", "team", "enterprise"],
546
+ fallback_value=True,
547
+ help_link=SHOW_BUILT_WITH_REFLEX_INFO,
548
+ )
549
+
503
550
  # Set the log level.
504
551
  console.set_log_level(loglevel)
505
552
 
506
- if not token:
507
- # make sure user is logged in.
508
- if interactive:
509
- hosting_cli.login()
510
- else:
511
- raise SystemExit("Token is required for non-interactive mode.")
553
+ def convert_reflex_loglevel_to_reflex_cli_loglevel(
554
+ loglevel: constants.LogLevel,
555
+ ) -> HostingLogLevel:
556
+ if loglevel == constants.LogLevel.DEBUG:
557
+ return HostingLogLevel.DEBUG
558
+ if loglevel == constants.LogLevel.INFO:
559
+ return HostingLogLevel.INFO
560
+ if loglevel == constants.LogLevel.WARNING:
561
+ return HostingLogLevel.WARNING
562
+ if loglevel == constants.LogLevel.ERROR:
563
+ return HostingLogLevel.ERROR
564
+ if loglevel == constants.LogLevel.CRITICAL:
565
+ return HostingLogLevel.CRITICAL
566
+ return HostingLogLevel.INFO
512
567
 
513
568
  # Only check requirements if interactive.
514
569
  # There is user interaction for requirements update.
@@ -522,6 +577,7 @@ def deploy(
522
577
 
523
578
  hosting_cli.deploy(
524
579
  app_name=app_name,
580
+ app_id=app_id,
525
581
  export_fn=lambda zip_dest_dir,
526
582
  api_url,
527
583
  deploy_url,
@@ -542,13 +598,28 @@ def deploy(
542
598
  envfile=envfile,
543
599
  hostname=hostname,
544
600
  interactive=interactive,
545
- loglevel=type(loglevel).INFO, # type: ignore
601
+ loglevel=convert_reflex_loglevel_to_reflex_cli_loglevel(loglevel),
546
602
  token=token,
547
603
  project=project,
548
- config_path=config_path,
604
+ project_name=project_name,
605
+ **({"config_path": config_path} if config_path is not None else {}),
549
606
  )
550
607
 
551
608
 
609
+ @cli.command()
610
+ def rename(
611
+ new_name: str = typer.Argument(..., help="The new name for the app."),
612
+ loglevel: constants.LogLevel = typer.Option(
613
+ config.loglevel, help="The log level to use."
614
+ ),
615
+ ):
616
+ """Rename the app in the current directory."""
617
+ from reflex.utils import prerequisites
618
+
619
+ prerequisites.validate_app_name(new_name)
620
+ prerequisites.rename_app(new_name, loglevel)
621
+
622
+
552
623
  cli.add_typer(db_cli, name="db", help="Subcommands for managing the database schema.")
553
624
  cli.add_typer(script_cli, name="script", help="Subcommands running helper scripts.")
554
625
  cli.add_typer(
reflex/route.py CHANGED
@@ -103,7 +103,7 @@ def catchall_prefix(route: str) -> str:
103
103
  return route.replace(pattern, "") if pattern else ""
104
104
 
105
105
 
106
- def replace_brackets_with_keywords(input_string):
106
+ def replace_brackets_with_keywords(input_string: str) -> str:
107
107
  """Replace brackets and everything inside it in a string with a keyword.
108
108
 
109
109
  Args: