Skip to content

Commit

Permalink
fix: ensure interpreter change event is raised when using environment…
Browse files Browse the repository at this point in the history
…s extension (#24838)

Fixes microsoft/pylance-release#6968
  • Loading branch information
karthiknadig authored Feb 24, 2025
1 parent 60d0473 commit 5cdbc60
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 7 deletions.
55 changes: 49 additions & 6 deletions src/client/envExt/api.internal.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { Terminal, Uri } from 'vscode';
import { EventEmitter, Terminal, Uri, Disposable, ConfigurationTarget } from 'vscode';
import { getExtension } from '../common/vscodeApis/extensionsApi';
import {
GetEnvironmentScope,
Expand All @@ -10,8 +10,10 @@ import {
PythonEnvironmentApi,
PythonProcess,
RefreshEnvironmentsScope,
DidChangeEnvironmentEventArgs,
} from './types';
import { executeCommand } from '../common/vscodeApis/commandApis';
import { IInterpreterPathService } from '../common/types';

export const ENVS_EXTENSION_ID = 'ms-python.vscode-python-envs';

Expand All @@ -24,6 +26,17 @@ export function useEnvExtension(): boolean {
return _useExt;
}

const onDidChangeEnvironmentEnvExtEmitter: EventEmitter<DidChangeEnvironmentEventArgs> = new EventEmitter<
DidChangeEnvironmentEventArgs
>();
export function onDidChangeEnvironmentEnvExt(
listener: (e: DidChangeEnvironmentEventArgs) => unknown,
thisArgs?: unknown,
disposables?: Disposable[],
): Disposable {
return onDidChangeEnvironmentEnvExtEmitter.event(listener, thisArgs, disposables);
}

let _extApi: PythonEnvironmentApi | undefined;
export async function getEnvExtApi(): Promise<PythonEnvironmentApi> {
if (_extApi) {
Expand All @@ -33,14 +46,15 @@ export async function getEnvExtApi(): Promise<PythonEnvironmentApi> {
if (!extension) {
throw new Error('Python Environments extension not found.');
}
if (extension?.isActive) {
_extApi = extension.exports as PythonEnvironmentApi;
return _extApi;
if (!extension?.isActive) {
await extension.activate();
}

await extension.activate();

_extApi = extension.exports as PythonEnvironmentApi;
_extApi.onDidChangeEnvironment((e) => {
onDidChangeEnvironmentEnvExtEmitter.fire(e);
});

return _extApi;
}

Expand Down Expand Up @@ -106,3 +120,32 @@ export async function clearCache(): Promise<void> {
await executeCommand('python-envs.clearCache');
}
}

export function registerEnvExtFeatures(
disposables: Disposable[],
interpreterPathService: IInterpreterPathService,
): void {
if (useEnvExtension()) {
disposables.push(
onDidChangeEnvironmentEnvExt(async (e: DidChangeEnvironmentEventArgs) => {
const previousPath = interpreterPathService.get(e.uri);

if (previousPath !== e.new?.environmentPath.fsPath) {
if (e.uri) {
await interpreterPathService.update(
e.uri,
ConfigurationTarget.WorkspaceFolder,
e.new?.environmentPath.fsPath,
);
} else {
await interpreterPathService.update(
undefined,
ConfigurationTarget.Global,
e.new?.environmentPath.fsPath,
);
}
}
}),
);
}
}
15 changes: 14 additions & 1 deletion src/client/environmentApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { PythonEnvInfo, PythonEnvKind, PythonEnvType } from './pythonEnvironment
import { getEnvPath } from './pythonEnvironments/base/info/env';
import { IDiscoveryAPI, ProgressReportStage } from './pythonEnvironments/base/locator';
import { IPythonExecutionFactory } from './common/process/types';
import { traceError, traceVerbose } from './logging';
import { traceError, traceInfo, traceVerbose } from './logging';
import { isParentPath, normCasePath } from './common/platform/fs-paths';
import { sendTelemetryEvent } from './telemetry';
import { EventName } from './telemetry/constants';
Expand Down Expand Up @@ -42,7 +42,13 @@ type ActiveEnvironmentChangeEvent = {
};

const onDidActiveInterpreterChangedEvent = new EventEmitter<ActiveEnvironmentPathChangeEvent>();
const previousEnvMap = new Map<string, string>();
export function reportActiveInterpreterChanged(e: ActiveEnvironmentChangeEvent): void {
const oldPath = previousEnvMap.get(e.resource?.uri.fsPath ?? '');
if (oldPath === e.path) {
return;
}
previousEnvMap.set(e.resource?.uri.fsPath ?? '', e.path);
onDidActiveInterpreterChangedEvent.fire({ id: getEnvID(e.path), path: e.path, resource: e.resource });
reportActiveInterpreterChangedDeprecated({ path: e.path, resource: e.resource?.uri });
}
Expand Down Expand Up @@ -172,6 +178,13 @@ export function buildEnvironmentApi(
}

disposables.push(
onDidActiveInterpreterChangedEvent.event((e) => {
let scope = 'global';
if (e.resource) {
scope = e.resource instanceof Uri ? e.resource.fsPath : e.resource.uri.fsPath;
}
traceInfo(`Active interpreter [${scope}]: `, e.path);
}),
discoveryApi.onProgress((e) => {
if (e.stage === ProgressReportStage.discoveryFinished) {
knownCache = initKnownCache();
Expand Down
2 changes: 2 additions & 0 deletions src/client/extensionActivation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { registerTriggerForTerminalREPL } from './terminals/codeExecution/termin
import { registerPythonStartup } from './terminals/pythonStartup';
import { registerPixiFeatures } from './pythonEnvironments/common/environmentManagers/pixi';
import { registerCustomTerminalLinkProvider } from './terminals/pythonStartupLinkProvider';
import { registerEnvExtFeatures } from './envExt/api.internal';

export async function activateComponents(
// `ext` is passed to any extra activation funcs.
Expand Down Expand Up @@ -101,6 +102,7 @@ export function activateFeatures(ext: ExtensionState, _components: Components):
const interpreterService: IInterpreterService = ext.legacyIOC.serviceContainer.get<IInterpreterService>(
IInterpreterService,
);
registerEnvExtFeatures(ext.disposables, interpreterPathService);
const pathUtils = ext.legacyIOC.serviceContainer.get<IPathUtils>(IPathUtils);
registerPixiFeatures(ext.disposables);
registerAllCreateEnvironmentFeatures(
Expand Down
9 changes: 9 additions & 0 deletions src/test/common/persistentState.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { assert, expect } from 'chai';
import * as TypeMoq from 'typemoq';
import * as sinon from 'sinon';
import { Memento } from 'vscode';
import { ICommandManager } from '../../client/common/application/types';
import { Commands } from '../../client/common/constants';
Expand All @@ -17,17 +18,25 @@ import {
import { IDisposable } from '../../client/common/types';
import { sleep } from '../core';
import { MockMemento } from '../mocks/mementos';
import * as apiInt from '../../client/envExt/api.internal';

suite('Persistent State', () => {
let cmdManager: TypeMoq.IMock<ICommandManager>;
let persistentStateFactory: PersistentStateFactory;
let workspaceMemento: Memento;
let globalMemento: Memento;
let useEnvExtensionStub: sinon.SinonStub;
setup(() => {
cmdManager = TypeMoq.Mock.ofType<ICommandManager>();
workspaceMemento = new MockMemento();
globalMemento = new MockMemento();
persistentStateFactory = new PersistentStateFactory(globalMemento, workspaceMemento, cmdManager.object);

useEnvExtensionStub = sinon.stub(apiInt, 'useEnvExtension');
useEnvExtensionStub.returns(false);
});
teardown(() => {
sinon.restore();
});

test('Global states created are restored on invoking clean storage command', async () => {
Expand Down

0 comments on commit 5cdbc60

Please sign in to comment.