exec.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. """Everything regarding execution of the built app."""
  2. from __future__ import annotations
  3. import os
  4. import platform
  5. import sys
  6. from pathlib import Path
  7. import uvicorn
  8. from reflex import constants
  9. from reflex.config import get_config
  10. from reflex.utils import console, path_ops, prerequisites, processes
  11. from reflex.utils.watch import AssetFolderWatch
  12. def start_watching_assets_folder(root):
  13. """Start watching assets folder.
  14. Args:
  15. root: root path of the project.
  16. """
  17. asset_watch = AssetFolderWatch(root)
  18. asset_watch.start()
  19. def run_process_and_launch_url(
  20. run_command: list[str],
  21. ):
  22. """Run the process and launch the URL.
  23. Args:
  24. run_command: The command to run.
  25. """
  26. process = processes.new_process(
  27. run_command, cwd=constants.WEB_DIR, shell=constants.IS_WINDOWS
  28. )
  29. if process.stdout:
  30. for line in process.stdout:
  31. if "ready started server on" in line:
  32. url = line.split("url: ")[-1].strip()
  33. console.print(f"App running at: [bold green]{url}")
  34. else:
  35. console.debug(line)
  36. def run_frontend(
  37. root: Path,
  38. port: str,
  39. ):
  40. """Run the frontend.
  41. Args:
  42. root: The root path of the project.
  43. port: The port to run the frontend on.
  44. """
  45. # Start watching asset folder.
  46. start_watching_assets_folder(root)
  47. # validate dependencies before run
  48. prerequisites.validate_frontend_dependencies(init=False)
  49. # Run the frontend in development mode.
  50. console.rule("[bold green]App Running")
  51. os.environ["PORT"] = str(get_config().frontend_port if port is None else port)
  52. run_process_and_launch_url([prerequisites.get_package_manager(), "run", "dev"]) # type: ignore
  53. def run_frontend_prod(
  54. root: Path,
  55. port: str,
  56. ):
  57. """Run the frontend.
  58. Args:
  59. root: The root path of the project (to keep same API as run_frontend).
  60. port: The port to run the frontend on.
  61. """
  62. # Set the port.
  63. os.environ["PORT"] = str(get_config().frontend_port if port is None else port)
  64. # validate dependencies before run
  65. prerequisites.validate_frontend_dependencies(init=False)
  66. # Run the frontend in production mode.
  67. console.rule("[bold green]App Running")
  68. run_process_and_launch_url([prerequisites.get_package_manager(), "run", "prod"]) # type: ignore
  69. def run_backend(
  70. app_name: str,
  71. host: str,
  72. port: int,
  73. loglevel: constants.LogLevel = constants.LogLevel.ERROR,
  74. ):
  75. """Run the backend.
  76. Args:
  77. host: The app host
  78. app_name: The app name.
  79. port: The app port
  80. loglevel: The log level.
  81. """
  82. uvicorn.run(
  83. app=f"{app_name}:{constants.APP_VAR}.{constants.API_VAR}",
  84. host=host,
  85. port=port,
  86. log_level=loglevel.value,
  87. reload=True,
  88. reload_dirs=[app_name.split(".")[0]],
  89. )
  90. def run_backend_prod(
  91. app_name: str,
  92. host: str,
  93. port: int,
  94. loglevel: constants.LogLevel = constants.LogLevel.ERROR,
  95. ):
  96. """Run the backend.
  97. Args:
  98. host: The app host
  99. app_name: The app name.
  100. port: The app port
  101. loglevel: The log level.
  102. """
  103. num_workers = processes.get_num_workers()
  104. config = get_config()
  105. RUN_BACKEND_PROD = f"gunicorn --worker-class uvicorn.workers.UvicornH11Worker --preload --timeout {config.timeout} --log-level critical".split()
  106. RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {config.timeout}".split()
  107. command = (
  108. [
  109. *RUN_BACKEND_PROD_WINDOWS,
  110. "--host",
  111. host,
  112. "--port",
  113. str(port),
  114. f"{app_name}:{constants.APP_VAR}",
  115. ]
  116. if constants.IS_WINDOWS
  117. else [
  118. *RUN_BACKEND_PROD,
  119. "--bind",
  120. f"{host}:{port}",
  121. "--threads",
  122. str(num_workers),
  123. f"{app_name}:{constants.APP_VAR}()",
  124. ]
  125. )
  126. command += [
  127. "--log-level",
  128. loglevel.value,
  129. "--workers",
  130. str(num_workers),
  131. ]
  132. processes.new_process(command, run=True, show_logs=True)
  133. def output_system_info():
  134. """Show system information if the loglevel is in DEBUG."""
  135. if console.LOG_LEVEL > constants.LogLevel.DEBUG:
  136. return
  137. config = get_config()
  138. try:
  139. config_file = sys.modules[config.__module__].__file__
  140. except Exception:
  141. config_file = None
  142. console.rule(f"System Info")
  143. console.debug(f"Config file: {config_file!r}")
  144. console.debug(f"Config: {config}")
  145. dependencies = [
  146. f"[Reflex {constants.VERSION} with Python {platform.python_version()} (PATH: {sys.executable})]",
  147. f"[Node {prerequisites.get_node_version()} (Expected: {constants.NODE_VERSION}) (PATH:{path_ops.get_node_path()})]",
  148. ]
  149. system = platform.system()
  150. if system != "Windows":
  151. dependencies.extend(
  152. [
  153. f"[FNM {constants.FNM_VERSION} (Expected: {constants.FNM_VERSION}) (PATH: {constants.FNM_EXE})]",
  154. f"[Bun {prerequisites.get_bun_version()} (Expected: {constants.BUN_VERSION}) (PATH: {config.bun_path})]",
  155. ],
  156. )
  157. else:
  158. dependencies.append(
  159. f"[FNM {constants.FNM_VERSION} (Expected: {constants.FNM_VERSION}) (PATH: {constants.FNM_EXE})]",
  160. )
  161. if system == "Linux":
  162. import distro # type: ignore
  163. os_version = distro.name(pretty=True)
  164. else:
  165. os_version = platform.version()
  166. dependencies.append(f"[OS {platform.system()} {os_version}]")
  167. for dep in dependencies:
  168. console.debug(f"{dep}")
  169. console.debug(
  170. f"Using package installer at: {prerequisites.get_install_package_manager()}" # type: ignore
  171. )
  172. console.debug(f"Using package executer at: {prerequisites.get_package_manager()}") # type: ignore
  173. if system != "Windows":
  174. console.debug(f"Unzip path: {path_ops.which('unzip')}")