Insight_UX_1.0 / packaging /InsightUX.spec
Sanyam0385
Add browser chrome, packaging pipeline, and fix UTF-8 crash in calibration
1b83c9c
Raw
History Blame Contribute Delete
5.34 kB
# -*- mode: python ; coding: utf-8 -*-
#
# InsightUX PyInstaller build spec β€” onedir, not onefile.
#
# Onefile re-extracts the whole bundle (torch alone is ~1.2GB) to a temp
# directory on EVERY launch, which is a bad startup-time tradeoff on top of
# an already-heavy ML stack. Onedir installs once as a persistent folder
# (InsightUX.exe + _internal/), which is also just what installer.iss wants
# to package anyway.
#
# Build (from the repo root, inside the project venv β€” this project's venv
# uses Python 3.10, which is the proven-working interpreter; PyInstaller
# must be installed into that same venv first: `pip install pyinstaller`):
# pyinstaller packaging/InsightUX.spec --noconfirm
#
# Output lands in dist/InsightUX/ β€” that whole folder is what
# packaging/installer.iss packages into the installer.
#
# Known risk areas likely to need iteration on the FIRST real build (this is
# normal for this dependency stack, not a sign anything here is wrong):
# - mediapipe ships its own .tflite/.binarypb data files as package data;
# collect_all() below should catch them, but if face_mesh fails to load
# at runtime with a "file not found", that's the first place to check.
# - onnxruntime/torch/torchvision ship native DLLs; "DLL load failed" at
# startup means collect_all() missed one β€” check dist/InsightUX/_internal
# for what's actually present vs. what the traceback wants.
# - pywebview + pythonnet's `clr` bridge needs the Microsoft Edge WebView2
# Runtime on the TARGET machine. Usually preinstalled on modern Windows
# 10/11, but installer.iss bundles + silently runs Microsoft's WebView2
# bootstrapper as a safety net for older/locked-down machines.
import os
import dis
from PyInstaller.utils.hooks import collect_all
import PyInstaller.lib.modulegraph.util as _mg_util
# A handful of very large single-file modules in this dependency tree
# (bottle.py, matplotlib/pyplot.py, and possibly others not yet hit) trip a
# real dis.get_instructions() bug on this Python 3.10.0 build β€” an IndexError
# walking co_consts for certain EXTENDED_ARG-heavy bytecode in big modules.
# Confirmed this is purely a build-time INTROSPECTION bug, not a real problem:
# `import bottle` / `import matplotlib.pyplot` both work completely normally
# at actual runtime; only PyInstaller's static bytecode scan of them (used to
# discover further hidden imports) crashes.
#
# Patched once here instead of excluding modules one at a time as each is
# discovered (there's no way to know in advance how many more of these exist
# inside torch's ~1.2GB of dependencies). Worst case if this ever masks a
# real import PyInstaller would otherwise have auto-discovered: the
# hiddenimports/collect_all() list below already covers every package this
# app actually needs bundled, so a skipped scan on one giant module isn't
# depended on for correctness.
_orig_iterate_instructions = _mg_util.iterate_instructions
def _safe_iterate_instructions(code_object):
try:
yield from _orig_iterate_instructions(code_object)
except Exception as e:
print(f"[InsightUX.spec] bytecode scan skipped on a module "
f"(dis bug workaround): {type(e).__name__}: {e}")
return
_mg_util.iterate_instructions = _safe_iterate_instructions
PACKAGING_DIR = os.path.abspath(os.path.dirname(os.path.abspath(SPEC)))
REPO_ROOT = os.path.dirname(PACKAGING_DIR)
datas = [
(os.path.join(REPO_ROOT, "models", "gaze_cnn_v4.onnx"), "models"),
(os.path.join(REPO_ROOT, "models", "gaze_cnn_v4.onnx.data"), "models"),
(os.path.join(REPO_ROOT, "checkpoints", "best_model_v4.pt"), "checkpoints"),
]
binaries = []
# models.model_v4 is only reached via a try/except-guarded dynamic import
# inside calibrate.py's fine-tune function β€” spell it out explicitly rather
# than trust static analysis to follow it through the try/except.
hiddenimports = ["models.model_v4"]
# The blunt-but-reliable safety net for packages that ship native binaries
# and/or non-.py package data PyInstaller's built-in hooks don't fully catch.
for pkg in ("mediapipe", "onnxruntime", "torch", "torchvision", "cv2"):
pkg_datas, pkg_binaries, pkg_hiddenimports = collect_all(pkg)
datas += pkg_datas
binaries += pkg_binaries
hiddenimports += pkg_hiddenimports
a = Analysis(
[os.path.join(REPO_ROOT, "browser_session.py")],
pathex=[REPO_ROOT],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name="InsightUX",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False,
# Keep the console. calibrate.py's live lighting/blink/quality readouts
# and the correlation "VERDICT" summary are the primary calibration
# feedback today β€” hiding the console would silently drop the one thing
# a user needs to read after every calibration run.
console=True,
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=False,
upx_exclude=[],
name="InsightUX",
)