Skip to content

Commit cc65bfe

Browse files
committed
fix: 依赖安装失败时从失败的包开始切换镜像重试,而不是跳到下一个包
1 parent 6794d3d commit cc65bfe

1 file changed

Lines changed: 111 additions & 4 deletions

File tree

packaging/launch.py

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
151257
def 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

Comments
 (0)