dev.py 4.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. """
  2. dev.py
  3. ======
  4. This script acts as a local development orchestrator. Instead of recreating the frontend
  5. in Python, which would violate the "Don't Repeat Yourself" (DRY) principle and introduce
  6. massive technical debt, this script leverages the existing architecture.
  7. It performs four main actions sequentially:
  8. 1. Executes `roi.py` to fetch the latest market data and generate the frontend JSON files.
  9. 2. Executes `bun install` to ensure frontend JS dependencies are present.
  10. 3. Executes `bun run build` to compile the TypeScript frontend into browser-ready JavaScript.
  11. 4. Spawns a local Python HTTP server pointing to the `www/` directory.
  12. """
  13. import os # Required for changing working directories and checking paths.
  14. import subprocess # Required for spawning external processes (like 'bun' and other python scripts).
  15. import sys # Required to grab the current Python executable path.
  16. import http.server # Required to run the local web server.
  17. import socketserver # Required to bind the HTTP handler to a specific port.
  18. def main() -> None:
  19. """
  20. Main execution function for the local development server orchestrator.
  21. """
  22. # -------------------------------------------------------------------------
  23. # STEP 1: GENERATE BACKEND DATA
  24. # -------------------------------------------------------------------------
  25. print(">>> [1/4] Running roi.py to generate updated JSON data...")
  26. try:
  27. subprocess.run([sys.executable, "roi.py"], check=True)
  28. except subprocess.CalledProcessError as e:
  29. sys.exit(f"!!! Error: Failed to execute roi.py. Process exited with code {e.returncode}")
  30. # -------------------------------------------------------------------------
  31. # STEP 2: INSTALL FRONTEND DEPENDENCIES
  32. # -------------------------------------------------------------------------
  33. print(">>> [2/4] Installing frontend dependencies via Bun...")
  34. # EXTREME DETAIL: We create a copy of the current OS environment variables.
  35. # We then inject 'CI' = '1'. CI stands for Continuous Integration.
  36. # Most modern JS tools (like Bun, NPM, Yarn) look for this variable. If they see it,
  37. # they disable interactive terminal animations (like progress bars and emojis).
  38. # This prevents the child process from messing with the terminal driver and throwing
  39. # phantom SIGINT signals (KeyboardInterrupts) back to our Python script.
  40. env = os.environ.copy()
  41. env["CI"] = "1"
  42. try:
  43. # EXTREME DETAIL: stdin=subprocess.DEVNULL explicitly cuts the child process off
  44. # from reading keyboard input, further preventing terminal driver deadlocks.
  45. subprocess.run(["bun", "install"], check=True, env=env, stdin=subprocess.DEVNULL)
  46. except FileNotFoundError:
  47. sys.exit("!!! Error: 'bun' command not found. Please ensure Bun is installed (https://bun.sh/) and in your PATH.")
  48. except subprocess.CalledProcessError as e:
  49. sys.exit(f"!!! Error: Frontend dependency installation failed. Bun exited with code {e.returncode}")
  50. # -------------------------------------------------------------------------
  51. # STEP 3: BUILD THE FRONTEND
  52. # -------------------------------------------------------------------------
  53. print(">>> [3/4] Building the TypeScript frontend using Bun...")
  54. try:
  55. # We apply the same environment and stdin constraints to the build step for safety.
  56. subprocess.run(["bun", "run", "build"], check=True, env=env, stdin=subprocess.DEVNULL)
  57. except subprocess.CalledProcessError as e:
  58. sys.exit(f"!!! Error: Frontend build failed. Bun exited with code {e.returncode}")
  59. # -------------------------------------------------------------------------
  60. # STEP 4: SERVE THE FRONTEND LOCALLY
  61. # -------------------------------------------------------------------------
  62. print(">>> [4/4] Starting local development server...")
  63. PORT = 8000
  64. WEB_DIR = "www"
  65. if not os.path.isdir(WEB_DIR):
  66. sys.exit(f"!!! Error: Directory '{WEB_DIR}' does not exist. The build step may have failed silently.")
  67. os.chdir(WEB_DIR)
  68. Handler = http.server.SimpleHTTPRequestHandler
  69. with socketserver.TCPServer(("", PORT), Handler) as httpd:
  70. print(f">>> Serving at http://localhost:{PORT}")
  71. print(">>> Press Ctrl+C to stop the server.")
  72. httpd.serve_forever()
  73. if __name__ == '__main__':
  74. try:
  75. main()
  76. except KeyboardInterrupt:
  77. sys.exit("\n>>> Process interrupted by user. Exiting gracefully. Goodbye!")