-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfrozen_prompt_entry.py
More file actions
177 lines (153 loc) · 6.76 KB
/
Copy pathfrozen_prompt_entry.py
File metadata and controls
177 lines (153 loc) · 6.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#!/usr/bin/env python3
"""Frozen Prompt executable entrypoint.
PyInstaller/Nuitka --windowed builds can fail before a console is visible. This
small bootstrap runs before prompt_app imports, resolves a durable project log
root, and forces every frozen startup crash into root debug.log/run.log/errors.txt.
"""
from __future__ import annotations
import datetime
import os
from pathlib import Path
import sys
import traceback
def warnBootstrapException(error: BaseException, context: str) -> None:
"""Best-effort frozen-entry fault surface before DebugLog imports."""
try:
stamp = datetime.datetime.now().isoformat(sep=' ', timespec='microseconds')
line = f'{stamp} [WARN:FROZEN-ENTRY] pid={os.getpid()} context={context} {type(error).__name__}: {error}'
try:
sys.__stderr__.write(line + '\n')
sys.__stderr__.flush()
except Exception: # swallow-ok: stderr may not exist in --windowed frozen mode.
pass
for base in (Path.cwd(), Path(getattr(sys, 'executable', '') or '.').resolve().parent):
try:
with open(base / 'debug.log', 'a', encoding='utf-8', errors='replace') as handle:
handle.write(line + '\n')
except Exception: # swallow-ok: final fallback logging path may be read-only.
pass
except Exception: # swallow-ok: emergency fault surface must never crash startup.
pass
def _safe_resolve(path: object) -> Path | None:
try:
if not path:
return None
return Path(str(path)).expanduser().resolve()
except Exception as error:
warnBootstrapException(error, 'safe-resolve-primary')
try:
return Path(str(path)).expanduser().absolute()
except Exception as fallback_error:
warnBootstrapException(fallback_error, 'safe-resolve-fallback')
return None
def _candidate_roots() -> list[Path]:
candidates: list[Path] = []
def add(value: object) -> None:
path = _safe_resolve(value)
if path is None:
return
if path.is_file():
path = path.parent
if path not in candidates:
candidates.append(path)
for key in ('PROMPT_DEBUG_ROOT', 'PROMPT_RUN_LOG_ROOT', 'PROMPT_ROOT', 'PROMPT_RELEASE_ROOT'):
add(os.environ.get(key, ''))
add(getattr(sys, 'executable', ''))
if sys.argv:
add(sys.argv[0])
try:
add(Path.cwd())
except Exception as error:
warnBootstrapException(error, 'candidate-roots-cwd')
expanded: list[Path] = []
for path in candidates:
for item in (path, path.parent if path.name.lower() in {'dist', 'build'} else path):
if item not in expanded:
expanded.append(item)
return expanded
def _looks_like_prompt_root(path: Path) -> bool:
try:
score = 0
if (path / 'start.py').exists():
score += 5
if (path / 'prompt_app.py').exists():
score += 5
if (path / 'config.ini').exists():
score += 1
if (path / 'workflows').exists():
score += 1
if (path / 'doctypes').exists():
score += 1
if (path / 'assets').exists():
score += 1
return score >= 2
except Exception as error:
warnBootstrapException(error, 'looks-like-prompt-root')
return False
def _resolve_log_root() -> Path:
for candidate in _candidate_roots():
if _looks_like_prompt_root(candidate):
return candidate
for candidate in _candidate_roots():
if candidate.name.lower() == 'dist' and candidate.parent.exists():
return candidate.parent
candidates = _candidate_roots()
return candidates[0] if candidates else Path.cwd().resolve()
PROMPT_FROZEN_LOG_ROOT = _resolve_log_root()
os.environ.setdefault('PROMPT_FROZEN_ENTRYPOINT', '1')
os.environ.setdefault('PROMPT_DEBUG_ROOT', str(PROMPT_FROZEN_LOG_ROOT))
os.environ.setdefault('PROMPT_RUN_LOG_ROOT', str(PROMPT_FROZEN_LOG_ROOT))
os.environ.setdefault('PROMPT_DEBUG_LOG', str(PROMPT_FROZEN_LOG_ROOT / 'debug.log'))
os.environ.setdefault('PROMPT_RUN_LOG', str(PROMPT_FROZEN_LOG_ROOT / 'run.log'))
os.environ.setdefault('PROMPT_ERRORS_LOG', str(PROMPT_FROZEN_LOG_ROOT / 'errors.txt'))
os.environ.setdefault('PROMPT_APPEND_DEBUG_LOG', '1')
def _write_bootstrap_log(message: object) -> None:
try:
PROMPT_FROZEN_LOG_ROOT.mkdir(parents=True, exist_ok=True)
stamp = datetime.datetime.now().isoformat(sep=' ', timespec='microseconds')
line = f'{stamp} [FROZEN-ENTRY] pid={os.getpid()} {message}'
for name in ('debug.log', 'run.log'):
with open(PROMPT_FROZEN_LOG_ROOT / name, 'a', encoding='utf-8', errors='replace') as handle:
handle.write(line.rstrip() + '\n')
except Exception as error:
warnBootstrapException(error, 'write-bootstrap-log')
def main() -> int:
_write_bootstrap_log(
'BEGIN '
f'root={PROMPT_FROZEN_LOG_ROOT} executable={getattr(sys, "executable", "")} '
f'argv={list(sys.argv or [])} cwd={Path.cwd()}'
)
try:
import prompt_app
lowered_args = {str(arg or '').strip().lower() for arg in list(sys.argv or [])}
if '--frozen-import-smoke' in lowered_args or '--frozen-startup-smoke' in lowered_args:
has_sqlalchemy = bool(getattr(prompt_app, 'HAS_SQLALCHEMY', False))
_write_bootstrap_log(f'IMPORT-SMOKE has_sqlalchemy={has_sqlalchemy} prompt_app={getattr(prompt_app, "__file__", "")}')
if not has_sqlalchemy:
_write_bootstrap_log('IMPORT-SMOKE FAILED reason=missing-sqlalchemy')
return 80
print('FROZEN_IMPORT_SMOKE_OK')
return 0
exit_code = int(prompt_app.main() or 0)
_write_bootstrap_log(f'EXIT code={exit_code}')
return exit_code
except SystemExit as exc:
code = int(exc.code or 0) if isinstance(exc.code, int) else 1
_write_bootstrap_log(f'SYSTEMEXIT code={code}')
return code
except BaseException as error:
trace = traceback.format_exc()
_write_bootstrap_log(f'FATAL {type(error).__name__}: {error}')
try:
with open(PROMPT_FROZEN_LOG_ROOT / 'errors.txt', 'a', encoding='utf-8', errors='replace') as handle:
handle.write(trace.rstrip() + '\n')
except Exception as log_error:
warnBootstrapException(log_error, 'fatal-errors-log-write')
try:
with open(PROMPT_FROZEN_LOG_ROOT / 'debug.log', 'a', encoding='utf-8', errors='replace') as handle:
handle.write(trace.rstrip() + '\n')
except Exception as log_error:
warnBootstrapException(log_error, 'fatal-debug-log-write')
return 1
if __name__ == '__main__':
raise SystemExit(main())