1- import sys
21import importlib
2+ import shutil
33import subprocess
4- from tooldelta import Plugin , utils , fmts , plugin_entry
5- from importlib .metadata import version , PackageNotFoundError
4+ import sys
5+ from importlib .metadata import PackageNotFoundError , version
6+
7+ from tooldelta import Plugin , fmts , plugin_entry , utils
8+
9+ PYPI_INDEX_URL = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
610
711
812def decode_bytes (data : bytes ) -> str :
@@ -30,7 +34,7 @@ def __init__(self, frame):
3034 )
3135
3236 # ------------------------- API -----------------------------
33- def install (self , packages : list [str ], upgrade = False ):
37+ def install (self , packages : list [str ], upgrade : bool = False ):
3438 if not packages :
3539 return
3640 pending : list [str ] = []
@@ -48,33 +52,7 @@ def install(self, packages: list[str], upgrade=False):
4852 return
4953 packages = pending
5054
51- pyexec = sys .executable
52- if "py" not in pyexec :
53- # 这不是 Python, 是 ToolDelta
54- install_opts = [
55- "pip" ,
56- "install" ,
57- "-i" ,
58- "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" ,
59- "--target" ,
60- self .data_path ,
61- * packages ,
62- ]
63- else :
64- install_opts = [
65- sys .executable ,
66- "-m" ,
67- "pip" ,
68- "install" ,
69- "-i" ,
70- "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple" ,
71- "--target" ,
72- self .data_path ,
73- * packages ,
74- ]
75- if upgrade :
76- install_opts .append ("--upgrade" )
77-
55+ install_opts = self ._build_install_command (packages , upgrade )
7856 proc = subprocess .Popen (
7957 install_opts ,
8058 stdout = subprocess .PIPE ,
@@ -85,11 +63,73 @@ def install(self, packages: list[str], upgrade=False):
8563 self ._readline_stderr (proc )
8664 returncode = proc .wait ()
8765 if returncode != 0 :
88- fmts .print_err ("pip安装模块时出现错误 " )
66+ fmts .print_err ("模块安装时出现错误 " )
8967 raise SystemExit
9068 importlib .invalidate_caches ()
9169 fmts .print_suc (f"模块 { ', ' .join (packages )} 安装成功" )
9270
71+ def _build_install_command (
72+ self , packages : list [str ], upgrade : bool = False
73+ ) -> list [str ]:
74+ """Build the command used to install Python packages."""
75+ install_args = [
76+ "install" ,
77+ "--index-url" ,
78+ PYPI_INDEX_URL ,
79+ "--target" ,
80+ str (self .data_path ),
81+ ]
82+ if upgrade :
83+ install_args .append ("--upgrade" )
84+ install_args .extend (packages )
85+
86+ if self ._current_python_has_pip ():
87+ return [sys .executable , "-m" , "pip" , * install_args ]
88+
89+ uv_exec = shutil .which ("uv" )
90+ if uv_exec :
91+ uv_args = ["pip" , * install_args ]
92+ if self ._can_run_current_python ():
93+ uv_args [2 :2 ] = ["--python" , sys .executable ]
94+ return [uv_exec , * uv_args ]
95+
96+ pip_exec = shutil .which ("pip" )
97+ if pip_exec :
98+ return [pip_exec , * install_args ]
99+
100+ fmts .print_err ("未找到可用的 pip 或 uv 命令" )
101+ raise SystemExit
102+
103+ @staticmethod
104+ def _can_run_current_python () -> bool :
105+ """Return whether the current Python executable can be started."""
106+ try :
107+ proc = subprocess .run (
108+ [sys .executable , "-c" , "import sys" ],
109+ stdout = subprocess .DEVNULL ,
110+ stderr = subprocess .DEVNULL ,
111+ check = False ,
112+ )
113+ except OSError :
114+ return False
115+ return proc .returncode == 0
116+
117+ @classmethod
118+ def _current_python_has_pip (cls ) -> bool :
119+ """Return whether the current Python executable can run pip."""
120+ if not cls ._can_run_current_python ():
121+ return False
122+ try :
123+ proc = subprocess .run (
124+ [sys .executable , "-m" , "pip" , "--version" ],
125+ stdout = subprocess .DEVNULL ,
126+ stderr = subprocess .DEVNULL ,
127+ check = False ,
128+ )
129+ except OSError :
130+ return False
131+ return proc .returncode == 0
132+
93133 def require (self , module_pip_name_and_imp_name : dict [str , str ] | str | list [str ]):
94134 """
95135 显式需求一个 Python 模块。
0 commit comments