From 47cc95aa4d88bad480f81c6ee9e31007a75afdd0 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 24 Nov 2022 12:52:26 +1100 Subject: [PATCH] Support hot swapping between kernel finders (#12154) * Support hot swapping between kernel finders * oops * Misc * oops --- .../contributedKerneFinder.node.unit.test.ts | 83 +++++++++++------- .../contributedLocalKernelSpecFinder.node.ts | 20 ++--- ...tedLocalKernelSpecFinder.node.unit.test.ts | 19 ++-- .../contributedLocalPythonEnvFinder.node.ts | 22 ++--- ...utedLocalPythonEnvFinder.node.unit.test.ts | 23 +++-- .../finder/localKernelSpecFinderBase.node.ts | 17 +++- .../localKnownPathKernelSpecFinder.node.ts | 9 +- ...ndRelatedNonPythonKernelSpecFinder.node.ts | 23 ++--- ...onPythonKernelSpecFinder.node.unit.test.ts | 6 +- ...latedNonPythonKernelSpecFinder.old.node.ts | 21 +---- ...dNonPythonKernelSpecFinder.wrapper.node.ts | 86 +++++++++++++++++++ src/kernels/serviceRegistry.node.ts | 12 ++- .../kernelRankingHelper.unit.test.ts | 76 +++++++++------- 13 files changed, 246 insertions(+), 171 deletions(-) create mode 100644 src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.wrapper.node.ts diff --git a/src/kernels/raw/finder/contributedKerneFinder.node.unit.test.ts b/src/kernels/raw/finder/contributedKerneFinder.node.unit.test.ts index db24e2f5102..425a4be69f4 100644 --- a/src/kernels/raw/finder/contributedKerneFinder.node.unit.test.ts +++ b/src/kernels/raw/finder/contributedKerneFinder.node.unit.test.ts @@ -31,7 +31,7 @@ import { EnvironmentType, PythonEnvironment } from '../../../platform/pythonEnvi import { IPythonExtensionChecker } from '../../../platform/api/types'; import { PYTHON_LANGUAGE } from '../../../platform/common/constants'; import * as platform from '../../../platform/common/utils/platform'; -import { CancellationTokenSource, EventEmitter, Memento, Uri } from 'vscode'; +import { CancellationTokenSource, Disposable, EventEmitter, Memento, Uri } from 'vscode'; import { IDisposable, IExtensionContext, @@ -71,6 +71,8 @@ import { ContributedLocalPythonEnvFinder } from './contributedLocalPythonEnvFind import { takeTopRankKernel } from '../../../notebooks/controllers/kernelRanking/kernelRankingHelper.unit.test'; import { ITrustedKernelPaths } from './types'; import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from './localPythonAndRelatedNonPythonKernelSpecFinder.old.node'; +import { LocalPythonAndRelatedNonPythonKernelSpecFinderWrapper } from './localPythonAndRelatedNonPythonKernelSpecFinder.wrapper.node'; +import { ServiceContainer } from '../../../platform/ioc/container'; [false, true].forEach((isWindows) => { (['Stable', 'Insiders'] as KernelPickerType[]).forEach((kernelPickerType) => { @@ -279,59 +281,74 @@ import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from './localPython const featuresManager = mock(); when(featuresManager.features).thenReturn({ kernelPickerType }); kernelFinder = new KernelFinder(disposables); - const localPythonAndRelatedKernelFinder = new LocalPythonAndRelatedNonPythonKernelSpecFinder( - instance(interpreterService), - instance(fs), - instance(workspaceService), - jupyterPaths, - instance(extensionChecker), - nonPythonKernelSpecFinder, - instance(memento), - disposables, - instance(env), - instance(trustedKernels), - instance(featuresManager) + + const serviceContainer = mock(); + const iocStub = sinon.stub(ServiceContainer, 'instance').get(() => instance(serviceContainer)); + disposables.push(new Disposable(() => iocStub.restore())); + when( + serviceContainer.get( + LocalPythonAndRelatedNonPythonKernelSpecFinder + ) + ).thenCall( + () => + new LocalPythonAndRelatedNonPythonKernelSpecFinder( + instance(interpreterService), + instance(fs), + instance(workspaceService), + jupyterPaths, + instance(extensionChecker), + nonPythonKernelSpecFinder, + instance(memento), + disposables, + instance(env), + instance(trustedKernels) + ) ); - const localPythonAndRelatedKernelFinderOld = new LocalPythonAndRelatedNonPythonKernelSpecFinderOld( - instance(interpreterService), - instance(fs), - instance(workspaceService), - jupyterPaths, - instance(extensionChecker), - nonPythonKernelSpecFinder, - instance(memento), + when( + serviceContainer.get( + LocalPythonAndRelatedNonPythonKernelSpecFinderOld + ) + ).thenCall( + () => + new LocalPythonAndRelatedNonPythonKernelSpecFinderOld( + instance(interpreterService), + instance(fs), + instance(workspaceService), + jupyterPaths, + instance(extensionChecker), + nonPythonKernelSpecFinder, + instance(memento), + disposables, + instance(env), + instance(trustedKernels) + ) + ); + const pythonKernelFinderWrapper = new LocalPythonAndRelatedNonPythonKernelSpecFinderWrapper( disposables, - instance(env), - instance(trustedKernels), instance(featuresManager) ); const localKernelSpecFinder = new ContributedLocalKernelSpecFinder( nonPythonKernelSpecFinder, - localPythonAndRelatedKernelFinder, - localPythonAndRelatedKernelFinderOld, + pythonKernelFinderWrapper, kernelFinder, [], instance(extensionChecker), instance(interpreterService), - instance(extensions), - instance(featuresManager) + instance(extensions) ); const pythonEnvKernelFinder = new ContributedLocalPythonEnvFinder( - localPythonAndRelatedKernelFinder, - localPythonAndRelatedKernelFinderOld, + pythonKernelFinderWrapper, kernelFinder, [], instance(extensionChecker), instance(interpreterService), - instance(extensions), - instance(featuresManager) + instance(extensions) ); changeEventFired = createEventHandler(kernelFinder, 'onDidChangeKernels', disposables); localKernelSpecFinder.activate(); pythonEnvKernelFinder.activate(); nonPythonKernelSpecFinder.activate(); - localPythonAndRelatedKernelFinder.activate(); - localPythonAndRelatedKernelFinderOld.activate(); + pythonKernelFinderWrapper.activate(); kernelRankHelper = new KernelRankingHelper(instance(preferredRemote)); } diff --git a/src/kernels/raw/finder/contributedLocalKernelSpecFinder.node.ts b/src/kernels/raw/finder/contributedLocalKernelSpecFinder.node.ts index 5d63faf9eb4..caac7c1fe34 100644 --- a/src/kernels/raw/finder/contributedLocalKernelSpecFinder.node.ts +++ b/src/kernels/raw/finder/contributedLocalKernelSpecFinder.node.ts @@ -6,10 +6,9 @@ import { inject, injectable } from 'inversify'; import { EventEmitter } from 'vscode'; import { IKernelFinder, LocalKernelConnectionMetadata } from '../../types'; -import { LocalPythonAndRelatedNonPythonKernelSpecFinder } from './localPythonAndRelatedNonPythonKernelSpecFinder.node'; import { LocalKnownPathKernelSpecFinder } from './localKnownPathKernelSpecFinder.node'; import { traceInfo, traceDecoratorError, traceError, traceVerbose } from '../../../platform/logging'; -import { IDisposableRegistry, IExtensions, IFeaturesManager } from '../../../platform/common/types'; +import { IDisposableRegistry, IExtensions } from '../../../platform/common/types'; import { capturePerfTelemetry, Telemetry } from '../../../telemetry'; import { areObjectsWithUrisTheSame, noop } from '../../../platform/common/utils/misc'; import { KernelFinder } from '../../kernelFinder'; @@ -22,7 +21,8 @@ import { PYTHON_LANGUAGE } from '../../../platform/common/constants'; import { PromiseMonitor } from '../../../platform/common/utils/promises'; import { getKernelRegistrationInfo } from '../../helpers'; import { createDeferred, Deferred } from '../../../platform/common/utils/async'; -import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from './localPythonAndRelatedNonPythonKernelSpecFinder.old.node'; +import { LocalPythonAndRelatedNonPythonKernelSpecFinderWrapper } from './localPythonAndRelatedNonPythonKernelSpecFinder.wrapper.node'; +import { ILocalKernelFinder } from './localKernelSpecFinderBase.node'; // This class searches for local kernels. // First it searches on a global persistent state, then on the installed python interpreters, @@ -60,23 +60,15 @@ export class ContributedLocalKernelSpecFinder private wasPythonInstalledWhenFetchingControllers = false; private cache: LocalKernelConnectionMetadata[] = []; - private get pythonKernelFinder() { - return this.featuresManager.features.kernelPickerType === 'Insiders' - ? this.pythonKernelFinderNew - : this.pythonKernelFinderOld; - } constructor( @inject(LocalKnownPathKernelSpecFinder) private readonly nonPythonKernelFinder: LocalKnownPathKernelSpecFinder, - @inject(LocalPythonAndRelatedNonPythonKernelSpecFinder) - private readonly pythonKernelFinderNew: LocalPythonAndRelatedNonPythonKernelSpecFinder, - @inject(LocalPythonAndRelatedNonPythonKernelSpecFinderOld) - private readonly pythonKernelFinderOld: LocalPythonAndRelatedNonPythonKernelSpecFinderOld, + @inject(LocalPythonAndRelatedNonPythonKernelSpecFinderWrapper) + private readonly pythonKernelFinder: ILocalKernelFinder, @inject(IKernelFinder) kernelFinder: KernelFinder, @inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry, @inject(IPythonExtensionChecker) private readonly extensionChecker: IPythonExtensionChecker, @inject(IInterpreterService) private readonly interpreters: IInterpreterService, - @inject(IExtensions) private readonly extensions: IExtensions, - @inject(IFeaturesManager) private readonly featuresManager: IFeaturesManager + @inject(IExtensions) private readonly extensions: IExtensions ) { kernelFinder.registerKernelFinder(this); this.disposables.push(this._onDidChangeStatus); diff --git a/src/kernels/raw/finder/contributedLocalKernelSpecFinder.node.unit.test.ts b/src/kernels/raw/finder/contributedLocalKernelSpecFinder.node.unit.test.ts index ef0920b635c..5c68be44ea4 100644 --- a/src/kernels/raw/finder/contributedLocalKernelSpecFinder.node.unit.test.ts +++ b/src/kernels/raw/finder/contributedLocalKernelSpecFinder.node.unit.test.ts @@ -11,20 +11,17 @@ import { IDisposable, IExtensions, IFeaturesManager, KernelPickerType } from '.. import { IInterpreterService } from '../../../platform/interpreter/contracts'; import { createEventHandler } from '../../../test/common'; import { KernelFinder } from '../../kernelFinder'; -import { LocalKernelSpecConnectionMetadata } from '../../types'; +import { LocalKernelConnectionMetadata, LocalKernelSpecConnectionMetadata } from '../../types'; import { ContributedLocalKernelSpecFinder } from './contributedLocalKernelSpecFinder.node'; +import { ILocalKernelFinder } from './localKernelSpecFinderBase.node'; import { LocalKnownPathKernelSpecFinder } from './localKnownPathKernelSpecFinder.node'; -import { LocalPythonAndRelatedNonPythonKernelSpecFinder } from './localPythonAndRelatedNonPythonKernelSpecFinder.node'; -import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from './localPythonAndRelatedNonPythonKernelSpecFinder.old.node'; (['Stable', 'Insiders'] as KernelPickerType[]).forEach((kernelPickerType) => { suite(`Contributed Local Kernel Spec Finder (Kernel Picker ${kernelPickerType})`, () => { let finder: ContributedLocalKernelSpecFinder; const disposables: IDisposable[] = []; let nonPythonKernelFinder: LocalKnownPathKernelSpecFinder; - let pythonKernelFinder: - | LocalPythonAndRelatedNonPythonKernelSpecFinder - | LocalPythonAndRelatedNonPythonKernelSpecFinderOld; + let pythonKernelFinder: ILocalKernelFinder; let kernelFinder: KernelFinder; let extensionChecker: IPythonExtensionChecker; let interpreterService: IInterpreterService; @@ -57,9 +54,7 @@ import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from './localPython }); setup(() => { nonPythonKernelFinder = mock(); - pythonKernelFinder = mock< - LocalPythonAndRelatedNonPythonKernelSpecFinder | LocalPythonAndRelatedNonPythonKernelSpecFinderOld - >(); + pythonKernelFinder = mock>(); kernelFinder = mock(); extensionChecker = mock(); interpreterService = mock(); @@ -87,14 +82,12 @@ import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from './localPython when(featureManager.features).thenReturn({ kernelPickerType }); finder = new ContributedLocalKernelSpecFinder( instance(nonPythonKernelFinder), - instance(pythonKernelFinder) as LocalPythonAndRelatedNonPythonKernelSpecFinder, - instance(pythonKernelFinder) as LocalPythonAndRelatedNonPythonKernelSpecFinderOld, + instance(pythonKernelFinder), instance(kernelFinder), disposables, instance(extensionChecker), instance(interpreterService), - instance(extensions), - instance(featureManager) + instance(extensions) ); clock = fakeTimers.install(); diff --git a/src/kernels/raw/finder/contributedLocalPythonEnvFinder.node.ts b/src/kernels/raw/finder/contributedLocalPythonEnvFinder.node.ts index ec19e8052cd..1929eb62aa6 100644 --- a/src/kernels/raw/finder/contributedLocalPythonEnvFinder.node.ts +++ b/src/kernels/raw/finder/contributedLocalPythonEnvFinder.node.ts @@ -5,10 +5,9 @@ import { inject, injectable } from 'inversify'; import { EventEmitter } from 'vscode'; -import { IKernelFinder, PythonKernelConnectionMetadata } from '../../../kernels/types'; -import { LocalPythonAndRelatedNonPythonKernelSpecFinder } from './localPythonAndRelatedNonPythonKernelSpecFinder.node'; +import { IKernelFinder, LocalKernelConnectionMetadata, PythonKernelConnectionMetadata } from '../../../kernels/types'; import { traceDecoratorError, traceError, traceVerbose } from '../../../platform/logging'; -import { IDisposableRegistry, IExtensions, IFeaturesManager } from '../../../platform/common/types'; +import { IDisposableRegistry, IExtensions } from '../../../platform/common/types'; import { capturePerfTelemetry, Telemetry } from '../../../telemetry'; import { areObjectsWithUrisTheSame, noop } from '../../../platform/common/utils/misc'; import { KernelFinder } from '../../kernelFinder'; @@ -20,7 +19,8 @@ import { ContributedKernelFinderKind, IContributedKernelFinder } from '../../int import { createDeferred, Deferred } from '../../../platform/common/utils/async'; import { PromiseMonitor } from '../../../platform/common/utils/promises'; import { getKernelRegistrationInfo } from '../../helpers'; -import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from './localPythonAndRelatedNonPythonKernelSpecFinder.old.node'; +import { LocalPythonAndRelatedNonPythonKernelSpecFinderWrapper } from './localPythonAndRelatedNonPythonKernelSpecFinder.wrapper.node'; +import { ILocalKernelFinder } from './localKernelSpecFinderBase.node'; // This class searches for local kernels. // First it searches on a global persistent state, then on the installed python interpreters, @@ -57,22 +57,14 @@ export class ContributedLocalPythonEnvFinder private wasPythonInstalledWhenFetchingControllers = false; private cache: PythonKernelConnectionMetadata[] = []; - private get pythonKernelFinder() { - return this.featuresManager.features.kernelPickerType === 'Insiders' - ? this.pythonKernelFinderNew - : this.pythonKernelFinderOld; - } constructor( - @inject(LocalPythonAndRelatedNonPythonKernelSpecFinder) - private readonly pythonKernelFinderNew: LocalPythonAndRelatedNonPythonKernelSpecFinder, - @inject(LocalPythonAndRelatedNonPythonKernelSpecFinderOld) - private readonly pythonKernelFinderOld: LocalPythonAndRelatedNonPythonKernelSpecFinderOld, + @inject(LocalPythonAndRelatedNonPythonKernelSpecFinderWrapper) + private readonly pythonKernelFinder: ILocalKernelFinder, @inject(IKernelFinder) kernelFinder: KernelFinder, @inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry, @inject(IPythonExtensionChecker) private readonly extensionChecker: IPythonExtensionChecker, @inject(IInterpreterService) private readonly interpreters: IInterpreterService, - @inject(IExtensions) private readonly extensions: IExtensions, - @inject(IFeaturesManager) private readonly featuresManager: IFeaturesManager + @inject(IExtensions) private readonly extensions: IExtensions ) { kernelFinder.registerKernelFinder(this); this.disposables.push(this.promiseMonitor); diff --git a/src/kernels/raw/finder/contributedLocalPythonEnvFinder.node.unit.test.ts b/src/kernels/raw/finder/contributedLocalPythonEnvFinder.node.unit.test.ts index 843f95cc406..7b0a88d2402 100644 --- a/src/kernels/raw/finder/contributedLocalPythonEnvFinder.node.unit.test.ts +++ b/src/kernels/raw/finder/contributedLocalPythonEnvFinder.node.unit.test.ts @@ -11,17 +11,18 @@ import { IDisposable, IExtensions, IFeaturesManager } from '../../../platform/co import { IInterpreterService } from '../../../platform/interpreter/contracts'; import { createEventHandler } from '../../../test/common'; import { KernelFinder } from '../../kernelFinder'; -import { LocalKernelSpecConnectionMetadata, PythonKernelConnectionMetadata } from '../../types'; +import { + LocalKernelConnectionMetadata, + LocalKernelSpecConnectionMetadata, + PythonKernelConnectionMetadata +} from '../../types'; import { ContributedLocalPythonEnvFinder } from './contributedLocalPythonEnvFinder.node'; -import { LocalPythonAndRelatedNonPythonKernelSpecFinder } from './localPythonAndRelatedNonPythonKernelSpecFinder.node'; -import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from './localPythonAndRelatedNonPythonKernelSpecFinder.old.node'; +import { ILocalKernelFinder } from './localKernelSpecFinderBase.node'; suite('Contributed Python Kernel Finder', () => { let finder: ContributedLocalPythonEnvFinder; const disposables: IDisposable[] = []; - let pythonKernelFinder: - | LocalPythonAndRelatedNonPythonKernelSpecFinder - | LocalPythonAndRelatedNonPythonKernelSpecFinderOld; + let pythonKernelFinder: ILocalKernelFinder; let kernelFinder: KernelFinder; let extensionChecker: IPythonExtensionChecker; let interpreterService: IInterpreterService; @@ -71,9 +72,7 @@ suite('Contributed Python Kernel Finder', () => { } }); setup(() => { - pythonKernelFinder = mock< - LocalPythonAndRelatedNonPythonKernelSpecFinder | LocalPythonAndRelatedNonPythonKernelSpecFinderOld - >(); + pythonKernelFinder = mock>(); kernelFinder = mock(); extensionChecker = mock(); interpreterService = mock(); @@ -98,14 +97,12 @@ suite('Contributed Python Kernel Finder', () => { const featuresManager = mock(); when(featuresManager.features).thenReturn({ kernelPickerType: 'Insiders' }); finder = new ContributedLocalPythonEnvFinder( - instance(pythonKernelFinder) as LocalPythonAndRelatedNonPythonKernelSpecFinder, - instance(pythonKernelFinder) as LocalPythonAndRelatedNonPythonKernelSpecFinderOld, + instance(pythonKernelFinder), instance(kernelFinder), disposables, instance(extensionChecker), instance(interpreterService), - instance(extensions), - instance(featuresManager) + instance(extensions) ); clock = fakeTimers.install(); diff --git a/src/kernels/raw/finder/localKernelSpecFinderBase.node.ts b/src/kernels/raw/finder/localKernelSpecFinderBase.node.ts index 82117c06a46..ebd3430b2e9 100644 --- a/src/kernels/raw/finder/localKernelSpecFinderBase.node.ts +++ b/src/kernels/raw/finder/localKernelSpecFinderBase.node.ts @@ -5,7 +5,7 @@ import * as path from '../../../platform/vscode-path/path'; import * as uriPath from '../../../platform/vscode-path/resources'; -import { CancellationToken, EventEmitter, Memento, Uri } from 'vscode'; +import { CancellationToken, Event, EventEmitter, Memento, Uri } from 'vscode'; import { IPythonExtensionChecker } from '../../../platform/api/types'; import { IApplicationEnvironment, IWorkspaceService } from '../../../platform/common/application/types'; import { PYTHON_LANGUAGE } from '../../../platform/common/constants'; @@ -182,13 +182,19 @@ export class LocalKernelSpecFinder implements IDisposable { return promise; } } - +export interface ILocalKernelFinder { + readonly status: 'discovering' | 'idle'; + onDidChangeStatus: Event; + onDidChangeKernels: Event; + refresh(): Promise; + readonly kernels: T[]; +} /** * Base class for searching for local kernels that are based on a kernel spec file. */ export abstract class LocalKernelSpecFinderBase< T extends LocalKernelSpecConnectionMetadata | PythonKernelConnectionMetadata -> implements IDisposable +> implements IDisposable, ILocalKernelFinder { protected readonly disposables: IDisposable[] = []; @@ -206,7 +212,8 @@ export abstract class LocalKernelSpecFinderBase< protected readonly promiseMonitor = new PromiseMonitor(); private readonly _onDidChangeStatus = new EventEmitter(); public readonly onDidChangeStatus = this._onDidChangeStatus.event; - + protected readonly _onDidChangeKernels = new EventEmitter(); + public readonly onDidChangeKernels = this._onDidChangeKernels.event; // Store our results when listing all possible kernelspecs for a resource private kernelSpecCache = new Map< string, @@ -239,6 +246,8 @@ export abstract class LocalKernelSpecFinderBase< this.kernelSpecFinder.clearCache(); } public abstract dispose(): void | undefined; + abstract refresh(): Promise; + abstract get kernels(): T[]; /** * @param {boolean} dependsOnPythonExtension Whether this list of kernels fetched depends on whether the python extension is installed/not installed. * If for instance first Python Extension isn't installed, then we call this again, after installing it, then the cache will be blown away diff --git a/src/kernels/raw/finder/localKnownPathKernelSpecFinder.node.ts b/src/kernels/raw/finder/localKnownPathKernelSpecFinder.node.ts index b3ae22a97e5..977fb8a36a4 100644 --- a/src/kernels/raw/finder/localKnownPathKernelSpecFinder.node.ts +++ b/src/kernels/raw/finder/localKnownPathKernelSpecFinder.node.ts @@ -4,7 +4,7 @@ 'use strict'; import { inject, injectable, named } from 'inversify'; -import { CancellationToken, CancellationTokenSource, EventEmitter, Memento } from 'vscode'; +import { CancellationToken, CancellationTokenSource, Memento } from 'vscode'; import { getKernelId } from '../../../kernels/helpers'; import { IJupyterKernelSpec, LocalKernelSpecConnectionMetadata } from '../../../kernels/types'; import { LocalKernelSpecFinderBase } from './localKernelSpecFinderBase.node'; @@ -32,13 +32,6 @@ export class LocalKnownPathKernelSpecFinder implements IExtensionSyncActivationService { private readonly _cachedKernels = new Map(); - private readonly _onDidChangeKernels = new EventEmitter(); - /** - * TODO: We can monitor the known kernel spec folders and files for changes and trigger the change event. - * Lets discuss with VS Code core if there are known perf issues. - * If there are, then there's no need to monitor these folders/files for now. - */ - public readonly onDidChangeKernels = this._onDidChangeKernels.event; constructor( @inject(IFileSystemNode) fs: IFileSystemNode, @inject(IWorkspaceService) workspaceService: IWorkspaceService, diff --git a/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node.ts b/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node.ts index 4f5ee7deedd..7aa65b7e6b0 100644 --- a/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node.ts +++ b/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node.ts @@ -4,7 +4,7 @@ 'use strict'; import { inject, injectable, named } from 'inversify'; -import { CancellationToken, CancellationTokenSource, EventEmitter, Memento } from 'vscode'; +import { CancellationToken, CancellationTokenSource, Memento } from 'vscode'; import { getKernelRegistrationInfo } from '../../../kernels/helpers'; import { isLocalConnection, @@ -19,12 +19,11 @@ import { IApplicationEnvironment, IWorkspaceService } from '../../../platform/co import { PYTHON_LANGUAGE } from '../../../platform/common/constants'; import { traceInfoIfCI, traceVerbose, traceError, traceWarning } from '../../../platform/logging'; import { IFileSystemNode } from '../../../platform/common/platform/types.node'; -import { IMemento, GLOBAL_MEMENTO, IDisposableRegistry, IFeaturesManager } from '../../../platform/common/types'; +import { IMemento, GLOBAL_MEMENTO, IDisposableRegistry } from '../../../platform/common/types'; import { IInterpreterService } from '../../../platform/interpreter/contracts'; import { capturePerfTelemetry, Telemetry } from '../../../telemetry'; import { areObjectsWithUrisTheSame, noop } from '../../../platform/common/utils/misc'; import { disposeAllDisposables } from '../../../platform/common/helpers'; -import { IExtensionSyncActivationService } from '../../../platform/activation/types'; import { ITrustedKernelPaths } from './types'; import { InterpreterKernelSpecFinderHelper, @@ -43,10 +42,7 @@ type InterpreterId = string; * - This will return any non-python kernels that are registered in Python environments (e.g. Java kernels within a conda environment) */ @injectable() -export class LocalPythonAndRelatedNonPythonKernelSpecFinder - extends LocalKernelSpecFinderBase - implements IExtensionSyncActivationService -{ +export class LocalPythonAndRelatedNonPythonKernelSpecFinder extends LocalKernelSpecFinderBase { /** * List of all kernels. * When opening a new instance of VS Code we load the cache from previous session, @@ -59,8 +55,7 @@ export class LocalPythonAndRelatedNonPythonKernelSpecFinder * This does not exclude any of the cached kernels from the previous sesion. */ private _kernelsExcludingCachedItems = new Map(); - private readonly _onDidChangeKernels = new EventEmitter(); - public readonly onDidChangeKernels = this._onDidChangeKernels.event; + private _kernelsFromCache: LocalKernelConnectionMetadata[] = []; private cachedInformationForPythonInterpreter = new Map>(); private updateCachePromise = Promise.resolve(); @@ -78,13 +73,10 @@ export class LocalPythonAndRelatedNonPythonKernelSpecFinder @inject(IMemento) @named(GLOBAL_MEMENTO) globalState: Memento, @inject(IDisposableRegistry) disposables: IDisposableRegistry, @inject(IApplicationEnvironment) env: IApplicationEnvironment, - @inject(ITrustedKernelPaths) trustedKernels: ITrustedKernelPaths, - @inject(IFeaturesManager) private readonly featuresManager: IFeaturesManager + @inject(ITrustedKernelPaths) trustedKernels: ITrustedKernelPaths ) { super(fs, workspaceService, extensionChecker, globalState, disposables, env, jupyterPaths); - if (featuresManager.features.kernelPickerType !== 'Insiders') { - return; - } + this.interpreterKernelSpecFinder = new InterpreterKernelSpecFinderHelper( jupyterPaths, this.kernelSpecFinder, @@ -129,9 +121,6 @@ export class LocalPythonAndRelatedNonPythonKernelSpecFinder ); } public activate() { - if (this.featuresManager.features.kernelPickerType !== 'Insiders') { - return; - } this.listKernelsFirstTimeFromMemento(LocalPythonKernelsCacheKey) .then((kernels) => { if (kernels.length) { diff --git a/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node.unit.test.ts b/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node.unit.test.ts index 4959c4882c5..c234f62645e 100644 --- a/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node.unit.test.ts +++ b/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node.unit.test.ts @@ -209,8 +209,7 @@ import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from './localPython instance(globalState), disposables, instance(env), - instance(trustedKernels), - instance(featuresManager) + instance(trustedKernels) ); } else { finder = new LocalPythonAndRelatedNonPythonKernelSpecFinderOld( @@ -223,8 +222,7 @@ import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from './localPython instance(globalState), disposables, instance(env), - instance(trustedKernels), - instance(featuresManager) + instance(trustedKernels) ); } diff --git a/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.old.node.ts b/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.old.node.ts index c2445563617..c31e791ae7b 100644 --- a/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.old.node.ts +++ b/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.old.node.ts @@ -4,7 +4,7 @@ 'use strict'; import { inject, injectable, named } from 'inversify'; -import { CancellationToken, CancellationTokenSource, EventEmitter, Memento } from 'vscode'; +import { CancellationToken, CancellationTokenSource, Memento } from 'vscode'; import { getKernelRegistrationInfo } from '../../../kernels/helpers'; import { isLocalConnection, @@ -19,12 +19,11 @@ import { IApplicationEnvironment, IWorkspaceService } from '../../../platform/co import { PYTHON_LANGUAGE } from '../../../platform/common/constants'; import { traceInfoIfCI, traceVerbose, traceError, traceWarning } from '../../../platform/logging'; import { IFileSystemNode } from '../../../platform/common/platform/types.node'; -import { IMemento, GLOBAL_MEMENTO, IDisposableRegistry, IFeaturesManager } from '../../../platform/common/types'; +import { IMemento, GLOBAL_MEMENTO, IDisposableRegistry } from '../../../platform/common/types'; import { IInterpreterService } from '../../../platform/interpreter/contracts'; import { capturePerfTelemetry, Telemetry } from '../../../telemetry'; import { areObjectsWithUrisTheSame, noop } from '../../../platform/common/utils/misc'; import { disposeAllDisposables } from '../../../platform/common/helpers'; -import { IExtensionSyncActivationService } from '../../../platform/activation/types'; import { ITrustedKernelPaths } from './types'; import { InterpreterKernelSpecFinderHelper, @@ -41,10 +40,7 @@ import { * - This will return any non-python kernels that are registered in Python environments (e.g. Java kernels within a conda environment) */ @injectable() -export class LocalPythonAndRelatedNonPythonKernelSpecFinderOld - extends LocalKernelSpecFinderBase - implements IExtensionSyncActivationService -{ +export class LocalPythonAndRelatedNonPythonKernelSpecFinderOld extends LocalKernelSpecFinderBase { /** * List of all kernels. * When opening a new instance of VS Code we load the cache from previous session, @@ -57,8 +53,6 @@ export class LocalPythonAndRelatedNonPythonKernelSpecFinderOld * This does not exclude any of the cached kernels from the previous sesion. */ private _kernelsExcludingCachedItems = new Map(); - private readonly _onDidChangeKernels = new EventEmitter(); - public readonly onDidChangeKernels = this._onDidChangeKernels.event; private _kernelsFromCache: LocalKernelConnectionMetadata[] = []; private readonly interpreterKernelSpecFinder: InterpreterKernelSpecFinderHelper; @@ -73,13 +67,9 @@ export class LocalPythonAndRelatedNonPythonKernelSpecFinderOld @inject(IMemento) @named(GLOBAL_MEMENTO) globalState: Memento, @inject(IDisposableRegistry) disposables: IDisposableRegistry, @inject(IApplicationEnvironment) env: IApplicationEnvironment, - @inject(ITrustedKernelPaths) trustedKernels: ITrustedKernelPaths, - @inject(IFeaturesManager) private readonly featuresManager: IFeaturesManager + @inject(ITrustedKernelPaths) trustedKernels: ITrustedKernelPaths ) { super(fs, workspaceService, extensionChecker, globalState, disposables, env, jupyterPaths); - if (this.featuresManager.features.kernelPickerType !== 'Stable') { - return; - } this.interpreterKernelSpecFinder = new InterpreterKernelSpecFinderHelper( jupyterPaths, this.kernelSpecFinder, @@ -124,9 +114,6 @@ export class LocalPythonAndRelatedNonPythonKernelSpecFinderOld ); } public activate() { - if (this.featuresManager.features.kernelPickerType !== 'Stable') { - return; - } this.listKernelsFirstTimeFromMemento(LocalPythonKernelsCacheKey) .then((kernels) => { if (kernels.length) { diff --git a/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.wrapper.node.ts b/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.wrapper.node.ts new file mode 100644 index 00000000000..5900961a44d --- /dev/null +++ b/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.wrapper.node.ts @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +'use strict'; + +import { inject, injectable } from 'inversify'; +import { LocalKernelConnectionMetadata } from '../../../kernels/types'; +import { IDisposable, IDisposableRegistry, IFeaturesManager, KernelPickerType } from '../../../platform/common/types'; +import { IExtensionSyncActivationService } from '../../../platform/activation/types'; +import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from './localPythonAndRelatedNonPythonKernelSpecFinder.old.node'; +import { ServiceContainer } from '../../../platform/ioc/container'; +import { LocalPythonAndRelatedNonPythonKernelSpecFinder } from './localPythonAndRelatedNonPythonKernelSpecFinder.node'; +import { ILocalKernelFinder } from './localKernelSpecFinderBase.node'; +import { EventEmitter } from 'vscode'; +import { disposeAllDisposables } from '../../../platform/common/helpers'; + +/** + * Returns all Python kernels and any related kernels registered in the python environment. + * If Python extension is not installed, this will return all Python kernels registered globally. + * If Python extension is installed, + * - This will return Python kernels registered by us in global locations. + * - This will return Python interpreters that can be started as kernels. + * - This will return any non-python kernels that are registered in Python environments (e.g. Java kernels within a conda environment) + */ +@injectable() +export class LocalPythonAndRelatedNonPythonKernelSpecFinderWrapper + implements IExtensionSyncActivationService, ILocalKernelFinder +{ + private readonly disposables: IDisposable[] = []; + private readonly finderDisposables: IDisposable[] = []; + private previousKernelType?: KernelPickerType; + private kernelFinder?: + | LocalPythonAndRelatedNonPythonKernelSpecFinderOld + | LocalPythonAndRelatedNonPythonKernelSpecFinder; + constructor( + @inject(IDisposableRegistry) disposables: IDisposableRegistry, + @inject(IFeaturesManager) private readonly featuresManager: IFeaturesManager + ) { + disposables.push(this); + this.disposables.push(this._onDidChangeStatus); + featuresManager.onDidChangeFeatures(this.initializeFinder, this, this.disposables); + } + get status() { + return this.kernelFinder?.status || 'idle'; + } + private readonly _onDidChangeStatus = new EventEmitter(); + onDidChangeStatus = this._onDidChangeStatus.event; + private readonly _onDidChangeKernels = new EventEmitter(); + onDidChangeKernels = this._onDidChangeKernels.event; + refresh(): Promise { + throw new Error('Method not implemented.'); + } + public activate() { + this.initializeFinder(); + } + public get kernels(): LocalKernelConnectionMetadata[] { + return this.kernelFinder?.kernels ?? []; + } + private initializeFinder() { + if (this.previousKernelType === this.featuresManager.features.kernelPickerType && this.kernelFinder) { + return; + } + disposeAllDisposables(this.finderDisposables); + this.previousKernelType = this.featuresManager.features.kernelPickerType; + this.kernelFinder = + this.previousKernelType === 'Insiders' + ? ServiceContainer.instance.get( + LocalPythonAndRelatedNonPythonKernelSpecFinder + ) + : ServiceContainer.instance.get( + LocalPythonAndRelatedNonPythonKernelSpecFinderOld + ); + this.kernelFinder.onDidChangeKernels(() => this._onDidChangeKernels.fire(), this, this.finderDisposables); + this.kernelFinder.onDidChangeStatus(() => this._onDidChangeStatus.fire(), this, this.finderDisposables); + this.kernelFinder.activate(); + this.finderDisposables.push(this.kernelFinder); + } + public dispose() { + this._onDidChangeStatus.dispose(); + disposeAllDisposables(this.disposables); + disposeAllDisposables(this.finderDisposables); + } +} diff --git a/src/kernels/serviceRegistry.node.ts b/src/kernels/serviceRegistry.node.ts index 1ee7474c9c4..4f07d2f8c6c 100644 --- a/src/kernels/serviceRegistry.node.ts +++ b/src/kernels/serviceRegistry.node.ts @@ -11,7 +11,6 @@ import { registerInstallerTypes } from './installer/serviceRegistry.node'; import { KernelDependencyService } from './kernelDependencyService.node'; import { JupyterPaths } from './raw/finder/jupyterPaths.node'; import { LocalKnownPathKernelSpecFinder } from './raw/finder/localKnownPathKernelSpecFinder.node'; -import { LocalPythonAndRelatedNonPythonKernelSpecFinder } from './raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node'; import { PreferredRemoteKernelIdProvider } from './jupyter/preferredRemoteKernelIdProvider'; import { KernelEnvironmentVariablesService } from './raw/launcher/kernelEnvVarsService.node'; import { KernelLauncher } from './raw/launcher/kernelLauncher.node'; @@ -52,7 +51,9 @@ import { KernelCompletionsPreWarmer } from './execution/kernelCompletionPreWarme import { ContributedLocalKernelSpecFinder } from './raw/finder/contributedLocalKernelSpecFinder.node'; import { ContributedLocalPythonEnvFinder } from './raw/finder/contributedLocalPythonEnvFinder.node'; import { KernelRefreshIndicator } from './kernelRefreshIndicator.node'; +import { LocalPythonAndRelatedNonPythonKernelSpecFinderWrapper } from './raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.wrapper.node'; import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from './raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.old.node'; +import { LocalPythonAndRelatedNonPythonKernelSpecFinder } from './raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node'; export function registerTypes(serviceManager: IServiceManager, isDevMode: boolean) { serviceManager.addSingleton(IExtensionSingleActivationService, Activation); @@ -92,17 +93,20 @@ export function registerTypes(serviceManager: IServiceManager, isDevMode: boolea LocalKnownPathKernelSpecFinder ); serviceManager.addBinding(LocalKnownPathKernelSpecFinder, IExtensionSyncActivationService); + serviceManager.addSingleton( LocalPythonAndRelatedNonPythonKernelSpecFinder, LocalPythonAndRelatedNonPythonKernelSpecFinder ); - serviceManager.addBinding(LocalPythonAndRelatedNonPythonKernelSpecFinder, IExtensionSyncActivationService); - serviceManager.addSingleton( LocalPythonAndRelatedNonPythonKernelSpecFinderOld, LocalPythonAndRelatedNonPythonKernelSpecFinderOld ); - serviceManager.addBinding(LocalPythonAndRelatedNonPythonKernelSpecFinderOld, IExtensionSyncActivationService); + serviceManager.addSingleton( + LocalPythonAndRelatedNonPythonKernelSpecFinderWrapper, + LocalPythonAndRelatedNonPythonKernelSpecFinderWrapper + ); + serviceManager.addBinding(LocalPythonAndRelatedNonPythonKernelSpecFinderWrapper, IExtensionSyncActivationService); serviceManager.addSingleton(IExtensionSyncActivationService, KernelStatusProvider); serviceManager.addSingleton( diff --git a/src/notebooks/controllers/kernelRanking/kernelRankingHelper.unit.test.ts b/src/notebooks/controllers/kernelRanking/kernelRankingHelper.unit.test.ts index 0d1595e9e4b..cfcc115f039 100644 --- a/src/notebooks/controllers/kernelRanking/kernelRankingHelper.unit.test.ts +++ b/src/notebooks/controllers/kernelRanking/kernelRankingHelper.unit.test.ts @@ -23,7 +23,7 @@ import { PythonEnvironment } from '../../../platform/pythonEnvironments/info'; import { IPythonExtensionChecker } from '../../../platform/api/types'; import { PYTHON_LANGUAGE } from '../../../platform/common/constants'; import * as platform from '../../../platform/common/utils/platform'; -import { CancellationTokenSource, EventEmitter, Memento, Uri } from 'vscode'; +import { CancellationTokenSource, Disposable, EventEmitter, Memento, Uri } from 'vscode'; import { IDisposable, IExtensionContext, @@ -53,6 +53,8 @@ import { IKernelRankingHelper } from '../../../notebooks/controllers/types'; import { RemoteKernelFinder } from '../../../kernels/jupyter/finder/remoteKernelFinder'; import { ITrustedKernelPaths } from '../../../kernels/raw/finder/types'; import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from '../../../kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.old.node'; +import { LocalPythonAndRelatedNonPythonKernelSpecFinderWrapper } from '../../../kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.wrapper.node'; +import { ServiceContainer } from '../../../platform/ioc/container'; [false, true].forEach((isWindows) => { (['Stable', 'Insiders'] as KernelPickerType[]).forEach((kernelPickerType) => { @@ -255,47 +257,63 @@ import { LocalPythonAndRelatedNonPythonKernelSpecFinderOld } from '../../../kern when(featuresManager.features).thenReturn({ kernelPickerType }); kernelFinder = new KernelFinder([]); - const localPythonAndRelatedKernelFinder = new LocalPythonAndRelatedNonPythonKernelSpecFinder( - instance(interpreterService), - instance(fs), - instance(workspaceService), - jupyterPaths, - instance(extensionChecker), - nonPythonKernelSpecFinder, - instance(memento), - disposables, - instance(env), - instance(trustedKernels), - instance(featuresManager) + const serviceContainer = mock(); + const iocStub = sinon.stub(ServiceContainer, 'instance').get(() => instance(serviceContainer)); + disposables.push(new Disposable(() => iocStub.restore())); + when( + serviceContainer.get( + LocalPythonAndRelatedNonPythonKernelSpecFinder + ) + ).thenCall( + () => + new LocalPythonAndRelatedNonPythonKernelSpecFinder( + instance(interpreterService), + instance(fs), + instance(workspaceService), + jupyterPaths, + instance(extensionChecker), + nonPythonKernelSpecFinder, + instance(memento), + disposables, + instance(env), + instance(trustedKernels) + ) ); - const localPythonAndRelatedKernelFinderOld = new LocalPythonAndRelatedNonPythonKernelSpecFinderOld( - instance(interpreterService), - instance(fs), - instance(workspaceService), - jupyterPaths, - instance(extensionChecker), - nonPythonKernelSpecFinder, - instance(memento), + when( + serviceContainer.get( + LocalPythonAndRelatedNonPythonKernelSpecFinderOld + ) + ).thenCall( + () => + new LocalPythonAndRelatedNonPythonKernelSpecFinderOld( + instance(interpreterService), + instance(fs), + instance(workspaceService), + jupyterPaths, + instance(extensionChecker), + nonPythonKernelSpecFinder, + instance(memento), + disposables, + instance(env), + instance(trustedKernels) + ) + ); + const pythonKernelFinderWrapper = new LocalPythonAndRelatedNonPythonKernelSpecFinderWrapper( disposables, - instance(env), - instance(trustedKernels), instance(featuresManager) ); localKernelFinder = new ContributedLocalKernelSpecFinder( nonPythonKernelSpecFinder, - localPythonAndRelatedKernelFinder, - localPythonAndRelatedKernelFinderOld, + pythonKernelFinderWrapper, kernelFinder, [], instance(extensionChecker), instance(interpreterService), - instance(extensions), - instance(featuresManager) + instance(extensions) ); localKernelFinder.activate(); nonPythonKernelSpecFinder.activate(); - localPythonAndRelatedKernelFinder.activate(); - localPythonAndRelatedKernelFinderOld.activate(); + pythonKernelFinderWrapper.activate(); kernelRankHelper = new KernelRankingHelper(instance(preferredRemote)); }