df
This commit is contained in:
@@ -14,13 +14,35 @@ _CONFIG_CACHE: Dict[str, Dict[str, Any]] = {}
|
||||
|
||||
|
||||
def _strip_inline_comment(line: str) -> str:
|
||||
# Keep it simple: only strip full-line comments and inline comments that start after whitespace.
|
||||
# Users can always quote values that contain '#' or ';'.
|
||||
# Strip comments in a way that's friendly to common .conf usage:
|
||||
# - Full-line comments starting with '#' or ';'
|
||||
# - Inline comments starting with '#' or ';' *outside quotes*
|
||||
# (e.g. dtype="float16" # optional)
|
||||
stripped = line.strip()
|
||||
if not stripped:
|
||||
return ""
|
||||
if stripped.startswith("#") or stripped.startswith(";"):
|
||||
return ""
|
||||
|
||||
in_single = False
|
||||
in_double = False
|
||||
for i, ch in enumerate(line):
|
||||
if ch == "'" and not in_double:
|
||||
in_single = not in_single
|
||||
continue
|
||||
if ch == '"' and not in_single:
|
||||
in_double = not in_double
|
||||
continue
|
||||
if in_single or in_double:
|
||||
continue
|
||||
|
||||
if ch in {"#", ";"}:
|
||||
# Treat as a comment start only when preceded by whitespace.
|
||||
# This keeps values like paths or tokens containing '#' working
|
||||
# when quoted, and reduces surprises for unquoted values.
|
||||
if i == 0 or line[i - 1].isspace():
|
||||
return line[:i].rstrip()
|
||||
|
||||
return line
|
||||
|
||||
|
||||
|
||||
119
SYS/optional_deps.py
Normal file
119
SYS/optional_deps.py
Normal file
@@ -0,0 +1,119 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Any, Dict, Iterable, List, Optional, Tuple
|
||||
|
||||
from SYS.logger import log
|
||||
from SYS.rich_display import stdout_console
|
||||
|
||||
|
||||
def _as_bool(value: Any, default: bool = False) -> bool:
|
||||
if value is None:
|
||||
return default
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
s = str(value).strip().lower()
|
||||
if s in {"1", "true", "yes", "on"}:
|
||||
return True
|
||||
if s in {"0", "false", "no", "off"}:
|
||||
return False
|
||||
return default
|
||||
|
||||
|
||||
def _is_pytest() -> bool:
|
||||
return bool(os.environ.get("PYTEST_CURRENT_TEST"))
|
||||
|
||||
|
||||
def _try_import(module: str) -> bool:
|
||||
try:
|
||||
importlib.import_module(module)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def florencevision_missing_modules() -> List[str]:
|
||||
missing: List[str] = []
|
||||
# pillow is already in requirements, but keep the check for robustness.
|
||||
if not _try_import("transformers"):
|
||||
missing.append("transformers")
|
||||
if not _try_import("torch"):
|
||||
missing.append("torch")
|
||||
if not _try_import("PIL"):
|
||||
missing.append("pillow")
|
||||
# Florence-2 remote code frequently requires these extras.
|
||||
if not _try_import("einops"):
|
||||
missing.append("einops")
|
||||
if not _try_import("timm"):
|
||||
missing.append("timm")
|
||||
return missing
|
||||
|
||||
|
||||
def _pip_install(requirements: List[str]) -> Tuple[bool, str]:
|
||||
if not requirements:
|
||||
return True, "No requirements"
|
||||
|
||||
cmd = [sys.executable, "-m", "pip", "install", "--upgrade", *requirements]
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
cmd,
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if proc.returncode == 0:
|
||||
importlib.invalidate_caches()
|
||||
return True, proc.stdout.strip() or "Installed"
|
||||
out = (proc.stdout or "") + "\n" + (proc.stderr or "")
|
||||
return False, out.strip() or f"pip exited with code {proc.returncode}"
|
||||
except Exception as exc:
|
||||
return False, str(exc)
|
||||
|
||||
|
||||
def maybe_auto_install_configured_tools(config: Dict[str, Any]) -> None:
|
||||
"""Best-effort dependency auto-installer for configured tools.
|
||||
|
||||
This is intentionally conservative:
|
||||
- Only acts when a tool block is enabled.
|
||||
- Skips under pytest.
|
||||
|
||||
Current supported tool(s): florencevision
|
||||
"""
|
||||
if _is_pytest():
|
||||
return
|
||||
|
||||
tool_cfg = (config or {}).get("tool")
|
||||
if not isinstance(tool_cfg, dict):
|
||||
return
|
||||
|
||||
fv = tool_cfg.get("florencevision")
|
||||
if isinstance(fv, dict) and _as_bool(fv.get("enabled"), False):
|
||||
auto_install = _as_bool(fv.get("auto_install"), True)
|
||||
if not auto_install:
|
||||
return
|
||||
|
||||
missing = florencevision_missing_modules()
|
||||
if not missing:
|
||||
return
|
||||
|
||||
names = ", ".join(missing)
|
||||
try:
|
||||
with stdout_console().status(
|
||||
f"Installing FlorenceVision dependencies: {names}",
|
||||
spinner="dots",
|
||||
):
|
||||
ok, detail = _pip_install(missing)
|
||||
except Exception:
|
||||
log(f"[startup] FlorenceVision dependencies missing ({names}). Attempting auto-install...")
|
||||
ok, detail = _pip_install(missing)
|
||||
|
||||
if ok:
|
||||
log("[startup] FlorenceVision dependency install OK")
|
||||
else:
|
||||
log(f"[startup] FlorenceVision dependency auto-install failed. {detail}")
|
||||
|
||||
|
||||
__all__ = ["maybe_auto_install_configured_tools", "florencevision_missing_modules"]
|
||||
Reference in New Issue
Block a user