diff --git a/src/kernels/execution/helpers.unit.test.ts b/src/kernels/execution/helpers.unit.test.ts index 45a7fe59c9b..98d5fd01524 100644 --- a/src/kernels/execution/helpers.unit.test.ts +++ b/src/kernels/execution/helpers.unit.test.ts @@ -22,9 +22,7 @@ suite(`UpdateNotebookMetadata`, () => { id: Uri.file('/usr/bin/python36').fsPath, sysPrefix: '/usr', displayName: 'Python 3.6', - envType: EnvironmentType.Unknown, - sysVersion: '3.6.0', - version: { major: 3, minor: 6, patch: 0, raw: '3.6.0' } + envType: EnvironmentType.Unknown }; const pythonDefaultKernelSpec: IJupyterKernelSpec = { argv: ['python', '-f', '{connection_file}'], @@ -37,9 +35,7 @@ suite(`UpdateNotebookMetadata`, () => { id: Uri.file('/usr/bin/python37').fsPath, sysPrefix: '/usr', displayName: 'Python 3.7', - envType: EnvironmentType.Unknown, - sysVersion: '3.7.0', - version: { major: 3, minor: 7, patch: 0, raw: '3.7.0' } + envType: EnvironmentType.Unknown }; let environments: PythonExtension['environments']; let disposables: { dispose: () => void }[] = []; diff --git a/src/kernels/helpers.ts b/src/kernels/helpers.ts index 4158a46c198..381197817a6 100644 --- a/src/kernels/helpers.ts +++ b/src/kernels/helpers.ts @@ -496,7 +496,7 @@ export const autoGeneratedKernelNameIdentifier = 'jvsc74a57bd0'; export async function getInterpreterKernelSpecName(interpreter?: PythonEnvironment): Promise { // Generate a name from a hash of the interpreter // Note it must be prefixed with 'python' and the version number. - const version = interpreter?.sysVersion ? getTelemetrySafeVersion(interpreter.sysVersion) || '3' : ''; + const version = getTelemetrySafeVersion(getCachedVersion(interpreter)) || '3'; const versionWithSafeStrings = version.replace(/\./g, ''); const prefix = interpreter ? `python${versionWithSafeStrings}` : ''; return interpreter diff --git a/src/kernels/helpers.unit.test.ts b/src/kernels/helpers.unit.test.ts index 71f356c913f..1c4cefeda6b 100644 --- a/src/kernels/helpers.unit.test.ts +++ b/src/kernels/helpers.unit.test.ts @@ -290,7 +290,6 @@ suite('Kernel Connection Helpers', () => { id: Uri.file('pyPath').fsPath, sysPrefix: 'sysPrefix', envName: '', - version: undefined, displayName: 'Something', envType: EnvironmentType.Pipenv } @@ -338,12 +337,6 @@ suite('Kernel Connection Helpers', () => { sysPrefix: 'sysPrefix', envName: '', displayName: 'Something 64-bit', - version: { - major: 9, - minor: 8, - patch: 1, - raw: '9.8.7.6-pre' - }, envType: EnvironmentType.Conda } }) @@ -379,12 +372,6 @@ suite('Kernel Connection Helpers', () => { sysPrefix: 'sysPrefix', envName: '.env', displayName: 'Something 64-bit', - version: { - major: 9, - minor: 8, - patch: 7, - raw: '9.8.7.6-pre' - }, envType: EnvironmentType.Conda } }) @@ -420,12 +407,6 @@ suite('Kernel Connection Helpers', () => { sysPrefix: 'sysPrefix', envName: '.env', displayName: 'Something 64-bit', - version: { - major: 9, - minor: 8, - patch: 7, - raw: '9.8.7.6-pre' - }, envType: EnvironmentType.Conda } }) @@ -507,7 +488,6 @@ suite('Kernel Connection Helpers', () => { id: Uri.file('pyPath').fsPath, sysPrefix: 'sysPrefix', envName: '', - version: { major: 1, minor: 2, patch: 3, raw: '1.2.3' }, displayName: 'Something 64-bit', envType: EnvironmentType.Unknown } @@ -567,12 +547,6 @@ suite('Kernel Connection Helpers', () => { when(kernelSpec.language).thenReturn('python'); when(interpreter.id).thenReturn('xyz'); when(interpreter.envName).thenReturn(''); - when(interpreter.version).thenReturn({ - major: 9, - minor: 8, - patch: 1, - raw: '9.8.7.6-pre' - }); when(interpreter.displayName).thenReturn('Something 64-bit'); when(interpreter.envType).thenReturn(EnvironmentType.Pipenv); when(environments.known).thenReturn([ @@ -603,12 +577,6 @@ suite('Kernel Connection Helpers', () => { when(kernelSpec.language).thenReturn('python'); when(interpreter.id).thenReturn('xyz'); when(interpreter.envName).thenReturn('.env'); - when(interpreter.version).thenReturn({ - major: 9, - minor: 8, - patch: 7, - raw: '9.8.7.6-pre' - }); when(interpreter.displayName).thenReturn('Something'); when(interpreter.envType).thenReturn(EnvironmentType.Conda); when(environments.known).thenReturn([ @@ -639,12 +607,6 @@ suite('Kernel Connection Helpers', () => { when(kernelSpec.language).thenReturn('python'); when(interpreter.id).thenReturn('xyz'); when(interpreter.envName).thenReturn('.env'); - when(interpreter.version).thenReturn({ - major: 9, - minor: 8, - patch: 7, - raw: '9.8.7.6-pre' - }); when(interpreter.displayName).thenReturn('Something 64-bit'); when(interpreter.envType).thenReturn(EnvironmentType.Conda); when(environments.known).thenReturn([ diff --git a/src/kernels/jupyter/interpreter/jupyterInterpreterDependencyService.unit.test.ts b/src/kernels/jupyter/interpreter/jupyterInterpreterDependencyService.unit.test.ts index 9a6405963a3..609de10226d 100644 --- a/src/kernels/jupyter/interpreter/jupyterInterpreterDependencyService.unit.test.ts +++ b/src/kernels/jupyter/interpreter/jupyterInterpreterDependencyService.unit.test.ts @@ -25,8 +25,7 @@ suite('Jupyter Interpreter Configuration', () => { const pythonInterpreter: PythonEnvironment = { uri: Uri.file(''), id: Uri.file('').fsPath, - sysPrefix: '', - sysVersion: '' + sysPrefix: '' }; let disposables: Disposable[] = []; setup(() => { diff --git a/src/kernels/jupyter/interpreter/jupyterInterpreterService.unit.test.ts b/src/kernels/jupyter/interpreter/jupyterInterpreterService.unit.test.ts index ea3c6a8491e..517367e9e8a 100644 --- a/src/kernels/jupyter/interpreter/jupyterInterpreterService.unit.test.ts +++ b/src/kernels/jupyter/interpreter/jupyterInterpreterService.unit.test.ts @@ -33,14 +33,12 @@ suite('Jupyter Interpreter Service', () => { const pythonInterpreter: PythonEnvironment = { uri: Uri.file('some path'), id: Uri.file('some path').fsPath, - sysPrefix: '', - sysVersion: '' + sysPrefix: '' }; const secondPythonInterpreter: PythonEnvironment = { uri: Uri.file('second interpreter path'), id: Uri.file('second interpreter path').fsPath, - sysPrefix: '', - sysVersion: '' + sysPrefix: '' }; let disposables: IDisposable[] = []; diff --git a/src/kernels/jupyter/launcher/juypterServerProvider.unit.test.ts b/src/kernels/jupyter/launcher/juypterServerProvider.unit.test.ts index 91146c6e18a..234f4847485 100644 --- a/src/kernels/jupyter/launcher/juypterServerProvider.unit.test.ts +++ b/src/kernels/jupyter/launcher/juypterServerProvider.unit.test.ts @@ -2,7 +2,6 @@ // Licensed under the MIT License. import { expect } from 'chai'; -import { SemVer } from 'semver'; import { anything, instance, mock, when } from 'ts-mockito'; import * as typemoq from 'typemoq'; import { CancellationTokenSource, Disposable, EventEmitter, Uri } from 'vscode'; @@ -32,8 +31,6 @@ suite('Jupyter Server Provider', () => { const workingPython: PythonEnvironment = { uri: Uri.file('/foo/bar/python.exe'), id: Uri.file('/foo/bar/python.exe').fsPath, - version: new SemVer('3.6.6-final'), - sysVersion: '1.0.0.0', sysPrefix: 'Python' }; let disposables: Disposable[] = []; diff --git a/src/kernels/jupyter/session/jupyterKernelService.unit.test.ts b/src/kernels/jupyter/session/jupyterKernelService.unit.test.ts index ae98b824af0..6bd8c9413d4 100644 --- a/src/kernels/jupyter/session/jupyterKernelService.unit.test.ts +++ b/src/kernels/jupyter/session/jupyterKernelService.unit.test.ts @@ -68,8 +68,7 @@ suite('JupyterKernelService', () => { id: '/usr/bin/python3', displayName: 'Python 3 Environment', uri: Uri.file(os.platform() === 'win32' ? '/usr/bin/python3.exe' : '/usr/bin/python3'), - sysPrefix: 'python', - version: { major: 3, minor: 8, raw: '3.8', patch: 0 } + sysPrefix: 'python' }, id: '0' }), @@ -125,8 +124,7 @@ suite('JupyterKernelService', () => { id: '/usr/bin/python3', displayName: 'Python 3 Environment', uri: Uri.file(os.platform() === 'win32' ? '/usr/bin/python3.exe' : '/usr/bin/python3'), - sysPrefix: 'python', - version: { major: 3, minor: 8, raw: '3.8', patch: 0 } + sysPrefix: 'python' }, id: '2' }), @@ -160,8 +158,7 @@ suite('JupyterKernelService', () => { id: '/usr/bin/python', displayName: 'Python 2 Environment', uri: Uri.file(os.platform() === 'win32' ? '/usr/bin/python.exe' : '/usr/bin/python'), - sysPrefix: 'python', - version: { major: 2, minor: 7, raw: '2.7', patch: 0 } + sysPrefix: 'python' }, id: '4' }), @@ -192,8 +189,7 @@ suite('JupyterKernelService', () => { id: '/usr/bin/python3', displayName: 'Python 3 Environment', uri: Uri.file(os.platform() === 'win32' ? '/usr/bin/python3.exe' : '/usr/bin/python3'), - sysPrefix: 'python', - version: { major: 3, minor: 8, raw: '3.8', patch: 0 } + sysPrefix: 'python' }, id: '5' }), @@ -227,8 +223,7 @@ suite('JupyterKernelService', () => { id: '/usr/bin/python', displayName: 'Python 2 Environment', uri: Uri.file(os.platform() === 'win32' ? '/usr/bin/python.exe' : '/usr/bin/python'), - sysPrefix: 'python', - version: { major: 2, minor: 7, raw: '2.7', patch: 0 } + sysPrefix: 'python' }, id: '7' }), @@ -259,8 +254,7 @@ suite('JupyterKernelService', () => { id: '/usr/bin/python3', displayName: 'Python 3 Environment', uri: Uri.file(os.platform() === 'win32' ? '/usr/bin/python3.exe' : '/usr/bin/python3'), - sysPrefix: 'python', - version: { major: 3, minor: 8, raw: '3.8', patch: 0 } + sysPrefix: 'python' }, id: '8' }), @@ -294,8 +288,7 @@ suite('JupyterKernelService', () => { id: '/usr/bin/python', displayName: 'Python 2 Environment', uri: Uri.file(os.platform() === 'win32' ? '/usr/bin/python.exe' : '/usr/bin/python'), - sysPrefix: 'python', - version: { major: 2, minor: 7, raw: '2.7', patch: 0 } + sysPrefix: 'python' }, id: '10' }), @@ -378,8 +371,7 @@ suite('JupyterKernelService', () => { ? '/usr/don/home/envs/sample/bin/python.exe' : '/usr/don/home/envs/sample/bin/python' ), - sysPrefix: 'python', - version: { major: 3, minor: 8, raw: '3.8', patch: 0 } + sysPrefix: 'python' }, id: '12' }), @@ -417,8 +409,7 @@ suite('JupyterKernelService', () => { ? '/usr/don/home/envs/sample/bin/python.exe' : '/usr/don/home/envs/sample/bin/python' ), - sysPrefix: 'python', - version: { major: 3, minor: 8, raw: '3.8', patch: 0 } + sysPrefix: 'python' }, id: '13' }), @@ -453,8 +444,7 @@ suite('JupyterKernelService', () => { ? '/usr/don/home/envs/sample/bin/python.exe' : '/usr/don/home/envs/sample/bin/python' ), - sysPrefix: 'python', - version: { major: 3, minor: 8, raw: '3.8', patch: 0 } + sysPrefix: 'python' }, id: '14' }) diff --git a/src/kernels/kernelDependencyService.unit.test.ts b/src/kernels/kernelDependencyService.unit.test.ts index e0c8e69039b..9d9afa1280b 100644 --- a/src/kernels/kernelDependencyService.unit.test.ts +++ b/src/kernels/kernelDependencyService.unit.test.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import * as sinon from 'sinon'; import { assert } from 'chai'; import { anything, instance, mock, verify, when } from 'ts-mockito'; import { CancellationTokenSource, Memento, NotebookDocument, NotebookEditor, Uri } from 'vscode'; @@ -19,6 +20,9 @@ import { getResourceType } from '../platform/common/utils'; import { mockedVSCodeNamespaces, resetVSCodeMocks } from '../test/vscode-mock'; import { Disposable } from 'vscode'; import { dispose } from '../platform/common/utils/lifecycle'; +import { PythonExtension } from '@vscode/python-extension'; +import { resolvableInstance } from '../test/datascience/helpers'; +import { setPythonApi } from '../platform/interpreter/helpers'; /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -37,14 +41,7 @@ suite('Kernel Dependency Service', () => { uri: Uri.file('abc') }); let metadata: PythonKernelConnectionMetadata; - suiteSetup(async () => { - metadata = PythonKernelConnectionMetadata.create({ - interpreter, - kernelSpec: await createInterpreterKernelSpec(interpreter, Uri.file('')), - id: '1' - }); - }); - setup(() => { + setup(async () => { resetVSCodeMocks(); disposables.push(new Disposable(() => resetVSCodeMocks())); installer = mock(); @@ -65,6 +62,24 @@ suite('Kernel Dependency Service', () => { instance(rawSupport), instance(serviceContainer) ); + + const mockedApi = mock(); + sinon.stub(PythonExtension, 'api').resolves(resolvableInstance(mockedApi)); + disposables.push({ dispose: () => sinon.restore() }); + const environments = mock(); + when(mockedApi.environments).thenReturn(instance(environments)); + when(environments.known).thenReturn([]); + setPythonApi(instance(mockedApi)); + disposables.push({ dispose: () => setPythonApi(undefined as any) }); + + metadata = PythonKernelConnectionMetadata.create({ + interpreter, + kernelSpec: await createInterpreterKernelSpec(interpreter, Uri.file('')), + id: '1' + }); + // when(environments.resolveEnvironment(jupyterInterpreter.id)).thenResolve({ + // executable: { sysPrefix: '' } + // } as any); }); teardown(() => (disposables = dispose(disposables))); [undefined, Uri.file('test.py'), Uri.file('test.ipynb')].forEach((resource) => { diff --git a/src/kernels/raw/finder/contributedKerneFinder.node.unit.test.ts b/src/kernels/raw/finder/contributedKerneFinder.node.unit.test.ts index fe62028685b..7fd1d4f70db 100644 --- a/src/kernels/raw/finder/contributedKerneFinder.node.unit.test.ts +++ b/src/kernels/raw/finder/contributedKerneFinder.node.unit.test.ts @@ -47,12 +47,14 @@ import { IJupyterServerUriStorage } from '../../jupyter/types'; import { getUserHomeDir } from '../../../platform/common/utils/platform.node'; import { IApplicationEnvironment } from '../../../platform/common/application/types'; import { noop } from '../../../platform/common/utils/misc'; -import { uriEquals } from '../../../test/datascience/helpers'; +import { resolvableInstance, uriEquals } from '../../../test/datascience/helpers'; import { createEventHandler, TestEventHandler } from '../../../test/common'; import { ContributedLocalKernelSpecFinder } from './contributedLocalKernelSpecFinder.node'; import { ITrustedKernelPaths } from './types'; import { ServiceContainer } from '../../../platform/ioc/container'; import { IPythonExecutionService, IPythonExecutionFactory } from '../../../platform/interpreter/types.node'; +import { PythonExtension } from '@vscode/python-extension'; +import { setPythonApi } from '../../../platform/interpreter/helpers'; [false, true].forEach((isWindows) => { suite(`Contributed Local Kernel Spec Finder ${isWindows ? 'Windows' : 'Unix'}`, () => { @@ -162,8 +164,7 @@ import { IPythonExecutionService, IPythonExecutionFactory } from '../../../platf instance(memento), instance(fs), instance(context), - instance(pythonExecFactory), - instance(interpreterService) + instance(pythonExecFactory) ); const kernelSpecsBySpecFile = new Map(); @@ -267,11 +268,6 @@ import { IPythonExecutionService, IPythonExecutionFactory } from '../../../platf nonPythonKernelSpecFinder.activate(); pythonKernelFinderWrapper.activate(); } - teardown(() => { - disposables = dispose(disposables); - sinon.restore(); - }); - const juliaKernelSpec: KernelSpec.ISpecModel = { argv: ['julia', 'start', 'kernel'], display_name: 'Julia Kernel', @@ -318,27 +314,21 @@ import { IPythonExecutionService, IPythonExecutionFactory } from '../../../platf id: Uri.file(isWindows ? 'C:/Python/Python2/scripts/python.exe' : '/usr/bin/python27').fsPath, sysPrefix: isWindows ? 'C:/Python/Python2' : '/usr', displayName: 'Python 2.7', - envType: EnvironmentType.Unknown, - sysVersion: '2.7.0', - version: { major: 2, minor: 7, patch: 0, raw: '2.7.0' } + envType: EnvironmentType.Unknown }; const python36Global: PythonEnvironment = { uri: Uri.file(isWindows ? 'C:/Python/Python3.6/scripts/python.exe' : '/usr/bin/python36'), id: Uri.file(isWindows ? 'C:/Python/Python3.6/scripts/python.exe' : '/usr/bin/python36').fsPath, sysPrefix: isWindows ? 'C:/Python/Python3.6' : '/usr', displayName: 'Python 3.6', - envType: EnvironmentType.Unknown, - sysVersion: '3.6.0', - version: { major: 3, minor: 6, patch: 0, raw: '3.6.0' } + envType: EnvironmentType.Unknown }; const python37Global: PythonEnvironment = { uri: Uri.file(isWindows ? 'C:/Python/Python3.7/scripts/python.exe' : '/usr/bin/python37'), id: Uri.file(isWindows ? 'C:/Python/Python3.7/scripts/python.exe' : '/usr/bin/python37').fsPath, sysPrefix: isWindows ? 'C:/Python/Python3.7' : '/usr', displayName: 'Python 3.7', - envType: EnvironmentType.Unknown, - sysVersion: '3.7.0', - version: { major: 3, minor: 7, patch: 0, raw: '3.6.0' } + envType: EnvironmentType.Unknown }; const python39PyEnv_HelloWorld: PythonEnvironment = { uri: Uri.file( @@ -349,9 +339,7 @@ import { IPythonExecutionService, IPythonExecutionFactory } from '../../../platf sysPrefix: isWindows ? 'C:/pyenv/envs/temp' : '/users/username/pyenv/envs/temp', displayName: 'Temporary Python 3.9', envName: 'temp', - envType: EnvironmentType.Pyenv, - sysVersion: '3.9.0', - version: { major: 3, minor: 9, patch: 0, raw: '3.9.0' } + envType: EnvironmentType.Pyenv }; const python38VenvEnv: PythonEnvironment = { uri: Uri.file( @@ -362,9 +350,7 @@ import { IPythonExecutionService, IPythonExecutionFactory } from '../../../platf sysPrefix: isWindows ? 'C:/temp/venv/.venv' : '/users/username/temp/.venv', displayName: 'Virtual Env Python 3.8', envName: '.venv', - envType: EnvironmentType.VirtualEnv, - sysVersion: '3.8.0', - version: { major: 3, minor: 8, patch: 0, raw: '3.8.0' } + envType: EnvironmentType.VirtualEnv }; const condaEnv1: PythonEnvironment = { uri: Uri.file(isWindows ? 'C:/conda/envs/env1/scripts/python.exe' : '/conda/envs/env1/bin/python'), @@ -372,9 +358,7 @@ import { IPythonExecutionService, IPythonExecutionFactory } from '../../../platf sysPrefix: isWindows ? 'C:/conda/envs/env1' : '/conda/envs/env1', envName: 'env1', displayName: 'Conda Env1 3.6', - envType: EnvironmentType.Conda, - sysVersion: '3.6.0', - version: { major: 3, minor: 6, patch: 0, raw: '3.6.0' } + envType: EnvironmentType.Conda }; const javaKernelSpec: KernelSpec.ISpecModel = { argv: ['java', 'xyz.jar', '{connection_file}', 'moreargs'], @@ -439,7 +423,16 @@ import { IPythonExecutionService, IPythonExecutionFactory } from '../../../platf } } }; - suiteSetup(async () => { + setup(async () => { + const mockedApi = mock(); + sinon.stub(PythonExtension, 'api').resolves(resolvableInstance(mockedApi)); + disposables.push({ dispose: () => sinon.restore() }); + const environments = mock(); + when(mockedApi.environments).thenReturn(instance(environments)); + when(environments.known).thenReturn([]); + setPythonApi(instance(mockedApi)); + disposables.push({ dispose: () => setPythonApi(undefined as any) }); + kernelspecRegisteredByOlderVersionOfExtension = { argv: [python38VenvEnv.uri.fsPath, '-m', 'ipykernel_launcher', '-f', '{connection_file}', 'moreargs'], display_name: 'Kernelspec registered by older version of extension', @@ -454,6 +447,10 @@ import { IPythonExecutionService, IPythonExecutionFactory } from '../../../platf } }; }); + teardown(() => { + disposables = dispose(disposables); + sinon.restore(); + }); async function generateExpectedKernels( expectedGlobalKernelSpecs: KernelSpec.ISpecModel[], diff --git a/src/kernels/raw/finder/interpreterKernelSpecFinderHelper.node.ts b/src/kernels/raw/finder/interpreterKernelSpecFinderHelper.node.ts index 6cc01b3b587..5f06b2981d0 100644 --- a/src/kernels/raw/finder/interpreterKernelSpecFinderHelper.node.ts +++ b/src/kernels/raw/finder/interpreterKernelSpecFinderHelper.node.ts @@ -34,7 +34,8 @@ import { getTelemetrySafeHashedString } from '../../../platform/telemetry/helper import { isKernelLaunchedViaLocalPythonIPyKernel, isLikelyAPythonExecutable } from '../../helpers.node'; import { LocalKnownPathKernelSpecFinder } from './localKnownPathKernelSpecFinder.node'; import { areObjectsWithUrisTheSame, noop } from '../../../platform/common/utils/misc'; -import { getSysPrefix } from '../../../platform/interpreter/helpers'; +import { getCachedSysPrefix, getCachedVersion, getSysPrefix } from '../../../platform/interpreter/helpers'; +import { Environment } from '@vscode/python-extension'; export function localPythonKernelsCacheKey() { const LocalPythonKernelsCacheKey = 'LOCAL_KERNEL_PYTHON_AND_RELATED_SPECS_CACHE_KEY_V_2023_3'; @@ -129,6 +130,8 @@ export async function findKernelSpecsInInterpreter( export class InterpreterSpecificKernelSpecsFinder extends DisposableBase { private cancelToken = new CancellationTokenSource(); private kernelSpecPromise?: Promise; + private lastKnownInterpreterVersion?: Environment['version']; + private lastKnownInterpreterSysPrefix?: string; private _kernels = new Map(); private _onDidChangeKernels = this._register( new EventEmitter<{ @@ -145,6 +148,8 @@ export class InterpreterSpecificKernelSpecsFinder extends DisposableBase { private readonly kernelSpecFinder: LocalKernelSpecFinder ) { super(); + this.lastKnownInterpreterVersion = getCachedVersion(interpreter); + this.lastKnownInterpreterSysPrefix = getCachedSysPrefix(interpreter); this._register({ dispose: () => this.cancelToken.cancel() }); this._register(this.cancelToken); this._register(this.interpreterService.onDidChangeInterpreter(this.clearCacheWhenInterpretersChange, this)); @@ -174,13 +179,17 @@ export class InterpreterSpecificKernelSpecsFinder extends DisposableBase { if (!interpreter) { return; } + const version = getCachedVersion(interpreter); + const sysPrefix = getCachedSysPrefix(interpreter); if ( // If the version, syspath has changed, then we need to re-discover the kernels. this.interpreter.envPath !== interpreter.envPath || - this.interpreter.version?.raw !== interpreter.version?.raw || + this.lastKnownInterpreterVersion?.sysVersion !== version?.sysVersion || this.interpreter.envType !== interpreter.envType || - this.interpreter.sysPrefix !== interpreter.sysPrefix + this.lastKnownInterpreterSysPrefix !== sysPrefix ) { + this.lastKnownInterpreterVersion = version; + this.lastKnownInterpreterSysPrefix = sysPrefix; this.listKernelSpecs(true).catch(noop); } } diff --git a/src/kernels/raw/finder/interpreterKernelSpecFinderHelper.node.unit.test.ts b/src/kernels/raw/finder/interpreterKernelSpecFinderHelper.node.unit.test.ts index 9b6fb25ad47..dfbb02b83b7 100644 --- a/src/kernels/raw/finder/interpreterKernelSpecFinderHelper.node.unit.test.ts +++ b/src/kernels/raw/finder/interpreterKernelSpecFinderHelper.node.unit.test.ts @@ -61,8 +61,7 @@ suite('Interpreter Kernel Spec Finder Helper', () => { venvInterpreter = { id: 'venvPython', sysPrefix: 'home/venvPython', - uri: Uri.file('home/venvPython/bin/python'), - version: { major: 3, minor: 10, patch: 0, raw: '3.10.0' } + uri: Uri.file('home/venvPython/bin/python') }; disposables.push(helper); const mockedApi = mock(); diff --git a/src/kernels/raw/finder/jupyterPaths.node.ts b/src/kernels/raw/finder/jupyterPaths.node.ts index a2ed2834551..7b9e5a7679a 100644 --- a/src/kernels/raw/finder/jupyterPaths.node.ts +++ b/src/kernels/raw/finder/jupyterPaths.node.ts @@ -33,7 +33,7 @@ import { IPythonExecutionFactory } from '../../../platform/interpreter/types.nod import { getDisplayPath } from '../../../platform/common/platform/fs-paths'; import { StopWatch } from '../../../platform/common/utils/stopWatch'; import { ResourceMap, ResourceSet } from '../../../platform/common/utils/map'; -import { IInterpreterService } from '../../../platform/interpreter/contracts'; +import { getSysPrefix } from '../../../platform/interpreter/helpers'; const winJupyterPath = path.join('AppData', 'Roaming', 'jupyter', 'kernels'); const linuxJupyterPath = path.join('.local', 'share', 'jupyter', 'kernels'); @@ -62,8 +62,7 @@ export class JupyterPaths { @inject(IMemento) @named(GLOBAL_MEMENTO) private readonly globalState: Memento, @inject(IFileSystemNode) private readonly fs: IFileSystem, @inject(IExtensionContext) private readonly context: IExtensionContext, - @inject(IPythonExecutionFactory) private readonly pythonExecFactory: IPythonExecutionFactory, - @inject(IInterpreterService) private readonly interpreters: IInterpreterService + @inject(IPythonExecutionFactory) private readonly pythonExecFactory: IPythonExecutionFactory ) { this.envVarsProvider.onDidEnvironmentVariablesChange( () => { @@ -210,8 +209,8 @@ export class JupyterPaths { // 3. Add the paths based on user and env data directories if (interpreter && !interpreter.sysPrefix) { traceWarning(`sysPrefix was not set for ${interpreter.id}`); - const details = await this.interpreters.getInterpreterDetails(interpreter.uri); - interpreter.sysPrefix = details?.sysPrefix || interpreter.sysPrefix; + const sysPrefix = await getSysPrefix(interpreter); + interpreter.sysPrefix = sysPrefix || interpreter.sysPrefix; traceInfoIfCI(`sysPrefix after getting details ${interpreter.sysPrefix}`); } const possibleEnvJupyterPath = interpreter?.sysPrefix diff --git a/src/kernels/raw/finder/jupyterPaths.node.unit.test.ts b/src/kernels/raw/finder/jupyterPaths.node.unit.test.ts index 53f2879edc1..c7bc086a8db 100644 --- a/src/kernels/raw/finder/jupyterPaths.node.unit.test.ts +++ b/src/kernels/raw/finder/jupyterPaths.node.unit.test.ts @@ -74,8 +74,7 @@ suite('Jupyter Paths', () => { instance(memento), instance(fs), instance(context), - instance(pythonExecFactory), - instance(interpreterService) + instance(pythonExecFactory) ); delete process.env['JUPYTER_PATH']; delete process.env['APPDATA']; diff --git a/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node.unit.test.ts b/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node.unit.test.ts index 096e68445ff..640f4d87440 100644 --- a/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node.unit.test.ts +++ b/src/kernels/raw/finder/localPythonAndRelatedNonPythonKernelSpecFinder.node.unit.test.ts @@ -31,6 +31,7 @@ import { localPythonKernelsCacheKey } from './interpreterKernelSpecFinderHelper. import { mockedVSCodeNamespaces } from '../../../test/vscode-mock'; import { ResourceMap } from '../../../platform/common/utils/map'; import { PythonExtension } from '@vscode/python-extension'; +import { setPythonApi } from '../../../platform/interpreter/helpers'; suite(`Local Python and related kernels`, async () => { let finder: LocalPythonAndRelatedNonPythonKernelSpecFinder; @@ -129,12 +130,10 @@ suite(`Local Python and related kernels`, async () => { const venvInterpreter: PythonEnvironment = { id: 'venvPython', sysPrefix: 'home/venvPython', - uri: Uri.file('home/venvPython/bin/python'), - version: { major: 3, minor: 10, patch: 0, raw: '3.10.0' } + uri: Uri.file('home/venvPython/bin/python') }; const cachedVenvInterpreterWithOlderVersionOfPython = { - ...deserializePythonEnvironment(serializePythonEnvironment(venvInterpreter), venvInterpreter.id)!, - version: { major: 3, minor: 8, patch: 0, raw: '3.8.0' } + ...deserializePythonEnvironment(serializePythonEnvironment(venvInterpreter), venvInterpreter.id)! }; let venvPythonKernel: PythonKernelConnectionMetadata; @@ -176,6 +175,27 @@ suite(`Local Python and related kernels`, async () => { when(jupyterPaths.getKernelSpecRootPath()).thenResolve(globalKernelRootPath); when(mockedVSCodeNamespaces.workspace.workspaceFolders).thenReturn([]); + const mockedApi = mock(); + sinon.stub(PythonExtension, 'api').resolves(resolvableInstance(mockedApi)); + disposables.push({ dispose: () => sinon.restore() }); + const environments = mock(); + when(mockedApi.environments).thenReturn(instance(environments)); + when(environments.known).thenReturn([]); + setPythonApi(instance(mockedApi)); + disposables.push({ dispose: () => setPythonApi(undefined as any) }); + when(environments.resolveEnvironment(pythonKernelSpec.id)).thenResolve({ + executable: { sysPrefix: 'home/python' } + } as any); + when(environments.resolveEnvironment(condaInterpreter.id)).thenResolve({ + executable: { sysPrefix: 'home/conda' } + } as any); + when(environments.resolveEnvironment(globalInterpreter.id)).thenResolve({ + executable: { sysPrefix: 'home/global' } + } as any); + when(environments.resolveEnvironment(venvInterpreter.id)).thenResolve({ + executable: { sysPrefix: 'home/venvPython' } + } as any); + // Initialize the kernel specs (test data). let kernelSpec = await createInterpreterKernelSpec(venvInterpreter, tempDirForKernelSpecs); venvPythonKernel = PythonKernelConnectionMetadata.create({ @@ -231,24 +251,6 @@ suite(`Local Python and related kernels`, async () => { }); disposables.push(new Disposable(() => loadKernelSpecStub.restore())); traceInfo(`Start Test (completed) ${this.currentTest?.title}`); - - const mockedApi = mock(); - sinon.stub(PythonExtension, 'api').resolves(resolvableInstance(mockedApi)); - disposables.push({ dispose: () => sinon.restore() }); - const environments = mock(); - when(mockedApi.environments).thenReturn(instance(environments)); - when(environments.resolveEnvironment(pythonKernelSpec.id)).thenResolve({ - executable: { sysPrefix: 'home/python' } - } as any); - when(environments.resolveEnvironment(condaInterpreter.id)).thenResolve({ - executable: { sysPrefix: 'home/conda' } - } as any); - when(environments.resolveEnvironment(globalInterpreter.id)).thenResolve({ - executable: { sysPrefix: 'home/global' } - } as any); - when(environments.resolveEnvironment(venvInterpreter.id)).thenResolve({ - executable: { sysPrefix: 'home/venvPython' } - } as any); }); teardown(async function () { traceInfo(`Ended Test (completed) ${this.currentTest?.title}`); diff --git a/src/kernels/raw/finder/pythonKernelInterruptDaemon.node.ts b/src/kernels/raw/finder/pythonKernelInterruptDaemon.node.ts index b47ba54fd61..4420a86e57e 100644 --- a/src/kernels/raw/finder/pythonKernelInterruptDaemon.node.ts +++ b/src/kernels/raw/finder/pythonKernelInterruptDaemon.node.ts @@ -34,13 +34,8 @@ function isBestPythonInterpreterForAnInterruptDaemon(interpreter: PythonEnvironm return false; } function isSupportedPythonVersion(interpreter: PythonEnvironment) { - let major = interpreter?.version?.major ?? 3; - let minor = interpreter?.version?.minor ?? 6; - const version = getCachedVersion(interpreter); - if (version) { - major = version.major || major; - minor = version.minor || minor; - } + let major = getCachedVersion(interpreter)?.major ?? 3; + let minor = getCachedVersion(interpreter)?.minor ?? 6; if ( major >= 3 && // Even thought 3.6 is no longer supported, we know this works well enough for what we want. diff --git a/src/kernels/variables/preWarmVariables.unit.test.ts b/src/kernels/variables/preWarmVariables.unit.test.ts index 47022a2d7ce..38b162e44ed 100644 --- a/src/kernels/variables/preWarmVariables.unit.test.ts +++ b/src/kernels/variables/preWarmVariables.unit.test.ts @@ -27,8 +27,7 @@ suite('PreWarm Env Vars', () => { interpreter = { uri: Uri.file(''), id: Uri.file('').fsPath, - sysPrefix: '', - sysVersion: '' + sysPrefix: '' }; onDidChangeInterpreter = new EventEmitter(); envActivationService = mock(); diff --git a/src/platform/api/pythonApi.ts b/src/platform/api/pythonApi.ts index 02813775d4d..54852e52fea 100644 --- a/src/platform/api/pythonApi.ts +++ b/src/platform/api/pythonApi.ts @@ -40,7 +40,7 @@ import { PythonExtensionActicationFailedError } from '../errors/pythonExtActivat import { PythonExtensionApiNotExportedError } from '../errors/pythonExtApiNotExportedError'; import { getOSType, OSType } from '../common/utils/platform'; import { SemVer } from 'semver'; -import { getEnvironmentType, setPythonApi } from '../interpreter/helpers'; +import { getCachedVersion, getEnvironmentType, setPythonApi } from '../interpreter/helpers'; import { getWorkspaceFolderIdentifier } from '../common/application/workspace.base'; export function deserializePythonEnvironment( @@ -128,15 +128,7 @@ export function resolvedPythonEnvToJupyterEnv( uri, displayName: env.environment?.name || '', envType, - isCondaEnvWithoutPython, - version: env.version - ? { - major: env.version.major, - minor: env.version.minor, - patch: env.version.micro, - raw: env.version.sysVersion - } - : undefined + isCondaEnvWithoutPython }; } export function pythonEnvToJupyterEnv(env: Environment, supportsEmptyCondaEnv: boolean): PythonEnvironment | undefined { @@ -172,15 +164,7 @@ export function pythonEnvToJupyterEnv(env: Environment, supportsEmptyCondaEnv: b uri, displayName: env.environment?.name || '', envType, - isCondaEnvWithoutPython, - version: env.version - ? { - major: env.version.major || 0, - minor: env.version.minor || 0, - patch: env.version.micro || 0, - raw: env.version.sysVersion || '' - } - : undefined + isCondaEnvWithoutPython }; } @@ -590,11 +574,11 @@ export class InterpreterService implements IInterpreterService { return; } this.lastLoggedResourceAndInterpreterId = key; + const version = getCachedVersion(item); traceInfo( `Active Interpreter ${resource ? `for '${getDisplayPath(resource)}' ` : ''}is ${getDisplayPath( item?.id - )} (${item?.envType}, '${item?.envName}', ${item?.version?.major}.${item?.version?.minor}.${item - ?.version?.patch})` + )} (${item?.envType}, '${item?.envName}', ${version?.major}.${version?.minor}.${version?.micro})` ); }) .catch(noop); diff --git a/src/platform/interpreter/installer/channelManager.messages.unit.test.ts b/src/platform/interpreter/installer/channelManager.messages.unit.test.ts index 5cea68f9216..b695630904f 100644 --- a/src/platform/interpreter/installer/channelManager.messages.unit.test.ts +++ b/src/platform/interpreter/installer/channelManager.messages.unit.test.ts @@ -2,7 +2,6 @@ // Licensed under the MIT License. import assert from 'assert'; -import { SemVer } from 'semver'; import { IPlatformService } from '../../../platform/common/platform/types'; import { IInterpreterService } from '../../../platform/interpreter/contracts'; import { ServiceContainer } from '../../../platform/ioc/container'; @@ -20,9 +19,7 @@ const info: PythonEnvironment = { uri: Uri.file(''), id: Uri.file('').fsPath, envType: EnvironmentType.Unknown, - version: new SemVer('0.0.0-alpha'), - sysPrefix: '', - sysVersion: '' + sysPrefix: '' }; suite('Installation - channel messages', () => { diff --git a/src/platform/interpreter/internal/scripts/index.node.ts b/src/platform/interpreter/internal/scripts/index.node.ts index 7d5da32fe63..0150d214801 100644 --- a/src/platform/interpreter/internal/scripts/index.node.ts +++ b/src/platform/interpreter/internal/scripts/index.node.ts @@ -39,7 +39,6 @@ type PythonVersionInfo = [number, number, number, ReleaseLevel, number]; export type PythonEnvInfo = { versionInfo: PythonVersionInfo; sysPrefix: string; - sysVersion: string; is64Bit: boolean; exe: string; }; diff --git a/src/platform/pythonEnvironments/info/index.ts b/src/platform/pythonEnvironments/info/index.ts index 545f36436c3..073ed385d92 100644 --- a/src/platform/pythonEnvironments/info/index.ts +++ b/src/platform/pythonEnvironments/info/index.ts @@ -3,7 +3,6 @@ import { Uri } from 'vscode'; -import { PythonVersion } from './pythonVersion'; /** * The supported Python environment types. @@ -25,14 +24,11 @@ export type InterpreterId = string; * * @prop path - the location of the executable file * @prop version - the runtime version - * @prop sysVersion - the raw value of `sys.version` * @prop sysPrefix - the environment's install root (`sys.prefix`) */ export type InterpreterInformation = { id: InterpreterId; uri: Uri; - version?: PythonVersion; - sysVersion?: string; sysPrefix: string; }; diff --git a/src/test/datascience/notebook/kernelRankingHelper.ts b/src/test/datascience/notebook/kernelRankingHelper.ts index 9f8661d8b09..a18b5d7588b 100644 --- a/src/test/datascience/notebook/kernelRankingHelper.ts +++ b/src/test/datascience/notebook/kernelRankingHelper.ts @@ -22,6 +22,7 @@ import { traceError, traceInfo, traceInfoIfCI } from '../../../platform/logging' import { PythonEnvironment } from '../../../platform/pythonEnvironments/info'; import { getInterpreterHash } from '../../../platform/pythonEnvironments/info/interpreter'; import * as path from '../../../platform/vscode-path/path'; +import { getCachedVersion } from '../../../platform/interpreter/helpers'; /** * Given an interpreter, find the kernel connection that matches this interpreter. @@ -347,18 +348,9 @@ export function compareKernels( if (!a.interpreter && b.interpreter) { return -1; } - const aSysVersion = a.interpreter?.sysPrefix || ''; - const aVersion = - a.interpreter?.version?.major || - (aSysVersion.length && !isNaN(parseInt(aSysVersion.substring(0))) - ? parseInt(aSysVersion.substring(0)) - : 0); - const bSysVersion = a.interpreter?.sysPrefix || ''; - const bVersion = - a.interpreter?.version?.major || - (bSysVersion.length && !isNaN(parseInt(bSysVersion.substring(0))) - ? parseInt(bSysVersion.substring(0)) - : 0); + // const version = + const aVersion = getCachedVersion(a.interpreter)?.major || 0; + const bVersion = getCachedVersion(b.interpreter)?.major || 0; if (aVersion !== bVersion) { return aVersion > bVersion ? 1 : -1; } @@ -639,7 +631,7 @@ function compareKernelSpecOrEnvNames( const majorVersion = parseInt(notebookMetadata.kernelspec.name.toLowerCase().replace('python', ''), 10); if ( majorVersion && - a.interpreter?.version?.major === b.interpreter?.version?.major && + getCachedVersion(a.interpreter)?.major === getCachedVersion(b.interpreter)?.major && a.kind === b.kind && comparisonOfDisplayNames === 0 && comparisonOfInterpreter === 0 @@ -657,8 +649,8 @@ function compareKernelSpecOrEnvNames( } } else if ( majorVersion && - a.interpreter?.version?.major !== b.interpreter?.version?.major && - a.interpreter?.version?.major === majorVersion && + getCachedVersion(a.interpreter)?.major !== getCachedVersion(b.interpreter)?.major && + getCachedVersion(a.interpreter)?.major === majorVersion && a.kind !== 'startUsingRemoteKernelSpec' && comparisonOfDisplayNames >= 0 && comparisonOfInterpreter >= 0 @@ -666,8 +658,8 @@ function compareKernelSpecOrEnvNames( return 1; } else if ( majorVersion && - a.interpreter?.version?.major !== b.interpreter?.version?.major && - b.interpreter?.version?.major === majorVersion && + getCachedVersion(a.interpreter)?.major !== getCachedVersion(b.interpreter)?.major && + getCachedVersion(b.interpreter)?.major === majorVersion && b.kind !== 'startUsingRemoteKernelSpec' && comparisonOfDisplayNames <= 0 && comparisonOfInterpreter <= 0 diff --git a/src/test/datascience/variableView/variableView.vscode.test.ts b/src/test/datascience/variableView/variableView.vscode.test.ts index 80c7f215e5d..c685d68b213 100644 --- a/src/test/datascience/variableView/variableView.vscode.test.ts +++ b/src/test/datascience/variableView/variableView.vscode.test.ts @@ -27,6 +27,7 @@ import { IVariableViewProvider } from '../../../webviews/extension-side/variable import { IKernelProvider } from '../../../kernels/types'; import { trimQuotes } from '../../../platform/common/helpers'; import { commands, window } from 'vscode'; +import { getVersion } from '../../../platform/interpreter/helpers'; suite('VariableView @variableViewer', function () { let api: IExtensionTestApi; @@ -189,7 +190,8 @@ suite('VariableView @variableViewer', function () { // Test that we are working will a larger set of basic types test('VariableView basic types A (webview-test)', async function () { - if (activeInterpreter.version?.major === 3 && activeInterpreter.version.minor >= 10) { + const version = await getVersion(activeInterpreter); + if (version?.major === 3 && (version.minor || 0) >= 10) { // https://github.com/microsoft/vscode-jupyter/issues/8523 return this.skip(); } diff --git a/src/test/interpreters/index.node.ts b/src/test/interpreters/index.node.ts index 183c0c8f86c..ec08d392408 100644 --- a/src/test/interpreters/index.node.ts +++ b/src/test/interpreters/index.node.ts @@ -58,7 +58,6 @@ export async function getInterpreterInfo(pythonPath: Uri | undefined): Promise

): Pyth id: Uri.file(`somePath${rnd}`).path, uri: Uri.file(`somePath${rnd}`), sysPrefix: `someSysPrefix${rnd}`, - sysVersion: `1.1.1`, ...(info || {}) }; } diff --git a/src/webviews/extension-side/dataviewer/dataViewerDependencyServiceInterpreter.node.unit.test.ts b/src/webviews/extension-side/dataviewer/dataViewerDependencyServiceInterpreter.node.unit.test.ts index 4d766948ed6..397d5069720 100644 --- a/src/webviews/extension-side/dataviewer/dataViewerDependencyServiceInterpreter.node.unit.test.ts +++ b/src/webviews/extension-side/dataviewer/dataViewerDependencyServiceInterpreter.node.unit.test.ts @@ -3,7 +3,6 @@ import { assert } from 'chai'; import * as path from '../../../platform/vscode-path/path'; -import { SemVer } from 'semver'; import { anything, deepEqual, instance, mock, verify, when } from 'ts-mockito'; import { Common, DataScience } from '../../../platform/common/utils/localize'; import { IInterpreterService } from '../../../platform/interpreter/contracts'; @@ -32,9 +31,7 @@ suite('DataViewerDependencyService (PythonEnvironment, Node)', () => { displayName: '', id: Uri.file(path.join('users', 'python', 'bin', 'python.exe')).fsPath, uri: Uri.file(path.join('users', 'python', 'bin', 'python.exe')), - sysPrefix: '', - sysVersion: '', - version: new SemVer('3.3.3') + sysPrefix: '' }; pythonExecService = mock(); installer = mock(ProductInstaller);