From 97749b0c0061b32dabe10562c20b96842e497fb1 Mon Sep 17 00:00:00 2001 From: Stella Huang Date: Fri, 14 Aug 2026 10:33:37 -0700 Subject: [PATCH 1/2] Handle Conda environments without Python Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2f537bc5-b389-4ee2-aac2-e2b83be47f5c --- src/client/envExt/api.legacy.ts | 9 +- src/client/envExt/envExtApi.ts | 122 +++++++----- src/client/envExt/utils.ts | 24 +++ src/test/envExt/api.legacy.unit.test.ts | 16 ++ src/test/envExt/envExtApi.unit.test.ts | 245 ++++++++++++++++++++++++ 5 files changed, 362 insertions(+), 54 deletions(-) create mode 100644 src/client/envExt/utils.ts create mode 100644 src/test/envExt/envExtApi.unit.test.ts diff --git a/src/client/envExt/api.legacy.ts b/src/client/envExt/api.legacy.ts index 679607fca68d..481ee4f748af 100644 --- a/src/client/envExt/api.legacy.ts +++ b/src/client/envExt/api.legacy.ts @@ -6,11 +6,11 @@ import { getEnvExtApi, getEnvironment } from './api.internal'; import { EnvironmentType, PythonEnvironment as PythonEnvironmentLegacy } from '../pythonEnvironments/info'; import { PythonEnvironment, PythonTerminalCreateOptions } from './types'; import { Architecture } from '../common/utils/platform'; -import { parseVersion } from '../pythonEnvironments/base/info/pythonVersion'; import { PythonEnvType } from '../pythonEnvironments/base/info'; import { traceError } from '../logging'; import { reportActiveInterpreterChanged } from '../environmentApi'; import { getWorkspaceFolder, getWorkspaceFolders } from '../common/vscodeApis/workspaceApis'; +import { parsePythonEnvironmentVersion } from './utils'; function toEnvironmentType(pythonEnv: PythonEnvironment): EnvironmentType { if (pythonEnv.envId.managerId.toLowerCase().endsWith('system')) { @@ -73,8 +73,11 @@ function getEnvType(kind: EnvironmentType): PythonEnvType | undefined { } } -function toLegacyType(env: PythonEnvironment): PythonEnvironmentLegacy { - const ver = parseVersion(env.version); +function toLegacyType(env: PythonEnvironment): PythonEnvironmentLegacy | undefined { + const ver = parsePythonEnvironmentVersion(env); + if (!ver) { + return undefined; + } const envType = toEnvironmentType(env); return { id: env.execInfo.run.executable, diff --git a/src/client/envExt/envExtApi.ts b/src/client/envExt/envExtApi.ts index 34f42f0d6954..43aab94a6fe4 100644 --- a/src/client/envExt/envExtApi.ts +++ b/src/client/envExt/envExtApi.ts @@ -4,7 +4,7 @@ import * as path from 'path'; import { Event, EventEmitter, Disposable, Uri } from 'vscode'; -import { PythonEnvInfo, PythonEnvKind, PythonEnvType, PythonVersion } from '../pythonEnvironments/base/info'; +import { PythonEnvInfo, PythonEnvKind, PythonEnvType } from '../pythonEnvironments/base/info'; import { GetRefreshEnvironmentsOptions, IDiscoveryAPI, @@ -26,8 +26,8 @@ import { } from './types'; import { FileChangeType } from '../common/platform/fileSystemWatcher'; import { Architecture, isWindows } from '../common/utils/platform'; -import { parseVersion } from '../pythonEnvironments/base/info/pythonVersion'; import { Interpreters } from '../common/utils/localize'; +import { parsePythonEnvironmentVersion } from './utils'; function getKind(pythonEnv: PythonEnvironment): PythonEnvKind { if (pythonEnv.envId.managerId.toLowerCase().endsWith('system')) { @@ -127,39 +127,51 @@ function getEnvType(kind: PythonEnvKind): PythonEnvType | undefined { } function toPythonEnvInfo(pythonEnv: PythonEnvironment): PythonEnvInfo | undefined { - const kind = getKind(pythonEnv); - const arch = Architecture.x64; - const version: PythonVersion = parseVersion(pythonEnv.version); - const { name, displayName, sysPrefix } = pythonEnv; - const executable = getExecutable(pythonEnv); - const location = getLocation(pythonEnv); - - return { - name, - location, - kind, - id: executable, - executable: { - filename: executable, - sysPrefix, - ctime: -1, - mtime: -1, - }, - version: { - sysVersion: pythonEnv.version, - major: version.major, - minor: version.minor, - micro: version.micro, - }, - arch, - distro: { - org: '', - }, - source: [], - detailedDisplayName: displayName, - display: displayName, - type: getEnvType(kind), - }; + const version = parsePythonEnvironmentVersion(pythonEnv); + if (!version) { + return undefined; + } + + try { + const kind = getKind(pythonEnv); + const arch = Architecture.x64; + const { name, displayName, sysPrefix } = pythonEnv; + const executable = getExecutable(pythonEnv); + const location = getLocation(pythonEnv); + + return { + name, + location, + kind, + id: executable, + executable: { + filename: executable, + sysPrefix, + ctime: -1, + mtime: -1, + }, + version: { + sysVersion: pythonEnv.version, + major: version.major, + minor: version.minor, + micro: version.micro, + }, + arch, + distro: { + org: '', + }, + source: [], + detailedDisplayName: displayName, + display: displayName, + type: getEnvType(kind), + }; + } catch (error) { + traceError( + `Failed to convert environment "${pythonEnv.displayName}" from the Python Environments extension`, + error, + ); + return undefined; + } } function hasChanged(old: PythonEnvInfo, newEnv: PythonEnvInfo): boolean { @@ -214,11 +226,16 @@ class EnvExtApis implements IDiscoveryAPI, Disposable { this._onChanged, this.envExtApi.onDidChangeEnvironments((e) => this.onDidChangeEnvironments(e)), this.envExtApi.onDidChangeEnvironment((e) => { + const oldEnv = e.old ? toPythonEnvInfo(e.old) : undefined; + const newEnv = e.new ? toPythonEnvInfo(e.new) : undefined; + if ((e.old && !oldEnv) || (e.new && !newEnv)) { + return; + } this._onChanged.fire({ type: FileChangeType.Changed, searchLocation: e.uri, - old: e.old ? toPythonEnvInfo(e.old) : undefined, - new: e.new ? toPythonEnvInfo(e.new) : undefined, + old: oldEnv, + new: newEnv, }); }), ); @@ -293,15 +310,11 @@ class EnvExtApis implements IDiscoveryAPI, Disposable { return info; } - private removeEnv(env: PythonEnvInfo | string): void { - if (typeof env === 'string') { - const old = this._envs.find((item) => item.executable.filename === env); - this._envs = this._envs.filter((item) => item.executable.filename !== env); - this._onChanged.fire({ type: FileChangeType.Deleted, old }); - return; - } - this._envs = this._envs.filter((item) => item.executable.filename !== env.executable.filename); - this._onChanged.fire({ type: FileChangeType.Deleted, old: env }); + private removeEnv(env: PythonEnvironment): void { + const executable = getExecutable(env); + const old = this._envs.find((item) => item.executable.filename === executable); + this._envs = this._envs.filter((item) => item.executable.filename !== executable); + this._onChanged.fire({ type: FileChangeType.Deleted, old }); } async resolveEnv(envPath?: string): Promise { @@ -328,11 +341,18 @@ class EnvExtApis implements IDiscoveryAPI, Disposable { onDidChangeEnvironments(e: DidChangeEnvironmentsEventArgs): void { e.forEach((item) => { - if (item.kind === EnvironmentChangeKind.remove) { - this.removeEnv(item.environment.environmentPath.fsPath); - } - if (item.kind === EnvironmentChangeKind.add) { - this.addEnv(item.environment); + try { + if (item.kind === EnvironmentChangeKind.remove) { + this.removeEnv(item.environment); + } + if (item.kind === EnvironmentChangeKind.add) { + this.addEnv(item.environment); + } + } catch (error) { + traceError( + `Failed to process environment change for "${item?.environment?.displayName ?? 'unknown environment'}" from the Python Environments extension`, + error, + ); } }); } diff --git a/src/client/envExt/utils.ts b/src/client/envExt/utils.ts new file mode 100644 index 000000000000..79307949b60d --- /dev/null +++ b/src/client/envExt/utils.ts @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { traceError, traceVerbose } from '../logging'; +import { PythonVersion } from '../pythonEnvironments/base/info'; +import { parseVersion } from '../pythonEnvironments/base/info/pythonVersion'; +import { PythonEnvironment } from './types'; + +export function parsePythonEnvironmentVersion(pythonEnv: PythonEnvironment): PythonVersion | undefined { + if (pythonEnv.version === 'no-python') { + traceVerbose(`Skipping environment without Python: ${pythonEnv.displayName}`); + return undefined; + } + + try { + return parseVersion(pythonEnv.version); + } catch (error) { + traceError( + `Failed to parse version for environment "${pythonEnv.displayName}" from the Python Environments extension`, + error, + ); + return undefined; + } +} diff --git a/src/test/envExt/api.legacy.unit.test.ts b/src/test/envExt/api.legacy.unit.test.ts index 2d9d681f3fa2..75f69ba362b9 100644 --- a/src/test/envExt/api.legacy.unit.test.ts +++ b/src/test/envExt/api.legacy.unit.test.ts @@ -72,4 +72,20 @@ suite('Env extension legacy API - getActiveInterpreterLegacy', () => { expect(getEnvironmentStub.callCount).to.equal(2); }); + + test('Returns undefined for an environment without Python', async () => { + getEnvironmentStub.resolves(buildEnv('/usr/bin/conda', 'no-python')); + + const result = await getActiveInterpreterLegacy(undefined); + + expect(result).to.equal(undefined); + }); + + test('Returns undefined for an environment with an invalid version', async () => { + getEnvironmentStub.resolves(buildEnv('/usr/bin/python', 'not-a-version')); + + const result = await getActiveInterpreterLegacy(undefined); + + expect(result).to.equal(undefined); + }); }); diff --git a/src/test/envExt/envExtApi.unit.test.ts b/src/test/envExt/envExtApi.unit.test.ts new file mode 100644 index 000000000000..72301e133711 --- /dev/null +++ b/src/test/envExt/envExtApi.unit.test.ts @@ -0,0 +1,245 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import { Disposable, EventEmitter, Uri } from 'vscode'; +import { FileChangeType } from '../../client/common/platform/fileSystemWatcher'; +import * as apiInternal from '../../client/envExt/api.internal'; +import { createEnvExtApi } from '../../client/envExt/envExtApi'; +import { + DidChangeEnvironmentEventArgs, + DidChangeEnvironmentsEventArgs, + EnvironmentChangeKind, + PythonEnvironment, + PythonEnvironmentApi, +} from '../../client/envExt/types'; +import { PythonEnvCollectionChangedEvent } from '../../client/pythonEnvironments/base/watcher'; + +function buildCondaEnvironment( + name: string, + version: string, + prefix: string, + executable = Uri.joinPath(Uri.file(prefix), 'bin', 'python').fsPath, +): PythonEnvironment { + return { + envId: { id: `${name}-${version}`, managerId: 'ms-python.python:conda' }, + name, + displayName: `${name} (${version})`, + displayPath: prefix, + version, + environmentPath: Uri.file(prefix), + execInfo: { run: { executable } }, + sysPrefix: prefix, + }; +} + +suite('Python Environments extension discovery adapter', () => { + let environmentChanges: EventEmitter; + let activeEnvironmentChanges: EventEmitter; + let disposables: Disposable[]; + let envExtApi: PythonEnvironmentApi; + + setup(() => { + environmentChanges = new EventEmitter(); + activeEnvironmentChanges = new EventEmitter(); + disposables = []; + envExtApi = { + onDidChangeEnvironments: environmentChanges.event, + onDidChangeEnvironment: activeEnvironmentChanges.event, + refreshEnvironments: sinon.stub().resolves(), + resolveEnvironment: sinon.stub().resolves(undefined), + } as unknown as PythonEnvironmentApi; + sinon.stub(apiInternal, 'getEnvExtApi').resolves(envExtApi); + }); + + teardown(() => { + disposables.forEach((disposable) => disposable.dispose()); + environmentChanges.dispose(); + activeEnvironmentChanges.dispose(); + sinon.restore(); + }); + + test('skips a no-Python Conda environment without dropping later valid environments', async () => { + const api = await createEnvExtApi(disposables); + const noPython = buildCondaEnvironment('empty', 'no-python', '/conda/envs/empty', '/conda/bin/conda'); + const first = buildCondaEnvironment('first', '3.12.1', '/conda/envs/first'); + const second = buildCondaEnvironment('second', '3.11.9', '/conda/envs/second'); + const events: PythonEnvCollectionChangedEvent[] = []; + disposables.push(api.onChanged((event) => events.push(event))); + + expect(() => + environmentChanges.fire([ + { kind: EnvironmentChangeKind.add, environment: noPython }, + { kind: EnvironmentChangeKind.add, environment: first }, + { kind: EnvironmentChangeKind.add, environment: second }, + ]), + ).not.to.throw(); + + expect(api.getEnvs().map((env) => env.executable.filename)).to.deep.equal([ + first.execInfo.run.executable, + second.execInfo.run.executable, + ]); + expect(events.map((event) => event.type)).to.deep.equal([ + FileChangeType.Created, + FileChangeType.Created, + ]); + }); + + test('isolates an unexpected invalid version from later environments in the batch', async () => { + const api = await createEnvExtApi(disposables); + const first = buildCondaEnvironment('first', '3.12.1', '/conda/envs/first'); + const invalid = buildCondaEnvironment('invalid', 'not-a-version', '/conda/envs/invalid'); + const second = buildCondaEnvironment('second', '3.11.9', '/conda/envs/second'); + + expect(() => + environmentChanges.fire([ + { kind: EnvironmentChangeKind.add, environment: first }, + { kind: EnvironmentChangeKind.add, environment: invalid }, + { kind: EnvironmentChangeKind.add, environment: second }, + ]), + ).not.to.throw(); + + expect(api.getEnvs().map((env) => env.executable.filename)).to.deep.equal([ + first.execInfo.run.executable, + second.execInfo.run.executable, + ]); + }); + + test('isolates a structurally malformed event from later environments in the batch', async () => { + const api = await createEnvExtApi(disposables); + const valid = buildCondaEnvironment('valid', '3.12.1', '/conda/envs/valid'); + + expect(() => + environmentChanges.fire([ + { + kind: EnvironmentChangeKind.remove, + environment: undefined, + } as unknown as DidChangeEnvironmentsEventArgs[number], + { kind: EnvironmentChangeKind.add, environment: valid }, + ]), + ).not.to.throw(); + + expect(api.getEnvs().map((env) => env.executable.filename)).to.deep.equal([ + valid.execInfo.run.executable, + ]); + }); + + test('does not publish an active-environment event for a no-Python environment', async () => { + const api = await createEnvExtApi(disposables); + const noPython = buildCondaEnvironment('empty', 'no-python', '/conda/envs/empty', '/conda/bin/conda'); + const events: PythonEnvCollectionChangedEvent[] = []; + disposables.push(api.onChanged((event) => events.push(event))); + + expect(() => activeEnvironmentChanges.fire({ uri: undefined, old: undefined, new: noPython })).not.to.throw(); + + expect(events).to.be.empty; + expect(api.getEnvs()).to.be.empty; + }); + + test('does not publish a partial active-environment change when one side is invalid', async () => { + const api = await createEnvExtApi(disposables); + const noPython = buildCondaEnvironment('empty', 'no-python', '/conda/envs/empty', '/conda/bin/conda'); + const valid = buildCondaEnvironment('valid', '3.12.1', '/conda/envs/valid'); + const events: PythonEnvCollectionChangedEvent[] = []; + disposables.push(api.onChanged((event) => events.push(event))); + + activeEnvironmentChanges.fire({ uri: Uri.file('/workspace'), old: noPython, new: valid }); + + expect(events).to.be.empty; + }); + + test('preserves valid active-environment changes', async () => { + const api = await createEnvExtApi(disposables); + const oldEnvironment = buildCondaEnvironment('old', '3.11.9', '/conda/envs/old'); + const newEnvironment = buildCondaEnvironment('new', '3.12.1', '/conda/envs/new'); + const events: PythonEnvCollectionChangedEvent[] = []; + disposables.push(api.onChanged((event) => events.push(event))); + + activeEnvironmentChanges.fire({ + uri: Uri.file('/workspace'), + old: oldEnvironment, + new: newEnvironment, + }); + + expect(events).to.have.length(1); + expect(events[0].type).to.equal(FileChangeType.Changed); + expect(events[0].old?.executable.filename).to.equal(oldEnvironment.execInfo.run.executable); + expect(events[0].new?.executable.filename).to.equal(newEnvironment.execInfo.run.executable); + expect(events[0].searchLocation?.fsPath).to.equal(Uri.file('/workspace').fsPath); + }); + + test('preserves valid active-environment set and clear events', async () => { + const api = await createEnvExtApi(disposables); + const environment = buildCondaEnvironment('valid', '3.12.1', '/conda/envs/valid'); + const events: PythonEnvCollectionChangedEvent[] = []; + disposables.push(api.onChanged((event) => events.push(event))); + + activeEnvironmentChanges.fire({ uri: Uri.file('/workspace'), old: undefined, new: environment }); + activeEnvironmentChanges.fire({ uri: Uri.file('/workspace'), old: environment, new: undefined }); + + expect(events).to.have.length(2); + expect(events[0].old).to.equal(undefined); + expect(events[0].new?.executable.filename).to.equal(environment.execInfo.run.executable); + expect(events[1].old?.executable.filename).to.equal(environment.execInfo.run.executable); + expect(events[1].new).to.equal(undefined); + }); + + test('removes Conda environments using their executable identity', async () => { + const api = await createEnvExtApi(disposables); + const environment = buildCondaEnvironment('first', '3.12.1', '/conda/envs/first'); + const events: PythonEnvCollectionChangedEvent[] = []; + disposables.push(api.onChanged((event) => events.push(event))); + environmentChanges.fire([{ kind: EnvironmentChangeKind.add, environment }]); + + environmentChanges.fire([{ kind: EnvironmentChangeKind.remove, environment }]); + + expect(api.getEnvs()).to.be.empty; + expect(events.map((event) => event.type)).to.deep.equal([ + FileChangeType.Created, + FileChangeType.Deleted, + ]); + expect(events[1].old?.executable.filename).to.equal(environment.execInfo.run.executable); + }); + + test('restores all valid Conda environments after a refresh batch containing a no-Python item', async () => { + const api = await createEnvExtApi(disposables); + const oldFirst = buildCondaEnvironment('first', '3.12.0', '/conda/envs/first'); + const oldSecond = buildCondaEnvironment('second', '3.11.8', '/conda/envs/second'); + environmentChanges.fire([ + { kind: EnvironmentChangeKind.add, environment: oldFirst }, + { kind: EnvironmentChangeKind.add, environment: oldSecond }, + ]); + + const noPython = buildCondaEnvironment('empty', 'no-python', '/conda/envs/empty', '/conda/bin/conda'); + const newFirst = buildCondaEnvironment('first', '3.12.1', '/conda/envs/first'); + const newSecond = buildCondaEnvironment('second', '3.11.9', '/conda/envs/second'); + environmentChanges.fire([ + { kind: EnvironmentChangeKind.remove, environment: oldFirst }, + { kind: EnvironmentChangeKind.remove, environment: oldSecond }, + { kind: EnvironmentChangeKind.add, environment: noPython }, + { kind: EnvironmentChangeKind.add, environment: newFirst }, + { kind: EnvironmentChangeKind.add, environment: newSecond }, + ]); + + expect(api.getEnvs().map((env) => env.version.sysVersion)).to.deep.equal(['3.12.1', '3.11.9']); + }); + + test('completes a requested refresh and retains valid environments after a no-Python item', async () => { + const noPython = buildCondaEnvironment('empty', 'no-python', '/conda/envs/empty', '/conda/bin/conda'); + const valid = buildCondaEnvironment('valid', '3.12.1', '/conda/envs/valid'); + (envExtApi.refreshEnvironments as sinon.SinonStub).callsFake(async () => { + environmentChanges.fire([ + { kind: EnvironmentChangeKind.add, environment: noPython }, + { kind: EnvironmentChangeKind.add, environment: valid }, + ]); + }); + const api = await createEnvExtApi(disposables); + + await api.triggerRefresh(); + + expect(api.getEnvs().map((env) => env.executable.filename)).to.deep.equal([ + valid.execInfo.run.executable, + ]); + }); +}); From c7c86db49fdaecf5e17f827906496c00619da9c6 Mon Sep 17 00:00:00 2001 From: Stella Huang Date: Fri, 14 Aug 2026 10:39:12 -0700 Subject: [PATCH 2/2] Format EnvExt adapter changes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2f537bc5-b389-4ee2-aac2-e2b83be47f5c --- src/client/envExt/envExtApi.ts | 4 +++- src/test/envExt/envExtApi.unit.test.ts | 26 ++++++++------------------ 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/client/envExt/envExtApi.ts b/src/client/envExt/envExtApi.ts index 43aab94a6fe4..41df625bd245 100644 --- a/src/client/envExt/envExtApi.ts +++ b/src/client/envExt/envExtApi.ts @@ -350,7 +350,9 @@ class EnvExtApis implements IDiscoveryAPI, Disposable { } } catch (error) { traceError( - `Failed to process environment change for "${item?.environment?.displayName ?? 'unknown environment'}" from the Python Environments extension`, + `Failed to process environment change for "${ + item?.environment?.displayName ?? 'unknown environment' + }" from the Python Environments extension`, error, ); } diff --git a/src/test/envExt/envExtApi.unit.test.ts b/src/test/envExt/envExtApi.unit.test.ts index 72301e133711..13878a219986 100644 --- a/src/test/envExt/envExtApi.unit.test.ts +++ b/src/test/envExt/envExtApi.unit.test.ts @@ -44,12 +44,12 @@ suite('Python Environments extension discovery adapter', () => { environmentChanges = new EventEmitter(); activeEnvironmentChanges = new EventEmitter(); disposables = []; - envExtApi = { + envExtApi = ({ onDidChangeEnvironments: environmentChanges.event, onDidChangeEnvironment: activeEnvironmentChanges.event, refreshEnvironments: sinon.stub().resolves(), resolveEnvironment: sinon.stub().resolves(undefined), - } as unknown as PythonEnvironmentApi; + } as unknown) as PythonEnvironmentApi; sinon.stub(apiInternal, 'getEnvExtApi').resolves(envExtApi); }); @@ -80,10 +80,7 @@ suite('Python Environments extension discovery adapter', () => { first.execInfo.run.executable, second.execInfo.run.executable, ]); - expect(events.map((event) => event.type)).to.deep.equal([ - FileChangeType.Created, - FileChangeType.Created, - ]); + expect(events.map((event) => event.type)).to.deep.equal([FileChangeType.Created, FileChangeType.Created]); }); test('isolates an unexpected invalid version from later environments in the batch', async () => { @@ -112,17 +109,15 @@ suite('Python Environments extension discovery adapter', () => { expect(() => environmentChanges.fire([ - { + ({ kind: EnvironmentChangeKind.remove, environment: undefined, - } as unknown as DidChangeEnvironmentsEventArgs[number], + } as unknown) as DidChangeEnvironmentsEventArgs[number], { kind: EnvironmentChangeKind.add, environment: valid }, ]), ).not.to.throw(); - expect(api.getEnvs().map((env) => env.executable.filename)).to.deep.equal([ - valid.execInfo.run.executable, - ]); + expect(api.getEnvs().map((env) => env.executable.filename)).to.deep.equal([valid.execInfo.run.executable]); }); test('does not publish an active-environment event for a no-Python environment', async () => { @@ -195,10 +190,7 @@ suite('Python Environments extension discovery adapter', () => { environmentChanges.fire([{ kind: EnvironmentChangeKind.remove, environment }]); expect(api.getEnvs()).to.be.empty; - expect(events.map((event) => event.type)).to.deep.equal([ - FileChangeType.Created, - FileChangeType.Deleted, - ]); + expect(events.map((event) => event.type)).to.deep.equal([FileChangeType.Created, FileChangeType.Deleted]); expect(events[1].old?.executable.filename).to.equal(environment.execInfo.run.executable); }); @@ -238,8 +230,6 @@ suite('Python Environments extension discovery adapter', () => { await api.triggerRefresh(); - expect(api.getEnvs().map((env) => env.executable.filename)).to.deep.equal([ - valid.execInfo.run.executable, - ]); + expect(api.getEnvs().map((env) => env.executable.filename)).to.deep.equal([valid.execInfo.run.executable]); }); });