@@ -103,7 +103,7 @@ def run_pip(args, desc=None):
103103
104104 import urllib .parse
105105
106- def build_pip_command (mirror_url = None ):
106+ def build_pip_command (pip_args , mirror_url = None ):
107107 """构建pip命令"""
108108 index_url_line = f' --index-url { mirror_url } ' if mirror_url else ''
109109 trusted_host_line = ''
@@ -114,7 +114,7 @@ def build_pip_command(mirror_url=None):
114114 trusted_host_line += f' --trusted-host { parsed .hostname } '
115115 trusted_host_line += ' --trusted-host download.pytorch.org'
116116
117- return f'"{ python } " -m pip { args } --prefer-binary{ index_url_line } { trusted_host_line } --disable-pip-version-check --no-warn-script-location'
117+ return f'"{ python } " -m pip { pip_args } --prefer-binary{ index_url_line } { trusted_host_line } --disable-pip-version-check --no-warn-script-location'
118118
119119 # 如果用户指定了 INDEX_URL,优先使用
120120 if index_url :
@@ -131,7 +131,7 @@ def build_pip_command(mirror_url=None):
131131 else :
132132 print (f"尝试备用镜像源: { mirror_name } " )
133133
134- cmd = build_pip_command (mirror )
134+ cmd = build_pip_command (args , mirror )
135135 result = subprocess .run (cmd , shell = True , env = os .environ )
136136
137137 if result .returncode == 0 :
@@ -148,6 +148,112 @@ def build_pip_command(mirror_url=None):
148148 raise RuntimeError (f"无法安装 { desc } ,所有镜像源均失败。最后错误: { last_error } " )
149149
150150
151+ def run_pip_requirements (requirements_file , desc = None ):
152+ """逐个安装requirements文件中的包,失败时从失败的包开始切换镜像重试"""
153+ if skip_install :
154+ return
155+
156+ import urllib .parse
157+ from pathlib import Path
158+
159+ def build_pip_command (pip_args , mirror_url = None ):
160+ """构建pip命令"""
161+ index_url_line = f' --index-url { mirror_url } ' if mirror_url else ''
162+ trusted_host_line = ''
163+
164+ if mirror_url :
165+ parsed = urllib .parse .urlparse (mirror_url )
166+ if parsed .hostname :
167+ trusted_host_line += f' --trusted-host { parsed .hostname } '
168+ trusted_host_line += ' --trusted-host download.pytorch.org'
169+
170+ return f'"{ python } " -m pip { pip_args } --prefer-binary{ index_url_line } { trusted_host_line } --disable-pip-version-check --no-warn-script-location'
171+
172+ # 读取 requirements 文件
173+ req_path = Path (requirements_file )
174+ if not req_path .exists ():
175+ raise RuntimeError (f"找不到依赖文件: { requirements_file } " )
176+
177+ # 解析 requirements 文件,提取有效的包
178+ packages = []
179+ with open (req_path , 'r' , encoding = 'utf-8' ) as f :
180+ for line in f :
181+ line = line .strip ()
182+ # 跳过空行、注释、pip选项
183+ if not line or line .startswith ('#' ) or line .startswith ('-' ):
184+ continue
185+ # 去除行内注释
186+ line = line .split ('#' )[0 ].strip ()
187+ if line :
188+ packages .append (line )
189+
190+ if not packages :
191+ print (f"[警告] { requirements_file } 中没有找到有效的依赖包" )
192+ return
193+
194+ # 如果用户指定了 INDEX_URL,优先使用
195+ if index_url :
196+ mirrors_to_try = [index_url ] + [m for m in MIRROR_URLS if m != index_url ]
197+ else :
198+ mirrors_to_try = MIRROR_URLS .copy ()
199+
200+ total = len (packages )
201+ print (f"正在安装 { desc or requirements_file } ... (共 { total } 个包)" )
202+
203+ # 当前镜像索引
204+ current_mirror_idx = 0
205+ # 当前包索引
206+ pkg_idx = 0
207+
208+ while pkg_idx < total :
209+ pkg = packages [pkg_idx ]
210+ mirror = mirrors_to_try [current_mirror_idx ]
211+ mirror_name = urllib .parse .urlparse (mirror ).hostname or mirror
212+
213+ # 获取包名用于显示(去除版本约束)
214+ pkg_display = pkg .split ('==' )[0 ].split ('>=' )[0 ].split ('<=' )[0 ].split ('[' )[0 ].strip ()
215+ print (f"[{ pkg_idx + 1 } /{ total } ] 安装 { pkg_display } ..." )
216+
217+ cmd = build_pip_command (f'install "{ pkg } "' , mirror )
218+
219+ try :
220+ result = subprocess .run (cmd , shell = True , env = os .environ )
221+
222+ if result .returncode == 0 :
223+ # 安装成功,继续下一个包
224+ pkg_idx += 1
225+ else :
226+ # 安装失败,尝试下一个镜像
227+ print (f"[失败] { pkg_display } 在 { mirror_name } 安装失败" )
228+
229+ # 切换到下一个镜像
230+ current_mirror_idx += 1
231+
232+ if current_mirror_idx >= len (mirrors_to_try ):
233+ # 所有镜像都失败了
234+ raise RuntimeError (f"无法安装 { pkg_display } ,所有镜像源均失败" )
235+
236+ next_mirror = mirrors_to_try [current_mirror_idx ]
237+ next_mirror_name = urllib .parse .urlparse (next_mirror ).hostname or next_mirror
238+ print (f"[重试] 切换到镜像 { next_mirror_name } ,从 { pkg_display } 重新开始..." )
239+ # 不增加 pkg_idx,从当前失败的包重试
240+
241+ except Exception as e :
242+ print (f"[错误] 安装 { pkg_display } 时出错: { e } " )
243+
244+ # 切换到下一个镜像
245+ current_mirror_idx += 1
246+
247+ if current_mirror_idx >= len (mirrors_to_try ):
248+ raise RuntimeError (f"无法安装 { pkg_display } ,所有镜像源均失败。错误: { e } " )
249+
250+ next_mirror = mirrors_to_try [current_mirror_idx ]
251+ next_mirror_name = urllib .parse .urlparse (next_mirror ).hostname or next_mirror
252+ print (f"[重试] 切换到镜像 { next_mirror_name } ,从 { pkg_display } 重新开始..." )
253+
254+ print (f"[完成] { desc or requirements_file } 安装完成" )
255+
256+
151257def ensure_git_safe_directory ():
152258 """确保当前目录在 Git safe.directory 列表中,解决所有权问题"""
153259 try :
@@ -899,7 +1005,8 @@ def prepare_environment(args):
8991005 print (f'强制重新安装所有依赖...' )
9001006 else :
9011007 print (f'发现缺失依赖,正在安装...' )
902- run_pip (f"install -r { requirements_file } " , f"{ requirements_file } 中的依赖" )
1008+ # 使用逐个包安装,失败时从失败的包开始切换镜像重试
1009+ run_pip_requirements (requirements_file , f"{ requirements_file } 中的依赖" )
9031010 else :
9041011 print (f'依赖已满足 ✓' )
9051012
0 commit comments