reflex 0.7.3a1__py3-none-any.whl → 0.7.4a0__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.

@@ -14,7 +14,6 @@ import platform
14
14
  import random
15
15
  import re
16
16
  import shutil
17
- import stat
18
17
  import sys
19
18
  import tempfile
20
19
  import time
@@ -23,7 +22,7 @@ import zipfile
23
22
  from datetime import datetime
24
23
  from pathlib import Path
25
24
  from types import ModuleType
26
- from typing import Callable, NamedTuple
25
+ from typing import Callable, NamedTuple, Sequence
27
26
  from urllib.parse import urlparse
28
27
 
29
28
  import httpx
@@ -43,13 +42,11 @@ from reflex.utils.exceptions import (
43
42
  SystemPackageMissingError,
44
43
  )
45
44
  from reflex.utils.format import format_library_name
46
- from reflex.utils.registry import _get_npm_registry
45
+ from reflex.utils.registry import get_npm_registry
47
46
 
48
47
  if typing.TYPE_CHECKING:
49
48
  from reflex.app import App
50
49
 
51
- CURRENTLY_INSTALLING_NODE = False
52
-
53
50
 
54
51
  class AppInfo(NamedTuple):
55
52
  """A tuple containing the app instance and module."""
@@ -191,24 +188,6 @@ def get_node_version() -> version.Version | None:
191
188
  return None
192
189
 
193
190
 
194
- def get_fnm_version() -> version.Version | None:
195
- """Get the version of fnm.
196
-
197
- Returns:
198
- The version of FNM.
199
- """
200
- try:
201
- result = processes.new_process([constants.Fnm.EXE, "--version"], run=True)
202
- return version.parse(result.stdout.split(" ")[1]) # pyright: ignore [reportOptionalMemberAccess, reportAttributeAccessIssue]
203
- except (FileNotFoundError, TypeError):
204
- return None
205
- except version.InvalidVersion as e:
206
- console.warn(
207
- f"The detected fnm version ({e.args[0]}) is not valid. Defaulting to None."
208
- )
209
- return None
210
-
211
-
212
191
  def get_bun_version() -> version.Version | None:
213
192
  """Get the version of bun.
214
193
 
@@ -231,42 +210,107 @@ def get_bun_version() -> version.Version | None:
231
210
  return None
232
211
 
233
212
 
234
- def get_install_package_manager(on_failure_return_none: bool = False) -> str | None:
235
- """Get the package manager executable for installation.
236
- Currently, bun is used for installation only.
213
+ def prefer_npm_over_bun() -> bool:
214
+ """Check if npm should be preferred over bun.
215
+
216
+ Returns:
217
+ If npm should be preferred over bun.
218
+ """
219
+ return npm_escape_hatch() or (
220
+ constants.IS_WINDOWS and windows_check_onedrive_in_path()
221
+ )
222
+
223
+
224
+ def get_nodejs_compatible_package_managers(
225
+ raise_on_none: bool = True,
226
+ ) -> Sequence[str]:
227
+ """Get the package manager executable for installation. Typically, bun is used for installation.
237
228
 
238
229
  Args:
239
- on_failure_return_none: Whether to return None on failure.
230
+ raise_on_none: Whether to raise an error if the package manager is not found.
240
231
 
241
232
  Returns:
242
233
  The path to the package manager.
234
+
235
+ Raises:
236
+ FileNotFoundError: If the package manager is not found and raise_on_none is True.
243
237
  """
244
- if constants.IS_WINDOWS and (
245
- windows_check_onedrive_in_path() or windows_npm_escape_hatch()
238
+ bun_package_manager = (
239
+ str(bun_path) if (bun_path := path_ops.get_bun_path()) else None
240
+ )
241
+
242
+ npm_package_manager = (
243
+ str(npm_path) if (npm_path := path_ops.get_npm_path()) else None
244
+ )
245
+
246
+ if prefer_npm_over_bun():
247
+ package_managers = [npm_package_manager, bun_package_manager]
248
+ else:
249
+ package_managers = [bun_package_manager, npm_package_manager]
250
+
251
+ package_managers = list(filter(None, package_managers))
252
+
253
+ if not package_managers and not raise_on_none:
254
+ raise FileNotFoundError(
255
+ "Bun or npm not found. You might need to rerun `reflex init` or install either."
256
+ )
257
+
258
+ return package_managers
259
+
260
+
261
+ def is_outdated_nodejs_installed():
262
+ """Check if the installed Node.js version is outdated.
263
+
264
+ Returns:
265
+ If the installed Node.js version is outdated.
266
+ """
267
+ current_version = get_node_version()
268
+ if current_version is not None and current_version < version.parse(
269
+ constants.Node.MIN_VERSION
246
270
  ):
247
- return get_package_manager(on_failure_return_none)
248
- return str(get_config().bun_path)
271
+ console.warn(
272
+ f"Your version ({current_version}) of Node.js is out of date. Upgrade to {constants.Node.MIN_VERSION} or higher."
273
+ )
274
+ return True
275
+ return False
249
276
 
250
277
 
251
- def get_package_manager(on_failure_return_none: bool = False) -> str | None:
252
- """Get the package manager executable for running app.
253
- Currently on unix systems, npm is used for running the app only.
278
+ def get_js_package_executor(raise_on_none: bool = False) -> Sequence[Sequence[str]]:
279
+ """Get the paths to package managers for running commands. Ordered by preference.
280
+ This is currently identical to get_install_package_managers, but may change in the future.
254
281
 
255
282
  Args:
256
- on_failure_return_none: Whether to return None on failure.
283
+ raise_on_none: Whether to raise an error if no package managers is not found.
257
284
 
258
285
  Returns:
259
- The path to the package manager.
286
+ The paths to the package managers as a list of lists, where each list is the command to run and its arguments.
260
287
 
261
288
  Raises:
262
- FileNotFoundError: If the package manager is not found.
289
+ FileNotFoundError: If no package managers are found and raise_on_none is True.
263
290
  """
264
- npm_path = path_ops.get_npm_path()
265
- if npm_path is not None:
266
- return str(npm_path)
267
- if on_failure_return_none:
268
- return None
269
- raise FileNotFoundError("NPM not found. You may need to run `reflex init`.")
291
+ bun_package_manager = (
292
+ [str(bun_path)] + (["--bun"] if is_outdated_nodejs_installed() else [])
293
+ if (bun_path := path_ops.get_bun_path())
294
+ else None
295
+ )
296
+
297
+ npm_package_manager = (
298
+ [str(npm_path)] if (npm_path := path_ops.get_npm_path()) else None
299
+ )
300
+
301
+ if prefer_npm_over_bun():
302
+ package_managers = [npm_package_manager, bun_package_manager]
303
+ else:
304
+ package_managers = [bun_package_manager, npm_package_manager]
305
+
306
+ package_managers = list(filter(None, package_managers))
307
+
308
+ if not package_managers and raise_on_none:
309
+ raise FileNotFoundError(
310
+ "Bun or npm not found. You might need to rerun `reflex init` or install either."
311
+ )
312
+
313
+ return package_managers
270
314
 
271
315
 
272
316
  def windows_check_onedrive_in_path() -> bool:
@@ -278,8 +322,8 @@ def windows_check_onedrive_in_path() -> bool:
278
322
  return "onedrive" in str(Path.cwd()).lower()
279
323
 
280
324
 
281
- def windows_npm_escape_hatch() -> bool:
282
- """For windows, if the user sets REFLEX_USE_NPM, use npm instead of bun.
325
+ def npm_escape_hatch() -> bool:
326
+ """If the user sets REFLEX_USE_NPM, prefer npm over bun.
283
327
 
284
328
  Returns:
285
329
  If the user has set REFLEX_USE_NPM.
@@ -862,7 +906,7 @@ def initialize_bun_config():
862
906
  bunfig_content = custom_bunfig.read_text()
863
907
  console.info(f"Copying custom bunfig.toml inside {get_web_dir()} folder")
864
908
  else:
865
- best_registry = _get_npm_registry()
909
+ best_registry = get_npm_registry()
866
910
  bunfig_content = constants.Bun.DEFAULT_CONFIG.format(registry=best_registry)
867
911
 
868
912
  bun_config_path.write_text(bunfig_content)
@@ -970,92 +1014,6 @@ def download_and_run(url: str, *args, show_status: bool = False, **env):
970
1014
  show(f"Installing {url}", process)
971
1015
 
972
1016
 
973
- def download_and_extract_fnm_zip():
974
- """Download and run a script.
975
-
976
- Raises:
977
- Exit: If an error occurs while downloading or extracting the FNM zip.
978
- """
979
- # Download the zip file
980
- url = constants.Fnm.INSTALL_URL
981
- console.debug(f"Downloading {url}")
982
- fnm_zip_file: Path = constants.Fnm.DIR / f"{constants.Fnm.FILENAME}.zip"
983
- # Function to download and extract the FNM zip release.
984
- try:
985
- # Download the FNM zip release.
986
- # TODO: show progress to improve UX
987
- response = net.get(url, follow_redirects=True)
988
- response.raise_for_status()
989
- with fnm_zip_file.open("wb") as output_file:
990
- for chunk in response.iter_bytes():
991
- output_file.write(chunk)
992
-
993
- # Extract the downloaded zip file.
994
- with zipfile.ZipFile(fnm_zip_file, "r") as zip_ref:
995
- zip_ref.extractall(constants.Fnm.DIR)
996
-
997
- console.debug("FNM package downloaded and extracted successfully.")
998
- except Exception as e:
999
- console.error(f"An error occurred while downloading fnm package: {e}")
1000
- raise typer.Exit(1) from e
1001
- finally:
1002
- # Clean up the downloaded zip file.
1003
- path_ops.rm(fnm_zip_file)
1004
-
1005
-
1006
- def install_node():
1007
- """Install fnm and nodejs for use by Reflex.
1008
- Independent of any existing system installations.
1009
- """
1010
- if not constants.Fnm.FILENAME:
1011
- # fnm only support Linux, macOS and Windows distros.
1012
- console.debug("")
1013
- return
1014
-
1015
- # Skip installation if check_node_version() checks out
1016
- if check_node_version():
1017
- console.debug("Skipping node installation as it is already installed.")
1018
- return
1019
-
1020
- path_ops.mkdir(constants.Fnm.DIR)
1021
- if not constants.Fnm.EXE.exists():
1022
- download_and_extract_fnm_zip()
1023
-
1024
- if constants.IS_WINDOWS:
1025
- # Install node
1026
- fnm_exe = Path(constants.Fnm.EXE).resolve()
1027
- fnm_dir = Path(constants.Fnm.DIR).resolve()
1028
- process = processes.new_process(
1029
- [
1030
- "powershell",
1031
- "-Command",
1032
- f'& "{fnm_exe}" install {constants.Node.VERSION} --fnm-dir "{fnm_dir}"',
1033
- ],
1034
- )
1035
- else: # All other platforms (Linux, MacOS).
1036
- # Add execute permissions to fnm executable.
1037
- constants.Fnm.EXE.chmod(stat.S_IXUSR)
1038
- # Install node.
1039
- # Specify arm64 arch explicitly for M1s and M2s.
1040
- architecture_arg = (
1041
- ["--arch=arm64"]
1042
- if platform.system() == "Darwin" and platform.machine() == "arm64"
1043
- else []
1044
- )
1045
-
1046
- process = processes.new_process(
1047
- [
1048
- constants.Fnm.EXE,
1049
- "install",
1050
- *architecture_arg,
1051
- constants.Node.VERSION,
1052
- "--fnm-dir",
1053
- constants.Fnm.DIR,
1054
- ],
1055
- )
1056
- processes.show_status("Installing node", process)
1057
-
1058
-
1059
1017
  def install_bun():
1060
1018
  """Install bun onto the user's system.
1061
1019
 
@@ -1069,7 +1027,9 @@ def install_bun():
1069
1027
  )
1070
1028
 
1071
1029
  # Skip if bun is already installed.
1072
- if get_bun_version() == version.parse(constants.Bun.VERSION):
1030
+ if (current_version := get_bun_version()) and current_version >= version.parse(
1031
+ constants.Bun.MIN_VERSION
1032
+ ):
1073
1033
  console.debug("Skipping bun installation as it is already installed.")
1074
1034
  return
1075
1035
 
@@ -1157,38 +1117,19 @@ def install_frontend_packages(packages: set[str], config: Config):
1157
1117
  packages: A list of package names to be installed.
1158
1118
  config: The config object.
1159
1119
 
1160
- Raises:
1161
- FileNotFoundError: If the package manager is not found.
1162
-
1163
1120
  Example:
1164
1121
  >>> install_frontend_packages(["react", "react-dom"], get_config())
1165
1122
  """
1166
- # unsupported archs(arm and 32bit machines) will use npm anyway. so we dont have to run npm twice
1167
- fallback_command = (
1168
- get_package_manager(on_failure_return_none=True)
1169
- if (
1170
- not constants.IS_WINDOWS
1171
- or (constants.IS_WINDOWS and not windows_check_onedrive_in_path())
1172
- )
1173
- else None
1123
+ install_package_managers = get_nodejs_compatible_package_managers(
1124
+ raise_on_none=True
1174
1125
  )
1175
1126
 
1176
- install_package_manager = (
1177
- get_install_package_manager(on_failure_return_none=True) or fallback_command
1178
- )
1179
-
1180
- if install_package_manager is None:
1181
- raise FileNotFoundError(
1182
- "Could not find a package manager to install frontend packages. You may need to run `reflex init`."
1183
- )
1184
-
1185
- fallback_command = (
1186
- fallback_command if fallback_command is not install_package_manager else None
1187
- )
1127
+ primary_package_manager = install_package_managers[0]
1128
+ fallbacks = install_package_managers[1:]
1188
1129
 
1189
- processes.run_process_with_fallback(
1190
- [install_package_manager, "install", "--legacy-peer-deps"],
1191
- fallback=fallback_command,
1130
+ processes.run_process_with_fallbacks(
1131
+ [primary_package_manager, "install", "--legacy-peer-deps"],
1132
+ fallbacks=fallbacks,
1192
1133
  analytics_enabled=True,
1193
1134
  show_status_message="Installing base frontend packages",
1194
1135
  cwd=get_web_dir(),
@@ -1196,16 +1137,16 @@ def install_frontend_packages(packages: set[str], config: Config):
1196
1137
  )
1197
1138
 
1198
1139
  if config.tailwind is not None:
1199
- processes.run_process_with_fallback(
1140
+ processes.run_process_with_fallbacks(
1200
1141
  [
1201
- install_package_manager,
1142
+ primary_package_manager,
1202
1143
  "add",
1203
1144
  "--legacy-peer-deps",
1204
1145
  "-d",
1205
1146
  constants.Tailwind.VERSION,
1206
1147
  *((config.tailwind or {}).get("plugins", [])),
1207
1148
  ],
1208
- fallback=fallback_command,
1149
+ fallbacks=fallbacks,
1209
1150
  analytics_enabled=True,
1210
1151
  show_status_message="Installing tailwind",
1211
1152
  cwd=get_web_dir(),
@@ -1214,9 +1155,9 @@ def install_frontend_packages(packages: set[str], config: Config):
1214
1155
 
1215
1156
  # Install custom packages defined in frontend_packages
1216
1157
  if len(packages) > 0:
1217
- processes.run_process_with_fallback(
1218
- [install_package_manager, "add", "--legacy-peer-deps", *packages],
1219
- fallback=fallback_command,
1158
+ processes.run_process_with_fallbacks(
1159
+ [primary_package_manager, "add", "--legacy-peer-deps", *packages],
1160
+ fallbacks=fallbacks,
1220
1161
  analytics_enabled=True,
1221
1162
  show_status_message="Installing frontend packages from config and components",
1222
1163
  cwd=get_web_dir(),
@@ -1338,24 +1279,19 @@ def validate_frontend_dependencies(init: bool = True):
1338
1279
  Exit: If the package manager is invalid.
1339
1280
  """
1340
1281
  if not init:
1341
- # we only need to validate the package manager when running app.
1342
- # `reflex init` will install the deps anyway(if applied).
1343
- package_manager = get_package_manager()
1344
- if not package_manager:
1345
- console.error(
1346
- "Could not find NPM package manager. Make sure you have node installed."
1347
- )
1348
- raise typer.Exit(1)
1282
+ try:
1283
+ get_js_package_executor(raise_on_none=True)
1284
+ except FileNotFoundError as e:
1285
+ raise typer.Exit(1) from e
1349
1286
 
1287
+ if prefer_npm_over_bun():
1350
1288
  if not check_node_version():
1351
1289
  node_version = get_node_version()
1352
1290
  console.error(
1353
1291
  f"Reflex requires node version {constants.Node.MIN_VERSION} or higher to run, but the detected version is {node_version}",
1354
1292
  )
1355
1293
  raise typer.Exit(1)
1356
-
1357
- if init:
1358
- # we only need bun for package install on `reflex init`.
1294
+ else:
1359
1295
  validate_bun()
1360
1296
 
1361
1297
 
@@ -1400,12 +1336,8 @@ def initialize_frontend_dependencies():
1400
1336
  """Initialize all the frontend dependencies."""
1401
1337
  # validate dependencies before install
1402
1338
  validate_frontend_dependencies()
1403
- # Avoid warning about Node installation while we're trying to install it.
1404
- global CURRENTLY_INSTALLING_NODE
1405
- CURRENTLY_INSTALLING_NODE = True
1406
1339
  # Install the frontend dependencies.
1407
- processes.run_concurrently(install_node, install_bun)
1408
- CURRENTLY_INSTALLING_NODE = False
1340
+ processes.run_concurrently(install_bun)
1409
1341
  # Set up the web directory.
1410
1342
  initialize_web_directory()
1411
1343
 
reflex/utils/processes.py CHANGED
@@ -10,7 +10,7 @@ import signal
10
10
  import subprocess
11
11
  from concurrent import futures
12
12
  from pathlib import Path
13
- from typing import Callable, Generator, Tuple
13
+ from typing import Callable, Generator, Sequence, Tuple
14
14
 
15
15
  import psutil
16
16
  import typer
@@ -171,14 +171,9 @@ def new_process(
171
171
 
172
172
  # Add node_bin_path to the PATH environment variable.
173
173
  if not environment.REFLEX_BACKEND_ONLY.get():
174
- node_bin_path = str(path_ops.get_node_bin_path())
175
- if not node_bin_path and not prerequisites.CURRENTLY_INSTALLING_NODE:
176
- console.warn(
177
- "The path to the Node binary could not be found. Please ensure that Node is properly "
178
- "installed and added to your system's PATH environment variable or try running "
179
- "`reflex init` again."
180
- )
181
- path_env = os.pathsep.join([node_bin_path, path_env])
174
+ node_bin_path = path_ops.get_node_bin_path()
175
+ if node_bin_path:
176
+ path_env = os.pathsep.join([str(node_bin_path), path_env])
182
177
 
183
178
  env: dict[str, str] = {
184
179
  **os.environ,
@@ -380,11 +375,11 @@ def get_command_with_loglevel(command: list[str]) -> list[str]:
380
375
  return command
381
376
 
382
377
 
383
- def run_process_with_fallback(
378
+ def run_process_with_fallbacks(
384
379
  args: list[str],
385
380
  *,
386
381
  show_status_message: str,
387
- fallback: str | list | None = None,
382
+ fallbacks: str | Sequence[str] | Sequence[Sequence[str]] | None = None,
388
383
  analytics_enabled: bool = False,
389
384
  **kwargs,
390
385
  ):
@@ -393,12 +388,12 @@ def run_process_with_fallback(
393
388
  Args:
394
389
  args: A string, or a sequence of program arguments.
395
390
  show_status_message: The status message to be displayed in the console.
396
- fallback: The fallback command to run.
391
+ fallbacks: The fallback command to run if the initial command fails.
397
392
  analytics_enabled: Whether analytics are enabled for this command.
398
393
  kwargs: Kwargs to pass to new_process function.
399
394
  """
400
395
  process = new_process(get_command_with_loglevel(args), **kwargs)
401
- if fallback is None:
396
+ if not fallbacks:
402
397
  # No fallback given, or this _is_ the fallback command.
403
398
  show_status(
404
399
  show_status_message,
@@ -408,16 +403,24 @@ def run_process_with_fallback(
408
403
  else:
409
404
  # Suppress errors for initial command, because we will try to fallback
410
405
  show_status(show_status_message, process, suppress_errors=True)
406
+
407
+ current_fallback = fallbacks[0] if not isinstance(fallbacks, str) else fallbacks
408
+ next_fallbacks = fallbacks[1:] if not isinstance(fallbacks, str) else None
409
+
411
410
  if process.returncode != 0:
412
411
  # retry with fallback command.
413
- fallback_args = [fallback, *args[1:]]
412
+ fallback_with_args = (
413
+ [current_fallback, *args[1:]]
414
+ if isinstance(fallbacks, str)
415
+ else [*current_fallback, *args[1:]]
416
+ )
414
417
  console.warn(
415
- f"There was an error running command: {args}. Falling back to: {fallback_args}."
418
+ f"There was an error running command: {args}. Falling back to: {fallback_with_args}."
416
419
  )
417
- run_process_with_fallback(
418
- fallback_args,
420
+ run_process_with_fallbacks(
421
+ fallback_with_args,
419
422
  show_status_message=show_status_message,
420
- fallback=None,
423
+ fallbacks=next_fallbacks,
421
424
  analytics_enabled=analytics_enabled,
422
425
  **kwargs,
423
426
  )
reflex/utils/registry.py CHANGED
@@ -35,11 +35,11 @@ def average_latency(registry: str, attempts: int = 3) -> int:
35
35
  return sum(latency(registry) for _ in range(attempts)) // attempts
36
36
 
37
37
 
38
- def get_best_registry() -> str:
38
+ def _get_best_registry() -> str:
39
39
  """Get the best registry based on latency.
40
40
 
41
41
  Returns:
42
- str: The best registry.
42
+ The best registry.
43
43
  """
44
44
  registries = [
45
45
  "https://registry.npmjs.org",
@@ -49,10 +49,10 @@ def get_best_registry() -> str:
49
49
  return min(registries, key=average_latency)
50
50
 
51
51
 
52
- def _get_npm_registry() -> str:
52
+ def get_npm_registry() -> str:
53
53
  """Get npm registry. If environment variable is set, use it first.
54
54
 
55
55
  Returns:
56
- str:
56
+ The npm registry.
57
57
  """
58
- return environment.NPM_CONFIG_REGISTRY.get() or get_best_registry()
58
+ return environment.NPM_CONFIG_REGISTRY.get() or _get_best_registry()
@@ -1,45 +1,52 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reflex
3
- Version: 0.7.3a1
3
+ Version: 0.7.4a0
4
4
  Summary: Web apps in pure Python.
5
- Keywords: web,framework
6
- Author: Elijah Ahianyo
7
- Author-Email: Nikhil Rao <nikhil@reflex.dev>, Alek Petuskey <alek@reflex.dev>, Masen Furer <masen@reflex.dev>, =?utf-8?q?Thomas_Brand=C3=A9ho?= <thomas@reflex.dev>, Khaleel Al-Adhami <khaleel@reflex.dev>
8
- Maintainer-Email: Masen Furer <masen@reflex.dev>, =?utf-8?q?Thomas_Brand=C3=A9ho?= <thomas@reflex.dev>, Khaleel Al-Adhami <khaleel@reflex.dev>
9
- License-Expression: Apache-2.0
10
5
  Project-URL: homepage, https://reflex.dev
11
6
  Project-URL: repository, https://github.com/reflex-dev/reflex
12
7
  Project-URL: documentation, https://reflex.dev/docs/getting-started/introduction
8
+ Author-email: Nikhil Rao <nikhil@reflex.dev>, Alek Petuskey <alek@reflex.dev>, Masen Furer <masen@reflex.dev>, Elijah Ahianyo <elijahahianyo@gmail.com>, Thomas Brandeho <thomas@reflex.dev>, Khaleel Al-Adhami <khaleel@reflex.dev>
9
+ Maintainer-email: Masen Furer <masen@reflex.dev>, Thomas Brandeho <thomas@reflex.dev>, Khaleel Al-Adhami <khaleel@reflex.dev>
10
+ License: Apache-2.0
11
+ License-File: LICENSE
12
+ Keywords: framework,web
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: License :: OSI Approved :: Apache Software License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
13
20
  Requires-Python: <4.0,>=3.10
21
+ Requires-Dist: alembic<2.0,>=1.11.1
22
+ Requires-Dist: build<2.0,>=1.0.3
23
+ Requires-Dist: charset-normalizer<4.0,>=3.3.2
24
+ Requires-Dist: distro<2.0,>=1.8.0; platform_system == 'Linux'
14
25
  Requires-Dist: fastapi!=0.111.0,!=0.111.1,>=0.96.0
15
26
  Requires-Dist: gunicorn<24.0,>=20.1.0
27
+ Requires-Dist: httpx<1.0,>=0.25.1
16
28
  Requires-Dist: jinja2<4.0,>=3.1.2
29
+ Requires-Dist: lazy-loader>=0.4
30
+ Requires-Dist: packaging<25.0,>=23.1
31
+ Requires-Dist: platformdirs<5.0,>=3.10.0
17
32
  Requires-Dist: psutil<8.0,>=5.9.4
18
33
  Requires-Dist: pydantic<3.0,>=1.10.21
34
+ Requires-Dist: python-engineio!=4.6.0
19
35
  Requires-Dist: python-multipart<0.1,>=0.0.5
20
36
  Requires-Dist: python-socketio<6.0,>=5.7.0
21
37
  Requires-Dist: redis<6.0,>=4.3.5
38
+ Requires-Dist: reflex-hosting-cli>=0.1.29
22
39
  Requires-Dist: rich<14.0,>=13.0.0
40
+ Requires-Dist: setuptools>=75.0
23
41
  Requires-Dist: sqlmodel<0.1,>=0.0.14
42
+ Requires-Dist: starlette-admin<1.0,>=0.11.0
43
+ Requires-Dist: tomlkit<1.0,>=0.12.4
44
+ Requires-Dist: twine<7.0,>=4.0.0
24
45
  Requires-Dist: typer<1.0,>=0.15.1
46
+ Requires-Dist: typing-extensions>=4.6.0
25
47
  Requires-Dist: uvicorn>=0.20.0
26
- Requires-Dist: starlette-admin<1.0,>=0.11.0
27
- Requires-Dist: alembic<2.0,>=1.11.1
28
- Requires-Dist: platformdirs<5.0,>=3.10.0
29
- Requires-Dist: distro<2.0,>=1.8.0; platform_system == "Linux"
30
- Requires-Dist: python-engineio!=4.6.0
31
- Requires-Dist: wrapt<2.0,>=1.17.0
32
- Requires-Dist: packaging<25.0,>=23.1
33
- Requires-Dist: reflex-hosting-cli>=0.1.29
34
- Requires-Dist: charset-normalizer<4.0,>=3.3.2
35
48
  Requires-Dist: wheel<1.0,>=0.42.0
36
- Requires-Dist: build<2.0,>=1.0.3
37
- Requires-Dist: setuptools>=75.0
38
- Requires-Dist: httpx<1.0,>=0.25.1
39
- Requires-Dist: twine<7.0,>=4.0.0
40
- Requires-Dist: tomlkit<1.0,>=0.12.4
41
- Requires-Dist: lazy_loader>=0.4
42
- Requires-Dist: typing_extensions>=4.6.0
49
+ Requires-Dist: wrapt<2.0,>=1.17.0
43
50
  Description-Content-Type: text/markdown
44
51
 
45
52
  <div align="center">