Repository navigation
Expand file tree
/
Copy pathProFiles.pyw
More file actions
189 lines (151 loc) · 6.05 KB
/
Copy pathProFiles.pyw
File metadata and controls
189 lines (151 loc) · 6.05 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
178
179
180
181
182
183
184
185
186
187
188
189
#!/usr/bin/env python3
"""ProFiles desktop shortcut.
Double-click this file to launch the ProFiles GUI on Windows, macOS, or Linux.
Behavior:
* Resolves the right Python automatically:
1. ``.venv`` next to this file (created by ``install.py``)
2. ``pythonw`` / ``python3`` already on ``PATH``
* Bootstraps the ``src/`` layout if the package isn't installed.
* On Windows, no console flashes because the ``.pyw`` association uses
``pythonw.exe``. On macOS, open the file once with the Python Launcher
(or ``python3``) and tick "Always Open With" for one-click launches.
* On Linux, mark this file executable (``chmod +x ProFiles.pyw``) and
your file manager will run it with ``/usr/bin/env python3``.
Run from a terminal for verbose output::
python3 ProFiles.pyw
"""
from __future__ import annotations
import os
import subprocess
import sys
from pathlib import Path
def _project_root() -> Path:
"""Directory containing this script (the ProFiles repo root)."""
return Path(__file__).resolve().parent
def _venv_python(root: Path) -> str | None:
"""Return the venv interpreter path if one exists, else None."""
is_windows = os.name == "nt"
sub = "Scripts" if is_windows else "bin"
for name in ("pythonw", "python", "python3"):
candidate = (
root / ".venv" / sub / f"{name}.exe" if is_windows else root / ".venv" / sub / name
)
if candidate.is_file() and os.access(candidate, os.X_OK):
return str(candidate)
return None
def _system_python() -> str:
"""Fallback interpreter from PATH, preferring Python 3.11+."""
# First, try to find a recent Python version from PATH
import shutil
# List of Python executables to try (in order of preference)
python_names = ["python3.14", "python3.13", "python3.12", "python3.11", "python3", "python"]
for name in python_names:
try:
path = shutil.which(name)
if path:
# Verify it's Python 3.11+ (required for StrEnum)
import subprocess
result = subprocess.run(
[
path,
"-c",
"import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')",
],
capture_output=True,
text=True,
timeout=5,
)
if result.returncode == 0:
version = result.stdout.strip()
major, minor = map(int, version.split("."))
if major > 3 or (major == 3 and minor >= 11):
return path
except Exception:
continue
# Last resort: use current interpreter
return sys.executable
def _add_src_to_path(root: Path) -> None:
"""Make the ``src`` layout importable when running from a clone.
Forces ``src/`` to the front of ``sys.path`` so our ``profiles``
package wins against stdlib's single-file ``profiles`` module
(the profiler). The editable-install .pth puts src/ at the tail;
stdlib wins by default unless we reorder.
"""
src = root / "src"
if not src.is_dir():
return
src_str = str(src.resolve())
sys.path[:] = [src_str] + [p for p in sys.path if Path(p).resolve() != Path(src_str).resolve()]
for name in list(sys.modules):
if name == "profiles" or name.startswith("profiles."):
del sys.modules[name]
def _choose_working_dir(root: Path) -> Path:
"""Choose the working directory for this launch.
Priority:
1. The folder the shortcut was double-clicked from (via launch context)
2. The shortcut's own directory (fallback)
Returns:
The directory to chdir into before launching the app.
"""
from profiles.launch import detect_launch_context
ctx = detect_launch_context()
target = ctx.working_directory or root
return target if target.is_dir() else root
def _run() -> None:
root = _project_root()
# Add src/ to path first (needed for imports regardless of CWD)
_add_src_to_path(root)
# Choose working directory based on launch context
# This makes the shortcut use the invoking folder, not its own
target_dir = _choose_working_dir(root)
os.chdir(target_dir)
try:
from profiles.app import ProFileApp # noqa: E402
except ModuleNotFoundError as e:
print(f"ERROR: Cannot import profiles module: {e}")
print("\nPossible solutions:")
print("1. Install the package: pip install -e .")
print("2. Make sure you're in the project root directory")
print("\nPress Enter to exit...")
input()
sys.exit(1)
except Exception as e:
print(f"ERROR: Failed to import profiles: {e}")
import traceback
traceback.print_exc()
print("\nPress Enter to exit...")
input()
sys.exit(1)
try:
app = ProFileApp()
app.run()
except Exception as e:
print(f"ERROR: Application failed to start: {e}")
import traceback
traceback.print_exc()
print("\nPress Enter to exit...")
input()
sys.exit(1)
def _relaunch_in_venv() -> None:
"""If the current interpreter is *not* the project's venv, hand off to it."""
root = _project_root()
venv_py = _venv_python(root)
if not venv_py or os.path.normcase(venv_py) == os.path.normcase(sys.executable):
_run()
return
print(f"ProFiles: using venv interpreter {venv_py}")
raise SystemExit(subprocess.call([venv_py, str(__file__)]))
if __name__ == "__main__":
try:
_relaunch_in_venv()
except KeyboardInterrupt:
sys.exit(0)
except Exception as e:
# Catch any unhandled exceptions and display them
# This prevents the terminal from closing immediately on double-click
print(f"ERROR: ProFiles failed to start: {e}")
import traceback
traceback.print_exc()
print("\nPress Enter to exit...")
input() # Wait for user to press Enter
sys.exit(1)