Files
Mrpzx 977f97995a tripocli v2.0.0:Tripo AI 3D 生成与加工 CLI(面向 Agent 与开发者,纯 Python 标准库、零第三方依赖)
覆盖文生 3D、图生 3D、多视图、批量生成,以及重拓扑、绑定、动画、智能分割、
纹理、PBR、智能 UV、部件补全、格式导出与下载。

代码组织:一个功能一个模块(tripo/features/),入口薄壳 tripocli.py。
2026-10-13 19:22:54 +08:00

129 lines
4.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Self-update: refresh the installed CLI from the project source tree.
The project is distributed as a source directory (no PyPI release yet), so
"updating" means: find the source tree, re-install it with the *same*
interpreter that is running us, and rehash pyenv shims if pyenv is in use.
The source directory is remembered in ``~/.tripo/config.json`` (``install_source``)
the first time it is discovered, so later runs only need ``tripocli update``.
"""
import json
import os
import shutil
import subprocess
import sys
from pathlib import Path
from . import __version__
INSTALL_SOURCE_KEY = "install_source"
def _running_package_dir():
"""Directory of the ``tripo`` package that is currently imported."""
return Path(__file__).resolve().parent
def _is_site_packages(path):
parts = {p.lower() for p in Path(path).parts}
return bool(parts & {"site-packages", "dist-packages"})
def detect_source(explicit=None, config=None):
"""Locate the source tree to install from.
Order: ``--from`` > remembered ``install_source`` > the tree we were imported
from (only when that is not site-packages).
"""
if explicit:
candidate = Path(explicit).expanduser().resolve()
if not (candidate / "pyproject.toml").exists():
raise ValueError(f"{candidate} 看起来不是项目目录(缺少 pyproject.toml)")
return candidate
if config:
remembered = config.data.get(INSTALL_SOURCE_KEY)
if remembered and (Path(remembered) / "pyproject.toml").exists():
return Path(remembered)
package_dir = _running_package_dir()
if not _is_site_packages(package_dir):
root = package_dir.parent
if (root / "pyproject.toml").exists():
return root
return None
def installed_version():
"""Version of the copy in site-packages (what other directories will run)."""
try:
out = subprocess.run(
[sys.executable, "-m", "pip", "show", "tripocli"],
capture_output=True, text=True, timeout=60,
)
except (OSError, subprocess.SubprocessError):
return None
for line in out.stdout.splitlines():
if line.lower().startswith("version:"):
return line.split(":", 1)[1].strip()
return None
def _has_pyenv():
return bool(shutil.which("pyenv") or os.environ.get("PYENV_ROOT"))
def update(source, rehash=True, dry_run=False):
"""Re-install ``source`` with the running interpreter. Returns a report dict."""
source = Path(source)
steps = []
command = [
sys.executable, "-m", "pip", "install",
"--no-build-isolation", "--no-deps", "--upgrade", "--force-reinstall", str(source),
]
if dry_run:
planned = [" ".join(command)]
if rehash and _has_pyenv():
planned.append("pyenv rehash")
return {"source": str(source), "command": command, "steps": planned,
"rehash": False, "dry_run": True, "version": __version__}
proc = subprocess.run(command, capture_output=True, text=True)
steps.append(" ".join(command))
tail = "\n".join((proc.stdout or "").strip().splitlines()[-6:])
if proc.returncode != 0:
raise RuntimeError(
f"pip 安装失败(退出码 {proc.returncode}):\n{(proc.stderr or proc.stdout or '')[-800:]}"
)
steps.append(tail)
rehashed = False
if rehash and _has_pyenv():
subprocess.run(["pyenv", "rehash"], capture_output=True, text=True)
steps.append("pyenv rehash")
rehashed = True
return {
"source": str(source),
"command": command,
"steps": steps,
"rehash": rehashed,
"version": __version__,
"installed_version": installed_version(),
}
def report_lines(report):
prefix = "将执行" if report.get("dry_run") else "已执行"
lines = [
f"源码目录 : {report['source']}",
f"版本 : {report.get('installed_version') or report['version']}",
f"{prefix} : {'; '.join(report['steps'])}",
]
if report.get("rehash"):
lines.append("已执行 : pyenv rehash")
return lines