Skip to content

Commit 3f674dc

Browse files
committed
deployment debug
1 parent b9cf8f4 commit 3f674dc

File tree

5 files changed

+85
-12
lines changed

5 files changed

+85
-12
lines changed

picasso_workflow/_launcher.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""Entry point wrapper for the picasso-workflow GUI.
2+
3+
Using pythonw / a windowless executable means that any uncaught exception
4+
before the Qt event loop starts is completely invisible — no terminal output,
5+
no dialog. This module wraps the real startup so that:
6+
7+
1. A crash log is always written to ~/picasso-workflow-crash.log.
8+
2. A Qt error dialog is shown if Qt can be imported at all.
9+
10+
The gui-scripts entry point in pyproject.toml points here instead of
11+
directly to picasso_workflow.gui:main.
12+
"""
13+
14+
15+
def main() -> None:
16+
import sys
17+
from pathlib import Path
18+
19+
crash_log = Path.home() / "picasso-workflow-crash.log"
20+
21+
try:
22+
from picasso_workflow.gui import main as _gui_main
23+
24+
_gui_main()
25+
except SystemExit:
26+
raise
27+
except Exception:
28+
import traceback
29+
30+
msg = traceback.format_exc()
31+
32+
# Always write to a location that is writable regardless of cwd.
33+
try:
34+
crash_log.write_text(msg, encoding="utf-8")
35+
except Exception:
36+
pass
37+
38+
# Best-effort Qt dialog so the user sees something on screen.
39+
try:
40+
from PyQt5 import QtWidgets
41+
42+
_app = QtWidgets.QApplication.instance()
43+
if _app is None:
44+
_app = QtWidgets.QApplication(sys.argv)
45+
QtWidgets.QMessageBox.critical(
46+
None,
47+
"picasso-workflow failed to start",
48+
f"{msg}\n\nFull crash log:\n{crash_log}",
49+
)
50+
except Exception:
51+
pass
52+
53+
sys.exit(1)

picasso_workflow/gui.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11713,20 +11713,36 @@ def on_module_changed(self, text):
1171311713

1171411714
def main():
1171511715
app = QtWidgets.QApplication(sys.argv)
11716-
window = Window()
11717-
window.show()
1171811716

11719-
def excepthook(type, value, tback):
11720-
lib.cancel_dialogs()
11721-
QtCore.QCoreApplication.instance().processEvents()
11722-
message = "".join(traceback.format_exception(type, value, tback))
11723-
errorbox = QtWidgets.QMessageBox.critical(
11724-
window, "An error occured", message
11717+
# Keep a reference so the excepthook closure can use it even before
11718+
# Window() returns. It is None only if construction itself raises.
11719+
window = None
11720+
11721+
def excepthook(exc_type, exc_value, exc_tb):
11722+
message = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
11723+
try:
11724+
lib.cancel_dialogs()
11725+
QtCore.QCoreApplication.instance().processEvents()
11726+
except Exception:
11727+
pass
11728+
# QMessageBox.critical already shows the dialog and returns the
11729+
# clicked button (an int) — do not call .exec_() on the return value.
11730+
QtWidgets.QMessageBox.critical(
11731+
window, "An error occurred", message
1172511732
)
11726-
errorbox.exec_()
11727-
sys.__excepthook__(type, value, tback)
11733+
sys.__excepthook__(exc_type, exc_value, exc_tb)
1172811734

11735+
# Install before creating Window so construction errors are caught.
1172911736
sys.excepthook = excepthook
11737+
11738+
try:
11739+
window = Window()
11740+
except Exception:
11741+
message = traceback.format_exc()
11742+
QtWidgets.QMessageBox.critical(None, "Startup error", message)
11743+
sys.exit(1)
11744+
11745+
window.show()
1173011746
sys.exit(app.exec_())
1173111747

1173211748

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,14 @@ dependencies = [
3232
"memory-profiler>=0.61.0",
3333
"loguru>=0.7.3",
3434
"h5py",
35+
"memory_profiler",
3536
# "mpi4py",
3637
# "openmpi"
3738
]
3839
keywords = ["picasso"]
3940

4041
[project.gui-scripts]
41-
picasso-workflow-gui = "picasso_workflow.gui:main"
42+
picasso-workflow-gui = "picasso_workflow._launcher:main"
4243

4344
[project.optional-dependencies]
4445
cluster = [

tools/deploy_gui_mac.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ mkdir -p "$APP_BUNDLE/Contents/Resources"
6262
# -- Launcher script (runs inside the bundle, no shell environment assumed) --
6363
cat > "$APP_BUNDLE/Contents/MacOS/$APP_NAME" <<LAUNCHER
6464
#!/usr/bin/env bash
65+
# Use the user's home directory as working directory so that relative paths
66+
# (e.g. the logs/ directory created by config_logger) land somewhere writable.
67+
cd "\$HOME"
6568
source "${CONDA_SH}"
6669
conda activate "${CONDA_ENV_PATH}"
6770
exec "${EXEC_PATH}" "\$@"

tools/deploy_gui_shortcut.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ $Shell = New-Object -ComObject WScript.Shell
124124
$Shortcut = $Shell.CreateShortcut($ShortcutPath)
125125

126126
$Shortcut.TargetPath = $ExePath
127-
$Shortcut.WorkingDirectory = $CondaEnvPath
127+
$Shortcut.WorkingDirectory = $env:USERPROFILE
128128
$Shortcut.Description = "picasso-workflow GUI"
129129
$Shortcut.IconLocation = "$IconPath,0"
130130

0 commit comments

Comments
 (0)