v0.3.3 wrapper only logged once it reached _main(). User reports CMD window flashes shut and no log file created — meaning Python likely never reached our code. Two fixes: 1. Move first log write to module top (before any project import) and write to BOTH the exe directory AND %LOCALAPPDATA%/sephiria_inv/. Either log existing proves Python booted; neither existing means PyInstaller bootloader itself failed. 2. Add run-debug.bat that runs the exe with stdout/stderr captured to sephiria_inv_console.log and pauses, so the window does not close before the user can read it.
117 lines
3.4 KiB
Python
117 lines
3.4 KiB
Python
"""PyInstaller entry shim with aggressive startup diagnostics.
|
|
|
|
Writes a log entry the moment this module is loaded (before any project
|
|
imports). Logs to two locations:
|
|
1. Next to the exe (sys.executable directory) -- usually the user's
|
|
Downloads folder; trivially findable.
|
|
2. %LOCALAPPDATA%/sephiria_inv/startup.log
|
|
|
|
Either log existing tells us Python started. Neither existing means the
|
|
PyInstaller bootloader died before Python ran (DLL/runtime issue).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
import traceback
|
|
from datetime import datetime
|
|
|
|
|
|
def _candidate_log_paths():
|
|
paths = []
|
|
# 1. Next to the exe (most discoverable for the user)
|
|
try:
|
|
exe_dir = os.path.dirname(os.path.abspath(sys.executable))
|
|
if exe_dir:
|
|
paths.append(os.path.join(exe_dir, "sephiria_inv_startup.log"))
|
|
except Exception:
|
|
pass
|
|
# 2. LOCALAPPDATA / home
|
|
try:
|
|
base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~")
|
|
folder = os.path.join(base, "sephiria_inv")
|
|
os.makedirs(folder, exist_ok=True)
|
|
paths.append(os.path.join(folder, "startup.log"))
|
|
except Exception:
|
|
pass
|
|
return paths
|
|
|
|
|
|
def _write_log(msg: str) -> list:
|
|
written = []
|
|
stamp = datetime.now().isoformat()
|
|
for path in _candidate_log_paths():
|
|
try:
|
|
with open(path, "a", encoding="utf-8") as fh:
|
|
fh.write(f"\n=== {stamp} ===\n{msg}\n")
|
|
written.append(path)
|
|
except Exception:
|
|
pass
|
|
return written
|
|
|
|
|
|
def _env_dump() -> str:
|
|
lines = [
|
|
f"python: {sys.version}",
|
|
f"executable: {sys.executable}",
|
|
f"argv: {sys.argv}",
|
|
f"frozen: {getattr(sys, 'frozen', False)}",
|
|
f"_MEIPASS: {getattr(sys, '_MEIPASS', '<none>')}",
|
|
f"cwd: {os.getcwd()}",
|
|
f"PATH head: {os.environ.get('PATH', '')[:200]}",
|
|
f"LOCALAPPDATA: {os.environ.get('LOCALAPPDATA', '<none>')}",
|
|
]
|
|
return "\n".join(lines)
|
|
|
|
|
|
# === MODULE-IMPORT BREADCRUMB ===
|
|
# This runs the moment Python loads the entry script. If this never appears
|
|
# in either log, the PyInstaller bootloader failed before Python started.
|
|
_BOOT_LOGS = _write_log("BOOT: run.py loaded\n" + _env_dump())
|
|
|
|
|
|
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("780x460")
|
|
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:
|
|
pass
|
|
|
|
|
|
def _main() -> int:
|
|
try:
|
|
from sephiria_inv.__main__ import main
|
|
except BaseException:
|
|
tb = traceback.format_exc()
|
|
logs = _write_log("IMPORT FAIL\n" + tb + "\n--env--\n" + _env_dump())
|
|
_show_error(
|
|
"sephiria_inv: import failed",
|
|
f"Failed to import sephiria_inv.__main__\n\nLogs: {logs}\n\n{tb}",
|
|
)
|
|
return 11
|
|
try:
|
|
return int(main() or 0)
|
|
except SystemExit:
|
|
raise
|
|
except BaseException:
|
|
tb = traceback.format_exc()
|
|
logs = _write_log("RUNTIME FAIL\n" + tb)
|
|
_show_error(
|
|
"sephiria_inv: runtime error",
|
|
f"Crashed during main()\n\nLogs: {logs}\n\n{tb}",
|
|
)
|
|
return 12
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(_main())
|