Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f763ea4
test: enforce headless package conformance
edvilme Aug 17, 2026
cb05041
Potential fix for pull request finding
edvilme Aug 18, 2026
e1f3481
Merge branch 'main' into package-manager-integration-coverage-feedback
edvilme Aug 18, 2026
df6d1a2
test: disable unstable package network CI (#1717)
edvilme Aug 18, 2026
efe2f9c
Enforce headless package manager behavior
edvilme Aug 19, 2026
cd739a5
Run package network tests in integration matrix
edvilme Aug 19, 2026
13cd44d
Stabilize package network integration setup
edvilme Aug 19, 2026
6f93cf0
Use existing Conda environment in network tests
edvilme Aug 19, 2026
a435996
Wait for package network results
edvilme Aug 19, 2026
a9de0e7
Skip ambiguous package version results
edvilme Aug 19, 2026
8fe8255
Avoid blocking Conda prerequisite lookup
edvilme Aug 19, 2026
0462436
test: address package manager review feedback
edvilme Aug 19, 2026
148891f
test: add ephemeral environment fixtures
edvilme Aug 19, 2026
6764006
test: allow unavailable pip versions
edvilme Aug 19, 2026
ebb222d
test: harden fixture cleanup
edvilme Aug 19, 2026
0601d34
test: preserve package refresh failures
edvilme Aug 19, 2026
26250c5
fix: distinguish unsupported version lookup
edvilme Aug 19, 2026
76c45af
test: harden package integration retries
edvilme Aug 19, 2026
a0fe872
test: assert version lookup capability
edvilme Aug 19, 2026
afac6d2
fix: keep version lookup error internal
edvilme Aug 19, 2026
1917253
Merge main into package manager integration coverage
edvilme Aug 20, 2026
eb998c5
fix: stabilize pip package integration tests
edvilme Aug 20, 2026
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
17 changes: 9 additions & 8 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ jobs:
name: Integration Tests
runs-on: ${{ matrix.os }}
needs: [smoke-tests]
env:
CONDA_PLUGINS_AUTO_ACCEPT_TOS: 'true'
VSC_PYTHON_PACKAGE_NETWORK_TEST: '1'
Comment thread
edvilme marked this conversation as resolved.
Comment thread
edvilme marked this conversation as resolved.
Comment thread
edvilme marked this conversation as resolved.
Comment thread
edvilme marked this conversation as resolved.
Comment thread
edvilme marked this conversation as resolved.
strategy:
Comment thread
edvilme marked this conversation as resolved.
fail-fast: false
matrix:
Expand Down Expand Up @@ -325,6 +328,12 @@ jobs:
- name: Compile Tests
run: npm run compile-tests

- name: Set up Conda
uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3
with:
activate-environment: ''
auto-activate: false

- name: Run Integration Tests (Linux)
if: runner.os == 'Linux'
uses: GabrielBB/xvfb-action@86d97bde4a65fe9b290c0b3fb92c2c4ed0e5302d # v1.6
Expand All @@ -335,14 +344,6 @@ jobs:
if: runner.os != 'Linux'
Comment thread
edvilme marked this conversation as resolved.
run: npm run integration-test

- name: Run Package Manager Network Integration Tests
if: runner.os == 'Linux' && matrix.python-version == '3.12'
uses: GabrielBB/xvfb-action@86d97bde4a65fe9b290c0b3fb92c2c4ed0e5302d # v1.6
env:
VSC_PYTHON_PACKAGE_NETWORK_TEST: '1'
with:
run: npm run integration-test -- --grep "Package Manager"

integration-tests-multiroot:
name: Integration Tests (Multi-Root)
runs-on: ${{ matrix.os }}
Expand Down
17 changes: 9 additions & 8 deletions .github/workflows/push-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ jobs:
name: Integration Tests
runs-on: ${{ matrix.os }}
needs: [smoke-tests]
env:
CONDA_PLUGINS_AUTO_ACCEPT_TOS: 'true'
VSC_PYTHON_PACKAGE_NETWORK_TEST: '1'
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -326,6 +329,12 @@ jobs:
- name: Compile Tests
run: npm run compile-tests

- name: Set up Conda
uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3
with:
activate-environment: ''
auto-activate: false

- name: Run Integration Tests (Linux)
if: runner.os == 'Linux'
uses: GabrielBB/xvfb-action@86d97bde4a65fe9b290c0b3fb92c2c4ed0e5302d # v1.6
Expand All @@ -335,11 +344,3 @@ jobs:
- name: Run Integration Tests (non-Linux)
if: runner.os != 'Linux'
run: npm run integration-test

- name: Run Package Manager Network Integration Tests
if: runner.os == 'Linux' && matrix.python-version == '3.12'
uses: GabrielBB/xvfb-action@86d97bde4a65fe9b290c0b3fb92c2c4ed0e5302d # v1.6
env:
VSC_PYTHON_PACKAGE_NETWORK_TEST: '1'
with:
run: npm run integration-test -- --grep "Package Manager"
17 changes: 12 additions & 5 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import type {
import { extensions } from 'vscode';

export type { Pep440Version } from '@renovatebot/pep440';
export {
isPackageVersionLookupNotSupportedError,
PackageVersionLookupNotSupportedError,
} from './common/errors/NotSupportedError';

/*
* Do not introduce any breaking changes to this API.
Expand Down Expand Up @@ -749,8 +753,11 @@ export interface PackageManager {
* Retrieves the list of available versions for a given package.
* @param environment - The Python environment context for the lookup.
* @param packageName - The name of the package to look up.
* @returns A promise that resolves to an array of {@link Pep440Version} objects (newest first),
* or `undefined` if this manager does not support version listing.
* Managers that do not support version listing should reject with
* {@link PackageVersionLookupNotSupportedError}.
*
* @returns A promise that resolves to an array of {@link Pep440Version} objects (newest first).
* `undefined` remains supported for compatibility with existing manager implementations.
*/
getPackageAvailableVersions?(
environment: PythonEnvironment,
Expand Down Expand Up @@ -1140,12 +1147,12 @@ export interface PythonPackageGetterApi {
* Get the list of available versions for a package, newest first.
*
* Support depends on the package manager backing the environment. Managers that do
* not implement version lookup resolve to `undefined`.
* not implement version lookup reject with {@link PackageVersionLookupNotSupportedError}.
*
* @param environment The Python Environment context for the lookup.
* @param packageName The name of the package to look up.
* @returns A promise that resolves to an array of {@link Pep440Version} objects (newest first),
* or `undefined` if the package manager does not support version listing.
* @returns A promise that resolves to an array of {@link Pep440Version} objects (newest first).
* `undefined` remains supported for compatibility with existing manager implementations.
*/
getPackageAvailableVersions(
environment: PythonEnvironment,
Expand Down
29 changes: 29 additions & 0 deletions src/common/errors/NotSupportedError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,32 @@ export class RemoveEnvironmentNotSupported extends BaseError {
super('NotSupported', message);
}
}

/**
* Indicates that a package manager cannot list available package versions.
*/
export class PackageVersionLookupNotSupportedError extends BaseError {
readonly code = 'PackageVersionLookupNotSupported';

constructor(message: string) {
super('NotSupported', message);
}
}

/**
* Checks whether an error represents unsupported package version lookup.
*
* The stable code check supports errors crossing extension bundle boundaries,
* where `instanceof` may not use the same class constructor.
*/
export function isPackageVersionLookupNotSupportedError(
error: unknown,
): error is PackageVersionLookupNotSupportedError {
return (
error instanceof PackageVersionLookupNotSupportedError ||
(typeof error === 'object' &&
error !== null &&
'code' in error &&
error.code === 'PackageVersionLookupNotSupported')
);
}
10 changes: 10 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,16 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
commands.registerCommand('python-envs.test.getPackageManagerIds', () =>
envManagers.packageManagers.map((manager) => manager.id),
),
commands.registerCommand(
'python-envs.test.resolveEnvironmentWithManager',
async (managerId: string, environmentUri: Uri) => {
const manager = envManagers.getEnvironmentManager(managerId);
if (!manager) {
throw new Error(`Environment manager not found: ${managerId}`);
}
return manager.resolve(environmentUri);
},
),
commands.registerCommand(
'python-envs.test.getDirectPackageNames',
async (environment: PythonEnvironment) => {
Expand Down
17 changes: 13 additions & 4 deletions src/features/envCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
} from 'vscode';
import {
CreateEnvironmentOptions,
isPackageVersionLookupNotSupportedError,
Pep440Version,
PythonEnvironment,
PythonEnvironmentApi,
PythonProject,
Expand Down Expand Up @@ -363,10 +365,17 @@ export async function managePackageVersion(context: unknown, em: EnvironmentMana
let version: string | undefined;

// Try to fetch available versions for a QuickPick experience
const availableVersions = await withProgress(
{ location: ProgressLocation.Window, title: l10n.t('Fetching available versions for {0}...', pkg.name) },
() => packageManager.getPackageAvailableVersions(environment, pkg.name),
);
let availableVersions: Pep440Version[] | undefined;
try {
availableVersions = await withProgress(
{ location: ProgressLocation.Window, title: l10n.t('Fetching available versions for {0}...', pkg.name) },
() => packageManager.getPackageAvailableVersions(environment, pkg.name),
);
} catch (error) {
if (!isPackageVersionLookupNotSupportedError(error)) {
throw error;
}
}

if (availableVersions && availableVersions.length > 0) {
const items = availableVersions.map((v) => ({
Expand Down
5 changes: 4 additions & 1 deletion src/features/pythonApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
ResolveEnvironmentContext,
SetEnvironmentScope,
} from '../api';
import { PackageVersionLookupNotSupportedError } from '../common/errors/NotSupportedError';
import { traceError, traceInfo } from '../common/logging';
import { pickEnvironmentManager } from '../common/pickers/managers';
import { timeout } from '../common/utils/asyncUtils';
Expand Down Expand Up @@ -326,7 +327,9 @@ export class PythonEnvironmentApiImpl implements PythonEnvironmentApi {
await waitForEnvManagerId([context.envId.managerId]);
const manager = this.envManagers.getPackageManager(context);
if (!manager) {
return Promise.resolve(undefined);
throw new PackageVersionLookupNotSupportedError(
`No package manager supports version lookup for: ${context.envId.id}`,
);
}
Comment thread
edvilme marked this conversation as resolved.
return manager.getPackageAvailableVersions(context, packageName);
Comment thread
edvilme marked this conversation as resolved.
Outdated
}
Expand Down
10 changes: 8 additions & 2 deletions src/internal.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ import {
SetEnvironmentScope,
} from './api';
import { ISSUES_URL } from './common/constants';
import { CreateEnvironmentNotSupported, RemoveEnvironmentNotSupported } from './common/errors/NotSupportedError';
import {
CreateEnvironmentNotSupported,
PackageVersionLookupNotSupportedError,
RemoveEnvironmentNotSupported,
} from './common/errors/NotSupportedError';
import { traceWarn } from './common/logging';
import { StopWatch } from './common/stopWatch';
import { EventNames } from './common/telemetry/constants';
Expand Down Expand Up @@ -403,7 +407,9 @@ export class InternalPackageManager implements PackageManager {
): Promise<Pep440Version[] | undefined> {
return this.manager.getPackageAvailableVersions
? this.manager.getPackageAvailableVersions(environment, packageName)
: Promise.resolve(undefined);
: Promise.reject(
new PackageVersionLookupNotSupportedError(`Package version lookup not supported by: ${this.id}`),
);
}

getDirectPackageNames(environment: PythonEnvironment): Promise<Set<string> | undefined> {
Expand Down
Loading
Loading