reflex 0.5.4a3__py3-none-any.whl → 0.5.5a1__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 (260) hide show
  1. reflex/.templates/apps/demo/code/demo.py +1 -0
  2. reflex/.templates/apps/demo/code/pages/__init__.py +1 -0
  3. reflex/.templates/apps/demo/code/pages/datatable.py +1 -0
  4. reflex/.templates/apps/demo/code/pages/forms.py +1 -0
  5. reflex/.templates/apps/demo/code/pages/graphing.py +1 -0
  6. reflex/.templates/apps/demo/code/pages/home.py +1 -0
  7. reflex/.templates/apps/demo/code/styles.py +1 -0
  8. reflex/.templates/apps/demo/code/webui/components/loading_icon.py +1 -8
  9. reflex/.templates/web/components/reflex/chakra_color_mode_provider.js +27 -12
  10. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +19 -5
  11. reflex/.templates/web/utils/state.js +73 -7
  12. reflex/__init__.py +3 -0
  13. reflex/__init__.pyi +3 -0
  14. reflex/admin.py +1 -0
  15. reflex/app.py +8 -6
  16. reflex/app_module_for_backend.py +2 -1
  17. reflex/base.py +4 -4
  18. reflex/compiler/compiler.py +4 -3
  19. reflex/compiler/templates.py +2 -0
  20. reflex/compiler/utils.py +58 -35
  21. reflex/components/__init__.py +1 -0
  22. reflex/components/base/__init__.py +1 -0
  23. reflex/components/base/app_wrap.py +1 -0
  24. reflex/components/base/fragment.py +1 -0
  25. reflex/components/base/link.py +0 -1
  26. reflex/components/base/script.py +11 -15
  27. reflex/components/base/script.pyi +1 -2
  28. reflex/components/chakra/base.py +15 -13
  29. reflex/components/chakra/base.pyi +3 -1
  30. reflex/components/chakra/datadisplay/code.py +1 -0
  31. reflex/components/chakra/datadisplay/divider.py +1 -0
  32. reflex/components/chakra/datadisplay/table.py +1 -0
  33. reflex/components/chakra/datadisplay/table.pyi +3 -0
  34. reflex/components/chakra/datadisplay/tag.py +1 -0
  35. reflex/components/chakra/disclosure/transition.py +1 -0
  36. reflex/components/chakra/feedback/circularprogress.py +1 -0
  37. reflex/components/chakra/forms/button.py +1 -0
  38. reflex/components/chakra/forms/checkbox.py +4 -13
  39. reflex/components/chakra/forms/checkbox.pyi +1 -3
  40. reflex/components/chakra/forms/colormodeswitch.py +1 -0
  41. reflex/components/chakra/forms/editable.py +13 -16
  42. reflex/components/chakra/forms/editable.pyi +1 -3
  43. reflex/components/chakra/forms/form.py +1 -0
  44. reflex/components/chakra/forms/input.py +22 -21
  45. reflex/components/chakra/forms/input.pyi +4 -4
  46. reflex/components/chakra/forms/multiselect.py +1 -0
  47. reflex/components/chakra/forms/numberinput.py +3 -12
  48. reflex/components/chakra/forms/numberinput.pyi +1 -3
  49. reflex/components/chakra/forms/pininput.py +8 -14
  50. reflex/components/chakra/forms/pininput.pyi +2 -3
  51. reflex/components/chakra/forms/radio.py +4 -13
  52. reflex/components/chakra/forms/radio.pyi +2 -3
  53. reflex/components/chakra/forms/rangeslider.py +10 -13
  54. reflex/components/chakra/forms/rangeslider.pyi +2 -3
  55. reflex/components/chakra/forms/select.py +4 -12
  56. reflex/components/chakra/forms/select.pyi +2 -3
  57. reflex/components/chakra/forms/slider.py +10 -13
  58. reflex/components/chakra/forms/slider.pyi +2 -3
  59. reflex/components/chakra/forms/switch.py +4 -13
  60. reflex/components/chakra/forms/switch.pyi +1 -3
  61. reflex/components/chakra/forms/textarea.py +16 -17
  62. reflex/components/chakra/forms/textarea.pyi +1 -3
  63. reflex/components/chakra/media/avatar.py +4 -12
  64. reflex/components/chakra/media/avatar.pyi +1 -2
  65. reflex/components/chakra/media/icon.py +1 -0
  66. reflex/components/chakra/media/image.py +7 -11
  67. reflex/components/chakra/media/image.pyi +2 -2
  68. reflex/components/chakra/navigation/link.py +8 -4
  69. reflex/components/chakra/navigation/link.pyi +2 -1
  70. reflex/components/chakra/overlay/alertdialog.py +12 -14
  71. reflex/components/chakra/overlay/alertdialog.pyi +1 -2
  72. reflex/components/chakra/overlay/drawer.py +12 -14
  73. reflex/components/chakra/overlay/drawer.pyi +1 -2
  74. reflex/components/chakra/overlay/menu.py +7 -11
  75. reflex/components/chakra/overlay/menu.pyi +2 -2
  76. reflex/components/chakra/overlay/modal.py +13 -13
  77. reflex/components/chakra/overlay/modal.pyi +2 -2
  78. reflex/components/chakra/overlay/popover.py +6 -12
  79. reflex/components/chakra/overlay/popover.pyi +1 -2
  80. reflex/components/chakra/overlay/tooltip.py +7 -13
  81. reflex/components/chakra/overlay/tooltip.pyi +1 -2
  82. reflex/components/chakra/typography/heading.py +0 -1
  83. reflex/components/chakra/typography/span.py +1 -0
  84. reflex/components/chakra/typography/text.py +1 -0
  85. reflex/components/component.py +70 -43
  86. reflex/components/core/__init__.py +1 -0
  87. reflex/components/core/banner.py +27 -24
  88. reflex/components/core/banner.pyi +6 -2
  89. reflex/components/core/client_side_routing.py +1 -0
  90. reflex/components/core/cond.py +19 -17
  91. reflex/components/core/debounce.py +4 -11
  92. reflex/components/core/debounce.pyi +1 -1
  93. reflex/components/core/foreach.py +1 -0
  94. reflex/components/core/html.py +1 -0
  95. reflex/components/core/match.py +9 -6
  96. reflex/components/core/upload.py +26 -25
  97. reflex/components/core/upload.pyi +3 -4
  98. reflex/components/datadisplay/__init__.py +1 -0
  99. reflex/components/datadisplay/code.py +27 -23
  100. reflex/components/datadisplay/code.pyi +4 -2
  101. reflex/components/datadisplay/dataeditor.py +73 -80
  102. reflex/components/datadisplay/dataeditor.pyi +52 -4
  103. reflex/components/datadisplay/logo.py +1 -0
  104. reflex/components/el/__init__.py +1 -0
  105. reflex/components/el/__init__.pyi +8 -0
  106. reflex/components/el/element.py +0 -1
  107. reflex/components/el/elements/__init__.py +5 -0
  108. reflex/components/el/elements/__init__.pyi +12 -1
  109. reflex/components/el/elements/base.py +1 -0
  110. reflex/components/el/elements/forms.py +44 -61
  111. reflex/components/el/elements/forms.pyi +4 -6
  112. reflex/components/el/elements/inline.py +1 -0
  113. reflex/components/el/elements/media.py +53 -0
  114. reflex/components/el/elements/media.pyi +428 -0
  115. reflex/components/el/elements/metadata.py +11 -0
  116. reflex/components/el/elements/metadata.pyi +80 -0
  117. reflex/components/el/elements/other.py +1 -0
  118. reflex/components/el/elements/scripts.py +1 -0
  119. reflex/components/el/elements/tables.py +1 -0
  120. reflex/components/el/elements/typography.py +1 -0
  121. reflex/components/gridjs/datatable.py +9 -6
  122. reflex/components/gridjs/datatable.pyi +4 -1
  123. reflex/components/markdown/markdown.py +36 -41
  124. reflex/components/markdown/markdown.pyi +4 -3
  125. reflex/components/media/icon.py +1 -0
  126. reflex/components/moment/moment.py +11 -17
  127. reflex/components/moment/moment.pyi +4 -3
  128. reflex/components/next/base.py +1 -0
  129. reflex/components/next/image.py +6 -11
  130. reflex/components/next/image.pyi +2 -2
  131. reflex/components/plotly/plotly.py +1 -0
  132. reflex/components/props.py +1 -0
  133. reflex/components/radix/__init__.py +1 -0
  134. reflex/components/radix/primitives/__init__.py +1 -0
  135. reflex/components/radix/primitives/accordion.py +7 -15
  136. reflex/components/radix/primitives/accordion.pyi +7 -4
  137. reflex/components/radix/primitives/base.py +1 -0
  138. reflex/components/radix/primitives/drawer.py +17 -27
  139. reflex/components/radix/primitives/drawer.pyi +2 -4
  140. reflex/components/radix/primitives/form.py +4 -12
  141. reflex/components/radix/primitives/form.pyi +2 -3
  142. reflex/components/radix/primitives/slider.py +6 -11
  143. reflex/components/radix/primitives/slider.pyi +2 -2
  144. reflex/components/radix/themes/__init__.py +1 -0
  145. reflex/components/radix/themes/base.py +3 -3
  146. reflex/components/radix/themes/base.pyi +3 -2
  147. reflex/components/radix/themes/color_mode.py +31 -2
  148. reflex/components/radix/themes/color_mode.pyi +10 -1
  149. reflex/components/radix/themes/components/__init__.py +1 -0
  150. reflex/components/radix/themes/components/alert_dialog.py +13 -24
  151. reflex/components/radix/themes/components/alert_dialog.pyi +2 -4
  152. reflex/components/radix/themes/components/aspect_ratio.py +1 -0
  153. reflex/components/radix/themes/components/card.py +1 -0
  154. reflex/components/radix/themes/components/checkbox.py +6 -22
  155. reflex/components/radix/themes/components/checkbox.pyi +2 -4
  156. reflex/components/radix/themes/components/checkbox_group.py +15 -3
  157. reflex/components/radix/themes/components/checkbox_group.pyi +10 -2
  158. reflex/components/radix/themes/components/context_menu.py +29 -38
  159. reflex/components/radix/themes/components/context_menu.pyi +2 -5
  160. reflex/components/radix/themes/components/dialog.py +18 -26
  161. reflex/components/radix/themes/components/dialog.pyi +2 -4
  162. reflex/components/radix/themes/components/dropdown_menu.py +32 -57
  163. reflex/components/radix/themes/components/dropdown_menu.pyi +2 -7
  164. reflex/components/radix/themes/components/hover_card.py +5 -12
  165. reflex/components/radix/themes/components/hover_card.pyi +2 -3
  166. reflex/components/radix/themes/components/icon_button.py +1 -0
  167. reflex/components/radix/themes/components/icon_button.pyi +1 -0
  168. reflex/components/radix/themes/components/inset.py +1 -0
  169. reflex/components/radix/themes/components/popover.py +22 -27
  170. reflex/components/radix/themes/components/popover.pyi +2 -4
  171. reflex/components/radix/themes/components/radio_group.py +25 -17
  172. reflex/components/radix/themes/components/radio_group.pyi +2 -3
  173. reflex/components/radix/themes/components/scroll_area.py +1 -0
  174. reflex/components/radix/themes/components/segmented_control.py +18 -5
  175. reflex/components/radix/themes/components/segmented_control.pyi +16 -7
  176. reflex/components/radix/themes/components/select.py +13 -23
  177. reflex/components/radix/themes/components/select.pyi +1 -4
  178. reflex/components/radix/themes/components/separator.py +1 -0
  179. reflex/components/radix/themes/components/slider.py +7 -12
  180. reflex/components/radix/themes/components/slider.pyi +2 -3
  181. reflex/components/radix/themes/components/switch.py +5 -12
  182. reflex/components/radix/themes/components/switch.pyi +2 -3
  183. reflex/components/radix/themes/components/table.py +1 -0
  184. reflex/components/radix/themes/components/tabs.py +4 -11
  185. reflex/components/radix/themes/components/tabs.pyi +2 -2
  186. reflex/components/radix/themes/components/text_area.py +19 -18
  187. reflex/components/radix/themes/components/text_area.pyi +2 -3
  188. reflex/components/radix/themes/components/text_field.py +19 -18
  189. reflex/components/radix/themes/components/text_field.pyi +3 -3
  190. reflex/components/radix/themes/components/tooltip.py +10 -13
  191. reflex/components/radix/themes/components/tooltip.pyi +2 -3
  192. reflex/components/radix/themes/layout/__init__.py +1 -0
  193. reflex/components/radix/themes/layout/box.py +1 -0
  194. reflex/components/radix/themes/layout/container.py +1 -0
  195. reflex/components/radix/themes/layout/list.py +1 -0
  196. reflex/components/radix/themes/layout/list.pyi +1 -0
  197. reflex/components/radix/themes/layout/section.py +1 -0
  198. reflex/components/radix/themes/typography/__init__.py +1 -0
  199. reflex/components/radix/themes/typography/base.py +1 -0
  200. reflex/components/radix/themes/typography/blockquote.py +1 -0
  201. reflex/components/radix/themes/typography/code.py +1 -0
  202. reflex/components/radix/themes/typography/heading.py +1 -0
  203. reflex/components/radix/themes/typography/link.py +8 -3
  204. reflex/components/radix/themes/typography/link.pyi +2 -1
  205. reflex/components/react_player/audio.py +1 -0
  206. reflex/components/react_player/audio.pyi +48 -0
  207. reflex/components/react_player/react_player.py +49 -0
  208. reflex/components/react_player/react_player.pyi +49 -0
  209. reflex/components/react_player/video.py +1 -0
  210. reflex/components/react_player/video.pyi +48 -0
  211. reflex/components/recharts/__init__.py +1 -0
  212. reflex/components/recharts/cartesian.py +264 -74
  213. reflex/components/recharts/cartesian.pyi +573 -58
  214. reflex/components/recharts/charts.py +68 -78
  215. reflex/components/recharts/charts.pyi +373 -156
  216. reflex/components/recharts/general.py +52 -20
  217. reflex/components/recharts/general.pyi +52 -6
  218. reflex/components/recharts/polar.py +30 -18
  219. reflex/components/recharts/polar.pyi +66 -5
  220. reflex/components/recharts/recharts.py +5 -3
  221. reflex/components/recharts/recharts.pyi +2 -1
  222. reflex/components/sonner/toast.py +2 -2
  223. reflex/components/sonner/toast.pyi +1 -1
  224. reflex/components/suneditor/editor.py +39 -26
  225. reflex/components/suneditor/editor.pyi +4 -4
  226. reflex/components/tags/iter_tag.py +1 -0
  227. reflex/constants/__init__.py +3 -2
  228. reflex/constants/base.py +20 -21
  229. reflex/constants/compiler.py +3 -1
  230. reflex/constants/config.py +1 -0
  231. reflex/constants/event.py +1 -0
  232. reflex/constants/installer.py +3 -2
  233. reflex/constants/style.py +2 -8
  234. reflex/event.py +36 -6
  235. reflex/experimental/assets.py +1 -0
  236. reflex/experimental/client_state.py +9 -3
  237. reflex/experimental/hooks.py +1 -0
  238. reflex/experimental/misc.py +12 -3
  239. reflex/middleware/hydrate_middleware.py +1 -0
  240. reflex/middleware/middleware.py +1 -0
  241. reflex/state.py +38 -1
  242. reflex/style.py +67 -20
  243. reflex/testing.py +6 -2
  244. reflex/utils/build.py +76 -72
  245. reflex/utils/exec.py +17 -9
  246. reflex/utils/export.py +13 -9
  247. reflex/utils/imports.py +34 -5
  248. reflex/utils/lazy_loader.py +1 -0
  249. reflex/utils/path_ops.py +39 -33
  250. reflex/utils/prerequisites.py +35 -27
  251. reflex/utils/processes.py +1 -1
  252. reflex/utils/serializers.py +3 -6
  253. reflex/utils/watch.py +3 -1
  254. reflex/vars.py +26 -10
  255. reflex/vars.pyi +3 -3
  256. {reflex-0.5.4a3.dist-info → reflex-0.5.5a1.dist-info}/METADATA +2 -2
  257. {reflex-0.5.4a3.dist-info → reflex-0.5.5a1.dist-info}/RECORD +260 -260
  258. {reflex-0.5.4a3.dist-info → reflex-0.5.5a1.dist-info}/LICENSE +0 -0
  259. {reflex-0.5.4a3.dist-info → reflex-0.5.5a1.dist-info}/WHEEL +0 -0
  260. {reflex-0.5.4a3.dist-info → reflex-0.5.5a1.dist-info}/entry_points.txt +0 -0
reflex/utils/build.py CHANGED
@@ -18,7 +18,7 @@ from reflex.utils import console, path_ops, prerequisites, processes
18
18
  def set_env_json():
19
19
  """Write the upload url to a REFLEX_JSON."""
20
20
  path_ops.update_json_file(
21
- constants.Dirs.ENV_JSON,
21
+ str(prerequisites.get_web_dir() / constants.Dirs.ENV_JSON),
22
22
  {endpoint.name: endpoint.get_url() for endpoint in constants.Endpoint},
23
23
  )
24
24
 
@@ -55,8 +55,8 @@ def generate_sitemap_config(deploy_url: str, export=False):
55
55
 
56
56
  config = json.dumps(config)
57
57
 
58
- with open(constants.Next.SITEMAP_CONFIG_FILE, "w") as f:
59
- f.write(templates.SITEMAP_CONFIG(config=config))
58
+ sitemap = prerequisites.get_web_dir() / constants.Next.SITEMAP_CONFIG_FILE
59
+ sitemap.write_text(templates.SITEMAP_CONFIG(config=config))
60
60
 
61
61
 
62
62
  def _zip(
@@ -129,85 +129,89 @@ def _zip(
129
129
  zipf.write(file, os.path.relpath(file, root_dir))
130
130
 
131
131
 
132
- def export(
133
- backend: bool = True,
132
+ def zip_app(
134
133
  frontend: bool = True,
135
- zip: bool = False,
134
+ backend: bool = True,
136
135
  zip_dest_dir: str = os.getcwd(),
137
- deploy_url: str | None = None,
138
136
  upload_db_file: bool = False,
139
137
  ):
140
- """Export the app for deployment.
138
+ """Zip up the app.
141
139
 
142
140
  Args:
143
- backend: Whether to zip up the backend app.
144
141
  frontend: Whether to zip up the frontend app.
145
- zip: Whether to zip the app.
146
- zip_dest_dir: The destination directory for created zip files (if any)
147
- deploy_url: The URL of the deployed app.
148
- upload_db_file: Whether to include local sqlite db files from the backend zip.
142
+ backend: Whether to zip up the backend app.
143
+ zip_dest_dir: The directory to export the zip file to.
144
+ upload_db_file: Whether to upload the database file.
149
145
  """
150
- # Remove the static folder.
151
- path_ops.rm(constants.Dirs.WEB_STATIC)
152
-
153
- # The export command to run.
154
- command = "export"
146
+ files_to_exclude = {
147
+ constants.ComponentName.FRONTEND.zip(),
148
+ constants.ComponentName.BACKEND.zip(),
149
+ }
155
150
 
156
151
  if frontend:
157
- checkpoints = [
158
- "Linting and checking ",
159
- "Creating an optimized production build",
160
- "Route (pages)",
161
- "prerendered as static HTML",
162
- "Collecting page data",
163
- "Finalizing page optimization",
164
- "Collecting build traces",
165
- ]
152
+ _zip(
153
+ component_name=constants.ComponentName.FRONTEND,
154
+ target=os.path.join(zip_dest_dir, constants.ComponentName.FRONTEND.zip()),
155
+ root_dir=str(prerequisites.get_web_dir() / constants.Dirs.STATIC),
156
+ files_to_exclude=files_to_exclude,
157
+ exclude_venv_dirs=False,
158
+ )
166
159
 
167
- # Generate a sitemap if a deploy URL is provided.
168
- if deploy_url is not None:
169
- generate_sitemap_config(deploy_url, export=zip)
170
- command = "export-sitemap"
160
+ if backend:
161
+ _zip(
162
+ component_name=constants.ComponentName.BACKEND,
163
+ target=os.path.join(zip_dest_dir, constants.ComponentName.BACKEND.zip()),
164
+ root_dir=".",
165
+ dirs_to_exclude={"__pycache__"},
166
+ files_to_exclude=files_to_exclude,
167
+ top_level_dirs_to_exclude={"assets"},
168
+ exclude_venv_dirs=True,
169
+ upload_db_file=upload_db_file,
170
+ )
171
171
 
172
- checkpoints.extend(["Loading next-sitemap", "Generation completed"])
173
172
 
174
- # Start the subprocess with the progress bar.
175
- process = processes.new_process(
176
- [prerequisites.get_package_manager(), "run", command],
177
- cwd=constants.Dirs.WEB,
178
- shell=constants.IS_WINDOWS,
179
- )
180
- processes.show_progress("Creating Production Build", process, checkpoints)
181
-
182
- # Zip up the app.
183
- if zip:
184
- files_to_exclude = {
185
- constants.ComponentName.FRONTEND.zip(),
186
- constants.ComponentName.BACKEND.zip(),
187
- }
188
- if frontend:
189
- _zip(
190
- component_name=constants.ComponentName.FRONTEND,
191
- target=os.path.join(
192
- zip_dest_dir, constants.ComponentName.FRONTEND.zip()
193
- ),
194
- root_dir=constants.Dirs.WEB_STATIC,
195
- files_to_exclude=files_to_exclude,
196
- exclude_venv_dirs=False,
197
- )
198
- if backend:
199
- _zip(
200
- component_name=constants.ComponentName.BACKEND,
201
- target=os.path.join(
202
- zip_dest_dir, constants.ComponentName.BACKEND.zip()
203
- ),
204
- root_dir=".",
205
- dirs_to_exclude={"__pycache__"},
206
- files_to_exclude=files_to_exclude,
207
- top_level_dirs_to_exclude={"assets"},
208
- exclude_venv_dirs=True,
209
- upload_db_file=upload_db_file,
210
- )
173
+ def build(
174
+ deploy_url: str | None = None,
175
+ for_export: bool = False,
176
+ ):
177
+ """Build the app for deployment.
178
+
179
+ Args:
180
+ deploy_url: The deployment URL.
181
+ for_export: Whether the build is for export.
182
+ """
183
+ wdir = prerequisites.get_web_dir()
184
+
185
+ # Clean the static directory if it exists.
186
+ path_ops.rm(str(wdir / constants.Dirs.STATIC))
187
+
188
+ # The export command to run.
189
+ command = "export"
190
+
191
+ checkpoints = [
192
+ "Linting and checking ",
193
+ "Creating an optimized production build",
194
+ "Route (pages)",
195
+ "prerendered as static HTML",
196
+ "Collecting page data",
197
+ "Finalizing page optimization",
198
+ "Collecting build traces",
199
+ ]
200
+
201
+ # Generate a sitemap if a deploy URL is provided.
202
+ if deploy_url is not None:
203
+ generate_sitemap_config(deploy_url, export=for_export)
204
+ command = "export-sitemap"
205
+
206
+ checkpoints.extend(["Loading next-sitemap", "Generation completed"])
207
+
208
+ # Start the subprocess with the progress bar.
209
+ process = processes.new_process(
210
+ [prerequisites.get_package_manager(), "run", command],
211
+ cwd=wdir,
212
+ shell=constants.IS_WINDOWS,
213
+ )
214
+ processes.show_progress("Creating Production Build", process, checkpoints)
211
215
 
212
216
 
213
217
  def setup_frontend(
@@ -226,7 +230,7 @@ def setup_frontend(
226
230
  # Copy asset files to public folder.
227
231
  path_ops.cp(
228
232
  src=str(root / constants.Dirs.APP_ASSETS),
229
- dest=str(root / constants.Dirs.WEB_ASSETS),
233
+ dest=str(root / prerequisites.get_web_dir() / constants.Dirs.PUBLIC),
230
234
  )
231
235
 
232
236
  # Set the environment variables in client (env.json).
@@ -242,7 +246,7 @@ def setup_frontend(
242
246
  "telemetry",
243
247
  "disable",
244
248
  ],
245
- cwd=constants.Dirs.WEB,
249
+ cwd=prerequisites.get_web_dir(),
246
250
  stdout=subprocess.DEVNULL,
247
251
  shell=constants.IS_WINDOWS,
248
252
  )
@@ -259,7 +263,7 @@ def setup_frontend_prod(
259
263
  disable_telemetry: Whether to disable the Next telemetry.
260
264
  """
261
265
  setup_frontend(root, disable_telemetry)
262
- export(deploy_url=get_config().deploy_url)
266
+ build(deploy_url=get_config().deploy_url)
263
267
 
264
268
 
265
269
  def _looks_like_venv_dir(dir_to_check: str) -> bool:
reflex/utils/exec.py CHANGED
@@ -17,6 +17,7 @@ import psutil
17
17
  from reflex import constants
18
18
  from reflex.config import get_config
19
19
  from reflex.utils import console, path_ops
20
+ from reflex.utils.prerequisites import get_web_dir
20
21
  from reflex.utils.watch import AssetFolderWatch
21
22
 
22
23
  # For uvicorn windows bug fix (#2335)
@@ -82,8 +83,8 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
82
83
  """
83
84
  from reflex.utils import processes
84
85
 
85
- json_file_path = os.path.join(constants.Dirs.WEB, "package.json")
86
- last_hash = detect_package_change(json_file_path)
86
+ json_file_path = get_web_dir() / constants.PackageJson.PATH
87
+ last_hash = detect_package_change(str(json_file_path))
87
88
  process = None
88
89
  first_run = True
89
90
 
@@ -94,7 +95,7 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
94
95
  kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP # type: ignore
95
96
  process = processes.new_process(
96
97
  run_command,
97
- cwd=constants.Dirs.WEB,
98
+ cwd=get_web_dir(),
98
99
  shell=constants.IS_WINDOWS,
99
100
  **kwargs,
100
101
  )
@@ -108,7 +109,14 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
108
109
  url = match.group(1)
109
110
  if get_config().frontend_path != "":
110
111
  url = urljoin(url, get_config().frontend_path)
111
- console.print(f"App running at: [bold green]{url}")
112
+
113
+ console.print(
114
+ f"App running at: [bold green]{url}[/bold green]{' (Frontend-only mode)' if not backend_present else ''}"
115
+ )
116
+ if backend_present:
117
+ console.print(
118
+ f"Backend running at: [bold green]http://0.0.0.0:{get_config().backend_port}[/bold green]"
119
+ )
112
120
  first_run = False
113
121
  else:
114
122
  console.print("New packages detected: Updating app...")
@@ -121,7 +129,7 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
121
129
  "`REFLEX_USE_NPM=1 reflex init`\n"
122
130
  "`REFLEX_USE_NPM=1 reflex run`"
123
131
  )
124
- new_hash = detect_package_change(json_file_path)
132
+ new_hash = detect_package_change(str(json_file_path))
125
133
  if new_hash != last_hash:
126
134
  last_hash = new_hash
127
135
  kill(process.pid)
@@ -194,10 +202,10 @@ def run_backend(
194
202
  config = get_config()
195
203
  app_module = f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
196
204
 
205
+ web_dir = get_web_dir()
197
206
  # Create a .nocompile file to skip compile for backend.
198
- if os.path.exists(constants.Dirs.WEB):
199
- with open(constants.NOCOMPILE_FILE, "w"):
200
- pass
207
+ if web_dir.exists():
208
+ (web_dir / constants.NOCOMPILE_FILE).touch()
201
209
 
202
210
  # Run the backend in development mode.
203
211
  uvicorn.run(
@@ -207,7 +215,7 @@ def run_backend(
207
215
  log_level=loglevel.value,
208
216
  reload=True,
209
217
  reload_dirs=[config.app_name],
210
- reload_excludes=[constants.Dirs.WEB],
218
+ reload_excludes=[str(web_dir)],
211
219
  )
212
220
 
213
221
 
reflex/utils/export.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Export utilities."""
2
+
2
3
  import os
3
4
  from pathlib import Path
4
5
  from typing import Optional
@@ -55,15 +56,18 @@ def export(
55
56
  # Set up .web directory and install frontend dependencies.
56
57
  build.setup_frontend(Path.cwd())
57
58
 
58
- # Export the app.
59
- build.export(
60
- backend=backend,
61
- frontend=frontend,
62
- zip=zipping,
63
- zip_dest_dir=zip_dest_dir,
64
- deploy_url=config.deploy_url,
65
- upload_db_file=upload_db_file,
66
- )
59
+ # Build the static app.
60
+ if frontend:
61
+ build.build(deploy_url=config.deploy_url, for_export=True)
62
+
63
+ # Zip up the app.
64
+ if zipping:
65
+ build.zip_app(
66
+ frontend=frontend,
67
+ backend=backend,
68
+ zip_dest_dir=zip_dest_dir,
69
+ upload_db_file=upload_db_file,
70
+ )
67
71
 
68
72
  # Post a telemetry event.
69
73
  telemetry.send("export")
reflex/utils/imports.py CHANGED
@@ -3,12 +3,12 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from collections import defaultdict
6
- from typing import Dict, List, Optional
6
+ from typing import Dict, List, Optional, Union
7
7
 
8
8
  from reflex.base import Base
9
9
 
10
10
 
11
- def merge_imports(*imports) -> ImportDict:
11
+ def merge_imports(*imports: ImportDict | ParsedImportDict) -> ParsedImportDict:
12
12
  """Merge multiple import dicts together.
13
13
 
14
14
  Args:
@@ -24,7 +24,31 @@ def merge_imports(*imports) -> ImportDict:
24
24
  return all_imports
25
25
 
26
26
 
27
- def collapse_imports(imports: ImportDict) -> ImportDict:
27
+ def parse_imports(imports: ImportDict | ParsedImportDict) -> ParsedImportDict:
28
+ """Parse the import dict into a standard format.
29
+
30
+ Args:
31
+ imports: The import dict to parse.
32
+
33
+ Returns:
34
+ The parsed import dict.
35
+ """
36
+
37
+ def _make_list(value: ImportTypes) -> list[str | ImportVar] | list[ImportVar]:
38
+ if isinstance(value, (str, ImportVar)):
39
+ return [value]
40
+ return value
41
+
42
+ return {
43
+ package: [
44
+ ImportVar(tag=tag) if isinstance(tag, str) else tag
45
+ for tag in _make_list(maybe_tags)
46
+ ]
47
+ for package, maybe_tags in imports.items()
48
+ }
49
+
50
+
51
+ def collapse_imports(imports: ParsedImportDict) -> ParsedImportDict:
28
52
  """Remove all duplicate ImportVar within an ImportDict.
29
53
 
30
54
  Args:
@@ -33,7 +57,10 @@ def collapse_imports(imports: ImportDict) -> ImportDict:
33
57
  Returns:
34
58
  The collapsed import dict.
35
59
  """
36
- return {lib: list(set(import_vars)) for lib, import_vars in imports.items()}
60
+ return {
61
+ lib: list(set(import_vars)) if isinstance(import_vars, list) else import_vars
62
+ for lib, import_vars in imports.items()
63
+ }
37
64
 
38
65
 
39
66
  class ImportVar(Base):
@@ -90,4 +117,6 @@ class ImportVar(Base):
90
117
  )
91
118
 
92
119
 
93
- ImportDict = Dict[str, List[ImportVar]]
120
+ ImportTypes = Union[str, ImportVar, List[Union[str, ImportVar]], List[ImportVar]]
121
+ ImportDict = Dict[str, ImportTypes]
122
+ ParsedImportDict = Dict[str, List[ImportVar]]
@@ -1,4 +1,5 @@
1
1
  """Module to implement lazy loading in reflex."""
2
+
2
3
  import copy
3
4
 
4
5
  import lazy_loader as lazy
reflex/utils/path_ops.py CHANGED
@@ -14,19 +14,20 @@ from reflex import constants
14
14
  join = os.linesep.join
15
15
 
16
16
 
17
- def rm(path: str):
17
+ def rm(path: str | Path):
18
18
  """Remove a file or directory.
19
19
 
20
20
  Args:
21
21
  path: The path to the file or directory.
22
22
  """
23
- if os.path.isdir(path):
23
+ path = Path(path)
24
+ if path.is_dir():
24
25
  shutil.rmtree(path)
25
- elif os.path.isfile(path):
26
- os.remove(path)
26
+ elif path.is_file():
27
+ path.unlink()
27
28
 
28
29
 
29
- def cp(src: str, dest: str, overwrite: bool = True) -> bool:
30
+ def cp(src: str | Path, dest: str | Path, overwrite: bool = True) -> bool:
30
31
  """Copy a file or directory.
31
32
 
32
33
  Args:
@@ -37,11 +38,12 @@ def cp(src: str, dest: str, overwrite: bool = True) -> bool:
37
38
  Returns:
38
39
  Whether the copy was successful.
39
40
  """
41
+ src, dest = Path(src), Path(dest)
40
42
  if src == dest:
41
43
  return False
42
- if not overwrite and os.path.exists(dest):
44
+ if not overwrite and dest.exists():
43
45
  return False
44
- if os.path.isdir(src):
46
+ if src.is_dir():
45
47
  rm(dest)
46
48
  shutil.copytree(src, dest)
47
49
  else:
@@ -49,7 +51,7 @@ def cp(src: str, dest: str, overwrite: bool = True) -> bool:
49
51
  return True
50
52
 
51
53
 
52
- def mv(src: str, dest: str, overwrite: bool = True) -> bool:
54
+ def mv(src: str | Path, dest: str | Path, overwrite: bool = True) -> bool:
53
55
  """Move a file or directory.
54
56
 
55
57
  Args:
@@ -60,25 +62,26 @@ def mv(src: str, dest: str, overwrite: bool = True) -> bool:
60
62
  Returns:
61
63
  Whether the move was successful.
62
64
  """
65
+ src, dest = Path(src), Path(dest)
63
66
  if src == dest:
64
67
  return False
65
- if not overwrite and os.path.exists(dest):
68
+ if not overwrite and dest.exists():
66
69
  return False
67
70
  rm(dest)
68
71
  shutil.move(src, dest)
69
72
  return True
70
73
 
71
74
 
72
- def mkdir(path: str):
75
+ def mkdir(path: str | Path):
73
76
  """Create a directory.
74
77
 
75
78
  Args:
76
79
  path: The path to the directory.
77
80
  """
78
- os.makedirs(path, exist_ok=True)
81
+ Path(path).mkdir(parents=True, exist_ok=True)
79
82
 
80
83
 
81
- def ln(src: str, dest: str, overwrite: bool = False) -> bool:
84
+ def ln(src: str | Path, dest: str | Path, overwrite: bool = False) -> bool:
82
85
  """Create a symbolic link.
83
86
 
84
87
  Args:
@@ -89,19 +92,20 @@ def ln(src: str, dest: str, overwrite: bool = False) -> bool:
89
92
  Returns:
90
93
  Whether the link was successful.
91
94
  """
95
+ src, dest = Path(src), Path(dest)
92
96
  if src == dest:
93
97
  return False
94
- if not overwrite and (os.path.exists(dest) or os.path.islink(dest)):
98
+ if not overwrite and (dest.exists() or dest.is_symlink()):
95
99
  return False
96
- if os.path.isdir(src):
100
+ if src.is_dir():
97
101
  rm(dest)
98
- os.symlink(src, dest, target_is_directory=True)
102
+ src.symlink_to(dest, target_is_directory=True)
99
103
  else:
100
- os.symlink(src, dest)
104
+ src.symlink_to(dest)
101
105
  return True
102
106
 
103
107
 
104
- def which(program: str) -> str | None:
108
+ def which(program: str | Path) -> str | Path | None:
105
109
  """Find the path to an executable.
106
110
 
107
111
  Args:
@@ -110,7 +114,7 @@ def which(program: str) -> str | None:
110
114
  Returns:
111
115
  The path to the executable.
112
116
  """
113
- return shutil.which(program)
117
+ return shutil.which(str(program))
114
118
 
115
119
 
116
120
  def get_node_bin_path() -> str | None:
@@ -119,10 +123,11 @@ def get_node_bin_path() -> str | None:
119
123
  Returns:
120
124
  The path to the node bin folder.
121
125
  """
122
- if not os.path.exists(constants.Node.BIN_PATH):
126
+ bin_path = Path(constants.Node.BIN_PATH)
127
+ if not bin_path.exists():
123
128
  str_path = which("node")
124
129
  return str(Path(str_path).parent.resolve()) if str_path else str_path
125
- return str(Path(constants.Node.BIN_PATH).resolve())
130
+ return str(bin_path.resolve())
126
131
 
127
132
 
128
133
  def get_node_path() -> str | None:
@@ -131,9 +136,10 @@ def get_node_path() -> str | None:
131
136
  Returns:
132
137
  The path to the node binary file.
133
138
  """
134
- if not os.path.exists(constants.Node.PATH):
135
- return which("node")
136
- return constants.Node.PATH
139
+ node_path = Path(constants.Node.PATH)
140
+ if not node_path.exists():
141
+ return str(which("node"))
142
+ return str(node_path)
137
143
 
138
144
 
139
145
  def get_npm_path() -> str | None:
@@ -142,12 +148,13 @@ def get_npm_path() -> str | None:
142
148
  Returns:
143
149
  The path to the npm binary file.
144
150
  """
145
- if not os.path.exists(constants.Node.PATH):
146
- return which("npm")
147
- return constants.Node.NPM_PATH
151
+ npm_path = Path(constants.Node.NPM_PATH)
152
+ if not npm_path.exists():
153
+ return str(which("npm"))
154
+ return str(npm_path)
148
155
 
149
156
 
150
- def update_json_file(file_path: str, update_dict: dict[str, int | str]):
157
+ def update_json_file(file_path: str | Path, update_dict: dict[str, int | str]):
151
158
  """Update the contents of a json file.
152
159
 
153
160
  Args:
@@ -176,7 +183,7 @@ def update_json_file(file_path: str, update_dict: dict[str, int | str]):
176
183
  json.dump(json_object, f, ensure_ascii=False)
177
184
 
178
185
 
179
- def find_replace(directory: str, find: str, replace: str):
186
+ def find_replace(directory: str | Path, find: str, replace: str):
180
187
  """Recursively find and replace text in files in a directory.
181
188
 
182
189
  Args:
@@ -184,11 +191,10 @@ def find_replace(directory: str, find: str, replace: str):
184
191
  find: The text to find.
185
192
  replace: The text to replace.
186
193
  """
194
+ directory = Path(directory)
187
195
  for root, _dirs, files in os.walk(directory):
188
196
  for file in files:
189
- filepath = os.path.join(root, file)
190
- with open(filepath, "r", encoding="utf-8") as f:
191
- text = f.read()
197
+ filepath = Path(root, file)
198
+ text = filepath.read_text(encoding="utf-8")
192
199
  text = re.sub(find, replace, text)
193
- with open(filepath, "w") as f:
194
- f.write(text)
200
+ filepath.write_text(text)