exec.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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. host: str,
  71. port: int,
  72. loglevel: constants.LogLevel = constants.LogLevel.ERROR,
  73. ):
  74. """Run the backend.
  75. Args:
  76. host: The app host
  77. port: The app port
  78. loglevel: The log level.
  79. """
  80. config = get_config()
  81. app_module = f"{config.app_name}.{config.app_name}:{constants.APP_VAR}"
  82. uvicorn.run(
  83. app=f"{app_module}.{constants.API_VAR}",
  84. host=host,
  85. port=port,
  86. log_level=loglevel.value,
  87. reload=True,
  88. reload_dirs=[config.app_name],
  89. )
  90. def run_backend_prod(
  91. host: str,
  92. port: int,
  93. loglevel: constants.LogLevel = constants.LogLevel.ERROR,
  94. ):
  95. """Run the backend.
  96. Args:
  97. host: The app host
  98. port: The app port
  99. loglevel: The log level.
  100. """
  101. num_workers = processes.get_num_workers()
  102. config = get_config()
  103. RUN_BACKEND_PROD = f"gunicorn --worker-class uvicorn.workers.UvicornH11Worker --preload --timeout {config.timeout} --log-level critical".split()
  104. RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {config.timeout}".split()
  105. app_module = f"{config.app_name}.{config.app_name}:{constants.APP_VAR}"
  106. command = (
  107. [
  108. *RUN_BACKEND_PROD_WINDOWS,
  109. "--host",
  110. host,
  111. "--port",
  112. str(port),
  113. app_module,
  114. ]
  115. if constants.IS_WINDOWS
  116. else [
  117. *RUN_BACKEND_PROD,
  118. "--bind",
  119. f"{host}:{port}",
  120. "--threads",
  121. str(num_workers),
  122. f"{app_module}()",
  123. ]
  124. )
  125. command += [
  126. "--log-level",
  127. loglevel.value,
  128. "--workers",
  129. str(num_workers),
  130. ]
  131. processes.new_process(
  132. command,
  133. run=True,
  134. show_logs=True,
  135. env={constants.SKIP_COMPILE_ENV_VAR: "yes"}, # skip compile for prod backend
  136. )
  137. def output_system_info():
  138. """Show system information if the loglevel is in DEBUG."""
  139. if console.LOG_LEVEL > constants.LogLevel.DEBUG:
  140. return
  141. config = get_config()
  142. try:
  143. config_file = sys.modules[config.__module__].__file__
  144. except Exception:
  145. config_file = None
  146. console.rule(f"System Info")
  147. console.debug(f"Config file: {config_file!r}")
  148. console.debug(f"Config: {config}")
  149. dependencies = [
  150. f"[Reflex {constants.VERSION} with Python {platform.python_version()} (PATH: {sys.executable})]",
  151. f"[Node {prerequisites.get_node_version()} (Expected: {constants.NODE_VERSION}) (PATH:{path_ops.get_node_path()})]",
  152. ]
  153. system = platform.system()
  154. if system != "Windows":
  155. dependencies.extend(
  156. [
  157. f"[FNM {constants.FNM_VERSION} (Expected: {constants.FNM_VERSION}) (PATH: {constants.FNM_EXE})]",
  158. f"[Bun {prerequisites.get_bun_version()} (Expected: {constants.BUN_VERSION}) (PATH: {config.bun_path})]",
  159. ],
  160. )
  161. else:
  162. dependencies.append(
  163. f"[FNM {constants.FNM_VERSION} (Expected: {constants.FNM_VERSION}) (PATH: {constants.FNM_EXE})]",
  164. )
  165. if system == "Linux":
  166. import distro # type: ignore
  167. os_version = distro.name(pretty=True)
  168. else:
  169. os_version = platform.version()
  170. dependencies.append(f"[OS {platform.system()} {os_version}]")
  171. for dep in dependencies:
  172. console.debug(f"{dep}")
  173. console.debug(
  174. f"Using package installer at: {prerequisites.get_install_package_manager()}" # type: ignore
  175. )
  176. console.debug(f"Using package executer at: {prerequisites.get_package_manager()}") # type: ignore
  177. if system != "Windows":
  178. console.debug(f"Unzip path: {path_ops.which('unzip')}")