| |
| """ |
| LandPPT Application Runner |
| |
| This script starts the LandPPT FastAPI application with proper configuration. |
| """ |
|
|
| import uvicorn |
| import sys |
| import os |
| import inspect |
| import threading |
| import time |
| import webbrowser |
| import socket |
| import traceback |
| from pathlib import Path |
| from dotenv import load_dotenv |
|
|
| |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src')) |
|
|
| |
| try: |
| load_dotenv() |
| except PermissionError as e: |
| print(f"Warning: Could not load .env file due to permission error: {e}") |
| print("Continuing with system environment variables...") |
| except Exception as e: |
| print(f"Warning: Could not load .env file: {e}") |
| print("Continuing with system environment variables...") |
|
|
| def _log_startup_error(exc: BaseException) -> None: |
| """Write startup errors to local log files for packaged runs.""" |
| log_paths = [] |
| try: |
| log_paths.append(Path.cwd() / "landppt_startup_error.log") |
| except Exception: |
| pass |
| try: |
| log_paths.append(Path(__file__).resolve().with_name("landppt_startup_error.log")) |
| except Exception: |
| pass |
| if getattr(sys, "frozen", False): |
| try: |
| log_paths.append(Path(sys.executable).resolve().with_name("landppt_startup_error.log")) |
| except Exception: |
| pass |
|
|
| seen = set() |
| for log_path in log_paths: |
| if not log_path or str(log_path) in seen: |
| continue |
| seen.add(str(log_path)) |
| try: |
| with log_path.open("a", encoding="utf-8") as f: |
| f.write("\n" + "=" * 80 + "\n") |
| f.write(time.strftime("%Y-%m-%d %H:%M:%S") + " Startup error\n") |
| f.write("=" * 80 + "\n") |
| f.write("Executable: " + str(getattr(sys, "executable", "")) + "\n") |
| f.write("CWD: " + os.getcwd() + "\n") |
| f.write("Frozen: " + str(getattr(sys, "frozen", False)) + "\n") |
| f.write("\n") |
| f.write("".join(traceback.format_exception(type(exc), exc, exc.__traceback__))) |
| f.write("\n") |
| except Exception: |
| pass |
|
|
|
|
| def _open_browser_later(url: str, host: str, port: int, timeout_seconds: float = 45.0) -> None: |
| """Open the LandPPT workspace only after the local server accepts connections.""" |
| def _open() -> None: |
| deadline = time.time() + timeout_seconds |
| while time.time() < deadline: |
| try: |
| with socket.create_connection((host, port), timeout=1.0): |
| webbrowser.open(url) |
| return |
| except OSError: |
| time.sleep(0.5) |
|
|
| print(f"Warning: Server did not become ready within {timeout_seconds:.0f}s; opening browser anyway.") |
| try: |
| webbrowser.open(url) |
| except Exception as e: |
| print(f"Warning: Could not open browser automatically: {e}") |
|
|
| threading.Thread(target=_open, daemon=True).start() |
|
|
|
|
| def main(): |
| """Main entry point for running the application""" |
|
|
| |
| host = os.getenv("HOST", "0.0.0.0") |
| port = int(os.getenv("PORT", "7860")) |
| |
| default_reload = "false" if getattr(sys, "frozen", False) else "true" |
| reload = os.getenv("RELOAD", default_reload).lower() in ("true", "1", "yes", "on") |
| log_level = os.getenv("LOG_LEVEL", "info").lower() |
| workers = int(os.getenv("WORKERS", "1")) |
|
|
| |
| |
| |
| try: |
| timeout_worker_healthcheck = int(os.getenv("UVICORN_TIMEOUT_WORKER_HEALTHCHECK", "60")) |
| except Exception: |
| timeout_worker_healthcheck = 60 |
| timeout_worker_healthcheck = max(5, timeout_worker_healthcheck) |
|
|
| |
| |
| if getattr(sys, "frozen", False): |
| reload = False |
| workers = 1 |
|
|
| |
| if workers > 1 and reload: |
| reload = False |
|
|
| |
| app_target = "landppt.main:app" |
| if getattr(sys, "frozen", False): |
| |
| |
| from landppt.main import app as app_target |
|
|
| config = { |
| "app": app_target, |
| "host": host, |
| "port": port, |
| "reload": reload, |
| "log_level": log_level, |
| "access_log": True, |
| } |
| if not getattr(sys, "frozen", False) and "timeout_worker_healthcheck" in inspect.signature(uvicorn.run).parameters: |
| config["timeout_worker_healthcheck"] = timeout_worker_healthcheck |
| if workers > 1: |
| config["workers"] = workers |
| |
| print("Starting LandPPT Server...") |
| print(f"Host: {config['host']}") |
| print(f"Port: {config['port']}") |
| print(f"Reload: {config['reload']}") |
| print(f"Log Level: {config['log_level']}") |
| print(f"Workers: {config.get('workers', 1)}") |
| browser_url = f"http://127.0.0.1:{config['port']}" |
| print(f"Server will be available at: {browser_url}") |
| print(f"Web Interface: {browser_url}/web") |
| print("=" * 60) |
|
|
| if os.getenv("LANDPPT_OPEN_BROWSER", "true").lower() in ("true", "1", "yes", "on"): |
| _open_browser_later(browser_url, "127.0.0.1", config['port']) |
|
|
| try: |
| if getattr(sys, "frozen", False): |
| server_config = uvicorn.Config( |
| app=config["app"], |
| host=config["host"], |
| port=config["port"], |
| log_level=config["log_level"], |
| access_log=config["access_log"], |
| ) |
| server = uvicorn.Server(server_config) |
| server.run() |
| else: |
| uvicorn.run(**config) |
| except KeyboardInterrupt: |
| print("\nServer stopped by user") |
| except BaseException as e: |
| if isinstance(e, KeyboardInterrupt): |
| raise |
| _log_startup_error(e) |
| print(f"Error starting server: {e}") |
| print("Detailed traceback has been written to landppt_startup_error.log") |
| if getattr(sys, "frozen", False) or os.getenv("LANDPPT_PAUSE_ON_ERROR", "false").lower() in ("true", "1", "yes", "on"): |
| try: |
| input("Press Enter to exit...") |
| except Exception: |
| pass |
| sys.exit(1) |
|
|
| if __name__ == "__main__": |
| main() |
|
|