fix: a spaCy model that does not load is downloaded on Update and Switch branch

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-10-10 15:38:40 +03:00
co-authored by Claude Haiku 5.5
parent 7bf9b51007
commit 4869c85ff2
2 changed files with 39 additions and 2 deletions
+11
View File
@@ -261,3 +261,14 @@ def test_a_branch_switch_installs_what_is_missing_even_when_the_requirements_fil
monkeypatch.setattr(update, "install_requirements", lambda: called.append(1) or {"ok": True})
update.checkout("other", pull_after=False, install_deps=True)
assert called, "the switch itself must check for missing packages"
def test_a_spacy_model_that_does_not_load_is_downloaded_on_the_switch(repo, monkeypatch):
(repo / "requirements.txt").write_text("spacy>=3.8,<3.9\nen_core_web_sm @ https://example.com/m.whl\n")
monkeypatch.setattr(update, "_installed_version", lambda n: "3.8.0")
ran = []
monkeypatch.setattr(update, "_spacy_model_loads", lambda: bool(ran) and ran[-1] == ["spacy", "download", "en_core_web_sm"])
monkeypatch.setattr(update.subprocess, "run", lambda cmd, **k: ran.append(cmd[-3:]) or subprocess.CompletedProcess(cmd, 0, "", ""))
monkeypatch.setattr(update, "_pip_freeze", lambda: {})
assert update.install_requirements()["ok"]
assert ran[-1] == ["spacy", "download", "en_core_web_sm"]
+28 -2
View File
@@ -370,6 +370,17 @@ def _satisfies(have: str, spec: str) -> bool:
return True
def _spacy_model_loads() -> bool:
"""Whether spaCy can load its English model, checked in a fresh interpreter: this process may
already hold an older spaCy. False when spaCy is not installed at all."""
try:
proc = subprocess.run([sys.executable, "-c", "import spacy; spacy.load('en_core_web_sm')"],
capture_output=True, timeout=180)
except (subprocess.TimeoutExpired, OSError):
return False
return proc.returncode == 0
def install_requirements(timeout: int = 900) -> dict:
"""Install ONLY the requirements that are absent. Never upgrades anything.
@@ -399,7 +410,11 @@ def install_requirements(timeout: int = 900) -> dict:
for n, h, sp in status["below_floor"])
+ f"\n\nTo do it yourself:\n {sys.executable} -m pip install -r {req}")
print(f"[FunPack update] {floor_note}")
if not status["missing"]:
# The spaCy model is a package the metadata can see even when it does not load (a half-finished
# install, a model built for another spaCy). Presence is judged by loading it, when spaCy is here.
model_broken = ("en_core_web_sm" in status["present"] and _installed_version("spacy") is not None
and not _spacy_model_loads())
if not status["missing"] and not model_broken:
return {"ran": False, "ok": True, "changed": [],
"detail": ("Nothing to install — every requirement is already present."
+ (f"\n\n{floor_note}" if floor_note else ""))}
@@ -412,7 +427,7 @@ def install_requirements(timeout: int = 900) -> dict:
# with nothing under it is not installed, it is broken. The freeze diff says whether anything
# already present moved as a result.
failed, done = [], []
for line in status["install"]:
for line in status.get("install", []):
cmd = [sys.executable, "-m", "pip", "install", "--disable-pip-version-check", line]
try:
proc = subprocess.run(cmd, cwd=REPO_ROOT, capture_output=True, text=True, timeout=timeout)
@@ -426,6 +441,17 @@ def install_requirements(timeout: int = 900) -> dict:
failed.append((line, ((proc.stderr or proc.stdout or "").strip() or "pip failed")[-400:]))
else:
done.append(line)
if model_broken:
cmd = [sys.executable, "-m", "spacy", "download", "en_core_web_sm"]
try:
proc = subprocess.run(cmd, cwd=REPO_ROOT, capture_output=True, text=True, timeout=timeout)
if proc.returncode != 0 or not _spacy_model_loads():
failed.append(("en_core_web_sm (spacy download)",
((proc.stderr or proc.stdout or "").strip() or "the model still does not load")[-400:]))
else:
done.append("en_core_web_sm (spacy download)")
except (subprocess.TimeoutExpired, OSError) as e:
failed.append(("en_core_web_sm (spacy download)", str(e)))
changed = _pip_diff(before, _pip_freeze())
if changed:
print("[FunPack update] pip changed these packages: " + ", ".join(changed))