""" Stock Screener Launcher ======================= Startup sequence: 1. Generate HWID 2. Load stored license key — show activation dialog if none found 3. Verify license against Supabase (with 30-min offline grace period) 4. Silently check for and apply updates 5. Load and run screener_gui.py Source file hygiene ------------------- On every launch, the launcher scans the parent folder of the exe for stray copies of screener_gui.py or stock_screener.py and removes them (pulling in any newer version first so no edits are lost). """ import importlib.util import os import shutil import sys import tkinter as tk from tkinter import messagebox # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _exe_dir() -> str: if getattr(sys, "frozen", False): return os.path.dirname(sys.executable) return os.path.dirname(os.path.abspath(__file__)) _SOURCE_FILES = ("screener_gui.py", "stock_screener.py") def _sync_source_files(exe_dir: str) -> None: parent_dir = os.path.dirname(exe_dir) if parent_dir == exe_dir: return for fname in _SOURCE_FILES: stray = os.path.join(parent_dir, fname) if not os.path.exists(stray): continue live = os.path.join(exe_dir, fname) if not os.path.exists(live) or os.path.getmtime(stray) > os.path.getmtime(live): try: shutil.copy2(stray, live) except OSError: pass try: os.remove(stray) except OSError: pass def _fatal(title: str, message: str) -> None: """Show an error dialog and exit.""" root = tk.Tk() root.withdraw() messagebox.showerror(title, message) root.destroy() sys.exit(1) # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- def main(): exe_dir = _exe_dir() _sync_source_files(exe_dir) # ------------------------------------------------------------------ # Step 1: HWID # ------------------------------------------------------------------ try: from hwid import get_hwid hwid = get_hwid() except Exception as exc: _fatal("Ultimate Investment Tool", f"Could not generate hardware ID:\n\n{exc}") return # unreachable, but satisfies linters # ------------------------------------------------------------------ # Step 2: Load or activate license # ------------------------------------------------------------------ import license_check license_key = license_check.load_license_key(hwid) if license_key is None: # First launch — show activation dialog import activation_dialog activated = activation_dialog.run_activation() if not activated: sys.exit(0) # user closed the window # Reload after successful save license_key = license_check.load_license_key(hwid) if license_key is None: _fatal("Ultimate Investment Tool", "Activation failed. Please restart and try again.") return # ------------------------------------------------------------------ # Step 3: Verify license on every launch # ------------------------------------------------------------------ valid, reason = license_check.verify_license(license_key, hwid) if not valid: # License invalid — clear stored key so they can re-enter on next launch license_check.delete_license_key() _fatal( "Ultimate Investment Tool — License Error", f"{reason}\n\nThe application will now close." ) return # ------------------------------------------------------------------ # Step 4: Silent update check # ------------------------------------------------------------------ try: import updater updater.check_and_apply_update() # If an update was applied, updater restarts the process — we never reach here. except Exception: pass # never block the app over a failed update check # ------------------------------------------------------------------ # Step 5: Load screener_gui.py # ------------------------------------------------------------------ script = os.path.join(exe_dir, "screener_gui.py") if os.path.exists(script): if exe_dir not in sys.path: sys.path.insert(0, exe_dir) spec = importlib.util.spec_from_file_location("screener_gui_live", script) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) mod.main() return # Fallback: frozen version bundled inside the exe try: import screener_gui screener_gui.main() except Exception as exc: _fatal( "Ultimate Investment Tool", f"Could not start.\n\n{exc}\n\n" "Make sure screener_gui.py is in the same folder as this exe." ) if __name__ == "__main__": main()