main
py 95 lines 2.53 KB
Raw
1 from __future__ import annotations
2
3 import importlib
4 import importlib.util
5 import shutil
6 import subprocess
7 import sys
8 import threading
9 from pathlib import Path
10
11 from helpers.errors import format_error
12 from helpers.print_style import PrintStyle
13
14
15 _LOCK = threading.Lock()
16 _CHECKED = False
17 _PLUGIN_DIR = Path(__file__).resolve().parent
18 _ROOT_REQUIREMENTS_FILE = _PLUGIN_DIR.parents[1] / "requirements.txt"
19
20
21 def has_liteparse() -> bool:
22 return importlib.util.find_spec("liteparse") is not None
23
24
25 def ensure_dependencies(raise_on_error: bool = True) -> bool:
26 """Install framework-runtime dependencies needed by the plugin."""
27 global _CHECKED
28
29 if _CHECKED and has_liteparse():
30 return True
31
32 with _LOCK:
33 if _CHECKED and has_liteparse():
34 return True
35 if has_liteparse():
36 _CHECKED = True
37 return True
38
39 try:
40 _install_requirements()
41 importlib.invalidate_caches()
42 if not has_liteparse():
43 raise RuntimeError(
44 "Document Query dependency 'liteparse' is still unavailable after installation"
45 )
46 _CHECKED = True
47 return True
48 except Exception as e:
49 message = (
50 "Document Query: failed to install LiteParse dependency: "
51 f"{format_error(e)}"
52 )
53 if raise_on_error:
54 raise RuntimeError(message) from e
55 PrintStyle.error(message)
56 return False
57
58
59 def install() -> bool:
60 return ensure_dependencies(raise_on_error=True)
61
62
63 def _install_requirements() -> None:
64 uv = shutil.which("uv")
65 if not uv:
66 raise RuntimeError(
67 "Document Query plugin requires 'uv' to install liteparse automatically"
68 )
69 requirement = _liteparse_requirement()
70 if not requirement:
71 raise RuntimeError(
72 f"Document Query LiteParse requirement not found in {_ROOT_REQUIREMENTS_FILE}"
73 )
74
75 cmd = [
76 uv,
77 "pip",
78 "install",
79 "--python",
80 sys.executable,
81 requirement,
82 ]
83
84 PrintStyle.info("Document Query: liteparse not found, installing plugin dependency")
85 subprocess.check_call(cmd, cwd=str(_PLUGIN_DIR))
86
87
88 def _liteparse_requirement() -> str:
89 if not _ROOT_REQUIREMENTS_FILE.is_file():
90 return ""
91 for line in _ROOT_REQUIREMENTS_FILE.read_text(encoding="utf-8").splitlines():
92 requirement = line.strip()
93 if requirement.startswith("liteparse"):
94 return requirement
95 return ""