Sfoglia il codice sorgente

Add_Dev_Server_Orchestrator

Added `dev.py` to root directory to act as a local development orchestrator.

        PURPOSE:
        To provide a streamlined, one-click entry point for testing frontend changes locally without needing to manually run the backend JSON generator (`roi.py`), manually compile the TypeScript via `bun run build`, and manually spin up the python http.server. This fulfills the need for local visual testing of the ROI backend logic while preserving the single source of truth for our UI, adhering to the "Law of Conservation of Logic" and preventing the accumulation of technical debt associated with maintaining a secondary, Python-based GUI testing tool.

        IMPLEMENTATION DETAILS:
        - Utilizes `subprocess.run` to execute `roi.py` and `bun run build`. Includes explicit error handling (`check=True` catching `CalledProcessError` and `FileNotFoundError`) to provide graceful exits and informative debugging messages if either dependency fails.
        - Changes the OS working directory to `www/` explicitly to ensure proper routing of static files.
        - Uses Python's native `http.server.SimpleHTTPRequestHandler` paired with `socketserver.TCPServer` to bind to localhost:8000.
        - Wraps the server loop in a `try/except KeyboardInterrupt` to catch user exit signals (Ctrl+C) to release the port socket appropriately.
        - Extremely dense, line-by-line documentation has been injected to explain the specific behavior of the `os`, `subprocess`, `http`, and `sys` modules to ensure approachability for newer contributors.
Thomas Knott 2 settimane fa
parent
commit
d6e45977e4
1 ha cambiato i file con 97 aggiunte e 0 eliminazioni
  1. 97 0
      dev.py

+ 97 - 0
dev.py

@@ -0,0 +1,97 @@
+"""
+dev.py
+======
+This script acts as a local development orchestrator. Instead of recreating the frontend
+in Python, which would violate the "Don't Repeat Yourself" (DRY) principle and introduce
+massive technical debt, this script leverages the existing architecture.
+
+It performs three main actions sequentially:
+1. Executes `roi.py` to fetch the latest market data and generate the frontend JSON files.
+2. Executes the `bun run build` command to compile the TypeScript frontend into browser-ready JavaScript.
+3. Spawns a local Python HTTP server pointing to the `www/` directory so you can view the changes in your browser.
+"""
+
+import os           # Required for changing working directories and checking paths.
+import subprocess   # Required for spawning external processes (like 'bun' and other python scripts).
+import sys          # Required to grab the current Python executable path.
+import http.server  # Required to run the local web server.
+import socketserver # Required to bind the HTTP handler to a specific port.
+
+def main() -> None:
+    """
+    Main execution function for the local development server orchestrator.
+    """
+    # -------------------------------------------------------------------------
+    # STEP 1: GENERATE BACKEND DATA
+    # -------------------------------------------------------------------------
+    # We first need to ensure that the JSON data files that the frontend consumes
+    # actually exist and are up to date. We do this by calling the existing `roi.py` script.
+    print(">>> [1/3] Running roi.py to generate updated JSON data...")
+    try:
+        # sys.executable ensures we use the exact same Python interpreter currently running this script.
+        # check=True ensures that if roi.py crashes, this script will also halt and raise a CalledProcessError.
+        subprocess.run([sys.executable, "roi.py"], check=True)
+    except subprocess.CalledProcessError as e:
+        # If roi.py fails, we catch the error, print a helpful message, and exit the program gracefully
+        # instead of spitting out a massive unhandled stack trace.
+        sys.exit(f"!!! Error: Failed to execute roi.py. Process exited with code {e.returncode}")
+
+    # -------------------------------------------------------------------------
+    # STEP 2: BUILD THE FRONTEND
+    # -------------------------------------------------------------------------
+    # The frontend is written in TypeScript (inside the ts/ folder). Browsers cannot natively
+    # execute TypeScript in the way this project is structured. As outlined in `package.json`,
+    # 'bun' is used as the bundler to compile these files into the 'www/' directory.
+    print(">>> [2/3] Building the TypeScript frontend using Bun...")
+    try:
+        # We invoke the 'build' script defined in package.json.
+        # This corresponds to: "bun build ts/buy.ts ... --outdir www --target browser --sourcemap=external"
+        subprocess.run(["bun", "run", "build"], check=True)
+    except FileNotFoundError:
+        # If 'bun' is not installed or not in the system's PATH, a FileNotFoundError is raised.
+        # We must alert the user that this system dependency is missing.
+        sys.exit("!!! Error: 'bun' command not found. Please ensure Bun is installed (https://bun.sh/) and in your PATH.")
+    except subprocess.CalledProcessError as e:
+        # Catch compilation errors in the TypeScript code itself.
+        sys.exit(f"!!! Error: Frontend build failed. Bun exited with code {e.returncode}")
+
+    # -------------------------------------------------------------------------
+    # STEP 3: SERVE THE FRONTEND LOCALLY
+    # -------------------------------------------------------------------------
+    # We must serve the compiled files via an HTTP server because opening local HTML files
+    # directly (via file:// protocol) often causes CORS (Cross-Origin Resource Sharing) errors
+    # when the JavaScript attempts to fetch the local JSON files generated in Step 1.
+    print(">>> [3/3] Starting local development server...")
+    
+    # Define the port we want to host the site on. 8000 is a standard web development port.
+    PORT = 8000
+    # Define the directory we want to serve. The bun build step outputs to 'www'.
+    WEB_DIR = "www"
+
+    # We must ensure the 'www' directory exists before attempting to switch into it.
+    if not os.path.isdir(WEB_DIR):
+        sys.exit(f"!!! Error: Directory '{WEB_DIR}' does not exist. The build step may have failed silently.")
+
+    # Change the current working directory to 'www'. This ensures the HTTP server roots itself here,
+    # making the index.html inside 'www' the default page, and allowing relative path resolutions to work.
+    os.chdir(WEB_DIR)
+
+    # http.server.SimpleHTTPRequestHandler is a built-in handler that serves files from the current directory.
+    Handler = http.server.SimpleHTTPRequestHandler
+
+    # socketserver.TCPServer creates a TCP server that listens on the specified port and binds our handler to it.
+    # We use a 'with' block to ensure the network socket is properly released and cleaned up when the server stops.
+    with socketserver.TCPServer(("", PORT), Handler) as httpd:
+        print(f">>> Serving at http://localhost:{PORT}")
+        print(">>> Press Ctrl+C to stop the server.")
+        try:
+            # serve_forever() puts the script into an infinite loop, constantly listening for and fulfilling HTTP requests.
+            httpd.serve_forever()
+        except KeyboardInterrupt:
+            # Catch the Ctrl+C command from the user to gracefully shut down the server.
+            print("\n>>> Shutting down development server gracefully. Goodbye!")
+
+if __name__ == '__main__':
+    # This standard Python idiom ensures that the main() function is only called if this script
+    # is executed directly from the command line (e.g., `python dev.py`), rather than imported as a module.
+    main()