-
Notifications
You must be signed in to change notification settings - Fork 77
Switch to directly invoke python-environments-ext api #849
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 9 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
22c77f0
first draft
eleanorjboyd 5d749eb
tests and split legacy python from python
eleanorjboyd cec4929
rebased and fixed
eleanorjboyd 464d6e2
support per file debug with correct interpreter
eleanorjboyd 332d2ec
fix tests
eleanorjboyd 1896cde
add extra logging
eleanorjboyd d37728d
fix tests and add type
eleanorjboyd 21c6011
remove extra comments
eleanorjboyd 00ed716
path fix
eleanorjboyd 331c9f1
test fixes
eleanorjboyd 728877c
fixes
eleanorjboyd b821396
error
eleanorjboyd 24af493
format
eleanorjboyd File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,240 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| /* eslint-disable @typescript-eslint/naming-convention */ | ||
| import { | ||
| ActiveEnvironmentPathChangeEvent, | ||
| Environment, | ||
| EnvironmentPath, | ||
| EnvironmentVariables, | ||
| PythonExtension, | ||
| ResolvedEnvironment, | ||
| Resource, | ||
| } from '@vscode/python-extension'; | ||
| import { EventEmitter, extensions, Uri, Disposable, Extension } from 'vscode'; | ||
| import { createDeferred } from './utils/async'; | ||
| import { traceError, traceLog } from './log/logging'; | ||
|
|
||
| /** | ||
| * Interface for the Python extension API. | ||
| */ | ||
| interface LegacyIExtensionApi { | ||
| ready: Promise<void>; | ||
| settings: { | ||
| getExecutionDetails(resource?: Resource): { execCommand: string[] | undefined }; | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Details about a Python interpreter. | ||
| */ | ||
| export interface LegacyIInterpreterDetails { | ||
| /** 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 legacyOnDidChangePythonInterpreterEvent = new EventEmitter<LegacyIInterpreterDetails>(); | ||
|
|
||
| // /** Event that fires when the active Python interpreter changes */ | ||
| // export const legacyOnDidChangePythonInterpreter: Event<LegacyIInterpreterDetails> = | ||
| // legacyOnDidChangePythonInterpreterEvent.event; | ||
| /** | ||
| * Activates the Python extension and ensures it's ready for use. | ||
| * @returns The activated Python extension instance | ||
| */ | ||
| async function legacyActivateExtension(): Promise<Extension<any> | undefined> { | ||
| console.log('Activating Python extension...'); | ||
| activateEnvsExtension(); | ||
| const extension = extensions.getExtension('ms-python.python'); | ||
| if (extension) { | ||
| if (!extension.isActive) { | ||
| await extension.activate(); | ||
| } | ||
| } | ||
| console.log('Python extension activated.'); | ||
| return extension; | ||
| } | ||
| /** | ||
| * Activates the Python environments extension. | ||
| * @returns The activated Python environments extension instance | ||
| */ | ||
| async function activateEnvsExtension(): Promise<Extension<any> | undefined> { | ||
| const extension = extensions.getExtension('ms-python.vscode-python-envs'); | ||
| if (extension) { | ||
| if (!extension.isActive) { | ||
| await extension.activate(); | ||
| } | ||
| } | ||
| return extension; | ||
| } | ||
|
|
||
| /** | ||
| * Gets the Python extension's API interface. | ||
| * @returns The Python extension API or undefined if not available | ||
| */ | ||
| async function legacyGetPythonExtensionAPI(): Promise<LegacyIExtensionApi | undefined> { | ||
| const extension = await legacyActivateExtension(); | ||
| return extension?.exports as LegacyIExtensionApi; | ||
| } | ||
|
|
||
| /** | ||
| * Gets the Python extension's environment API. | ||
| * @returns The Python extension environment API | ||
| */ | ||
| async function legacyGetPythonExtensionEnviromentAPI(): Promise<PythonExtension> { | ||
| // Load the Python extension API | ||
| await legacyActivateExtension(); | ||
| 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 legacyInitializePython( | ||
| disposables: Disposable[], | ||
| onDidChangePythonInterpreterEvent: EventEmitter<LegacyIInterpreterDetails>, | ||
| ): Promise<void> { | ||
| try { | ||
| traceLog('legacyInitializePython: Starting initialization'); | ||
| const api = await legacyGetPythonExtensionEnviromentAPI(); | ||
|
|
||
| if (api) { | ||
| disposables.push( | ||
| // This event is triggered when the active environment setting changes. | ||
| api.environments.onDidChangeActiveEnvironmentPath((e: ActiveEnvironmentPathChangeEvent) => { | ||
| traceLog(`legacyInitializePython: Active environment path changed to '${e.path}'`); | ||
| let resourceUri: Uri | undefined; | ||
| if (e.resource instanceof Uri) { | ||
| resourceUri = e.resource; | ||
| } | ||
| if (e.resource && 'uri' in e.resource) { | ||
| // WorkspaceFolder type | ||
| resourceUri = e.resource.uri; | ||
| } | ||
| onDidChangePythonInterpreterEvent.fire({ path: [e.path], resource: resourceUri }); | ||
| }), | ||
| ); | ||
|
|
||
| traceLog('Waiting for interpreter from python extension.'); | ||
| onDidChangePythonInterpreterEvent.fire(await legacyGetInterpreterDetails()); | ||
| traceLog('legacyInitializePython: Initial interpreter details fired'); | ||
| } | ||
| } catch (error) { | ||
| traceError('Error initializing python: ', error); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * 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 legacyGetSettingsPythonPath(resource?: Uri): Promise<string[] | undefined> { | ||
| const api = await legacyGetPythonExtensionAPI(); | ||
| const execCommand = api?.settings.getExecutionDetails(resource).execCommand; | ||
| traceLog(`legacyGetSettingsPythonPath: execCommand='${execCommand?.join(' ')}' resource='${resource?.fsPath}'`); | ||
| return execCommand; | ||
| } | ||
|
|
||
| /** | ||
| * 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 legacyGetEnvironmentVariables(resource?: Resource): Promise<EnvironmentVariables> { | ||
| const api = await legacyGetPythonExtensionEnviromentAPI(); | ||
| return Promise.resolve(api.environments.getEnvironmentVariables(resource)); | ||
| } | ||
|
|
||
| /** | ||
| * 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 legacyResolveEnvironment( | ||
| env: Environment | EnvironmentPath | string, | ||
| ): Promise<ResolvedEnvironment | undefined> { | ||
| const api = await legacyGetPythonExtensionEnviromentAPI(); | ||
| traceLog(`legacyResolveEnvironment: Resolving environment '${typeof env === 'string' ? env : (env as any).path}'`); | ||
| const resolved = api.environments.resolveEnvironment(env); | ||
| resolved.then((r) => traceLog(`legacyResolveEnvironment: Resolved executable='${r?.executable.uri?.fsPath}'`)); | ||
| return resolved; | ||
| } | ||
|
|
||
| /** | ||
| * 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 legacyGetActiveEnvironmentPath(resource?: Resource): Promise<EnvironmentPath> { | ||
| const api = await legacyGetPythonExtensionEnviromentAPI(); | ||
| const active = api.environments.getActiveEnvironmentPath(resource); | ||
| traceLog( | ||
| `legacyGetActiveEnvironmentPath: activePath='${active.path}' resource='${ | ||
| (resource as any)?.uri?.fsPath || (resource as Uri)?.fsPath || '' | ||
| }'`, | ||
| ); | ||
| return active; | ||
| } | ||
|
|
||
| /** | ||
| * 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 legacyGetInterpreterDetails(resource?: Uri): Promise<LegacyIInterpreterDetails> { | ||
| const api = await legacyGetPythonExtensionEnviromentAPI(); | ||
| const environment = await api.environments.resolveEnvironment(api.environments.getActiveEnvironmentPath(resource)); | ||
| if (environment?.executable.uri) { | ||
| traceLog( | ||
| `legacyGetInterpreterDetails: executable='${environment.executable.uri.fsPath}' resource='${resource?.fsPath}'`, | ||
| ); | ||
| return { path: [environment?.executable.uri.fsPath], resource }; | ||
| } | ||
| traceLog('legacyGetInterpreterDetails: No executable found'); | ||
| return { path: undefined, resource }; | ||
| } | ||
|
|
||
| /** | ||
| * Checks if any Python interpreters are available in the system. | ||
| * @returns True if interpreters are found, false otherwise | ||
| */ | ||
| export async function legacyHasInterpreters(): Promise<boolean> { | ||
| const api = await legacyGetPythonExtensionEnviromentAPI(); | ||
| const onAddedToCollection = createDeferred(); | ||
| api.environments.onDidChangeEnvironments(async () => { | ||
| if (api.environments.known) { | ||
| onAddedToCollection.resolve(); | ||
| } | ||
| }); | ||
| const initialEnvs = api.environments.known; | ||
| if (initialEnvs.length > 0) { | ||
| traceLog(`legacyHasInterpreters: Found ${initialEnvs.length} initial environments`); | ||
| return true; | ||
| } | ||
| // Initiates a refresh of Python environments within the specified scope. | ||
| await Promise.race([onAddedToCollection.promise, api?.environments.refreshEnvironments()]); | ||
| const has = api.environments.known.length > 0; | ||
| traceLog(`legacyHasInterpreters: After refresh count='${api.environments.known.length}' result='${has}'`); | ||
| return has; | ||
| } | ||
|
|
||
| /** | ||
| * 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 legacyGetInterpreters(): Promise<readonly Environment[]> { | ||
| const api = await legacyGetPythonExtensionEnviromentAPI(); | ||
| const known = api.environments.known || []; | ||
| traceLog(`legacyGetInterpreters: returning ${known.length} environments`); | ||
| return known; | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.