Wrap startup in try/except with file log + Tk messagebox

--noconsole exes silently exit on import-time errors, so when v0.3.2
crashed during startup the user just saw nothing happen. This wrapper:
- Catches BaseException at module-import and main() level
- Writes traceback to %LOCALAPPDATA%/sephiria_inv/startup.log
- Pops a Tk messagebox-equivalent window with the traceback
- Falls back gracefully if Tk itself is unavailable

Also build with --console (no --noconsole) so prints/tracebacks are
visible in real time. Once we know what is failing we can re-enable
windowed mode.
This commit is contained in:
Claude
2026-05-16 02:38:51 +09:00
parent 1f2024e85f
commit a70499edfa

80
run.py
View File

@@ -1,6 +1,80 @@
"""PyInstaller entry shim.""" """PyInstaller entry shim.
Wrapped in a top-level try/except so any startup failure is written to
%LOCALAPPDATA%\\sephiria_inv\\startup.log and surfaced to the user via a
Tk messagebox when possible. Without this wrapper, --noconsole builds
exit silently on import-time errors and the user sees "nothing happens".
"""
from __future__ import annotations
import os
import sys
import traceback
from datetime import datetime
def _log_path() -> str:
base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~")
folder = os.path.join(base, "sephiria_inv")
try:
os.makedirs(folder, exist_ok=True)
except OSError:
folder = os.path.dirname(os.path.abspath(sys.argv[0])) or "."
return os.path.join(folder, "startup.log")
def _write_log(msg: str) -> str:
path = _log_path()
try:
with open(path, "a", encoding="utf-8") as fh:
fh.write(f"\n=== {datetime.now().isoformat()} ===\n{msg}\n")
except OSError:
pass
return path
def _show_error(title: str, body: str) -> None:
try:
import tkinter as tk
from tkinter import scrolledtext
root = tk.Tk()
root.title(title)
root.geometry("760x420")
txt = scrolledtext.ScrolledText(root, wrap="word")
txt.pack(fill="both", expand=True)
txt.insert("1.0", body)
tk.Button(root, text="Close", command=root.destroy).pack(pady=4)
root.mainloop()
except Exception:
# Tk itself is broken; nothing we can show.
pass
def _main() -> int:
try:
from sephiria_inv.__main__ import main
except BaseException:
tb = traceback.format_exc()
log = _write_log("IMPORT FAIL\n" + tb)
_show_error(
"sephiria_inv: import failed",
f"Failed to import sephiria_inv.__main__\n\nLog: {log}\n\n{tb}",
)
return 11
try:
return int(main() or 0)
except SystemExit:
raise
except BaseException:
tb = traceback.format_exc()
log = _write_log("RUNTIME FAIL\n" + tb)
_show_error(
"sephiria_inv: runtime error",
f"Crashed during main()\n\nLog: {log}\n\n{tb}",
)
return 12
from sephiria_inv.__main__ import main
if __name__ == "__main__": if __name__ == "__main__":
raise SystemExit(main()) raise SystemExit(_main())