diff --git a/package.json b/package.json index c548601e00e9..cd465252aaad 100644 --- a/package.json +++ b/package.json @@ -1474,9 +1474,10 @@ "displayName": "Get Python Environment Info", "userDescription": "%python.languageModelTools.get_python_environment_details.userDescription%", "modelDescription": "This tool will retrieve the details of the Python Environment for the specified file or workspace. The details returned include the 1. Type of Environment (conda, venv, etec), 2. Version of Python, 3. List of all installed packages with their versions. ALWAYS call configure_python_environment before using this tool.", - "toolReferenceName": "pythonGetEnvironmentInfo", + "toolReferenceName": "getPythonEnvironmentInfo", "tags": [ "python", + "python environment", "extension_installed_by_tool", "enable_other_tool_configure_python_environment" ], @@ -1491,17 +1492,17 @@ } }, "required": [] - }, - "when": "!pythonEnvExtensionInstalled" + } }, { "name": "get_python_executable_details", "displayName": "Get Python Executable", "userDescription": "%python.languageModelTools.get_python_executable_details.userDescription%", "modelDescription": "This tool will retrieve the details of the Python Environment for the specified file or workspace. ALWAYS use this tool before executing any Python command in the terminal. This tool returns the details of how to construct the fully qualified path and or command including details such as arguments required to run Python in a terminal. Note: Instead of executing `python --version` or `python -c 'import sys; print(sys.executable)'`, use this tool to get the Python executable path to replace the `python` command. E.g. instead of using `python -c 'import sys; print(sys.executable)'`, use this tool to build the command `conda run -n -c 'import sys; print(sys.executable)'`. ALWAYS call configure_python_environment before using this tool.", - "toolReferenceName": "pythonExecutableCommand", + "toolReferenceName": "getPythonExecutableCommand", "tags": [ "python", + "python environment", "extension_installed_by_tool", "enable_other_tool_configure_python_environment" ], @@ -1516,17 +1517,17 @@ } }, "required": [] - }, - "when": "!pythonEnvExtensionInstalled" + } }, { "name": "install_python_packages", "displayName": "Install Python Package", "userDescription": "%python.languageModelTools.install_python_packages.userDescription%", "modelDescription": "Installs Python packages in the given workspace. Use this tool to install packages in the user's chosen environment. ALWAYS call configure_python_environment before using this tool.", - "toolReferenceName": "pythonInstallPackage", + "toolReferenceName": "installPythonPackage", "tags": [ "python", + "python environment", "install python package", "extension_installed_by_tool", "enable_other_tool_configure_python_environment" @@ -1551,8 +1552,7 @@ "required": [ "packageList" ] - }, - "when": "!pythonEnvExtensionInstalled" + } }, { "name": "configure_python_environment", @@ -1562,6 +1562,7 @@ "toolReferenceName": "configurePythonEnvironment", "tags": [ "python", + "python environment", "extension_installed_by_tool" ], "icon": "$(gear)", @@ -1575,8 +1576,7 @@ } }, "required": [] - }, - "when": "!pythonEnvExtensionInstalled" + } }, { "name": "create_virtual_environment", diff --git a/src/client/chat/configurePythonEnvTool.ts b/src/client/chat/configurePythonEnvTool.ts index 6117285a523e..a8a18a1d3852 100644 --- a/src/client/chat/configurePythonEnvTool.ts +++ b/src/client/chat/configurePythonEnvTool.ts @@ -28,7 +28,6 @@ import { ITerminalHelper } from '../common/terminal/types'; import { IRecommendedEnvironmentService } from '../interpreter/configuration/types'; import { CreateVirtualEnvTool } from './createVirtualEnvTool'; import { ISelectPythonEnvToolArguments, SelectPythonEnvTool } from './selectEnvTool'; -import { useEnvExtension } from '../envExt/api.internal'; export class ConfigurePythonEnvTool implements LanguageModelTool { private readonly terminalExecutionService: TerminalCodeExecutionProvider; @@ -78,10 +77,7 @@ export class ConfigurePythonEnvTool implements LanguageModelTool, + options: LanguageModelToolInvocationOptions, token: CancellationToken, ): Promise { const resource = resolveFilePath(options.input.resourcePath); @@ -83,14 +86,26 @@ export class CreateVirtualEnvTool implements LanguageModelTool resolve())); }); - const created = await raceCancellationError( - createVirtualEnvironment({ - interpreter: preferredGlobalPythonEnv.id, - workspaceFolder, - }), - token, - ); - if (!created?.path) { + let createdEnvPath: string | undefined = undefined; + if (useEnvExtension()) { + const result: PythonEnvironment | undefined = await commands.executeCommand('python-envs.createAny', { + quickCreate: true, + additionalPackages: options.input.packageList || [], + uri: workspaceFolder.uri, + selectEnvironment: true, + }); + createdEnvPath = result?.environmentPath.fsPath; + } else { + const created = await raceCancellationError( + createVirtualEnvironment({ + interpreter: preferredGlobalPythonEnv.id, + workspaceFolder, + }), + token, + ); + createdEnvPath = created?.path; + } + if (!createdEnvPath) { traceWarn(`${CreateVirtualEnvTool.toolName} tool not invoked, virtual env not created.`); throw new CancellationError(); } @@ -102,7 +117,7 @@ export class CreateVirtualEnvTool implements LanguageModelTool, + options: LanguageModelToolInvocationPrepareOptions, token: CancellationToken, ): Promise { const resource = resolveFilePath(options.input.resourcePath); diff --git a/src/client/chat/getPythonEnvTool.ts b/src/client/chat/getPythonEnvTool.ts index 5ec8e77c6c1e..121bdd6532c1 100644 --- a/src/client/chat/getPythonEnvTool.ts +++ b/src/client/chat/getPythonEnvTool.ts @@ -20,6 +20,7 @@ import { getEnvironmentDetails, getToolResponseIfNotebook, IResourceReference, r import { resolveFilePath } from './utils'; import { getPythonPackagesResponse } from './listPackagesTool'; import { ITerminalHelper } from '../common/terminal/types'; +import { getEnvExtApi, useEnvExtension } from '../envExt/api.internal'; export class GetEnvironmentInfoTool implements LanguageModelTool { private readonly terminalExecutionService: TerminalCodeExecutionProvider; @@ -56,14 +57,33 @@ export class GetEnvironmentInfoTool implements LanguageModelTool 0) { + // Installed Python packages, each in the format or (). The version may be omitted if unknown. Returns an empty array if no packages are installed. + const response = [ + 'Below is a list of the Python packages, each in the format or (). The version may be omitted if unknown: ', + ]; + pkgs.forEach((pkg) => { + const version = pkg.version; + response.push(version ? `- ${pkg.name} (${version})` : `- ${pkg.name}`); + }); + packages = response.join('\n'); + } + } + if (!packages) { + packages = await getPythonPackagesResponse( + environment, + this.pythonExecFactory, + this.processServiceFactory, + resourcePath, + token, + ); + } const message = await getEnvironmentDetails( resourcePath, this.api, diff --git a/src/client/chat/index.ts b/src/client/chat/index.ts index de7d53875305..b548860eaae3 100644 --- a/src/client/chat/index.ts +++ b/src/client/chat/index.ts @@ -1,13 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { commands, extensions, lm } from 'vscode'; +import { lm } from 'vscode'; import { PythonExtension } from '../api/types'; import { IServiceContainer } from '../ioc/types'; import { InstallPackagesTool } from './installPackagesTool'; import { IExtensionContext } from '../common/types'; import { DisposableStore } from '../common/utils/resourceLifecycle'; -import { ENVS_EXTENSION_ID } from '../envExt/api.internal'; import { IDiscoveryAPI } from '../pythonEnvironments/base/locator'; import { GetExecutableTool } from './getExecutableTool'; import { GetEnvironmentInfoTool } from './getPythonEnvTool'; @@ -21,11 +20,6 @@ export function registerTools( environmentsApi: PythonExtension['environments'], serviceContainer: IServiceContainer, ) { - if (extensions.getExtension(ENVS_EXTENSION_ID)) { - return; - } - const contextKey = 'pythonEnvExtensionInstalled'; - commands.executeCommand('setContext', contextKey, false); const ourTools = new DisposableStore(); context.subscriptions.push(ourTools); @@ -55,14 +49,4 @@ export function registerTools( new ConfigurePythonEnvTool(environmentsApi, serviceContainer, createVirtualEnvTool), ), ); - ourTools.add( - extensions.onDidChange(() => { - const envExtension = extensions.getExtension(ENVS_EXTENSION_ID); - if (envExtension) { - envExtension.activate(); - commands.executeCommand('setContext', contextKey, true); - ourTools.dispose(); - } - }), - ); } diff --git a/src/client/chat/installPackagesTool.ts b/src/client/chat/installPackagesTool.ts index 36544128582a..123c2580538f 100644 --- a/src/client/chat/installPackagesTool.ts +++ b/src/client/chat/installPackagesTool.ts @@ -25,6 +25,7 @@ import { resolveFilePath } from './utils'; import { IModuleInstaller } from '../common/installer/types'; import { ModuleInstallerType } from '../pythonEnvironments/info'; import { IDiscoveryAPI } from '../pythonEnvironments/base/locator'; +import { getEnvExtApi, useEnvExtension } from '../envExt/api.internal'; export interface IInstallPackageArgs extends IResourceReference { packageList: string[]; @@ -50,6 +51,24 @@ export class InstallPackagesTool implements LanguageModelTool { // environment const envPath = api.getActiveEnvironmentPath(resourcePath); - const environment = await raceCancellationError(api.resolveEnvironment(envPath), token); - if (!environment || !environment.version) { - throw new Error('No environment found for the provided resource path: ' + resourcePath?.fsPath); + let envType = ''; + let envVersion = ''; + let runCommand = ''; + if (useEnvExtension()) { + const environment = + (await raceCancellationError(resolveEnvironment(envPath.id), token)) || + (await raceCancellationError(resolveEnvironment(envPath.path), token)); + if (!environment || !environment.version) { + throw new Error('No environment found for the provided resource path: ' + resourcePath?.fsPath); + } + envVersion = environment.version; + try { + const managerId = environment.envId.managerId; + envType = + (!managerId.endsWith(':') && managerId.includes(':') ? managerId.split(':').reverse()[0] : '') || + 'unknown'; + } catch { + envType = 'unknown'; + } + + const execInfo = environment.execInfo; + const executable = execInfo?.activatedRun?.executable ?? execInfo?.run.executable ?? 'python'; + const args = execInfo?.activatedRun?.args ?? execInfo?.run.args ?? []; + runCommand = terminalHelper.buildCommandForTerminal(TerminalShellType.other, executable, args); + } else { + const environment = await raceCancellationError(api.resolveEnvironment(envPath), token); + if (!environment || !environment.version) { + throw new Error('No environment found for the provided resource path: ' + resourcePath?.fsPath); + } + envType = environment.environment?.type || 'unknown'; + envVersion = environment.version.sysVersion || 'unknown'; + runCommand = await raceCancellationError( + getTerminalCommand(environment, resourcePath, terminalExecutionService, terminalHelper), + token, + ); } - const runCommand = await raceCancellationError( - getTerminalCommand(environment, resourcePath, terminalExecutionService, terminalHelper), - token, - ); const message = [ `Following is the information about the Python environment:`, - `1. Environment Type: ${environment.environment?.type || 'unknown'}`, - `2. Version: ${environment.version.sysVersion || 'unknown'}`, + `1. Environment Type: ${envType}`, + `2. Version: ${envVersion}`, '', `3. Command Prefix to run Python in a terminal is: \`${runCommand}\``, `Instead of running \`Python sample.py\` in the terminal, you will now run: \`${runCommand} sample.py\``, @@ -183,7 +212,8 @@ export function doesWorkspaceHaveVenvOrCondaEnv(resource: Uri | undefined, api: env.environment?.folderUri && env.executable.sysPrefix && dirname(env.executable.sysPrefix) === workspaceFolder.uri.fsPath && - env.environment.name === '.venv' && + ((env.environment.name || '').startsWith('.venv') || + env.executable.sysPrefix === join(workspaceFolder.uri.fsPath, '.venv')) && env.environment.type === 'VirtualEnvironment' ); }; @@ -192,7 +222,8 @@ export function doesWorkspaceHaveVenvOrCondaEnv(resource: Uri | undefined, api: env.environment?.folderUri && env.executable.sysPrefix && dirname(env.executable.sysPrefix) === workspaceFolder.uri.fsPath && - env.environment.folderUri.fsPath === join(workspaceFolder.uri.fsPath, '.conda') && + (env.environment.folderUri.fsPath === join(workspaceFolder.uri.fsPath, '.conda') || + env.executable.sysPrefix === join(workspaceFolder.uri.fsPath, '.conda')) && env.environment.type === 'Conda' ); }; diff --git a/src/client/envExt/api.internal.ts b/src/client/envExt/api.internal.ts index 7d511eac49ea..552e31a0598e 100644 --- a/src/client/envExt/api.internal.ts +++ b/src/client/envExt/api.internal.ts @@ -71,6 +71,11 @@ export async function getEnvironment(scope: GetEnvironmentScope): Promise { + const envExtApi = await getEnvExtApi(); + return envExtApi.resolveEnvironment(Uri.file(pythonPath)); +} + export async function refreshEnvironments(scope: RefreshEnvironmentsScope): Promise { const envExtApi = await getEnvExtApi(); return envExtApi.refreshEnvironments(scope); diff --git a/src/client/envExt/api.legacy.ts b/src/client/envExt/api.legacy.ts index fb01e73bdfcf..0f942a13eea2 100644 --- a/src/client/envExt/api.legacy.ts +++ b/src/client/envExt/api.legacy.ts @@ -4,7 +4,7 @@ import { Terminal, Uri } from 'vscode'; import { getEnvExtApi, getEnvironment } from './api.internal'; import { EnvironmentType, PythonEnvironment as PythonEnvironmentLegacy } from '../pythonEnvironments/info'; -import { PythonEnvironment, PythonTerminalOptions } from './types'; +import { PythonEnvironment, PythonTerminalCreateOptions } from './types'; import { Architecture } from '../common/utils/platform'; import { parseVersion } from '../pythonEnvironments/base/info/pythonVersion'; import { PythonEnvType } from '../pythonEnvironments/base/info'; @@ -137,7 +137,7 @@ export async function resetInterpreterLegacy(uri: Uri | undefined): Promise { const api = await getEnvExtApi(); const pythonEnv = await api.getEnvironment(resource); diff --git a/src/client/envExt/types.ts b/src/client/envExt/types.ts index 190c0ccea5b9..707d641bbfe8 100644 --- a/src/client/envExt/types.ts +++ b/src/client/envExt/types.ts @@ -2,16 +2,16 @@ // Licensed under the MIT License. import { - Uri, Disposable, - MarkdownString, Event, + FileChangeType, LogOutputChannel, - ThemeIcon, - Terminal, + MarkdownString, TaskExecution, + Terminal, TerminalOptions, - FileChangeType, + ThemeIcon, + Uri, } from 'vscode'; /** @@ -48,23 +48,6 @@ export interface PythonCommandRunConfiguration { args?: string[]; } -export enum TerminalShellType { - powershell = 'powershell', - powershellCore = 'powershellCore', - commandPrompt = 'commandPrompt', - gitbash = 'gitbash', - bash = 'bash', - zsh = 'zsh', - ksh = 'ksh', - fish = 'fish', - cshell = 'cshell', - tcshell = 'tshell', - nushell = 'nushell', - wsl = 'wsl', - xonsh = 'xonsh', - unknown = 'unknown', -} - /** * Contains details on how to use a particular python environment * @@ -73,7 +56,7 @@ export enum TerminalShellType { * 2. If {@link PythonEnvironmentExecutionInfo.activatedRun} is not provided, then: * - If {@link PythonEnvironmentExecutionInfo.shellActivation} is provided and shell type is known, then that will be used. * - If {@link PythonEnvironmentExecutionInfo.shellActivation} is provided and shell type is not known, then: - * - {@link TerminalShellType.unknown} will be used if provided. + * - 'unknown' will be used if provided. * - {@link PythonEnvironmentExecutionInfo.activation} will be used otherwise. * - If {@link PythonEnvironmentExecutionInfo.shellActivation} is not provided, then {@link PythonEnvironmentExecutionInfo.activation} will be used. * - If {@link PythonEnvironmentExecutionInfo.activation} is not provided, then {@link PythonEnvironmentExecutionInfo.run} will be used. @@ -82,7 +65,7 @@ export enum TerminalShellType { * 1. If {@link PythonEnvironmentExecutionInfo.shellActivation} is provided and shell type is known, then that will be used. * 2. If {@link PythonEnvironmentExecutionInfo.shellActivation} is provided and shell type is not known, then {@link PythonEnvironmentExecutionInfo.activation} will be used. * 3. If {@link PythonEnvironmentExecutionInfo.shellActivation} is not provided, then: - * - {@link TerminalShellType.unknown} will be used if provided. + * - 'unknown' will be used if provided. * - {@link PythonEnvironmentExecutionInfo.activation} will be used otherwise. * 4. If {@link PythonEnvironmentExecutionInfo.activation} is not provided, then {@link PythonEnvironmentExecutionInfo.run} will be used. * @@ -107,11 +90,11 @@ export interface PythonEnvironmentExecutionInfo { /** * Details on how to activate an environment using a shell specific command. * If set this will override the {@link PythonEnvironmentExecutionInfo.activation}. - * {@link TerminalShellType.unknown} is used if shell type is not known. - * If {@link TerminalShellType.unknown} is not provided and shell type is not known then + * 'unknown' is used if shell type is not known. + * If 'unknown' is not provided and shell type is not known then * {@link PythonEnvironmentExecutionInfo.activation} if set. */ - shellActivation?: Map; + shellActivation?: Map; /** * Details on how to deactivate an environment. @@ -121,11 +104,11 @@ export interface PythonEnvironmentExecutionInfo { /** * Details on how to deactivate an environment using a shell specific command. * If set this will override the {@link PythonEnvironmentExecutionInfo.deactivation} property. - * {@link TerminalShellType.unknown} is used if shell type is not known. - * If {@link TerminalShellType.unknown} is not provided and shell type is not known then + * 'unknown' is used if shell type is not known. + * If 'unknown' is not provided and shell type is not known then * {@link PythonEnvironmentExecutionInfo.deactivation} if set. */ - shellDeactivation?: Map; + shellDeactivation?: Map; } /** @@ -143,6 +126,33 @@ export interface PythonEnvironmentId { managerId: string; } +/** + * Display information for an environment group. + */ +export interface EnvironmentGroupInfo { + /** + * The name of the environment group. This is used as an identifier for the group. + * + * Note: The first instance of the group with the given name will be used in the UI. + */ + readonly name: string; + + /** + * The description of the environment group. + */ + readonly description?: string; + + /** + * The tooltip for the environment group, which can be a string or a Markdown string. + */ + readonly tooltip?: string | MarkdownString; + + /** + * The icon path for the environment group, which can be a string, Uri, or an object with light and dark theme paths. + */ + readonly iconPath?: IconPath; +} + /** * Interface representing information about a Python environment. */ @@ -193,16 +203,20 @@ export interface PythonEnvironmentInfo { readonly iconPath?: IconPath; /** - * Information on how to execute the Python environment. If not provided, {@link PythonEnvironmentApi.resolveEnvironment} will be - * used to to get the details at later point if needed. The recommendation is to fill this in if known. + * Information on how to execute the Python environment. This is required for executing Python code in the environment. */ - readonly execInfo?: PythonEnvironmentExecutionInfo; + readonly execInfo: PythonEnvironmentExecutionInfo; /** * `sys.prefix` is the path to the base directory of the Python installation. Typically obtained by executing `sys.prefix` in the Python interpreter. * This is required by extension like Jupyter, Pylance, and other extensions to provide better experience with python. */ readonly sysPrefix: string; + + /** + * Optional `group` for this environment. This is used to group environments in the Environment Manager UI. + */ + readonly group?: string | EnvironmentGroupInfo; } /** @@ -219,7 +233,7 @@ export interface PythonEnvironment extends PythonEnvironmentInfo { * Type representing the scope for setting a Python environment. * Can be undefined or a URI. */ -export type SetEnvironmentScope = undefined | Uri; +export type SetEnvironmentScope = undefined | Uri | Uri[]; /** * Type representing the scope for getting a Python environment. @@ -300,14 +314,26 @@ export type DidChangeEnvironmentsEventArgs = { /** * Type representing the context for resolving a Python environment. */ -export type ResolveEnvironmentContext = PythonEnvironment | Uri; +export type ResolveEnvironmentContext = Uri; + +export interface QuickCreateConfig { + /** + * The description of the quick create step. + */ + readonly description: string; + + /** + * The detail of the quick create step. + */ + readonly detail?: string; +} /** * Interface representing an environment manager. */ export interface EnvironmentManager { /** - * The name of the environment manager. + * The name of the environment manager. Allowed characters (a-z, A-Z, 0-9, -, _). */ readonly name: string; @@ -317,7 +343,9 @@ export interface EnvironmentManager { readonly displayName?: string; /** - * The preferred package manager ID for the environment manager. + * The preferred package manager ID for the environment manager. This is a combination + * of publisher id, extension id, and {@link EnvironmentManager.name package manager name}. + * `.:` * * @example * 'ms-python.python:pip' @@ -344,12 +372,19 @@ export interface EnvironmentManager { */ readonly log?: LogOutputChannel; + /** + * The quick create details for the environment manager. Having this method also enables the quick create feature + * for the environment manager. Should Implement {@link EnvironmentManager.create} to support quick create. + */ + quickCreateConfig?(): QuickCreateConfig | undefined; + /** * Creates a new Python environment within the specified scope. * @param scope - The scope within which to create the environment. + * @param options - Optional parameters for creating the Python environment. * @returns A promise that resolves to the created Python environment, or undefined if creation failed. */ - create?(scope: CreateEnvironmentScope): Promise; + create?(scope: CreateEnvironmentScope, options?: CreateEnvironmentOptions): Promise; /** * Removes the specified Python environment. @@ -529,7 +564,7 @@ export interface DidChangePackagesEventArgs { */ export interface PackageManager { /** - * The name of the package manager. + * The name of the package manager. Allowed characters (a-z, A-Z, 0-9, -, _). */ name: string; @@ -559,20 +594,12 @@ export interface PackageManager { log?: LogOutputChannel; /** - * Installs packages in the specified Python environment. + * Installs/Uninstall packages in the specified Python environment. * @param environment - The Python environment in which to install packages. - * @param packages - The packages to install. + * @param options - Options for managing packages. * @returns A promise that resolves when the installation is complete. */ - install(environment: PythonEnvironment, packages: string[], options: PackageInstallOptions): Promise; - - /** - * Uninstalls packages from the specified Python environment. - * @param environment - The Python environment from which to uninstall packages. - * @param packages - The packages to uninstall, which can be an array of packages or strings. - * @returns A promise that resolves when the uninstall is complete. - */ - uninstall(environment: PythonEnvironment, packages: Package[] | string[]): Promise; + manage(environment: PythonEnvironment, options: PackageManagementOptions): Promise; /** * Refreshes the package list for the specified Python environment. @@ -588,17 +615,6 @@ export interface PackageManager { */ getPackages(environment: PythonEnvironment): Promise; - /** - * Get a list of installable items for a Python project. - * - * @param environment The Python environment for which to get installable items. - * - * Note: An environment can be used by multiple projects, so the installable items returned. - * should be for the environment. If you want to do it for a particular project, then you should - * ask user to select a project, and filter the installable items based on the project. - */ - getInstallable?(environment: PythonEnvironment): Promise; - /** * Event that is fired when packages change. */ @@ -651,9 +667,14 @@ export interface PythonProjectCreatorOptions { name: string; /** - * Optional path that may be provided as a root for the project. + * Path provided as the root for the project. */ - uri?: Uri; + rootUri: Uri; + + /** + * Boolean indicating whether the project should be created without any user input. + */ + quickCreate?: boolean; } /** @@ -686,11 +707,20 @@ export interface PythonProjectCreator { readonly iconPath?: IconPath; /** - * Creates a new Python project or projects. - * @param options - Optional parameters for creating the Python project. - * @returns A promise that resolves to a Python project, an array of Python projects, or undefined. + * Creates a new Python project(s) or, if files are not a project, returns Uri(s) to the created files. + * Anything that needs its own python environment constitutes a project. + * @param options Optional parameters for creating the Python project. + * @returns A promise that resolves to one of the following: + * - PythonProject or PythonProject[]: when a single or multiple projects are created. + * - Uri or Uri[]: when files are created that do not constitute a project. + * - undefined: if project creation fails. */ - create(options?: PythonProjectCreatorOptions): Promise; + create(options?: PythonProjectCreatorOptions): Promise; + + /** + * A flag indicating whether the project creator supports quick create where no user input is required. + */ + readonly supportsQuickCreate?: boolean; } /** @@ -708,55 +738,70 @@ export interface DidChangePythonProjectsEventArgs { removed: PythonProject[]; } -/** - * Options for package installation. - */ -export interface PackageInstallOptions { - /** - * Upgrade the packages if it is already installed. - */ - upgrade?: boolean; -} +export type PackageManagementOptions = + | { + /** + * Upgrade the packages if they are already installed. + */ + upgrade?: boolean; -export interface Installable { - /** - * The display name of the package, requirements, pyproject.toml or any other project file. - */ - readonly displayName: string; + /** + * Show option to skip package installation or uninstallation. + */ + showSkipOption?: boolean; + /** + * The list of packages to install. + */ + install: string[]; - /** - * Arguments passed to the package manager to install the package. - * - * @example - * ['debugpy==1.8.7'] for `pip install debugpy==1.8.7`. - * ['--pre', 'debugpy'] for `pip install --pre debugpy`. - * ['-r', 'requirements.txt'] for `pip install -r requirements.txt`. - */ - readonly args: string[]; + /** + * The list of packages to uninstall. + */ + uninstall?: string[]; + } + | { + /** + * Upgrade the packages if they are already installed. + */ + upgrade?: boolean; - /** - * Installable group name, this will be used to group installable items in the UI. - * - * @example - * `Requirements` for any requirements file. - * `Packages` for any package. - */ - readonly group?: string; + /** + * Show option to skip package installation or uninstallation. + */ + showSkipOption?: boolean; + /** + * The list of packages to install. + */ + install?: string[]; + + /** + * The list of packages to uninstall. + */ + uninstall: string[]; + }; +/** + * Options for creating a Python environment. + */ +export interface CreateEnvironmentOptions { /** - * Description about the installable item. This can also be path to the requirements, - * version of the package, or any other project file path. + * Provides some context about quick create based on user input. + * - if true, the environment should be created without any user input or prompts. + * - if false, the environment creation can show user input or prompts. + * This also means user explicitly skipped the quick create option. + * - if undefined, the environment creation can show user input or prompts. + * You can show quick create option to the user if you support it. */ - readonly description?: string; - + quickCreate?: boolean; /** - * External Uri to the package on pypi or docs. - * @example - * https://pypi.org/project/debugpy/ for `debugpy`. + * Packages to install in addition to the automatically picked packages as a part of creating environment. */ - readonly uri?: Uri; + additionalPackages?: string[]; } +/** + * Object representing the process started using run in background API. + */ export interface PythonProcess { /** * The process ID of the Python process. @@ -817,9 +862,13 @@ export interface PythonEnvironmentManagementApi { * Create a Python environment using environment manager associated with the scope. * * @param scope Where the environment is to be created. + * @param options Optional parameters for creating the Python environment. * @returns The Python environment created. `undefined` if not created. */ - createEnvironment(scope: CreateEnvironmentScope): Promise; + createEnvironment( + scope: CreateEnvironmentScope, + options?: CreateEnvironmentOptions, + ): Promise; /** * Remove a Python environment. @@ -938,21 +987,13 @@ export interface PythonPackageItemApi { export interface PythonPackageManagementApi { /** - * Install packages into a Python Environment. + * Install/Uninstall packages into a Python Environment. * * @param environment The Python Environment into which packages are to be installed. * @param packages The packages to install. * @param options Options for installing packages. */ - installPackages(environment: PythonEnvironment, packages: string[], options?: PackageInstallOptions): Promise; - - /** - * Uninstall packages from a Python Environment. - * - * @param environment The Python Environment from which packages are to be uninstalled. - * @param packages The packages to uninstall. - */ - uninstallPackages(environment: PythonEnvironment, packages: PackageInfo[] | string[]): Promise; + managePackages(environment: PythonEnvironment, options: PackageManagementOptions): Promise; } export interface PythonPackageManagerApi @@ -1017,9 +1058,9 @@ export interface PythonProjectModifyApi { */ export interface PythonProjectApi extends PythonProjectCreationApi, PythonProjectGetterApi, PythonProjectModifyApi {} -export interface PythonTerminalOptions extends TerminalOptions { +export interface PythonTerminalCreateOptions extends TerminalOptions { /** - * Whether to show the terminal. + * Whether to disable activation on create. */ disableActivation?: boolean; } @@ -1033,7 +1074,7 @@ export interface PythonTerminalCreateApi { * * Note: Non-activatable environments have no effect on the terminal. */ - createTerminal(environment: PythonEnvironment, options: PythonTerminalOptions): Promise; + createTerminal(environment: PythonEnvironment, options: PythonTerminalCreateOptions): Promise; } /**