|
10 | 10 | import os, sys, json, shutil, subprocess, tempfile |
11 | 11 | import ov_cli |
12 | 12 | from ov_cli import TR |
13 | | -from ov_cli.features import get_packages, get_extra_pips, save as _save_features |
| 13 | +from ov_cli.features import get_packages, get_extra_pips, get_exclusive_packages, get_installed, save as _save_features |
14 | 14 |
|
15 | 15 |
|
16 | 16 | _ALL_FEATURES = {"chat", "image", "asr", "tts", "ui", "mcp", "server", "convert"} |
@@ -300,6 +300,60 @@ def _install_features(pip, features: set[str], workspace, fix_mode=False): |
300 | 300 | "--index-url", "https://download.pytorch.org/whl/cpu"]) |
301 | 301 |
|
302 | 302 |
|
| 303 | +def _remove_features(pip, venv_path, removed: set[str]): |
| 304 | + """卸载指定功能独有(不被其他已装功能需要)的 pip 包。""" |
| 305 | + installed = get_installed(venv_path) |
| 306 | + remaining = installed - removed |
| 307 | + |
| 308 | + if not removed: |
| 309 | + print(f" {TR('没有指定要移除的模块', 'No features to remove')}") |
| 310 | + return |
| 311 | + |
| 312 | + invalid = removed - _ALL_FEATURES |
| 313 | + if invalid: |
| 314 | + print(f" ❌ {TR('不支持的功能', 'Unsupported features')}: {', '.join(sorted(invalid))}") |
| 315 | + print(f" {TR('支持', 'Supported')}: {', '.join(sorted(_ALL_FEATURES))}") |
| 316 | + return |
| 317 | + |
| 318 | + not_installed = removed - installed |
| 319 | + if not_installed: |
| 320 | + print(f" - {TR('以下模块未安装,跳过', 'Already not installed')}: {', '.join(sorted(not_installed))}") |
| 321 | + |
| 322 | + to_remove = removed - not_installed |
| 323 | + if not to_remove: |
| 324 | + print(f" {TR('没有需要移除的模块', 'Nothing to remove')}") |
| 325 | + return |
| 326 | + |
| 327 | + exclusive = get_exclusive_packages(to_remove, remaining) |
| 328 | + if not exclusive: |
| 329 | + print(f" {TR('所有包均为其他模块共享,无需卸载', 'All packages are shared, nothing to uninstall')}") |
| 330 | + else: |
| 331 | + print(f" {TR('将卸载以下独有包', 'Will uninstall exclusive packages')}:") |
| 332 | + for f, pkgs in sorted(exclusive.items()): |
| 333 | + print(f" • {f}: {', '.join(pkgs)}") |
| 334 | + print() |
| 335 | + try: |
| 336 | + r = input(f" {TR('确认卸载?(y/N)', 'Confirm uninstall? (y/N)')}: ").strip().lower() |
| 337 | + if r != "y": |
| 338 | + print(f" {TR('已取消', 'Cancelled')}") |
| 339 | + return |
| 340 | + except (EOFError, KeyboardInterrupt): |
| 341 | + print() |
| 342 | + print(f" {TR('已取消', 'Cancelled')}") |
| 343 | + return |
| 344 | + all_pkgs = sorted(set(p for pkgs in exclusive.values() for p in pkgs)) |
| 345 | + try: |
| 346 | + subprocess.check_call([pip, "uninstall", "-y"] + all_pkgs) |
| 347 | + print(f" ✓ {TR('卸载完成', 'Uninstall done')}") |
| 348 | + except subprocess.CalledProcessError: |
| 349 | + print(f" ⚠ {TR('部分包卸载失败(可能已被手动移除)', 'Some packages may already be removed')}") |
| 350 | + |
| 351 | + _save_features(venv_path, remaining) |
| 352 | + print(f" ✓ {TR('已更新安装记录', 'Features list updated')}: {', '.join(sorted(remaining))}") |
| 353 | + print(f" {TR('基础依赖(openvino 等)始终保持不变', 'Base deps (openvino etc.) are kept')}") |
| 354 | + print(f" {TR('如需彻底清理,可重建环境', 'For full cleanup, recreate with ./ov-cli setup')}") |
| 355 | + |
| 356 | + |
303 | 357 | def cmd_setup(args, workspace): |
304 | 358 | """ov-cli setup: 创建虚拟环境并安装依赖""" |
305 | 359 |
|
@@ -363,6 +417,19 @@ def cmd_setup(args, workspace): |
363 | 417 | print(f" {TR('✅ 修复完成', '✅ Fix done')}") |
364 | 418 | return |
365 | 419 |
|
| 420 | + # ── 移除模式 ── |
| 421 | + if args.remove_features: |
| 422 | + raw = args.remove_features.strip() |
| 423 | + to_remove = {s.strip() for s in raw.split(",") if s.strip()} |
| 424 | + venv_path = args.venv or os.path.join(workspace, ".venv") |
| 425 | + if not os.path.isdir(venv_path): |
| 426 | + print(f" {TR('错误: 未找到虚拟环境', 'Error: venv not found')}: {venv_path}") |
| 427 | + print(f" {TR('请先运行', 'Run first')}: ./ov-cli setup") |
| 428 | + sys.exit(1) |
| 429 | + pip = _pip_path(venv_path) |
| 430 | + _remove_features(pip, venv_path, to_remove) |
| 431 | + return |
| 432 | + |
366 | 433 | # ── 打印安装概要 ── |
367 | 434 | print(f" {TR('即将安装以下模块', 'Will install:')}") |
368 | 435 | for f in sorted(features): |
|
0 commit comments