-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Python Environment APIs
These APIs provide a way for extensions to work with by python environments available in the user's machine as found by the Python extension.
-
getActiveEnvironmentPath(resource?: Resource): EnvironmentPath;
Returns the path to the python binary selected by the user or as in the settings. This is just the path to the python binary, this does not provide activation or any other activation command. The
resource
if provided will be used to determine the python binary in a multi-root scenario. If resource isundefined
then the API returns what ever is set for the workspace.Params:
-
resource
: Uri of a file or workspace folder. This is used to determine the env in a multi-root scenario. Ifundefined
, then the API returns what ever is set for the workspace.
-
-
updateActiveEnvironmentPath( environment: string | EnvironmentPath | Environment, resource?: Resource, ): Promise<void>;
Sets the active environment path for the python extension for the resource. Configuration target will always be the workspace folder.
Params:
-
environment
: Full path to environment folder or python executable for the environment. Can also pass the environment itself. -
resource
: [optional] File or workspace to scope to a particular workspace folder.
-
-
readonly known: Environment[];
Carries environments known to the extension at the time of fetching the property. Note this may not contain all environments in the system as a refresh might be going on.
-
resolveEnvironment( environment: Environment | EnvironmentPath | string, ): Promise<ResolvedEnvironment | undefined>;
Returns details for the given environment, or
undefined
if the env is invalid.Params:
- environment: Full path to environment folder or python executable for the environment. Can also pass the environment id or the environment itself.
-
refreshEnvironments(options?: RefreshOptions, token?: CancellationToken): Promise<void>;
This API will trigger environment discovery, but only if it has not already happened in this VSCode session. Useful for making sure env list is up-to-date when the caller needs it for the first time.
To force trigger a refresh regardless of whether a refresh was already triggered, see option
RefreshOptions.forceRefresh
.Params:
- options:
RefreshOptions
- forceRefresh: When
true
, force trigger a refresh regardless of whether a refresh was already triggered. Note this can be expensive so it's best to only use it if user manually triggers a refresh.
- forceRefresh: When
- options:
This event is triggered when the known environment list changes, like when a environment is found, existing environment is removed, or some details changed on an environment.
Events:
- "add": New environment is added.
- "remove": Existing environment in the list is removed.
- "update": New information found about existing environment.
This event addresses the API request from Jupyter extension https://github.com/microsoft/vscode-python/issues/17004
Event Type: EnvironmentsChangeEvent
Event triggered when either user selects an interpreter or updateActiveEnvironmentPath
results in a change.
Event Type: ActiveEnvironmentPathChangeEvent
export type EnvironmentPath = {
readonly id: string;
/**
* Path to environment folder or path to python executable that uniquely identifies an environment. Environments
* lacking a python executable are identified by environment folder paths, whereas other envs can be identified
* using python executable path.
*/
readonly path: string;
};
export type EnvironmentsChangeEvent = {
readonly env: Environment;
readonly type: 'add' | 'remove' | 'update';
};
export type ActiveEnvironmentPathChangeEvent = EnvironmentPath & {
/**
* Workspace folder the environment changed for.
*/
readonly resource: WorkspaceFolder | undefined;
};
export type RefreshOptions = {
/**
* When `true`, force trigger a refresh regardless of whether a refresh was already triggered. Note this can be expensive so
* it's best to only use it if user manually triggers a refresh.
*/
forceRefresh?: boolean;
};
Copy over contents of https://github.com/microsoft/vscode-python/blob/main/src/client/apiTypes.ts as needed.
const extension = extensions.getExtension('ms-python.python');
if (extension) {
if (!extension.isActive) {
await extension.activate();
}
const pythonApi: IExtensionAPI = extension.exports as IExtensionAPI;
}
// This will return something like /usr/bin/python
const environmentPath = pythonApi.environments.getActiveEnvironmentPath();
// `environmentPath.path` carries the value of the setting. Note that this path may point to a folder and not the
// python binary. Depends entirely on how the env was created.
// E.g., `conda create -n myenv python` ensures the env has a python binary
// `conda create -n myenv` does not include a python binary.
// Also, the path specified may not be valid, use the following to get complete details for this environment if
// need be.
const environment = await pythonApi.environments.resolveEnvironment(environmentPath);
if (environment) {
// run your script here.
}
await pythonApi.environments.updateActiveEnvironment('/bin/usr/python');
let currentActivePython = undefined;
extContext.subscriptions.push(
pythonApi.environments.onDidChangeActiveEnvironment((e: ActiveEnvironmentPathChangeEvent) => {
currentActivePython = e.path;
}),
);
// Get currently selected environment.
currentActivePython = pythonApi.environments.getActiveEnvironmentPath()?.path;
// Use this way if you don't really want to wait for the extension to fully load info for all environments.
const environments = pythonApi.environments.known;
const environments: string[] | undefined;
extContext.subscriptions.push(
pythonApi.environments.onDidEnvironmentsChanged((e: EnvironmentsChangeEvent) => {
if (environments) {
// handle changes here based on add, remove, update
}
}),
);
// Get the current list of environments.
environments = pythonApi.environments.known;
const detail = pythonApi.environments.resolveEnvironment(`usr/bin/python`);
// Trigger a full refresh and wait for it to complete.
await pythonApi.environments.refreshEnvironments();
const environments = pythonApi.environments.known;
const envs = pythonApi.environments.known;
const foundEnv = envs.find(p => p.name === 'envName');
if (foundEnv) {
await pythonApi.environments.updateActiveEnvironment(foundEnv);
} else {
// Could not find the path, maybe a refresh is needed?
await pythonApi.environments.refreshEnvironments();
const foundEnv = envs.find(p => p.name === 'envName');
if (foundEnv) {
await pythonApi.environments.updateActiveEnvironment(foundEnv);
} else {
// Environment not found in the system.
}
}
registerEnvironmentProvider(
environmentProvider: IEnvironmentProvider,
metadata: EnvironmentProviderMetadata,
): Promise<Disposable>;
// TODO: Figure out whether to return a promise or not
run: {
// Functions would only require the arguments. The env provider can internally decide on the commands.
exec: Function;
shellExec: Function; // Only for backwards compatibility.
execObservable: Function;
/**
* Uses a VSCode terminal.
* */
terminalExec: () => void;
/**
* Any environment variables that can be used to activate the environment, if supported.
* If not provided, Python extension itself uses the other execution APIs to calculate it.
*/
env?: { [key: string]: string | null | undefined };
};
https://github.com/microsoft/vscode-python/issues/15112 This can be problematic, although the request asks for PYTHONPATH
, in a activated environment scenario we will have to provide activated environment variables, and somehow also detect environment variable changes when new packages are introduced to the environment which can make those environment changes. At this point we provide the detail about the environment type, it is up to the consumer to get the variables as they are needed.
We could revisit this when we have better activation story for ourselves.