Studio: self-heal a pre-#6483-fix anyio>=4.14 stuck in existing installs (#6805)

* Studio: self-heal a pre-#6483-fix anyio>=4.14 stuck in existing installs

The <4.14 cap in constraints.txt/no-torch-runtime.txt only constrains new
anyio resolutions. An install made before that cap existed can already be
sitting on anyio 4.14+, and since it already satisfies mcp/fastmcp's
anyio>=4.5 floor, every later constrained install skips it as
already-satisfied -- so affected installs never recover and keep hitting
the cancel-scope RuntimeError on every request (#6797, a recurrence of
#6483). Force-reinstall anyio<4.14 whenever a stuck 4.14+ is detected.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Studio: also repair anyio on the update fast path

setup.sh's _SKIP_PYTHON_DEPS and setup.ps1's $SkipPythonDeps skip
install_python_stack.py entirely once the installed package version already
matches PyPI latest, so an install stuck on anyio>=4.14 with an otherwise
up-to-date package never reaches the repair added in install_python_stack.py.
Probe anyio on that fast path too and fall through to the full dependency
pass when it's still >=4.14, mirroring the existing ROCm/CPU-torch override
right below it.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Abdul Moiz 2026-07-02 18:49:40 +05:00 committed by GitHub
commit 91f4ec7ba7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 79 additions and 1 deletions

View file

@ -181,6 +181,41 @@ def _probe_installed_torch_version() -> str | None:
return lines[-1] if lines else None
# constraints.txt caps new anyio resolutions at <4.14 (#6483), but an install
# from before the cap existed can already be stuck at 4.14+, which later
# constrained installs won't touch since it already satisfies mcp/fastmcp.
_ANYIO_BAD_FLOOR = (4, 14)
def _installed_anyio_version() -> tuple[int, int] | None:
try:
from importlib.metadata import version as _pkg_version
raw = _pkg_version("anyio")
except Exception:
return None
parts = raw.split(".")
try:
major = int(parts[0])
minor = int(re.sub(r"[^0-9].*", "", parts[1])) if len(parts) > 1 else 0
except (IndexError, ValueError):
return None
return (major, minor)
def _repair_bad_anyio() -> None:
installed = _installed_anyio_version()
if installed is None or installed < _ANYIO_BAD_FLOOR:
return
_safe_print(_dim(f" anyio {installed[0]}.{installed[1]} found -- reinstalling anyio<4.14..."))
pip_install(
"Repairing anyio version",
"--no-cache-dir",
"--force-reinstall",
"anyio<4.14.0",
constrain = False,
)
# AMD Windows ROCm wheels (repo.amd.com/rocm/whl/{arch_family}/).
# Override with UNSLOTH_ROCM_WINDOWS_MIRROR for air-gapped/mirror installs.
_ROCM_WINDOWS_INDEX_BASE = (
@ -1970,7 +2005,7 @@ def install_python_stack() -> int:
package_name = os.environ.get("STUDIO_PACKAGE_NAME", "unsloth")
# --local overlays a local repo checkout after updating deps.
local_repo = os.environ.get("STUDIO_LOCAL_REPO", "")
base_total = 10 if IS_WINDOWS else 11
base_total = 11 if IS_WINDOWS else 12 # +1 for the anyio repair check (step 8b)
if IS_MACOS:
base_total -= 1 # triton step is skipped on macOS
if not IS_MACOS and not NO_TORCH:
@ -2284,6 +2319,10 @@ def install_python_stack() -> int:
req = REQ_ROOT / "studio.txt",
)
# 8b. anyio repair (#6483)
_progress("anyio check")
_repair_bad_anyio()
# 9. Data-designer dependencies
_progress("data designer deps")
pip_install(