Skip to content

Commit a96dc83

Browse files
lsteinclaude
andauthored
fix(launcher): force uv to use managed Python (avoids macOS Xcode prompt) (#322)
On macOS, uv's default --python-preference ("managed") still falls back to a system/framework CPython when one matches. The python.org framework build is not relocatable, so building the tool venv from it makes uv rewrite Mach-O load paths with install_name_tool, which triggers the Xcode Command Line Tools install dialog during first-run setup. Set UV_PYTHON_PREFERENCE=only-managed in the launcher's uvEnv() so every uv call (python install, tool install, GPU reinstall) uses uv's relocatable managed builds and never the framework Python. The launcher already pins/installs a managed 3.12; this just closes the gap where tool install could still pick up the framework interpreter. Add a regression test for the env. Fold the same flags into the documented PyPI/uv recipe across the docs and READMEs (--python 3.12 --python-preference only-managed) so the CLI path avoids the prompt too. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c7239a6 commit a96dc83

7 files changed

Lines changed: 52 additions & 8 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,9 @@ For PyPI, Docker, and manual install instructions, see the [Installation guide](
8686
If you already have Python 3.10–3.14 and prefer the command line:
8787

8888
```bash
89-
uv tool install photomapai --torch-backend auto # or: pip install photomapai
89+
uv tool install photomapai --python 3.12 --python-preference only-managed --torch-backend auto
9090
start_photomap
91+
# (or simply: pip install photomapai && start_photomap)
9192
```
9293

9394
Then open your browser to [http://127.0.0.1:8050](http://127.0.0.1:8050) (it opens automatically) and follow the prompts to create your first album.

README.zh-TW.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,9 @@ PhotoMapAI 也支援多相簿管理、依時間瀏覽照片、簡潔的全螢幕
101101
如果你已經安裝 Python 3.10–3.14,並偏好使用命令列:
102102

103103
```bash
104-
uv tool install photomapai --torch-backend auto # 或:pip install photomapai
104+
uv tool install photomapai --python 3.12 --python-preference only-managed --torch-backend auto
105105
start_photomap
106+
# (或改用 pip:pip install photomapai && start_photomap)
106107
```
107108

108109
接著開啟瀏覽器,前往 [http://127.0.0.1:8050](http://127.0.0.1:8050)(會自動開啟),並依照畫面提示建立你的第一個相簿。

docs/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@ See the [Installation guide](installation.md) for PyPI, Docker, source, GPU over
9090
If you already have Python 3.10–3.14:
9191

9292
```bash
93-
uv tool install photomapai --torch-backend auto # or: pip install photomapai
93+
uv tool install photomapai --python 3.12 --python-preference only-managed --torch-backend auto
9494
start_photomap
95+
# (or simply: pip install photomapai && start_photomap)
9596
```
9697

9798
After the startup messages your browser opens to http://localhost:8050 automatically.

docs/installation.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,14 @@ To also remove the downloaded Python/libraries, run `photomap --uninstall` (path
7474
If you are comfortable with the command line and already have Python 3.10–3.14, you can install the package directly. We recommend [uv](https://docs.astral.sh/uv/):
7575

7676
```bash
77-
uv tool install photomapai --torch-backend auto
77+
uv tool install photomapai --python 3.12 --python-preference only-managed --torch-backend auto
7878
start_photomap
7979
```
8080

81-
`--torch-backend auto` picks GPU or CPU PyTorch automatically. Or with plain `pip` in a virtual environment:
81+
`--torch-backend auto` picks GPU or CPU PyTorch automatically. `--python 3.12
82+
--python-preference only-managed` tells `uv` to use its own managed Python build
83+
rather than a system one; on macOS this avoids a pop-up that asks to install the
84+
Xcode command-line tools. Or with plain `pip` in a virtual environment:
8285

8386
```bash
8487
python -m venv photomap --prompt photomap
@@ -128,4 +131,4 @@ start_photomap
128131

129132
Both create an isolated virtual environment in `./.venv`; the difference is only which tool builds it. If you intend to *modify* the source, add `-e` to do an editable install (`uv pip install -e .` or `pip install -e .`) — but then the environment is tied to this folder's location, so don't move or rename it afterwards.
130133

131-
GPU acceleration needs only a recent **NVIDIA driver***not* the CUDA Toolkit (see [NVIDIA GPU Acceleration](installation/cuda.md)). On Linux and macOS the default `pip install .` already pulls a GPU-capable build of PyTorch, so there's nothing extra to do. On Windows the default PyTorch from PyPI is CPU-only; to enable the GPU, install the CUDA build of PyTorch from PyTorch's [official index](https://pytorch.org/get-started/locally/), or use the `uv tool install photomapai --torch-backend auto` recipe above, which selects the right build automatically. To start the server again later, re-run `start_photomap` from the activated environment.
134+
GPU acceleration needs only a recent **NVIDIA driver***not* the CUDA Toolkit (see [NVIDIA GPU Acceleration](installation/cuda.md)). On Linux and macOS the default `pip install .` already pulls a GPU-capable build of PyTorch, so there's nothing extra to do. On Windows the default PyTorch from PyPI is CPU-only; to enable the GPU, install the CUDA build of PyTorch from PyTorch's [official index](https://pytorch.org/get-started/locally/), or use the `uv tool install` recipe above, which selects the right build automatically. To start the server again later, re-run `start_photomap` from the activated environment.

docs/installation/cuda.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ installs the *driver*, not the CUDA Toolkit.
8989
(or want to force a re-detect), launch with the `--gpu` flag — see
9090
[GPU acceleration](../installation.md#gpu-acceleration) in the main installation
9191
guide.
92-
- **PyPI / `uv`:** `uv tool install photomapai --torch-backend auto` picks the GPU
93-
or CPU build of PyTorch automatically based on what `nvidia-smi` reports.
92+
- **PyPI / `uv`:** `uv tool install photomapai --python 3.12 --python-preference only-managed --torch-backend auto`
93+
picks the GPU or CPU build of PyTorch automatically based on what `nvidia-smi` reports.
9494

9595
To confirm GPU support is active, watch for a console message about GPU
9696
acceleration when PhotoMapAI starts up.

launcher/uv.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ func (l layout) uvEnv() []string {
4545
"UV_CACHE_DIR="+l.cacheDir,
4646
// Don't let a managed-Python download be disabled by inherited config.
4747
"UV_PYTHON_DOWNLOADS=automatic",
48+
// Only ever use uv's managed (python-build-standalone) interpreters,
49+
// never a system/framework CPython. uv's default preference ("managed")
50+
// will still fall back to a system interpreter if it finds a matching
51+
// version — on macOS that's the non-relocatable python.org framework
52+
// build at /Library/Frameworks/Python.framework. Building a venv from it
53+
// makes uv rewrite Mach-O load paths with `install_name_tool`, which
54+
// triggers the Xcode Command Line Tools install prompt. Managed builds
55+
// are relocatable, so forcing only-managed avoids that prompt entirely.
56+
"UV_PYTHON_PREFERENCE=only-managed",
4857
)
4958
return env
5059
}

launcher/uv_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,35 @@ func TestMarkerRoundTrip(t *testing.T) {
5151
}
5252
}
5353

54+
func TestUVEnvForcesManagedPython(t *testing.T) {
55+
l := layout{
56+
pythonDir: "/tmp/pm/python",
57+
toolDir: "/tmp/pm/tools",
58+
toolBin: "/tmp/pm/bin",
59+
cacheDir: "/tmp/pm/cache",
60+
}
61+
want := map[string]string{
62+
"UV_PYTHON_INSTALL_DIR": l.pythonDir,
63+
"UV_TOOL_DIR": l.toolDir,
64+
"UV_TOOL_BIN_DIR": l.toolBin,
65+
"UV_CACHE_DIR": l.cacheDir,
66+
// only-managed keeps uv off the non-relocatable macOS framework Python,
67+
// which would otherwise trigger the Xcode install_name_tool prompt.
68+
"UV_PYTHON_PREFERENCE": "only-managed",
69+
}
70+
got := map[string]string{}
71+
for _, kv := range l.uvEnv() {
72+
if k, v, ok := strings.Cut(kv, "="); ok {
73+
got[k] = v
74+
}
75+
}
76+
for k, v := range want {
77+
if got[k] != v {
78+
t.Errorf("uvEnv()[%q] = %q, want %q", k, got[k], v)
79+
}
80+
}
81+
}
82+
5483
// TestDownloadUVIntegration exercises the download + archive-extraction + exec
5584
// path against the real uv release (~30 MB, no torch). Skipped with -short.
5685
func TestDownloadUVIntegration(t *testing.T) {

0 commit comments

Comments
 (0)