Diagnostic v2: log on module import, dual log paths, ship debug.bat

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.
This commit is contained in:
Claude
2026-05-16 02:58:23 +09:00
parent a70499edfa
commit 915b5c9f45
2 changed files with 85 additions and 27 deletions

90
run.py
View File

@@ -1,9 +1,13 @@
"""PyInstaller entry shim.
"""PyInstaller entry shim with aggressive startup diagnostics.
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".
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
@@ -14,24 +18,57 @@ 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")
def _candidate_log_paths():
paths = []
# 1. Next to the exe (most discoverable for the user)
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:
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
return path
# 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:
@@ -40,14 +77,13 @@ def _show_error(title: str, body: str) -> None:
from tkinter import scrolledtext
root = tk.Tk()
root.title(title)
root.geometry("760x420")
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:
# Tk itself is broken; nothing we can show.
pass
@@ -56,10 +92,10 @@ def _main() -> int:
from sephiria_inv.__main__ import main
except BaseException:
tb = traceback.format_exc()
log = _write_log("IMPORT FAIL\n" + tb)
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\nLog: {log}\n\n{tb}",
f"Failed to import sephiria_inv.__main__\n\nLogs: {logs}\n\n{tb}",
)
return 11
try:
@@ -68,10 +104,10 @@ def _main() -> int:
raise
except BaseException:
tb = traceback.format_exc()
log = _write_log("RUNTIME FAIL\n" + tb)
logs = _write_log("RUNTIME FAIL\n" + tb)
_show_error(
"sephiria_inv: runtime error",
f"Crashed during main()\n\nLog: {log}\n\n{tb}",
f"Crashed during main()\n\nLogs: {logs}\n\n{tb}",
)
return 12