exec.py 4.7 KB

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