Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 29 additions & 36 deletions src/managers/builtin/pipPackageManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
import { showErrorMessageWithLogs } from '../../common/errors/utils';
import { PythonVersion } from '../../common/pythonVersion';
import { showErrorMessage, withProgress } from '../../common/window.apis';
import { PackageToolSupport, pythonToolSupport, throwIfCancelled } from '../../internal/pythonToolSupport';
import { CommandConstructorOptions } from '../base/commands/index';
import { updatePackagesAndNotify } from '../common/packageChanges';
import { parsePackageSpecs } from '../common/packageUtils';
Expand All @@ -48,7 +49,6 @@ import {
} from './commands/index';
import { getWorkspacePackagesToInstall } from './pipUtils';
import { VenvManager } from './venvManager';
import { PackageToolSupport, pythonToolSupport, throwIfCancelled } from '../../internal/pythonToolSupport';

export class PipPackageManager implements PackageManager, Disposable {
private readonly _onDidChangePackages = new EventEmitter<DidChangePackagesEventArgs>();
Expand Down Expand Up @@ -293,56 +293,49 @@ export class PipPackageManager implements PackageManager, Disposable {
}
}

async getPackageAvailableVersions(
private async getPackageAvailableVersionsCommand(
environment: PythonEnvironment,
packageName: string,
): Promise<Pep440Version[]> {
): Promise<PipAvailableVersionsCommand | UvAvailableVersionsCommand | PipAvailableVersionsTextCommand> {
Comment thread
edvilme marked this conversation as resolved.
const pythonExecutable = environment.execInfo?.run?.executable;
if (!pythonExecutable) {
throw new Error(`Python executable is unavailable for environment: ${environment.envId.id}`);
}

const pythonVersion = PythonVersion.tryParse(environment.version);
if (!pythonVersion) {
throw new Error(`Python version is unavailable for environment: ${environment.envId.id}`);
}
const baseVersion = pythonVersion.toReleaseString();

const availableVersions = await createPipOrUvCommandWithKind(
{ pythonExecutable, log: this.log },
environment.environmentPath.fsPath,
PipAvailableVersionsCommand,
UvAvailableVersionsCommand,
);
if (availableVersions.kind === 'uv') {
return availableVersions.command;
}

// For pip < 21.2.0, check version first.
if (availableVersions.kind === 'pip') {
const pipVersion = await new PipVersionCommand({ pythonExecutable, log: this.log }).execute();
if (!pipVersion) {
throw new Error(`Unable to determine pip version for environment: ${environment.envId.id}`);
}
if (compare(pipVersion.public, '21.2.0') < 0) {
throw new PackageVersionLookupNotSupportedError(
`Package version lookup requires pip 21.2 or newer; the environment has pip ${pipVersion.public}.`,
);
}
if (compare(pipVersion.public, '25.1') >= 0) {
const versions = await availableVersions.command.execute({
packageName,
pythonVersion: baseVersion,
});
return versions.sort((a, b) => compare(b.public, a.public));
}

const textCommand = new PipAvailableVersionsTextCommand({ pythonExecutable, log: this.log });
const textVersions = await textCommand.execute({ packageName, pythonVersion: baseVersion });
return textVersions.sort((a, b) => compare(b.public, a.public));
const version = await new PipVersionCommand({ pythonExecutable, log: this.log }).execute();
if (!version) {
throw new Error(`Unable to determine pip version for environment: ${environment.envId.id}`);
}
if (compare(version.public, '21.2.0') < 0) {
throw new PackageVersionLookupNotSupportedError(
`Package version lookup requires pip 21.2 or newer; the environment has pip ${version.public}.`,
);
}
if (compare(version.public, '25.1') >= 0) {
return availableVersions.command;
}

const versions = await availableVersions.command.execute({
packageName,
pythonVersion: baseVersion,
});
return new PipAvailableVersionsTextCommand({ pythonExecutable, log: this.log });
}

async getPackageAvailableVersions(environment: PythonEnvironment, packageName: string): Promise<Pep440Version[]> {
const baseVersion = environment.version
? PythonVersion.tryParse(environment.version)?.toReleaseString()
: undefined;
if (!baseVersion) {
throw new Error(`Python version is unavailable for environment: ${environment.envId.id}`);
}
const command = await this.getPackageAvailableVersionsCommand(environment);
const versions = await command.execute({ packageName, pythonVersion: baseVersion });
Comment thread
Copilot marked this conversation as resolved.
return versions.sort((a, b) => compare(b.public, a.public));
}

Expand Down
20 changes: 17 additions & 3 deletions src/test/managers/builtin/pipPackageManager.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,28 @@ suite('PipPackageManager', () => {
test('uses UV version lookup without querying the Pip version', async () => {
const manager = createManager();
const environment = createEnvironment();
sinon.stub(helpers, 'shouldUseUv').resolves(true);
sinon.stub(helpers, 'getUvExecutable').resolves('uv');
const shouldUseUv = sinon.stub(helpers, 'shouldUseUv').resolves(true);
const getUvExecutable = sinon.stub(helpers, 'getUvExecutable').resolves('workspace-uv');
const runPython = sinon.stub(helpers, 'runPython');
sinon.stub(helpers, 'runUV').resolves(JSON.stringify({ versions: ['2.32.5', '2.31.0'] }));
const runUV = sinon
.stub(helpers, 'runUV')
.resolves(JSON.stringify({ versions: ['2.32.5', '2.31.0'] }));

const versions = await manager.getPackageAvailableVersions(environment, 'requests');

assert.ok(runPython.notCalled);
assert.ok(
shouldUseUv.calledOnceWithExactly(
manager.log,
environment.environmentPath.fsPath,
environment.environmentPath.fsPath,
undefined,
),
);
assert.ok(
getUvExecutable.calledOnceWithExactly(manager.log, environment.environmentPath.fsPath, undefined),
);
assert.strictEqual(runUV.firstCall.args[5], 'workspace-uv');
assert.deepStrictEqual(
versions.map((version) => version.public),
['2.32.5', '2.31.0'],
Expand Down
Loading