Skip to content

Commit aef7859

Browse files
Add PyPI mirror fallbacks (#1526)
This extends pip install fallback behavior when a PyPI mirror is configured by adding multiple `--extra-index-url` values, ending with standard PyPI, so packages missing on TUNA (e.g. comfy-kitchen) still resolve. Fallback order: - https://mirrors.aliyun.com/pypi/simple/ - https://mirrors.cloud.tencent.com/pypi/simple/ - https://pypi.org/simple/ ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-1526-Add-TorchMirrorUrl-Default-as-extraIndexUrl-2e76d73d365081f9b0e4fabd583bea75) by [Unito](https://www.unito.io)
1 parent 676b957 commit aef7859

File tree

3 files changed

+54
-5
lines changed

3 files changed

+54
-5
lines changed

src/constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,12 @@ export const NIGHTLY_CPU_TORCH_URL = TorchMirrorUrl.NightlyCpu;
191191
/** @deprecated Use {@link TorchMirrorUrl} instead. */
192192
export const DEFAULT_PYPI_INDEX_URL = TorchMirrorUrl.Default;
193193

194+
export const PYPI_FALLBACK_INDEX_URLS: string[] = [
195+
'https://mirrors.aliyun.com/pypi/simple/',
196+
'https://mirrors.cloud.tencent.com/pypi/simple/',
197+
TorchMirrorUrl.Default,
198+
];
199+
194200
export const AMD_ROCM_SDK_PACKAGES: string[] = [
195201
'https://repo.radeon.com/rocm/windows/rocm-rel-7.1.1/rocm_sdk_core-0.1.dev0-py3-none-win_amd64.whl',
196202
'https://repo.radeon.com/rocm/windows/rocm-rel-7.1.1/rocm_sdk_libraries_custom-0.1.dev0-py3-none-win_amd64.whl',

src/virtualEnvironment.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
NVIDIA_TORCHVISION_VERSION,
1616
NVIDIA_TORCH_PACKAGES,
1717
NVIDIA_TORCH_VERSION,
18+
PYPI_FALLBACK_INDEX_URLS,
1819
TorchMirrorUrl,
1920
} from './constants';
2021
import { PythonImportVerificationError } from './infrastructure/pythonImportVerificationError';
@@ -45,7 +46,7 @@ export interface PythonExecutor {
4546
interface PipInstallConfig {
4647
packages: string[];
4748
indexUrl?: string;
48-
extraIndexUrl?: string;
49+
extraIndexUrls?: string[];
4950
prerelease?: boolean;
5051
upgradePackages?: boolean;
5152
requirementsFile?: string;
@@ -57,7 +58,7 @@ type TorchPackageVersions = Record<TorchPackageName, string | undefined>;
5758

5859
const TORCH_PACKAGE_NAMES: TorchPackageName[] = ['torch', 'torchaudio', 'torchvision'];
5960

60-
function getPipInstallArgs(config: PipInstallConfig): string[] {
61+
export function getPipInstallArgs(config: PipInstallConfig): string[] {
6162
const installArgs = ['pip', 'install'];
6263

6364
if (config.upgradePackages) {
@@ -78,8 +79,10 @@ function getPipInstallArgs(config: PipInstallConfig): string[] {
7879
installArgs.push('--index-url', config.indexUrl);
7980
}
8081

81-
if (config.extraIndexUrl) {
82-
installArgs.push('--extra-index-url', config.extraIndexUrl);
82+
if (config.extraIndexUrls) {
83+
for (const extraIndexUrl of config.extraIndexUrls) {
84+
installArgs.push('--extra-index-url', extraIndexUrl);
85+
}
8386
}
8487

8588
if (config.indexStrategy) {
@@ -153,6 +156,15 @@ export class VirtualEnvironment implements HasTelemetry, PythonExecutor {
153156
};
154157
}
155158

159+
/**
160+
* Returns extra index URLs to use for pip installs.
161+
* @returns The fallback index URLs, or `undefined` if none are configured.
162+
*/
163+
private getPypiFallbackIndexUrls(): string[] | undefined {
164+
const fallbackUrls = PYPI_FALLBACK_INDEX_URLS.filter((url) => url !== this.pypiMirror);
165+
return fallbackUrls.length > 0 ? fallbackUrls : undefined;
166+
}
167+
156168
/** @todo Refactor to `using` */
157169
get uvPtyInstance() {
158170
const env = {
@@ -401,6 +413,7 @@ export class VirtualEnvironment implements HasTelemetry, PythonExecutor {
401413
indexStrategy: 'unsafe-best-match',
402414
packages: [],
403415
indexUrl: this.pypiMirror,
416+
extraIndexUrls: this.getPypiFallbackIndexUrls(),
404417
});
405418
const { exitCode } = await this.runUvCommandAsync(installCmd, callbacks);
406419
if (exitCode !== 0) {
@@ -815,6 +828,7 @@ export class VirtualEnvironment implements HasTelemetry, PythonExecutor {
815828
requirementsFile: this.comfyUIRequirementsPath,
816829
packages: [],
817830
indexUrl: this.pypiMirror,
831+
extraIndexUrls: this.getPypiFallbackIndexUrls(),
818832
});
819833
const { exitCode } = await this.runUvCommandAsync(installCmd, callbacks);
820834
if (exitCode !== 0) {
@@ -846,6 +860,7 @@ export class VirtualEnvironment implements HasTelemetry, PythonExecutor {
846860
requirementsFile: this.comfyUIManagerRequirementsPath,
847861
packages: [],
848862
indexUrl: this.pypiMirror,
863+
extraIndexUrls: this.getPypiFallbackIndexUrls(),
849864
});
850865
const { exitCode } = await this.runUvCommandAsync(installCmd, callbacks);
851866
if (exitCode !== 0) {

tests/unit/virtualEnvironment.test.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import { type ChildProcess, spawn } from 'node:child_process';
33
import path from 'node:path';
44
import { test as baseTest, describe, expect, vi } from 'vitest';
55

6+
import { TorchMirrorUrl } from '@/constants';
67
import type { ITelemetry } from '@/services/telemetry';
7-
import { VirtualEnvironment } from '@/virtualEnvironment';
8+
import { VirtualEnvironment, getPipInstallArgs } from '@/virtualEnvironment';
89

910
vi.mock('@sentry/electron/main', () => ({
1011
init: vi.fn(),
@@ -151,6 +152,33 @@ test.for(allCombinations)('hasRequirements', async ({ core, manager }, { virtual
151152
});
152153

153154
describe('VirtualEnvironment', () => {
155+
describe('getPipInstallArgs', () => {
156+
test('includes unsafe-best-match and extra index URL args', () => {
157+
const args = getPipInstallArgs({
158+
requirementsFile: '/tmp/requirements.txt',
159+
packages: [],
160+
indexUrl: 'https://mirror.example/simple/',
161+
extraIndexUrls: ['https://mirror-two.example/simple/', TorchMirrorUrl.Default],
162+
indexStrategy: 'unsafe-best-match',
163+
});
164+
165+
expect(args).toEqual([
166+
'pip',
167+
'install',
168+
'-r',
169+
'/tmp/requirements.txt',
170+
'--index-url',
171+
'https://mirror.example/simple/',
172+
'--extra-index-url',
173+
'https://mirror-two.example/simple/',
174+
'--extra-index-url',
175+
TorchMirrorUrl.Default,
176+
'--index-strategy',
177+
'unsafe-best-match',
178+
]);
179+
});
180+
});
181+
154182
describe('hasRequirements', () => {
155183
test('returns OK when all packages are installed', async ({ virtualEnv }) => {
156184
mockSpawnOutputOnce('Would make no changes\n');

0 commit comments

Comments
 (0)