Skip to content
Open
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
1 change: 1 addition & 0 deletions api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [1.37.0]

- Aligned the API package version with the Python Environments extension version.
- Added `getPackageManager` to retrieve the registered package manager for an environment.
8 changes: 8 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,14 @@ export interface PythonPackageManagerRegistrationApi {
}

export interface PythonPackageGetterApi {
/**
* Get the registered package manager associated with a Python Environment.
*
* @param environment The Python Environment whose package manager is required.
* @returns The registered package manager, or undefined if no package manager is available.
*/
getPackageManager(environment: PythonEnvironment): Promise<PackageManager | undefined>;

/**
* Refresh the list of packages in a Python Environment.
*
Expand Down
4 changes: 4 additions & 0 deletions src/features/pythonApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,10 @@ export class PythonEnvironmentApiImpl implements PythonEnvironmentApi {
}
return manager.manage(context, options);
}
async getPackageManager(context: PythonEnvironment): Promise<PackageManager | undefined> {
await waitForEnvManagerId([context.envId.managerId]);
return this.envManagers.getPackageManager(context);
}
async refreshPackages(context: PythonEnvironment): Promise<Package[] | undefined> {
await waitForEnvManagerId([context.envId.managerId]);
const manager = this.envManagers.getPackageManager(context);
Expand Down
124 changes: 124 additions & 0 deletions src/test/integration/packageManager.integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import * as vscode from 'vscode';

import assert from 'assert';
import { PythonEnvironment, PythonEnvironmentApi } from '../../api';
import { CONDA_MANAGER_ID, VENV_MANAGER_ID } from '../../common/constants';
import { ENVS_EXTENSION_ID } from '../constants';
import { waitForCondition } from '../testUtils';

const profiles = [
{
environmentManagerId: VENV_MANAGER_ID,
environmentDirectory: '.venv',
name: 'Pip',
},
{
environmentManagerId: CONDA_MANAGER_ID,
environmentDirectory: '.conda',
name: 'Conda',
},
];

async function deleteEnvironmentDirectory(uri: vscode.Uri): Promise<void> {
try {
await vscode.workspace.fs.delete(uri, { recursive: true, useTrash: false });
} catch (error) {
if (!(error instanceof vscode.FileSystemError) || error.code !== 'FileNotFound') {
throw error;
}
}
}

for (const profile of profiles) {
suite(`${profile.name} Package Manager`, function () {
this.timeout(300_000);

let api: PythonEnvironmentApi;
let environment: PythonEnvironment | undefined;
let workspaceUri: vscode.Uri;
let previousDefaultEnvManager: string | undefined;
let defaultEnvManagerUpdated = false;
let previousAlwaysUseUv: boolean | undefined;
let alwaysUseUvUpdated = false;
suiteSetup(async function () {
const extension = vscode.extensions.getExtension(ENVS_EXTENSION_ID);
assert.ok(extension, 'Extension not found');
if (!extension.isActive) {
await extension.activate();
await waitForCondition(() => extension.isActive, 20_000, 'Extension did not activate in time');
}
api = extension.exports;
assert.ok(api, 'API not available');

const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
assert.ok(workspaceFolder, 'Integration test workspace not found');
workspaceUri = workspaceFolder.uri;
const config = vscode.workspace.getConfiguration('python-envs', workspaceUri);
previousDefaultEnvManager = config.inspect<string>('defaultEnvManager')?.workspaceValue;
await config.update(
'defaultEnvManager',
profile.environmentManagerId,
vscode.ConfigurationTarget.Workspace,
);
defaultEnvManagerUpdated = true;

if (profile.environmentManagerId === VENV_MANAGER_ID) {
previousAlwaysUseUv = config.inspect<boolean>('alwaysUseUv')?.globalValue;
await config.update('alwaysUseUv', false, vscode.ConfigurationTarget.Global);
alwaysUseUvUpdated = true;
}

const environmentDirectory = vscode.Uri.joinPath(workspaceUri, profile.environmentDirectory);
await deleteEnvironmentDirectory(environmentDirectory);
await api.refreshEnvironments(workspaceUri);

environment = await api.createEnvironment(workspaceUri, { quickCreate: true });
if (!environment) {
this.skip();
return;
}
assert.strictEqual(
environment.envId.managerId,
profile.environmentManagerId,
`Expected an environment created by ${profile.environmentManagerId}`,
);
});

test(`${profile.name} Package Manager should install, list, and uninstall a package`, async () => {
await api.managePackages(environment!, { install: ['requests'] });
let packages = await api.getPackages(environment!, { skipCache: true });
assert.ok(packages?.some((pkg) => pkg.name === 'requests'), 'Package not installed');

await api.managePackages(environment!, { uninstall: ['requests'] });
packages = await api.getPackages(environment!, { skipCache: true });
assert.ok(!packages?.some((pkg) => pkg.name === 'requests'), 'Package not uninstalled');
});

test(`${profile.name} Package Manager should list available package versions`, async () => {
const packageManager = await api.getPackageManager(environment!);
assert.ok(packageManager, 'Package manager not available');
assert.ok(packageManager.getPackageAvailableVersions, 'Available versions method not available');

const versions = await packageManager.getPackageAvailableVersions(environment!, 'requests');
assert.ok(versions, 'Package versions not available');
assert.ok(versions.length > 0, 'No package versions available');
});

suiteTeardown(async () => {
try {
await deleteEnvironmentDirectory(vscode.Uri.joinPath(workspaceUri, profile.environmentDirectory));
} finally {
if (alwaysUseUvUpdated) {
await vscode.workspace
.getConfiguration('python-envs')
.update('alwaysUseUv', previousAlwaysUseUv, vscode.ConfigurationTarget.Global);
}
if (defaultEnvManagerUpdated) {
await vscode.workspace
.getConfiguration('python-envs', workspaceUri)
.update('defaultEnvManager', previousDefaultEnvManager, vscode.ConfigurationTarget.Workspace);
}
}
});
});
}
Loading