From 287e1d28541ae4c1722571929c0ec96a1859ebb8 Mon Sep 17 00:00:00 2001 From: Pedro Barbosa Date: Wed, 24 Jun 2026 06:28:24 +0000 Subject: [PATCH 1/2] fix(cookbook): replay user-site .pth hooks on runtime --user installs Local optional-dep probing in `list_packages` decides "installed" by importing the package in-process. When a dep is installed at runtime via Cookbook -> Dependencies (`pip install --user`) into a long-lived server that started before the install, user-site was never processed by `site`. The recovery used `sys.path.append(user_site)`, which makes modules importable but does NOT execute user-site `.pth` files. On Python 3.12+ `distutils` is gone from the stdlib and is only restored by setuptools' `distutils-precedence.pth` (shipped in user-site). basicsr (a realesrgan dep) does `import distutils` at import time, so the probe failed with `ModuleNotFoundError: No module named 'distutils'` and reported realesrgan as not installed until a full restart. Use `site.addsitedir(user_site)` instead so user-site `.pth` hooks are replayed and the distutils shim activates. Verified end-to-end on the python:3.14 image: realesrgan now probes as installed without a restart. Fixes #4810 Co-Authored-By: Claude Opus 4.8 --- routes/shell_routes.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/routes/shell_routes.py b/routes/shell_routes.py index 82826f2f01..a51da3b21a 100644 --- a/routes/shell_routes.py +++ b/routes/shell_routes.py @@ -1063,8 +1063,19 @@ async def list_packages( importlib.invalidate_caches() try: user_site = site.getusersitepackages() - if user_site and os.path.isdir(user_site) and user_site not in sys.path: - sys.path.append(user_site) + if user_site and os.path.isdir(user_site): + # Use addsitedir(), NOT a bare sys.path.append(). When a package + # is `pip install --user`'d at runtime (Cookbook → Install) the + # long-lived server process started before the user-site existed, + # so site never processed it — including its `.pth` hooks. On + # Python 3.12+ `distutils` is gone from stdlib and is only + # restored by setuptools' `distutils-precedence.pth`, which ships + # in user-site. basicsr (a realesrgan dep) does `import distutils` + # at import time, so a plain append left the package importable + # but `import distutils` failing → realesrgan probed as + # not-installed until a full process restart. addsitedir() replays + # the `.pth` files so the shim is active. + site.addsitedir(user_site) except Exception: pass if ssh_port and str(ssh_port).strip() not in ("", "22"): From 5624b94b9823c29850d593b58881c697620f15eb Mon Sep 17 00:00:00 2001 From: Pedro Barbosa Date: Wed, 24 Jun 2026 06:33:51 +0000 Subject: [PATCH 2/2] test(cookbook): assert addsitedir replays user-site .pth hooks The regression test pinned the old `sys.path.append(user_site)` source text. Update it to assert the new `site.addsitedir(user_site)` guarantee that user-site `.pth` hooks are replayed for runtime --user installs. Co-Authored-By: Claude Opus 4.8 --- tests/test_cookbook_dependency_completion_regression.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_cookbook_dependency_completion_regression.py b/tests/test_cookbook_dependency_completion_regression.py index bc8c3ada6c..8bca23b90f 100644 --- a/tests/test_cookbook_dependency_completion_regression.py +++ b/tests/test_cookbook_dependency_completion_regression.py @@ -106,4 +106,9 @@ def test_local_dependency_probe_refreshes_user_site_visibility(): assert "importlib.invalidate_caches()" in source assert "user_site = site.getusersitepackages()" in source - assert "if user_site and os.path.isdir(user_site) and user_site not in sys.path:" in source + # addsitedir (not a bare sys.path.append) so user-site `.pth` hooks are + # replayed when a package is installed into an already-running process — + # otherwise setuptools' distutils shim never activates and basicsr-based + # deps (realesrgan) probe as not-installed until a restart. See #4810. + assert "if user_site and os.path.isdir(user_site):" in source + assert "site.addsitedir(user_site)" in source