1
0

exec.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. """Everything regarding execution of the built app."""
  2. from __future__ import annotations
  3. import os
  4. import platform
  5. import subprocess
  6. from datetime import datetime
  7. from pathlib import Path
  8. from typing import TYPE_CHECKING
  9. from rich import print
  10. from pynecone import constants
  11. from pynecone.config import get_config
  12. from pynecone.utils import console, prerequisites, processes
  13. from pynecone.utils.build import export_app, setup_backend, setup_frontend
  14. from pynecone.utils.watch import AssetFolderWatch
  15. if TYPE_CHECKING:
  16. from pynecone.app import App
  17. def start_watching_assets_folder(root):
  18. """Start watching assets folder.
  19. Args:
  20. root: root path of the project.
  21. """
  22. asset_watch = AssetFolderWatch(root)
  23. asset_watch.start()
  24. def run_process_and_launch_url(
  25. run_command: list[str],
  26. root: Path,
  27. loglevel: constants.LogLevel = constants.LogLevel.ERROR,
  28. ):
  29. """Run the process and launch the URL.
  30. Args:
  31. run_command: The command to run.
  32. root: root path of the project.
  33. loglevel: The log level to use.
  34. """
  35. process = subprocess.Popen(
  36. run_command,
  37. cwd=constants.WEB_DIR,
  38. env=os.environ,
  39. stderr=subprocess.STDOUT,
  40. stdout=subprocess.PIPE,
  41. universal_newlines=True,
  42. )
  43. current_time = datetime.now()
  44. if process.stdout:
  45. for line in process.stdout:
  46. if "ready started server on" in line:
  47. url = line.split("url: ")[-1].strip()
  48. print(f"App running at: [bold green]{url}")
  49. if (
  50. "Fast Refresh" in line
  51. or "compiling..." in line
  52. and (datetime.now() - current_time).total_seconds() > 1
  53. ):
  54. current_time = datetime.now()
  55. print(
  56. f"[yellow][Updating App][/yellow] Applying changes and refreshing. Time: {current_time}"
  57. )
  58. elif loglevel == constants.LogLevel.DEBUG:
  59. print(line, end="")
  60. def run_frontend(
  61. app: App,
  62. root: Path,
  63. port: str,
  64. loglevel: constants.LogLevel = constants.LogLevel.ERROR,
  65. ):
  66. """Run the frontend.
  67. Args:
  68. app: The app.
  69. root: root path of the project.
  70. port: port of the app.
  71. loglevel: The log level to use.
  72. """
  73. # validate bun version
  74. prerequisites.validate_and_install_bun(initialize=False)
  75. # Set up the frontend.
  76. setup_frontend(root)
  77. # Start watching asset folder.
  78. start_watching_assets_folder(root)
  79. # Run the frontend in development mode.
  80. console.rule("[bold green]App Running")
  81. os.environ["PORT"] = get_config().port if port is None else port
  82. run_process_and_launch_url(
  83. [prerequisites.get_package_manager(), "run", "dev"], root, loglevel
  84. )
  85. def run_frontend_prod(
  86. app: App,
  87. root: Path,
  88. port: str,
  89. loglevel: constants.LogLevel = constants.LogLevel.ERROR,
  90. ):
  91. """Run the frontend.
  92. Args:
  93. app: The app.
  94. root: root path of the project.
  95. port: port of the app.
  96. loglevel: The log level to use.
  97. """
  98. # Set up the frontend.
  99. setup_frontend(root)
  100. # Export the app.
  101. export_app(app, loglevel=loglevel)
  102. # Set the port.
  103. os.environ["PORT"] = get_config().port if port is None else port
  104. # Run the frontend in production mode.
  105. console.rule("[bold green]App Running")
  106. run_process_and_launch_url(
  107. [prerequisites.get_package_manager(), "run", "prod"], root, loglevel
  108. )
  109. def run_backend(
  110. app_name: str,
  111. host: str,
  112. port: int,
  113. loglevel: constants.LogLevel = constants.LogLevel.ERROR,
  114. ):
  115. """Run the backend.
  116. Args:
  117. host: The app host
  118. app_name: The app name.
  119. port: The app port
  120. loglevel: The log level.
  121. """
  122. setup_backend()
  123. cmd = [
  124. "uvicorn",
  125. f"{app_name}:{constants.APP_VAR}.{constants.API_VAR}",
  126. "--host",
  127. host,
  128. "--port",
  129. str(port),
  130. "--log-level",
  131. loglevel,
  132. "--reload",
  133. ]
  134. process = subprocess.Popen(cmd)
  135. try:
  136. process.wait()
  137. except KeyboardInterrupt:
  138. process.terminate()
  139. def run_backend_prod(
  140. app_name: str,
  141. host: str,
  142. port: int,
  143. loglevel: constants.LogLevel = constants.LogLevel.ERROR,
  144. ):
  145. """Run the backend.
  146. Args:
  147. host: The app host
  148. app_name: The app name.
  149. port: The app port
  150. loglevel: The log level.
  151. """
  152. setup_backend()
  153. num_workers = processes.get_num_workers()
  154. command = (
  155. [
  156. *constants.RUN_BACKEND_PROD_WINDOWS,
  157. "--host",
  158. host,
  159. "--port",
  160. str(port),
  161. f"{app_name}:{constants.APP_VAR}",
  162. ]
  163. if platform.system() == "Windows"
  164. else [
  165. *constants.RUN_BACKEND_PROD,
  166. "--bind",
  167. f"{host}:{port}",
  168. "--threads",
  169. str(num_workers),
  170. f"{app_name}:{constants.APP_VAR}()",
  171. ]
  172. )
  173. command += [
  174. "--log-level",
  175. loglevel.value,
  176. "--workers",
  177. str(num_workers),
  178. ]
  179. subprocess.run(command)