The @vscode/python-environments package lets VS Code extensions consume and
extend the API exposed by the
Python Environments extension.
Use this manual to:
- discover, select, create, and remove Python environments;
- inspect and manage installed packages;
- work with Python projects;
- run Python in terminals, tasks, or background processes;
- resolve environment variables; or
- contribute an environment manager, package manager, or project creator.
The shared runtime facade is src/api.ts; the npm entry point
api/runtime/main.ts adds the extension-version check.
The authoritative public
contracts are in src/types.ts, with public errors and type
guards in src/publicErrors.ts.
Capability types, catalog key sets, and raw-provider resolvers are in
src/capabilities.ts.
Important
The API is flat. Call api.getEnvironments(), not
api.environments.getEnvironments(). The smaller API interfaces organize the
TypeScript declarations; they are not nested runtime objects.
- Getting started
- Domains
- Environments
- Packages
- Projects
- Execution
- Environment variables
- Extensibility
- API interface groups
- Manager capabilities
- Compatibility guidance
- Related documentation
Declare the Python Environments extension as a dependency of your extension:
Then install the API package:
npm install @vscode/python-environmentsThe npm package provides the public types and the helper used to acquire the API. The Python Environments VS Code extension provides the implementation at runtime.
Call PythonEnvironments.api() during activation:
import * as vscode from 'vscode';
import {
PythonEnvironmentApi,
PythonEnvironments,
} from '@vscode/python-environments';
export async function activate(
context: vscode.ExtensionContext,
): Promise<void> {
const api: PythonEnvironmentApi = await PythonEnvironments.api();
const environments = await api.getEnvironments('all');
// Use the API or pass it to services owned by the extension.
}PythonEnvironments.api():
- Finds the extension with ID
ms-python.vscode-python-envs(exported asEXTENSION_ID). - Verifies that its version meets the npm package's
minimumExtensionVersion. - Activates it when necessary.
- Returns its
PythonEnvironmentApi.
It rejects when the extension is missing or disabled, its version is too old or invalid, activation fails, or the extension does not expose its API. Version checks apply to already-active extensions too and reject before activation or access to exports. Acquire the object once and reuse it.
Each domain below is self-contained and has the same shape:
-
Data types - the objects the domain accepts and returns. Every type is documented as a field table with these columns:
Column Meaning Field The property name as declared in src/types.ts.Type The TypeScript type of the property. Required Yeswhen the property must be present;Nowhen it is optional (?).Description What the property means and how the extension uses it. Most returned objects declare their properties
readonly. Treat everything the API hands back as immutable, and build new objects rather than mutating them. -
Methods - the calls the domain exposes. Each method documents its signature, a parameter table using the same
Requiredconvention, its return type, and an example.
Locations are not all the same type. Scopes, project locations, and resolution
contexts are declared as vscode.Uri, so pass a Uri there rather than a
string, and let the extension resolve the owning project. A few options carry a
filesystem path instead - PythonTaskExecutionOptions.cwd,
PythonBackgroundRunOptions.cwd, and PythonCommandRunConfiguration.executable
are all string. Use uri.fsPath for those so the separator is correct on
every platform.
| Domain | Use it to | Representative members |
|---|---|---|
| Environments | Discover, resolve, select, create, and remove interpreters. | getEnvironments, resolveEnvironment, setEnvironment, createEnvironment |
| Packages | Read and change what is installed in an environment. | getPackages, managePackages, getPackageAvailableVersions |
| Projects | Read and modify the set of Python projects. | getPythonProjects, getPythonProject, addPythonProject |
| Execution | Run Python with an environment already activated. | createTerminal, runInTerminal, runAsTask, runInBackground |
| Environment variables | Resolve the effective variables for a scope. | getEnvironmentVariables, onDidChangeEnvironmentVariables |
| Extensibility | Contribute your own managers and creators. | registerEnvironmentManager, registerPackageManager, registerPythonProjectCreator |
An environment is a Python interpreter plus the information needed to run and
activate it. Environments are supplied by environment managers (venv, conda,
and any manager contributed by another extension) and are identified by
envId, never by path alone.
Uniquely identifies an environment. Two environments are the same only when both fields match.
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Manager-scoped unique identifier for the environment. Unique only within managerId. |
managerId |
string |
Yes | Identifier of the environment manager that owns the environment, formatted <publisher>.<extension>:<manager-name>. |
const key = `${env.envId.managerId}:${env.envId.id}`;The descriptive payload of an environment. Providers build this object and pass
it to createPythonEnvironmentItem; consumers
read these fields off a PythonEnvironment.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Short internal name of the environment. |
displayName |
string |
Yes | Name shown in pickers and the environment tree. |
shortDisplayName |
string |
No | Compact name for constrained UI such as the status bar. |
displayPath |
string |
Yes | Human-readable path shown alongside the name. Use a home-relative or otherwise shortened form. |
version |
string |
Yes | Python version string, for example 3.12.1. |
environmentPath |
Uri |
Yes | Path to the Python binary or the environment folder. |
description |
string |
No | Extra descriptive text shown next to the environment. |
tooltip |
string | MarkdownString |
No | Hover text for the environment. |
iconPath |
IconPath |
No | Icon shown for the environment. |
execInfo |
PythonEnvironmentExecutionInfo |
Yes | How to run and activate the interpreter. Required for any execution. |
sysPrefix |
string |
Yes | Value of sys.prefix for the environment. Consumed by Pylance, Jupyter, and similar extensions. |
group |
string | EnvironmentGroupInfo |
No | Groups the environment in the environment manager UI. The first group instance with a given name wins. |
error |
string |
No | Set when the environment is broken or invalid, for example a missing interpreter or dangling symlink. The UI shows a warning with this message. |
const info: PythonEnvironmentInfo = {
name: 'my-venv',
displayName: 'Python 3.12.1 (my-venv)',
shortDisplayName: 'my-venv',
displayPath: '~/code/app/.venv',
version: '3.12.1',
environmentPath: vscode.Uri.file('/home/me/code/app/.venv/bin/python'),
sysPrefix: '/home/me/code/app/.venv',
execInfo: { run: { executable: '/home/me/code/app/.venv/bin/python' } },
};PythonEnvironmentInfo plus its identity. This is the object every environment
method returns and nearly every other method accepts.
| Field | Type | Required | Description |
|---|---|---|---|
envId |
PythonEnvironmentId |
Yes | Identity of the environment. Use this - there is no id property. |
| (inherited) | PythonEnvironmentInfo |
- | All descriptive fields listed above. |
const env = await api.getEnvironment(projectUri);
if (env) {
console.log(env.displayName, env.envId.id, env.execInfo.run.executable);
}Note
PythonEnvironment is a structural interface, so TypeScript will accept a
hand-written literal - but manager-backed calls such as getPackages and
removeEnvironment need an envId that belongs to a registered manager.
Consumers should pass environments the API returned; providers should build
them with createPythonEnvironmentItem, which attaches a valid envId.
Describes a group heading in the environment manager UI, for managers that want richer grouping than a plain string.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Group name, also used as the group identifier. |
description |
string |
No | Secondary text for the group. |
tooltip |
string | MarkdownString |
No | Hover text for the group. |
iconPath |
IconPath |
No | Icon shown for the group. |
const group: EnvironmentGroupInfo = {
name: 'Conda',
description: 'Managed by conda',
iconPath: new vscode.ThemeIcon('package'),
};Shared icon type used by environments, groups, packages, managers, and creators.
| Form | Description |
|---|---|
Uri |
A single icon used for every theme. |
{ light: Uri; dark: Uri } |
Theme-specific icons; both fields are required. |
ThemeIcon |
A built-in VS Code codicon, for example new vscode.ThemeIcon('snake'). |
Scopes tell the extension which project, folder, or global state a call applies to.
| Type | Values | Meaning | Used by |
|---|---|---|---|
GetEnvironmentsScope |
Uri | 'all' | 'global' |
Uri limits results to the owning project; 'all' returns everything known; 'global' returns base installations used to create virtual environments. |
getEnvironments |
RefreshEnvironmentsScope |
Uri | undefined |
Uri refreshes one project; undefined refreshes global and workspace discovery. |
refreshEnvironments |
GetEnvironmentScope |
Uri | undefined |
Uri reads the selection for that project or file; undefined reads the global selection. |
getEnvironment |
SetEnvironmentScope |
Uri | Uri[] | undefined |
Uri or Uri[] sets the selection for those projects; undefined sets the global selection. |
setEnvironment |
CreateEnvironmentScope |
Uri | Uri[] | 'global' |
Where to create the environment; 'global' creates one outside any project. |
createEnvironment |
ResolveEnvironmentContext |
Uri |
An interpreter path or environment folder to resolve. Only a Uri is accepted. |
resolveEnvironment |
| Field | Type | Required | Description |
|---|---|---|---|
quickCreate |
boolean |
No | true creates without any prompts. false means the user explicitly declined quick create, so prompts are allowed. undefined leaves the decision to the manager, which may offer quick create. |
additionalPackages |
string[] |
No | Packages to install in addition to whatever the manager installs by default. |
const env = await api.createEnvironment(projectUri, {
quickCreate: true,
additionalPackages: ['requests', 'pytest'],
});| Field | Type | Required | Description |
|---|---|---|---|
runHeadless |
boolean |
No | true removes the environment without a confirmation prompt. Intended for automated scenarios. Defaults to false. |
Returned by an environment manager's quickCreateConfig() to describe its
one-click creation path. Returning undefined disables quick create.
| Field | Type | Required | Description |
|---|---|---|---|
description |
string |
Yes | Short label for the quick create step. |
detail |
string |
No | Secondary text explaining what quick create will do. |
quickCreateConfig(): QuickCreateConfig | undefined {
return { description: 'Quick create', detail: 'Creates .venv with pip' };
}String enum describing a discovery change.
| Member | Value | Description |
|---|---|---|
EnvironmentChangeKind.add |
'add' |
An environment became known. |
EnvironmentChangeKind.remove |
'remove' |
An environment is no longer known. |
Payload of onDidChangeEnvironments. This is an
array; each element describes one discovery change.
| Field | Type | Required | Description |
|---|---|---|---|
kind |
EnvironmentChangeKind |
Yes | Whether the environment was added or removed. |
environment |
PythonEnvironment |
Yes | The environment that was added or removed. |
api.onDidChangeEnvironments((changes) => {
for (const { kind, environment } of changes) {
console.log(kind, environment.displayName);
}
});Payload of onDidChangeEnvironment, fired when the
selected environment changes.
| Field | Type | Required | Description |
|---|---|---|---|
uri |
Uri | undefined |
Yes (may be undefined) |
The scope whose selection changed. undefined means the global selection. |
old |
PythonEnvironment | undefined |
Yes (may be undefined) |
The previously selected environment, or undefined if nothing was selected. |
new |
PythonEnvironment | undefined |
Yes (may be undefined) |
The newly selected environment, or undefined if the selection was cleared. |
Returns the environments currently known for a scope. It does not trigger
discovery; call refreshEnvironments for that.
getEnvironments(scope: GetEnvironmentsScope): Promise<PythonEnvironment[]>;| Parameter | Type | Required | Description |
|---|---|---|---|
scope |
GetEnvironmentsScope |
Yes | Uri for environments associated with a project, folder, or file; 'all' for every known environment; 'global' for base installations suitable for creating virtual environments. |
Returns Promise<PythonEnvironment[]> - possibly empty; never undefined.
const all = await api.getEnvironments('all');
const bases = await api.getEnvironments('global');
const forProject = await api.getEnvironments(projectUri);Asks the relevant environment managers to re-discover environments.
refreshEnvironments(scope: RefreshEnvironmentsScope): Promise<void>;| Parameter | Type | Required | Description |
|---|---|---|---|
scope |
RefreshEnvironmentsScope |
Yes (may be undefined) |
Uri refreshes discovery for that project or folder; undefined refreshes global and workspace discovery. |
Returns Promise<void>, resolving when the managers finish discovery.
Read the results with getEnvironments once the promise
settles. Do not rely on onDidChangeEnvironments
to deliver them: that event is optional on EnvironmentManager, and
refreshEnvironments does not synthesize one, so whether a refresh produces
deltas is up to the provider.
await api.refreshEnvironments(undefined);
// Authoritative: read the list rather than waiting for an event.
const refreshed = await api.getEnvironments('all');Turns an interpreter path or environment folder into a fully populated
PythonEnvironment, including execInfo.
resolveEnvironment(
context: ResolveEnvironmentContext,
): Promise<PythonEnvironment | undefined>;| Parameter | Type | Required | Description |
|---|---|---|---|
context |
ResolveEnvironmentContext (Uri) |
Yes | A Uri pointing at a Python executable or at an environment folder. |
Returns Promise<PythonEnvironment | undefined> - undefined when no
manager recognizes the URI.
const env = await api.resolveEnvironment(
vscode.Uri.file('/usr/local/bin/python3.12'),
);Important
ResolveEnvironmentContext is Uri only. Even though nearby source comments
mention environments, the exported signature does not accept a
PythonEnvironment. Pass env.environmentPath if you have an environment
and want it re-resolved.
Reads the environment currently selected for a scope.
getEnvironment(scope: GetEnvironmentScope): Promise<PythonEnvironment | undefined>;| Parameter | Type | Required | Description |
|---|---|---|---|
scope |
GetEnvironmentScope |
Yes (may be undefined) |
Uri of a project, folder, or file to read the selection for; undefined reads the global selection. |
Returns Promise<PythonEnvironment | undefined> - undefined when nothing
is selected for the scope.
const active = await api.getEnvironment(
vscode.window.activeTextEditor?.document.uri,
);Important
This call can return a stale value. It is deliberately non-blocking: it
races the real resolution against a one-second timeout so that slow initial
discovery cannot stall callers. If resolution has not finished in time, it
returns the last-known environment for the scope - which may be undefined
on a first call - while resolution continues in the background.
The resolved value is published through
onDidChangeEnvironment once it settles. If your
feature needs the authoritative selection, subscribe to that event and treat
the value from getEnvironment as a fast first guess:
let current = await api.getEnvironment(projectUri); // May be last-known.
context.subscriptions.push(
api.onDidChangeEnvironment((e) => {
if (e.uri?.toString() === projectUri.toString()) {
current = e.new; // Authoritative once resolution settles.
}
}),
);Selects - or clears - the environment for one or more scopes, and persists the selection.
setEnvironment(
scope: SetEnvironmentScope,
environment?: PythonEnvironment,
): Promise<void>;| Parameter | Type | Required | Description |
|---|---|---|---|
scope |
SetEnvironmentScope |
Yes (may be undefined) |
Uri or Uri[] for the projects to update; undefined updates the global selection. |
environment |
PythonEnvironment |
No | The environment to select. Omit it to clear the selection for the scope. |
Returns Promise<void>. Fires
onDidChangeEnvironment.
await api.setEnvironment(projectUri, env);
await api.setEnvironment(projectUri); // clearCreates an environment using the environment manager associated with the scope.
createEnvironment(
scope: CreateEnvironmentScope,
options?: CreateEnvironmentOptions,
): Promise<PythonEnvironment | undefined>;| Parameter | Type | Required | Description |
|---|---|---|---|
scope |
CreateEnvironmentScope |
Yes | Uri or Uri[] for the projects the environment is created for; 'global' creates one outside any project. |
options |
CreateEnvironmentOptions |
No | Controls prompting (quickCreate) and extra packages (additionalPackages). |
Returns Promise<PythonEnvironment | undefined> - undefined when no
environment was created, for example because the user cancelled the flow.
Rejects when no environment manager is registered for the scope, when the
manager does not support creation, or when creation itself fails - so handle
errors as well as undefined.
const created = await api.createEnvironment(projectUri, {
quickCreate: true,
additionalPackages: ['requests'],
});
if (created) {
await api.setEnvironment(projectUri, created);
}Note
Creation is not guaranteed to select the new environment. Call
setEnvironment if your feature depends on it being
active.
Removes an environment through its owning manager.
removeEnvironment(
environment: PythonEnvironment,
options?: RemoveEnvironmentOptions,
): Promise<void>;| Parameter | Type | Required | Description |
|---|---|---|---|
environment |
PythonEnvironment |
Yes | The environment to remove. Its envId.managerId must belong to a registered manager, so pass an environment the API returned. |
options |
RemoveEnvironmentOptions |
No | Set runHeadless: true to skip the confirmation prompt. |
Returns Promise<void>, resolving when removal completes. Rejects when the
manager fails to remove the environment.
await api.removeEnvironment(env, { runHeadless: true });Warning
Removal is defined by the owning manager and is usually destructive - for a
local virtual environment it deletes the environment from disk. Only pass
runHeadless: true when the user has already agreed, or in automated tests.
Converts a provider's descriptive info into an identified PythonEnvironment.
Synchronous, and intended for environment manager implementations.
createPythonEnvironmentItem(
info: PythonEnvironmentInfo,
manager: EnvironmentManager,
): PythonEnvironment;| Parameter | Type | Required | Description |
|---|---|---|---|
info |
PythonEnvironmentInfo |
Yes | Descriptive details of the environment, including execInfo and sysPrefix. |
manager |
EnvironmentManager |
Yes | The manager that owns the environment; supplies managerId in the resulting envId. |
Returns PythonEnvironment with a valid envId.
const env = api.createPythonEnvironmentItem(info, this);Fires when environments are discovered or removed.
onDidChangeEnvironments: Event<DidChangeEnvironmentsEventArgs>;| Payload | Type | Description |
|---|---|---|
e |
DidChangeEnvironmentsEventArgs |
Array of { kind, environment } entries describing each change. |
Returns a Disposable from the subscription; add it to
context.subscriptions.
context.subscriptions.push(
api.onDidChangeEnvironments((changes) => {
for (const change of changes) {
console.log(change.kind, change.environment.displayName);
}
}),
);Fires when the selected environment changes for a project, folder, file, or the global scope.
onDidChangeEnvironment: Event<DidChangeEnvironmentEventArgs>;| Payload | Type | Description |
|---|---|---|
e |
DidChangeEnvironmentEventArgs |
{ uri, old, new } describing the scope and the selection transition. |
Returns a Disposable from the subscription.
context.subscriptions.push(
api.onDidChangeEnvironment((e) => {
console.log(e.uri?.fsPath ?? 'global', '->', e.new?.displayName);
}),
);Package methods read and change what is installed in an environment. Packages
are supplied by package managers (pip, conda, uv, and any manager contributed
by another extension) and are identified by pkgId.
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Unique identifier of the package within its manager and environment. |
managerId |
string |
Yes | Identifier of the package manager that reported the package. |
environmentId |
string |
Yes | Identifier of the environment the package is installed in. |
The descriptive payload of a package. Providers build this and pass it to
createPackageItem.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Distribution name, for example requests. |
displayName |
string |
Yes | Name shown in the packages view. |
version |
string |
No | Installed version, when the manager can report one. |
description |
string |
No | Summary text shown next to the package. |
tooltip |
string | MarkdownString |
No | Hover text for the package. |
iconPath |
IconPath |
No | Icon shown for the package. |
uris |
readonly Uri[] |
No | Related locations, such as the installed distribution folder. |
isTransitive |
boolean |
No | true when the package was pulled in as a dependency rather than requested directly. |
const info: PackageInfo = {
name: 'requests',
displayName: 'requests',
version: '2.31.0',
description: 'HTTP for Humans',
};PackageInfo plus its identity; this is what getPackages returns.
| Field | Type | Required | Description |
|---|---|---|---|
pkgId |
PackageId |
Yes | Identity of the package. Use this - there is no id property. |
| (inherited) | PackageInfo |
- | All descriptive fields listed above. |
| Field | Type | Required | Description |
|---|---|---|---|
skipCache |
boolean |
No | true bypasses the cache and queries the underlying tool. Defaults to false. |
| Field | Type | Required | Description |
|---|---|---|---|
runHeadless |
boolean |
No | true runs without prompts and uses only the packages given in the options. Steps that would normally prompt - such as asking which packages to install - are skipped. Defaults to false. |
An intersection of
PackageManagementInteractionOptions
with a union that requires at least one of install or uninstall. A
literal with neither does not compile.
| Field | Type | Required | Description |
|---|---|---|---|
install |
string[] |
Conditional | Requirement specifiers to install. Required unless uninstall is provided. |
uninstall |
string[] |
Conditional | Package names to uninstall. Required unless install is provided. |
upgrade |
boolean |
No | true upgrades packages that are already installed. |
showSkipOption |
boolean |
No | true offers the user a way to skip the operation. |
runHeadless |
boolean |
No | Inherited interaction flag; true suppresses all prompts. |
// Valid: install only
const a: PackageManagementOptions = { install: ['requests'] };
// Valid: uninstall only
const b: PackageManagementOptions = { uninstall: ['requests'] };
// Valid: both, headless
const c: PackageManagementOptions = {
install: ['httpx'],
uninstall: ['requests'],
runHeadless: true,
};
// Does not compile: neither install nor uninstall
// const d: PackageManagementOptions = { upgrade: true };| Field | Type | Required | Description |
|---|---|---|---|
errorMode |
'legacy' | 'throw' |
No | 'legacy' (the default) resolves to undefined for both unsupported lookups and operational failures. 'throw' rejects with PackageVersionLookupNotSupportedError when lookup is unsupported and lets operational failures propagate unchanged. |
Tip
Prefer { errorMode: 'throw' }. 'legacy' is kept for backward
compatibility and may be removed in a future major version, and it cannot
distinguish "not supported" from "the network failed".
Re-exported from @renovatebot/pep440. Represents a parsed PEP 440 version,
returned by getPackageAvailableVersions and by environment and package
managers' getVersion methods. Import it from
@vscode/python-environments so your types match the API exactly.
| Field | Type | Required | Description |
|---|---|---|---|
public |
string |
Yes | The normalized version string, for example 2.31.0. This is the field to display or feed back into a specifier - Pep440Version is a plain data object, so toString() yields [object Object]. |
base_version |
string |
Yes | The release segment only, with pre/post/dev/local parts stripped. |
is_prerelease |
boolean |
Yes | true for alpha, beta, release-candidate, and dev versions. |
is_devrelease |
boolean |
Yes | true when the version carries a .devN segment. |
is_postrelease |
boolean |
Yes | true when the version carries a .postN segment. |
epoch |
number |
Yes | PEP 440 epoch; 0 unless the project has reset its versioning scheme. |
release |
number[] |
Yes | Release segment components, for example [2, 31, 0]. |
pre |
(string | number)[] |
Yes | Pre-release segment, for example ['rc', 1]; empty when absent. |
post |
(string | number)[] |
Yes | Post-release segment; empty when absent. |
dev |
(string | number)[] |
Yes | Development-release segment; empty when absent. |
local |
string | null |
Yes | Local version label, or null when absent. |
// Show the newest non-prerelease version, if the manager returned any.
const stable = versions.find((v) => !v.is_prerelease);
console.log(stable?.public ?? 'no stable release found');| Member | Value | Description |
|---|---|---|
PackageChangeKind.add |
'add' |
A package was installed. |
PackageChangeKind.remove |
'remove' |
A package was uninstalled. |
| Field | Type | Required | Description |
|---|---|---|---|
environment |
PythonEnvironment |
Yes | The environment whose packages changed. |
manager |
PackageManager |
Yes | The package manager that reported the change. |
changes |
{ kind: PackageChangeKind; pkg: Package }[] |
Yes | One entry per changed package. |
Returns the packages installed in an environment.
getPackages(
environment: PythonEnvironment,
options?: GetPackagesOptions,
): Promise<Package[] | undefined>;| Parameter | Type | Required | Description |
|---|---|---|---|
environment |
PythonEnvironment |
Yes | The environment to inspect. |
options |
GetPackagesOptions |
No | Set skipCache: true to bypass the cache. |
Returns Promise<Package[] | undefined>. undefined means the manager
could not produce a list - for example no package manager is associated with
the environment, or a project-aware manager cannot identify one unique project
for the environment - which is different from an empty array meaning "nothing
installed".
const packages = await api.getPackages(env);
if (packages === undefined) {
// Package listing unavailable for this environment.
} else {
const direct = packages.filter((p) => !p.isTransitive);
}Forces the package manager to re-read the installed package list.
refreshPackages(environment: PythonEnvironment): Promise<void>;| Parameter | Type | Required | Description |
|---|---|---|---|
environment |
PythonEnvironment |
Yes | The environment whose package list should be refreshed. |
Returns Promise<void>. Changes surface through
onDidChangePackages. Rejects with
No package manager found when no package manager can be resolved for the
environment - including when a project-aware manager cannot identify one unique
project.
// Packages were installed outside the extension - re-read the list.
await api.refreshPackages(env);
const packages = await api.getPackages(env);Installs, upgrades, and uninstalls packages in one call.
managePackages(
environment: PythonEnvironment,
options: PackageManagementOptions,
): Promise<void>;| Parameter | Type | Required | Description |
|---|---|---|---|
environment |
PythonEnvironment |
Yes | The environment to modify. |
options |
PackageManagementOptions |
Yes | Must specify install, uninstall, or both. Also carries upgrade, showSkipOption, and runHeadless. |
Returns Promise<void>, resolving when the operation finishes. Rejects with
No package manager found when no package manager can be resolved for the
environment - including when a project-aware manager cannot identify one unique
project - or if the underlying tool fails.
await api.managePackages(env, {
install: ['requests', 'rich'],
uninstall: ['obsolete-package'],
upgrade: true,
});Note
Plain package names are the portable choice. Version pinning syntax belongs to
the package manager - requests==2.31.0 is pip's format, and conda and others
differ. A manager that implements formatInstallSpec produces the right
string for its own tool; do not assume name==version.
Looks up the versions available for a package, newest first.
// Overload 1 - recommended
getPackageAvailableVersions(
environment: PythonEnvironment,
packageName: string,
options: GetPackageAvailableVersionsOptions & { errorMode: 'throw' },
): Promise<Pep440Version[]>;
// Overload 2 - legacy default
getPackageAvailableVersions(
environment: PythonEnvironment,
packageName: string,
options?: GetPackageAvailableVersionsOptions,
): Promise<Pep440Version[] | undefined>;| Parameter | Type | Required | Description |
|---|---|---|---|
environment |
PythonEnvironment |
Yes | Environment context for the lookup; determines which package manager answers. |
packageName |
string |
Yes | Name of the package to look up. |
options |
GetPackageAvailableVersionsOptions |
No (overload 2) / Yes (overload 1) | Pass { errorMode: 'throw' } to select the first overload and get error-mode reporting. |
Returns
| Call form | Return type | Failure behavior |
|---|---|---|
{ errorMode: 'throw' } |
Promise<Pep440Version[]> |
Rejects with PackageVersionLookupNotSupportedError when unsupported; operational errors propagate unchanged. |
Omitted or { errorMode: 'legacy' } |
Promise<Pep440Version[] | undefined> |
Resolves to undefined for both unsupported lookups and operational failures. |
import { isPackageVersionLookupNotSupportedError } from '@vscode/python-environments';
try {
const versions = await api.getPackageAvailableVersions(env, 'requests', {
errorMode: 'throw',
});
// The array can be empty, and `Pep440Version` is a data object - read `public`.
const newest = versions[0]?.public;
if (newest) {
void vscode.window.showInformationMessage(`Newest requests: ${newest}`);
}
} catch (error) {
if (isPackageVersionLookupNotSupportedError(error)) {
// Fall back to manual version entry.
} else {
throw error; // Real failure: surface it.
}
}Important
Do not paste a returned version straight into an install entry. The
specifier syntax belongs to the package manager - name==version is pip's
format, not a universal one. A manager that implements formatInstallSpec
builds the correct string for its own tool.
Converts a provider's descriptive info into an identified Package.
Synchronous, and intended for package manager implementations.
createPackageItem(
info: PackageInfo,
environment: PythonEnvironment,
manager: PackageManager,
): Package;| Parameter | Type | Required | Description |
|---|---|---|---|
info |
PackageInfo |
Yes | Descriptive details of the package. |
environment |
PythonEnvironment |
Yes | The environment the package is installed in; supplies environmentId. |
manager |
PackageManager |
Yes | The reporting package manager; supplies managerId. |
Returns Package with a valid pkgId.
// Inside a PackageManager implementation:
const pkg = api.createPackageItem(
{ name: 'requests', displayName: 'requests', version: '2.31.0' },
environment,
this,
);Fires when packages are installed or removed.
onDidChangePackages: Event<DidChangePackagesEventArgs>;| Payload | Type | Description |
|---|---|---|
e |
DidChangePackagesEventArgs |
The environment, the reporting manager, and the list of { kind, pkg } changes. |
Returns a Disposable from the subscription.
context.subscriptions.push(
api.onDidChangePackages((e) => {
console.log(e.environment.displayName, e.changes.length);
}),
);Thrown when a package manager cannot list available versions at all. It separates an unsupported capability from an operational failure such as a failed command, a network error, or unparseable output.
| Member | Type | Required | Description |
|---|---|---|---|
code |
'PackageVersionLookupNotSupported' |
Yes | Stable discriminator that survives bundle boundaries. Declared readonly. |
name |
string |
Yes | Inherited from Error. Its runtime value is set to 'PackageVersionLookupNotSupportedError', but its static type stays string, so do not assign it to a string-literal type. |
message |
string |
Yes | Inherited from Error. Defaults to an explanation that version lookup is unsupported. |
Providers throw it with new PackageVersionLookupNotSupportedError(message?);
the single message parameter is optional.
import { PackageVersionLookupNotSupportedError } from '@vscode/python-environments';
// Inside a PackageManager implementation:
async getPackageAvailableVersions(): Promise<Pep440Version[]> {
throw new PackageVersionLookupNotSupportedError();
}isPackageVersionLookupNotSupportedError(
error: unknown,
): error is PackageVersionLookupNotSupportedError;| Parameter | Type | Required | Description |
|---|---|---|---|
error |
unknown |
Yes | The caught value to test. |
Returns boolean (a type guard). Checks the stable code discriminator, so
it still returns true when the error crossed an extension bundle boundary.
Important
Always use this guard instead of instanceof. Each extension bundle can load
its own copy of the error class, so instanceof can return false for an
error that is semantically the right one.
A project is anything that can own a Python environment: a workspace folder, a
subfolder, or even a single PEP 723 script. Every
vscode.workspace.workspaceFolders entry is a project by default.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Display name of the project. |
uri |
Uri |
Yes | Root folder, or the file for file-based projects. Used to match a project to a file. |
description |
string |
No | Secondary text shown next to the project. |
tooltip |
string | MarkdownString |
No | Hover text for the project. |
const project: PythonProject = {
name: 'service-api',
uri: vscode.Uri.file('/home/me/code/app/service-api'),
description: 'FastAPI service',
};Passed to a PythonProjectCreator.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Name for the project being created. |
rootUri |
Uri |
Yes | Folder to use as the project root. |
quickCreate |
boolean |
No | true requires creation to complete without any user input. |
| Field | Type | Required | Description |
|---|---|---|---|
added |
PythonProject[] |
Yes | Projects added in this change. May be empty. |
removed |
PythonProject[] |
Yes | Projects removed in this change. May be empty. |
Returns every project the extension currently tracks. Synchronous.
getPythonProjects(): readonly PythonProject[];Takes no parameters.
Returns readonly PythonProject[] - a snapshot; re-read it after
onDidChangePythonProjects rather than caching.
for (const project of api.getPythonProjects()) {
console.log(project.name, project.uri.fsPath);
}Finds the project that owns a URI. Synchronous.
getPythonProject(uri: Uri): PythonProject | undefined;| Parameter | Type | Required | Description |
|---|---|---|---|
uri |
Uri |
Yes | A file or folder URI. The extension resolves the owning project, so a file inside a project resolves to that project. |
Returns PythonProject | undefined - undefined when the URI is not inside
any known project.
const doc = vscode.window.activeTextEditor?.document;
const project = doc ? api.getPythonProject(doc.uri) : undefined;Adds one or more projects to the tracked collection. Synchronous.
addPythonProject(projects: PythonProject | PythonProject[]): void;| Parameter | Type | Required | Description |
|---|---|---|---|
projects |
PythonProject | PythonProject[] |
Yes | The project or projects to track. Each needs at least name and uri. |
Returns void. Fires
onDidChangePythonProjects with the additions.
api.addPythonProject({
name: 'tools',
uri: vscode.Uri.joinPath(workspaceFolder.uri, 'tools'),
});Stops tracking a project. Synchronous.
removePythonProject(project: PythonProject): void;| Parameter | Type | Required | Description |
|---|---|---|---|
project |
PythonProject |
Yes | The project to remove. Obtain it from getPythonProjects or getPythonProject so it matches a tracked entry. |
Returns void. Fires
onDidChangePythonProjects with the removal.
Removing a project stops environment tracking for it; it does not delete anything on disk.
const project = api.getPythonProject(projectUri);
if (project) {
api.removePythonProject(project);
}Fires when projects are added or removed.
onDidChangePythonProjects: Event<DidChangePythonProjectsEventArgs>;| Payload | Type | Description |
|---|---|---|
e |
DidChangePythonProjectsEventArgs |
{ added, removed } arrays for this change. |
Returns a Disposable from the subscription.
context.subscriptions.push(
api.onDidChangePythonProjects((e) => {
console.log('added', e.added.length, 'removed', e.removed.length);
}),
);Execution methods take an environment and use its
PythonEnvironmentExecutionInfo - including
its activation commands, where the method applies them - so you do not have to
assemble activation yourself. Choose a method by where the output should go: a
terminal the user watches, a VS Code task, or a background process you read
programmatically.
A single command to execute.
| Field | Type | Required | Description |
|---|---|---|---|
executable |
string |
Yes | Absolute path to a spawnable binary, such as python.exe or python3. |
args |
string[] |
No | Arguments passed on every execution of this command, for interpreter-specific flags. |
Tells the extension how to run and activate an environment. Providers must
populate at least run.
| Field | Type | Required | Description |
|---|---|---|---|
run |
PythonCommandRunConfiguration |
Yes | Base command used to run Python. |
activatedRun |
PythonCommandRunConfiguration |
No | Command used to run Python after the environment has been activated. When set, it overrides run. |
activation |
PythonCommandRunConfiguration[] |
No | Shell-agnostic commands that activate the environment. |
shellActivation |
Map<string, PythonCommandRunConfiguration[]> |
No | Shell-specific activation keyed by shell type, with 'unknown' as the fallback key. Overrides activation. |
deactivation |
PythonCommandRunConfiguration[] |
No | Shell-agnostic commands that deactivate the environment. |
shellDeactivation |
Map<string, PythonCommandRunConfiguration[]> |
No | Shell-specific deactivation keyed by shell type, with 'unknown' as the fallback key. Overrides deactivation. |
Resolution order when running in a terminal:
activatedRun, if present.- Otherwise
shellActivationfor the detected shell; if the shell is unknown, the'unknown'entry, thenactivation. - Otherwise
activation. - Otherwise
run.
const execInfo: PythonEnvironmentExecutionInfo = {
run: { executable: '/home/me/app/.venv/bin/python' },
activation: [
{ executable: 'source', args: ['/home/me/app/.venv/bin/activate'] },
],
shellActivation: new Map([
['pwsh', [{ executable: '/home/me/app/.venv/bin/Activate.ps1' }]],
]),
};Extends vscode.TerminalOptions,
so every standard terminal option - name, cwd, env, hideFromUser, and
the rest - is available alongside the field below.
| Field | Type | Required | Description |
|---|---|---|---|
disableActivation |
boolean |
No | true creates the terminal without running activation commands. |
| (inherited) | vscode.TerminalOptions |
- | See the VS Code API reference for the full list and their defaults. |
| Field | Type | Required | Description |
|---|---|---|---|
cwd |
string | Uri |
Yes | Working directory. Used only when the terminal is created. |
args |
string[] |
No | Arguments passed to the Python executable. |
show |
boolean |
No | true reveals the terminal. |
// python myscript.py --arg1
const script: PythonTerminalExecutionOptions = {
cwd: projectUri,
args: ['myscript.py', '--arg1'],
show: true,
};
// python -m my_module --arg1
const module: PythonTerminalExecutionOptions = {
cwd: projectUri,
args: ['-m', 'my_module', '--arg1'],
};| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Name of the task, shown in the task UI. |
args |
string[] |
Yes | Arguments passed to the Python executable. |
project |
PythonProject |
No | Project the task belongs to. |
cwd |
string |
No | Working directory for the task's shell execution. When omitted, VS Code resolves it from the task scope - the workspace folder containing project, or the global scope when project is not supplied. |
env |
{ [key: string]: string } |
No | Additional environment variables for the task. |
| Field | Type | Required | Description |
|---|---|---|---|
args |
string[] |
Yes | Arguments passed to the Python executable. |
cwd |
string |
No | Working directory, passed straight to the spawned process. When omitted, the process inherits the extension host's working directory, which is not your project folder - always supply cwd (for example project.uri.fsPath) if the script resolves relative paths. |
env |
{ [key: string]: string | undefined } |
No | Additional environment variables. An undefined value unsets a variable. |
Returned by runInBackground.
| Member | Type | Required | Description |
|---|---|---|---|
pid |
number |
No | Process ID, when available. |
stdin |
NodeJS.WritableStream |
Yes | Standard input stream. |
stdout |
NodeJS.ReadableStream |
Yes | Standard output stream. |
stderr |
NodeJS.ReadableStream |
Yes | Standard error stream. |
kill() |
() => void |
Yes | Terminates the process. |
onExit(listener) |
(listener: (code: number | null, signal: NodeJS.Signals | null) => void) => void |
Yes | Registers an exit listener receiving the exit code and signal. |
Creates a terminal with the environment activated, without running anything.
createTerminal(
environment: PythonEnvironment,
options: PythonTerminalCreateOptions,
): Promise<Terminal>;| Parameter | Type | Required | Description |
|---|---|---|---|
environment |
PythonEnvironment |
Yes | Environment to activate in the new terminal. |
options |
PythonTerminalCreateOptions |
Yes | Standard terminal options plus disableActivation. |
Returns Promise<vscode.Terminal>.
const terminal = await api.createTerminal(env, {
name: 'My Extension',
cwd: projectUri,
});
terminal.show();Environments that cannot be activated simply produce a normal terminal.
Runs Python in a shared terminal, creating one if needed.
runInTerminal(
environment: PythonEnvironment,
options: PythonTerminalExecutionOptions,
): Promise<Terminal>;| Parameter | Type | Required | Description |
|---|---|---|---|
environment |
PythonEnvironment |
Yes | Environment used to run the command. |
options |
PythonTerminalExecutionOptions |
Yes | cwd plus the args to pass to Python and whether to show the terminal. |
Returns Promise<vscode.Terminal> - the terminal the command was sent to.
await api.runInTerminal(env, {
cwd: projectUri,
args: ['-m', 'pytest', '-q'],
show: true,
});Terminal reuse has limits imposed by VS Code: reloading the window or closing the terminal creates a new one, and multi-root or multi-project scenarios get one terminal per project.
In PowerShell, Bash, Git Bash, and Zsh, pass unquoted argument values. The
extension quotes them for the shell so characters such as $, apostrophes
(including PowerShell's typographic single quotes), and $(...) are passed
literally, with or without shell integration.
Like runInTerminal, but keeps a terminal per key so repeated runs reuse the
same one.
runInDedicatedTerminal(
terminalKey: Uri | string,
environment: PythonEnvironment,
options: PythonTerminalExecutionOptions,
): Promise<Terminal>;| Parameter | Type | Required | Description |
|---|---|---|---|
terminalKey |
Uri | string |
Yes | Stable key identifying the dedicated terminal. Use the script's Uri for per-script terminals, or a string for a logical channel. |
environment |
PythonEnvironment |
Yes | Environment used to run the command. |
options |
PythonTerminalExecutionOptions |
Yes | cwd, args, and show. |
Returns Promise<vscode.Terminal> - the dedicated terminal for the key.
await api.runInDedicatedTerminal(scriptUri, env, {
cwd: projectUri,
args: [scriptUri.fsPath],
show: true,
});Runs Python as a VS Code task, so output appears in the task terminal and problem matchers apply.
runAsTask(
environment: PythonEnvironment,
options: PythonTaskExecutionOptions,
): Promise<TaskExecution>;| Parameter | Type | Required | Description |
|---|---|---|---|
environment |
PythonEnvironment |
Yes | Environment used to run the task. |
options |
PythonTaskExecutionOptions |
Yes | Task name and args, plus optional project, cwd, and env. |
Returns Promise<vscode.TaskExecution> - use it to observe or terminate the
task.
const execution = await api.runAsTask(env, {
name: 'Run tests',
args: ['-m', 'pytest'],
project,
});Starts a Python process you control programmatically.
runInBackground(
environment: PythonEnvironment,
options: PythonBackgroundRunOptions,
): Promise<PythonProcess>;| Parameter | Type | Required | Description |
|---|---|---|---|
environment |
PythonEnvironment |
Yes | Environment used to start the process. |
options |
PythonBackgroundRunOptions |
Yes | args for Python, plus optional cwd and env. Supply cwd - it is not inferred. |
Returns Promise<PythonProcess> with stdin, stdout, stderr, kill(),
and onExit().
Important
cwd is forwarded to the spawned process unchanged. There is no project
context to infer it from, so when you omit it the process inherits the
extension host's working directory rather than your project folder. Pass
cwd explicitly whenever the script resolves relative paths.
You own the process lifetime: call kill() when your feature is done, and
tie it to your disposables so it does not outlive deactivation.
const proc = await api.runInBackground(env, {
args: ['-c', 'import sys; print(sys.version)'],
cwd: projectUri.fsPath,
});
proc.stdout.on('data', (chunk) => console.log(String(chunk)));
proc.stderr.on('data', (chunk) => console.error(String(chunk)));
proc.onExit((code) => console.log('exited with', code));Important
You own the process lifetime. Always attach an onExit handler and call
kill() when your extension deactivates or the work is cancelled, otherwise
processes can outlive the window.
These methods resolve the variables Python should run with for a scope,
including .env files the extension monitors.
| Field | Type | Required | Description |
|---|---|---|---|
uri |
Uri |
No | The file that changed. Absent when a non-file source changed. |
changeType |
vscode.FileChangeType |
Yes | Whether the source was created, changed, or deleted. |
Resolves the effective environment variables for a scope.
getEnvironmentVariables(
uri: Uri | undefined,
overrides?: ({ [key: string]: string | undefined } | Uri)[],
baseEnvVar?: { [key: string]: string | undefined },
): Promise<{ [key: string]: string | undefined }>;| Parameter | Type | Required | Description |
|---|---|---|---|
uri |
Uri | undefined |
Yes (may be undefined) |
Project, workspace, or file to resolve variables for. undefined resolves the global scope. |
overrides |
({ [key: string]: string | undefined } | Uri)[] |
No | Additional sources applied in array order. A plain object contributes its entries; a Uri is read as an .env file. |
baseEnvVar |
{ [key: string]: string | undefined } |
No | Starting set of variables. Defaults to process.env. |
Returns Promise<{ [key: string]: string | undefined }> - the merged
result. An undefined value means the variable is unset.
Precedence, lowest to highest:
baseEnvVarif provided, otherwiseprocess.env.- The
.envfile named by thepython.envFilesetting for the workspace. - The
.envfile at the root of the Python project. - Each entry in
overrides, in order.
const env = await api.getEnvironmentVariables(
projectUri,
[vscode.Uri.joinPath(projectUri, '.env.test'), { CI: '1' }],
process.env,
);Fires when a monitored .env file or other variable source changes.
onDidChangeEnvironmentVariables: Event<DidChangeEnvironmentVariablesEventArgs>;| Payload | Type | Description |
|---|---|---|
e |
DidChangeEnvironmentVariablesEventArgs |
{ uri, changeType } for the changed source. |
Returns a Disposable from the subscription. Re-call
getEnvironmentVariables to get refreshed values; the event does not carry
them.
context.subscriptions.push(
api.onDidChangeEnvironmentVariables(async (e) => {
console.log('env source changed', e.uri?.fsPath, e.changeType);
const refreshed = await api.getEnvironmentVariables(projectUri);
}),
);Implement one of these interfaces and register it to contribute your own
environment manager, package manager, or project creator. Registration returns
a Disposable; dispose it on deactivation.
Discovers, creates, removes, and selects environments of one kind. The extension calls these methods in response to UI actions, startup, terminal activation, and API calls; treat the documented contract, not any particular trigger, as the specification.
| Member | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Manager name. Allowed characters: a-z, A-Z, 0-9, -, _. |
displayName |
string |
No | Name shown in the UI. |
preferredPackageManagerId |
string |
Yes | Package manager to pair with, formatted <publisher>.<extension>:<manager-name>, for example ms-python.python:pip. |
capabilities |
Capabilities<EnvironmentManagerCapability> |
No | Optional advertisements for legacy compatibility; when supplied, includes every environment capability key. See manager capabilities. |
description |
string |
No | Secondary text shown in the UI. |
tooltip |
string | MarkdownString |
No | Hover text for the manager. |
iconPath |
IconPath |
No | Icon shown for the manager. |
log |
LogOutputChannel |
No | Output channel used for the manager's logs. |
getVersion() |
() => Promise<Pep440Version | undefined> |
No | Returns the parsed PEP 440 version of the underlying environment management tool. Return undefined when the manager has no versioned tool or its version is unavailable. |
getEnvironments(scope) |
(scope: GetEnvironmentsScope) => Promise<PythonEnvironment[]> |
Yes | Returns the environments known for the scope. Called frequently by UI surfaces. |
refresh(scope) |
(scope: RefreshEnvironmentsScope) => Promise<void> |
Yes | Re-discovers environments for the scope. |
set(scope, environment?) |
(scope: SetEnvironmentScope, environment?: PythonEnvironment) => Promise<void> |
Yes | Sets or clears the active environment for the scope. Also called at startup to rehydrate persisted state. |
get(scope) |
(scope: GetEnvironmentScope) => Promise<PythonEnvironment | undefined> |
Yes | Returns the active environment for the scope. Called very frequently. |
resolve(context) |
(context: ResolveEnvironmentContext) => Promise<PythonEnvironment | undefined> |
Yes | Turns a Uri for an interpreter or environment folder into a fully populated environment with complete execInfo. |
create(scope, options?) |
(scope: CreateEnvironmentScope, options?: CreateEnvironmentOptions) => Promise<PythonEnvironment | undefined> |
No | Creates an environment. Omit the method entirely if creation is unsupported - the UI disables create when create === undefined. Add a .gitignore when creating a folder inside the workspace. |
remove(environment, options?) |
(environment: PythonEnvironment, options?: RemoveEnvironmentOptions) => Promise<void> |
No | Deletes an environment. |
quickCreateConfig() |
() => QuickCreateConfig | undefined |
No | Describes the quick create path. Implementing it enables quick create, which requires create too. |
clearCache() |
() => Promise<void> |
No | Drops cached environment data so later calls re-discover from disk. |
onDidChangeEnvironments |
Event<DidChangeEnvironmentsEventArgs> |
No | Fire when discovery results change. |
onDidChangeEnvironment |
Event<DidChangeEnvironmentEventArgs> |
No | Fire when the manager's active environment changes. |
class MyEnvManager implements EnvironmentManager {
readonly name = 'my-manager';
readonly displayName = 'My Manager';
readonly preferredPackageManagerId = 'ms-python.python:pip';
constructor(private readonly api: PythonEnvironmentApi) {}
async getVersion(): Promise<Pep440Version | undefined> {
return getMyManagerVersion();
}
async getEnvironments(
scope: GetEnvironmentsScope,
): Promise<PythonEnvironment[]> {
const found = await discover(scope);
return found.map((info) =>
this.api.createPythonEnvironmentItem(info, this),
);
}
async refresh(scope: RefreshEnvironmentsScope): Promise<void> {}
async set(
scope: SetEnvironmentScope,
environment?: PythonEnvironment,
): Promise<void> {}
async get(
scope: GetEnvironmentScope,
): Promise<PythonEnvironment | undefined> {
return undefined;
}
async resolve(
context: ResolveEnvironmentContext,
): Promise<PythonEnvironment | undefined> {
return undefined;
}
}Reports and changes the packages of an environment.
| Member | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Manager name. Allowed characters: a-z, A-Z, 0-9, -, _. |
displayName |
string |
No | Name shown in the UI. |
description |
string |
No | Secondary text shown in the UI. |
tooltip |
string | MarkdownString |
No | Hover text for the manager. |
iconPath |
IconPath |
No | Icon shown for the manager. |
log |
LogOutputChannel |
No | Output channel used for the manager's logs. |
manage(environment, options) |
(environment: PythonEnvironment, options: PackageManagementOptions) => Promise<void> |
Yes | Installs and uninstalls the requested packages. |
refresh(environment) |
(environment: PythonEnvironment) => Promise<void> |
Yes | Re-reads the installed package list. |
getPackages(environment, options?) |
(environment: PythonEnvironment, options?: GetPackagesOptions) => Promise<Package[] | undefined> |
Yes | Returns installed packages, or undefined if they cannot be retrieved. |
getPackageWatchTargets(environment) |
(environment: PythonEnvironment) => RelativePattern[] |
No | Extra filesystem patterns to watch for install and uninstall changes, appended to the default site-packages locations. Implement for manager-specific locations such as conda-meta. |
createForProject(project) |
(project: PythonProject) => PackageManager |
No | Creates a manager bound to a project for project-sensitive operations. |
capabilities |
Capabilities<PackageManagerCapability> |
No | Optional advertisements for legacy compatibility; when supplied, includes every package capability key, evaluated on the correct project-bound instance. See manager capabilities. |
dispose() |
() => void |
No | Releases resources owned by the manager. The extension disposes project-scoped managers when their project is removed or replaced, their provider is unregistered, or the extension shuts down. |
getDirectPackageNames(environment) |
(environment: PythonEnvironment) => Promise<Set<string> | undefined> |
No | Best-effort set of non-transitive package names. Most tools cannot record user intent - pip uses pip list --not-required, which reports leaf packages rather than explicitly installed ones. |
clearCache() |
() => Promise<void> |
No | Drops cached package data. |
getVersion(environment) |
(environment: PythonEnvironment) => Promise<Pep440Version | undefined> |
No | Version of the underlying tool, such as pip, uv, or conda. |
getPackageAvailableVersions(environment, packageName) |
(environment: PythonEnvironment, packageName: string) => Promise<Pep440Version[] | undefined> |
No | Available versions, newest first. Throw PackageVersionLookupNotSupportedError when unsupported and let operational failures propagate. Resolving to undefined is treated as unsupported. |
formatInstallSpec(packageName, version) |
(packageName: string, version: string) => string |
No | Formats a pinned specifier for this tool, for example requests==2.31.0 for pip or requests=2.31.0 for conda. Callers default to name==version when absent. |
onDidChangePackages |
Event<DidChangePackagesEventArgs> |
No | Fire when packages change. |
Implement createForProject when package operations depend on project files or
the process working directory, as they do for tools such as Poetry. Callers that
already have a PythonProject use that project directly. For environment-only
operations, the extension selects a project-scoped manager only when exactly one
tracked project uses the environment; it does not choose arbitrarily when no
project or multiple projects match.
Environment-only paths never use a project-aware provider as an unbound
fallback. When no unique project can be inferred, getPackageManager returns no
manager, so package reads return undefined, package views suppress those
operations, and package mutations or refreshes reject with No package manager found. Keep project-specific caches and mutable state on the manager returned by
createForProject, and implement dispose when that manager owns resources.
When a scoped manager fires onDidChangePackages, the event's manager must be
the exact scoped instance returned by createForProject. This requirement also
applies when root and scoped managers share an event emitter.
class ProjectPackageManager implements PackageManager {
readonly name = 'project-pm';
constructor(private readonly project?: PythonProject) {}
createForProject(project: PythonProject): PackageManager {
return new ProjectPackageManager(project);
}
dispose(): void {
// Release project-specific watchers or processes.
}
async manage(
environment: PythonEnvironment,
options: PackageManagementOptions,
): Promise<void> {
if (!this.project) {
throw new Error('Package management requires a Python project.');
}
await runPackageCommand(options, { cwd: this.project.uri.fsPath });
}
}class MyPackageManager implements PackageManager {
readonly name = 'my-pm';
constructor(private readonly api: PythonEnvironmentApi) {}
async manage(
environment: PythonEnvironment,
options: PackageManagementOptions,
): Promise<void> {
if (options.install?.length) {
/* install */
}
if (options.uninstall?.length) {
/* uninstall */
}
}
async refresh(environment: PythonEnvironment): Promise<void> {}
async getPackages(
environment: PythonEnvironment,
): Promise<Package[] | undefined> {
const infos = await readInstalled(environment);
return infos.map((info) =>
this.api.createPackageItem(info, environment, this),
);
}
async getPackageAvailableVersions(
environment: PythonEnvironment,
packageName: string,
): Promise<Pep440Version[]> {
throw new PackageVersionLookupNotSupportedError();
}
formatInstallSpec(packageName: string, version: string): string {
return `${packageName}==${version}`;
}
}Contributes a project-creation flow, such as a template or scaffolding wizard.
| Member | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Creator name. |
displayName |
string |
No | Name shown in the creation picker. |
description |
string |
No | Secondary text shown in the picker. |
tooltip |
string | MarkdownString |
No | Hover text for the creator. |
supportsQuickCreate |
boolean |
No | true when the creator can run with no user input. |
create(options?) |
(options?: PythonProjectCreatorOptions) => Promise<PythonProject | PythonProject[] | Uri | Uri[] | undefined> |
Yes | Creates the project or files. Return PythonProject(s) for real projects, Uri(s) for files that do not constitute a project, or undefined if creation fails or is cancelled. |
registerEnvironmentManager(
manager: EnvironmentManager,
options?: { extensionId?: string },
): Disposable;| Parameter | Type | Required | Description |
|---|---|---|---|
manager |
EnvironmentManager |
Yes | The manager implementation to register. |
options |
{ extensionId?: string } |
No | Registration options. |
options.extensionId |
string |
No | Extension ID of the calling extension. Detected automatically when omitted or not found. |
Returns Disposable that unregisters the manager.
context.subscriptions.push(
api.registerEnvironmentManager(new MyEnvManager(api)),
);registerPackageManager(
manager: PackageManager,
options?: { extensionId?: string },
): Disposable;| Parameter | Type | Required | Description |
|---|---|---|---|
manager |
PackageManager |
Yes | The package manager implementation to register. |
options |
{ extensionId?: string } |
No | Registration options. |
options.extensionId |
string |
No | Extension ID of the calling extension. Detected automatically when omitted or not found. |
Returns Disposable that unregisters the manager.
context.subscriptions.push(
api.registerPackageManager(new MyPackageManager(api)),
);To pair your package manager with your environment manager, set
preferredPackageManagerId on the environment manager to
<publisher>.<extension>:<package-manager-name>.
registerPythonProjectCreator(creator: PythonProjectCreator): Disposable;| Parameter | Type | Required | Description |
|---|---|---|---|
creator |
PythonProjectCreator |
Yes | The project creator implementation to register. |
Returns Disposable that unregisters the creator.
context.subscriptions.push(
api.registerPythonProjectCreator({
name: 'my-template',
displayName: 'My Project Template',
supportsQuickCreate: true,
async create(options) {
if (!options) {
return undefined;
}
await scaffold(options.rootUri, options.name);
return { name: options.name, uri: options.rootUri };
},
}),
);PythonEnvironmentApi is assembled from the interfaces below. They exist to
organize the TypeScript declarations only - at runtime every member lives
directly on the single flat API object.
| Interface | Members grouped by the interface | Description |
|---|---|---|
PythonEnvironmentsApi |
Environment discovery, resolution, and capabilities | Lists and refreshes discovered environments, resolves environment URIs, queries manager support, and reports discovery changes. |
PythonProjectEnvironmentApi |
Selected environment get/set | Reads, updates, and observes the selected environment for URI or global scopes. |
PythonEnvironmentManagementApi |
Environment creation/removal | Creates and removes environments through their associated environment managers. |
PythonEnvironmentItemApi |
Environment item creation | Converts provider-supplied environment information into an identified PythonEnvironment. |
PythonEnvironmentManagerRegistrationApi |
Environment manager registration | Registers an EnvironmentManager implementation with the extension. |
PythonEnvironmentManagerApi |
Combined environment API | Combines environment registration, item creation, lifecycle, discovery, and selection interfaces. |
PythonPackageGetterApi |
Package retrieval, version lookup, and capabilities | Retrieves and refreshes packages, looks up available versions, queries routed manager support, and reports package changes. |
PythonPackageManagementApi |
Package installation/removal | Installs, upgrades, or uninstalls packages in an environment. |
PythonPackageItemApi |
Package item creation | Converts provider-supplied package information into an identified Package. |
PythonPackageManagerRegistrationApi |
Package manager registration | Registers a PackageManager implementation with the extension. |
PythonPackageManagerApi |
Combined package API | Combines package registration, retrieval, management, and item creation interfaces. |
PythonProjectGetterApi |
Project lookup | Returns all known projects or the project associated with a URI. |
PythonProjectModifyApi |
Project collection modification | Adds, removes, and observes projects in the tracked project collection. |
PythonProjectCreationApi |
Project creator registration | Registers a PythonProjectCreator implementation. |
PythonProjectApi |
Combined project API | Combines project lookup, modification, events, and creator registration interfaces. |
PythonTerminalCreateApi |
Terminal creation | Creates a terminal configured for a Python environment. |
PythonTerminalRunApi |
Terminal execution | Runs Python in shared or dedicated terminals. |
PythonTaskRunApi |
Task execution | Runs Python as a VS Code task. |
PythonBackgroundRunApi |
Background execution | Starts Python as a background process with stream and exit access. |
PythonExecutionApi |
Combined execution API | Combines terminal creation, terminal execution, task execution, and background execution. |
PythonEnvironmentVariablesApi |
Environment variable lookup/events | Resolves effective environment variables and reports source changes. |
PythonEnvironmentApi |
Complete flat public API | Combines all environment, package, project, execution, and environment-variable APIs exposed at runtime. |
Capabilities are advisory, read-only checks of manager support. They do not invoke operations, validate requests, or guarantee success.
| Environment capabilities | Package capabilities |
|---|---|
environments.list |
packages.list |
environments.resolve |
packages.install |
environments.getSelected |
packages.uninstall |
environments.setSelected |
packages.upgrade |
environments.create |
packages.list.direct |
environments.remove |
packages.fetchversions |
Listing includes refresh. Package installation, uninstallation, and upgrades
use the existing manage() method. packages.list.direct is best-effort
classification, not exact installation intent. Selection is scoped environment
selection, not shell activation.
Pip reports installation, uninstallation, and upgrades as unsupported for Python 2 environments.
Public environment and package operations initiate the corresponding capability
queries before delegating to managers. These queries are observational: operations
do not wait for them or use their results to gate execution, and probe failures
are logged without changing operation results. They use the existing
getEnvironmentManagerCapability and getPackageManagerCapability methods,
including their legacy fallback and query telemetry. Package queries include the
routed manager's project when applicable. Registration, item factories, project
APIs, and execution APIs have no corresponding capability checks.
Use the exported EnvironmentCapability and PackageCapability constants or
their literal string values.
PackageCapability.ListDirect is packages.list.direct;
PackageCapability.FetchAvailableVersions is packages.fetchversions.
Environment queries target an explicit manager ID; package queries follow
environment/project routing. They use registered managers without activation,
waiting, or prompting. Unavailable managers return { supported: false, reason }.
An explicit package-query project must be registered; an unregistered nested
project cannot use its containing project's provider.
File-project paths are compared using platform-aware normalization and routed through the registered URI.
Feature-detect query methods when supporting older extension runtimes:
import { PackageCapability } from '@vscode/python-environments';
if (typeof api.getPackageManagerCapability === 'function') {
const support = await api.getPackageManagerCapability(environment, PackageCapability.ListDirect);
if (!support.supported) {
// support.reason explains why this feature is unavailable.
}
}Managers may omit capabilities for legacy compatibility. When supplied, the
map must advertise every capability key. An omitted map uses legacy method
inference; a missing key in a supplied map resolves { supported: false, reason: 'Capability not implemented' }. Checks receive CapabilityContext
and return Promise<Support>.
Legacy inference uses getLegacyPackageManagerCapability(manager, capability)
or getLegacyEnvironmentManagerCapability(manager, capability). They inspect method
presence without executing methods or reading advertisements. Listing requires both
listing and refresh methods; package install, uninstall, and upgrade require manage.
Other capabilities require their corresponding methods. This does not verify tool
versions or option behavior. Regular queries use this fallback only when the map is omitted.
Legacy helper results include isLegacy: true; other Support results may omit
the field, which means false.
import {
Capabilities,
PackageCapability,
PackageManagerCapability,
supportedCapability,
unsupportedCapability,
} from '@vscode/python-environments';
import { l10n } from 'vscode';
// Members of a PackageManager implementation:
readonly capabilities: Capabilities<PackageManagerCapability> = {
[PackageCapability.List]: supportedCapability,
[PackageCapability.ListDirect]: supportedCapability,
[PackageCapability.FetchAvailableVersions]: unsupportedCapability,
[PackageCapability.Install]: supportedCapability,
[PackageCapability.Uninstall]: supportedCapability,
[PackageCapability.Upgrade]: async (_context) => ({
supported: false,
reason: l10n.t('This manager does not support upgrading packages.'),
}),
};Checks must not prompt, mutate state, or invoke operations to test support.
Return { supported: false, reason } for unsupported features; let unexpected
probe failures reject.
Public routed queries emit MANAGER.CAPABILITY_QUERY with the manager ID, kind,
tool version, capability, outcome, and isLegacy (false when omitted from support).
Unavailable managers use ID none;
unknown or failed version lookups use unknown. Version collection runs in the
background with a one-second timeout and cannot delay query results or errors.
Context, paths, reasons, and error messages are not sent. Unresolved routing
emits an unavailable event. Ownership errors and routing exceptions thrown
before capture, as well as internal checks, do not emit this event.
- Use only exports from
@vscode/python-environments; do not importinternal.api.tsor extension implementation modules. - Declare
ms-python.vscode-python-envsinextensionDependencies. - Acquire the API through
PythonEnvironments.api(). - Remember that the API object is flat.
- Use
env.envIdandpkg.pkgId, notenv.idorpkg.id. - Pass URIs so the API can route project and environment operations.
- Feature-detect optional provider methods before calling them.
- Dispose event subscriptions and provider registrations.
- Preserve operational errors and handle documented
undefinedresults. - Use
isPackageVersionLookupNotSupportedError()across bundle boundaries. - Recompile after updating the npm package so TypeScript detects API changes.
The public API is intended to avoid breaking changes. Check
api/CHANGELOG.md when updating the package.
The npm package and VS Code extension are independently versioned. Each npm
release's package.json declares an exact stable minimumExtensionVersion.
The npm helper accepts installed versions greater than or equal to that floor
using SemVer precedence. A 1.40.0 floor rejects 1.40.0-rc.1, but accepts
1.41.0-rc.1, odd-minor preview builds such as 1.41.0, and build metadata.
Incompatible versions produce an error asking the consumer to update the
extension and reload VS Code; invalid or missing version metadata also rejects.
New npm releases use the latest stable extension as their baseline. Already published packages keep their original floor, so supporting an older extension may require pinning an older npm package. This is a minimum-version guarantee, not protection against future breaking extension changes. Type-only imports or direct access to the extension's exports bypass the helper's runtime check.
src/api.ts- runtime API facadeapi/runtime/main.ts- npm compatibility guardsrc/types.ts- authoritative public type contractssrc/publicErrors.ts- public errors and type guardsapi/README.md- npm package quick start- Making and Managing Python Projects
- Projects API Reference
- Python Environments API Design
- Startup Flow
{ "extensionDependencies": ["ms-python.vscode-python-envs"] }