"""Application entry logic — invoked by both `python -m app` and the bundled .exe. Lives as a regular module (not __main__.py) so it can be imported with absolute imports from a top-level entry script — PyInstaller doesn't preserve package context for the entry script. """ from __future__ import annotations import logging import sys from app.config import Config from app import paths as app_paths from app.ui import App def _setup_logging() -> None: try: handler: logging.Handler = logging.FileHandler( app_paths.log_file_path(), encoding="utf-8" ) except Exception: handler = logging.StreamHandler(sys.stderr) logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s", handlers=[handler], ) def main() -> int: _setup_logging() log = logging.getLogger(__name__) try: cfg = Config.load() except FileNotFoundError as e: log.error("Config not found: %s", e) print("config.json not found. Put one next to the .exe.", file=sys.stderr) return 2 except Exception as e: log.exception("Config load failed") print(f"Failed to load config.json: {e}", file=sys.stderr) return 2 log.info("Starting app — csv=%s", cfg.resolved_csv_path) app = App(cfg) app.mainloop() return 0