diff --git a/src/extension/common/python.ts b/src/extension/common/python.ts index 18686d1f..7043fe63 100644 --- a/src/extension/common/python.ts +++ b/src/extension/common/python.ts @@ -6,13 +6,18 @@ import { ActiveEnvironmentPathChangeEvent, Environment, EnvironmentPath, + EnvironmentVariables, PythonExtension, + ResolvedEnvironment, Resource, } from '@vscode/python-extension'; -import { commands, EventEmitter, extensions, Uri, Event, Disposable } from 'vscode'; +import { commands, EventEmitter, extensions, Uri, Event, Disposable, Extension } from 'vscode'; import { createDeferred } from './utils/async'; import { traceError, traceLog } from './log/logging'; +/** + * Interface for the Python extension API. + */ interface IExtensionApi { ready: Promise; settings: { @@ -20,14 +25,26 @@ interface IExtensionApi { }; } +/** + * Details about a Python interpreter. + */ export interface IInterpreterDetails { + /** Array of path components to the Python executable */ path?: string[]; + /** The workspace resource associated with this interpreter */ resource?: Uri; } +/** Event emitter for Python interpreter changes */ const onDidChangePythonInterpreterEvent = new EventEmitter(); + +/** Event that fires when the active Python interpreter changes */ export const onDidChangePythonInterpreter: Event = onDidChangePythonInterpreterEvent.event; -async function activateExtension() { +/** + * Activates the Python extension and ensures it's ready for use. + * @returns The activated Python extension instance + */ +async function activateExtension(): Promise | undefined> { console.log('Activating Python extension...'); activateEnvsExtension(); const extension = extensions.getExtension('ms-python.python'); @@ -39,7 +56,11 @@ async function activateExtension() { console.log('Python extension activated.'); return extension; } -async function activateEnvsExtension() { +/** + * Activates the Python environments extension. + * @returns The activated Python environments extension instance + */ +async function activateEnvsExtension(): Promise | undefined> { const extension = extensions.getExtension('ms-python.vscode-python-envs'); if (extension) { if (!extension.isActive) { @@ -49,23 +70,36 @@ async function activateEnvsExtension() { return extension; } +/** + * Gets the Python extension's API interface. + * @returns The Python extension API or undefined if not available + */ async function getPythonExtensionAPI(): Promise { const extension = await activateExtension(); return extension?.exports as IExtensionApi; } +/** + * Gets the Python extension's environment API. + * @returns The Python extension environment API + */ async function getPythonExtensionEnviromentAPI(): Promise { // Load the Python extension API await activateExtension(); return await PythonExtension.api(); } +/** + * Initializes Python integration by setting up event listeners and getting initial interpreter details. + * @param disposables Array to store disposable resources for cleanup + */ export async function initializePython(disposables: Disposable[]): Promise { try { const api = await getPythonExtensionEnviromentAPI(); if (api) { disposables.push( + // This event is triggered when the active environment setting changes. api.environments.onDidChangeActiveEnvironmentPath((e: ActiveEnvironmentPathChangeEvent) => { let resourceUri: Uri | undefined; if (e.resource instanceof Uri) { @@ -87,31 +121,68 @@ export async function initializePython(disposables: Disposable[]): Promise } } -export async function runPythonExtensionCommand(command: string, ...rest: any[]) { +/** + * Executes a command from the Python extension. + * @param command The command identifier to execute + * @param rest Additional arguments to pass to the command + * @returns The result of the command execution + */ +export async function runPythonExtensionCommand(command: string, ...rest: any[]): Promise { await activateExtension(); return await commands.executeCommand(command, ...rest); } +/** + * Returns all the details needed to execute code within the selected environment, + * corresponding to the specified resource taking into account any workspace-specific settings + * for the workspace to which this resource belongs. + * @param resource Optional workspace resource to get settings for + * @returns Array of command components or undefined if not available + */ export async function getSettingsPythonPath(resource?: Uri): Promise { const api = await getPythonExtensionAPI(); return api?.settings.getExecutionDetails(resource).execCommand; } -export async function getEnvironmentVariables(resource?: Resource) { +/** + * Returns the environment variables used by the extension for a resource, which includes the custom + * variables configured by user in `.env` files. + * @param resource Optional workspace resource to get environment variables for + * @returns Environment variables object + */ +export async function getEnvironmentVariables(resource?: Resource): Promise { const api = await getPythonExtensionEnviromentAPI(); - return api.environments.getEnvironmentVariables(resource); + return Promise.resolve(api.environments.getEnvironmentVariables(resource)); } -export async function resolveEnvironment(env: Environment | EnvironmentPath | string) { +/** + * Returns details for the given environment, or `undefined` if the env is invalid. + * @param env Environment to resolve (can be Environment object, path, or string) + * @returns Resolved environment details + */ +export async function resolveEnvironment( + env: Environment | EnvironmentPath | string, +): Promise { const api = await getPythonExtensionEnviromentAPI(); return api.environments.resolveEnvironment(env); } -export async function getActiveEnvironmentPath(resource?: Resource) { +/** + * Returns the environment configured by user in settings. Note that this can be an invalid environment, use + * resolve the environment to get full details. + * @param resource Optional workspace resource to get active environment for + * @returns Path to the active environment + */ +export async function getActiveEnvironmentPath(resource?: Resource): Promise { const api = await getPythonExtensionEnviromentAPI(); return api.environments.getActiveEnvironmentPath(resource); } +/** + * Gets detailed information about the active Python interpreter. + * @param resource Optional workspace resource to get interpreter details for + * @returns Interpreter details including path and resource information + */ export async function getInterpreterDetails(resource?: Uri): Promise { const api = await getPythonExtensionEnviromentAPI(); const environment = await api.environments.resolveEnvironment(api.environments.getActiveEnvironmentPath(resource)); @@ -126,7 +197,11 @@ export async function getInterpreterDetails(resource?: Uri): Promise { const api = await getPythonExtensionEnviromentAPI(); const onAddedToCollection = createDeferred(); api.environments.onDidChangeEnvironments(async () => { @@ -138,12 +213,18 @@ export async function hasInterpreters() { if (initialEnvs.length > 0) { return true; } + // Initiates a refresh of Python environments within the specified scope. await Promise.race([onAddedToCollection.promise, api?.environments.refreshEnvironments()]); return api.environments.known.length > 0; } -export async function getInterpreters() { +/** + * Gets 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. + * @returns Array of known Python environments + */ +export async function getInterpreters(): Promise { const api = await getPythonExtensionEnviromentAPI(); return api.environments.known || []; }