diff --git a/news/2 Fixes/14814.md b/news/2 Fixes/14814.md new file mode 100644 index 000000000000..4387ee8de263 --- /dev/null +++ b/news/2 Fixes/14814.md @@ -0,0 +1 @@ +Do not show "You need to select a Python interpreter before you start debugging" when "python" in debug configuration is invalid. diff --git a/src/client/debugger/extension/configuration/resolvers/launch.ts b/src/client/debugger/extension/configuration/resolvers/launch.ts index 86aa6e0abd44..a24eadb62866 100644 --- a/src/client/debugger/extension/configuration/resolvers/launch.ts +++ b/src/client/debugger/extension/configuration/resolvers/launch.ts @@ -12,6 +12,7 @@ import { IPlatformService } from '../../../../common/platform/types'; import { IConfigurationService } from '../../../../common/types'; import { DebuggerTypeName } from '../../../constants'; import { DebugOptions, LaunchRequestArguments } from '../../../types'; +import { PythonPathSource } from '../../types'; import { BaseConfigurationResolver } from './base'; import { IDebugEnvironmentVariablesService } from './helper'; @@ -183,18 +184,17 @@ export class LaunchConfigurationResolver extends BaseConfigurationResolver { const diagnosticService = this.invalidPythonPathInDebuggerService; - return ( - diagnosticService.validatePythonPath(debugConfiguration.python, this.pythonPathSource, folder?.uri) && - diagnosticService.validatePythonPath( - debugConfiguration.debugAdapterPython, - this.pythonPathSource, - folder?.uri - ) && - diagnosticService.validatePythonPath( - debugConfiguration.debugLauncherPython, - this.pythonPathSource, - folder?.uri - ) - ); + for (const executable of [ + debugConfiguration.python, + debugConfiguration.debugAdapterPython, + debugConfiguration.debugLauncherPython + ]) { + const source = + executable === debugConfiguration.pythonPath ? this.pythonPathSource : PythonPathSource.launchJson; + if (!(await diagnosticService.validatePythonPath(executable, source, folder?.uri))) { + return false; + } + } + return true; } } diff --git a/src/test/debugger/extension/configuration/resolvers/launch.unit.test.ts b/src/test/debugger/extension/configuration/resolvers/launch.unit.test.ts index c8d292afe2da..0dc098964496 100644 --- a/src/test/debugger/extension/configuration/resolvers/launch.unit.test.ts +++ b/src/test/debugger/extension/configuration/resolvers/launch.unit.test.ts @@ -18,6 +18,7 @@ import { OSType } from '../../../../../client/common/utils/platform'; import { DebuggerTypeName } from '../../../../../client/debugger/constants'; import { IDebugEnvironmentVariablesService } from '../../../../../client/debugger/extension/configuration/resolvers/helper'; import { LaunchConfigurationResolver } from '../../../../../client/debugger/extension/configuration/resolvers/launch'; +import { PythonPathSource } from '../../../../../client/debugger/extension/types'; import { DebugOptions, LaunchRequestArguments } from '../../../../../client/debugger/types'; import { IInterpreterHelper } from '../../../../../client/interpreter/contracts'; import { getOSType } from '../../../../common'; @@ -928,6 +929,8 @@ getInfoPerOS().forEach(([osName, osType, path]) => { test('Test validation of Python Path when launching debugger (with invalid "python")', async () => { const pythonPath = `PythonPath_${new Date().toString()}`; + const debugLauncherPython = `DebugLauncherPythonPath_${new Date().toString()}`; + const debugAdapterPython = `DebugAdapterPythonPath_${new Date().toString()}`; const workspaceFolder = createMoqWorkspaceFolder(__dirname); const pythonFile = 'xyz.py'; setupIoc(pythonPath); @@ -936,23 +939,49 @@ getInfoPerOS().forEach(([osName, osType, path]) => { diagnosticsService.reset(); diagnosticsService .setup((h) => - h.validatePythonPath(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny(), TypeMoq.It.isAny()) + h.validatePythonPath( + TypeMoq.It.isValue(pythonPath), + PythonPathSource.launchJson, + TypeMoq.It.isAny() + ) ) - .returns(() => Promise.resolve(false)) - .verifiable(TypeMoq.Times.atLeastOnce()); + // Invalid + .returns(() => Promise.resolve(false)); + diagnosticsService + .setup((h) => + h.validatePythonPath( + TypeMoq.It.isValue(debugLauncherPython), + PythonPathSource.launchJson, + TypeMoq.It.isAny() + ) + ) + .returns(() => Promise.resolve(true)); + diagnosticsService + .setup((h) => + h.validatePythonPath( + TypeMoq.It.isValue(debugAdapterPython), + PythonPathSource.launchJson, + TypeMoq.It.isAny() + ) + ) + .returns(() => Promise.resolve(true)); const debugConfig = await resolveDebugConfiguration(workspaceFolder, { ...launch, redirectOutput: false, - python: pythonPath + python: pythonPath, + debugLauncherPython, + debugAdapterPython }); diagnosticsService.verifyAll(); expect(debugConfig).to.be.equal(undefined, 'Not undefined'); }); - test('Test validation of Python Path when launching debugger (with valid "python")', async () => { + test('Test validation of Python Path when launching debugger (with invalid "debugLauncherPython")', async () => { const pythonPath = `PythonPath_${new Date().toString()}`; + const debugLauncherPython = `DebugLauncherPythonPath_${new Date().toString()}`; + const debugAdapterPython = `DebugAdapterPythonPath_${new Date().toString()}`; const workspaceFolder = createMoqWorkspaceFolder(__dirname); const pythonFile = 'xyz.py'; setupIoc(pythonPath); @@ -961,7 +990,111 @@ getInfoPerOS().forEach(([osName, osType, path]) => { diagnosticsService.reset(); diagnosticsService .setup((h) => - h.validatePythonPath(TypeMoq.It.isValue(pythonPath), TypeMoq.It.isAny(), TypeMoq.It.isAny()) + h.validatePythonPath( + TypeMoq.It.isValue(pythonPath), + PythonPathSource.launchJson, + TypeMoq.It.isAny() + ) + ) + .returns(() => Promise.resolve(true)); + diagnosticsService + .setup((h) => + h.validatePythonPath( + TypeMoq.It.isValue(debugLauncherPython), + PythonPathSource.launchJson, + TypeMoq.It.isAny() + ) + ) + // Invalid + .returns(() => Promise.resolve(false)); + diagnosticsService + .setup((h) => + h.validatePythonPath( + TypeMoq.It.isValue(debugAdapterPython), + PythonPathSource.launchJson, + TypeMoq.It.isAny() + ) + ) + .returns(() => Promise.resolve(true)); + + const debugConfig = await resolveDebugConfiguration(workspaceFolder, { + ...launch, + redirectOutput: false, + python: pythonPath, + debugLauncherPython, + debugAdapterPython + }); + + diagnosticsService.verifyAll(); + expect(debugConfig).to.be.equal(undefined, 'Not undefined'); + }); + + test('Test validation of Python Path when launching debugger (with invalid "debugAdapterPython")', async () => { + const pythonPath = `PythonPath_${new Date().toString()}`; + const debugLauncherPython = `DebugLauncherPythonPath_${new Date().toString()}`; + const debugAdapterPython = `DebugAdapterPythonPath_${new Date().toString()}`; + const workspaceFolder = createMoqWorkspaceFolder(__dirname); + const pythonFile = 'xyz.py'; + setupIoc(pythonPath); + setupActiveEditor(pythonFile, PYTHON_LANGUAGE); + + diagnosticsService.reset(); + diagnosticsService + .setup((h) => + h.validatePythonPath( + TypeMoq.It.isValue(pythonPath), + PythonPathSource.launchJson, + TypeMoq.It.isAny() + ) + ) + .returns(() => Promise.resolve(true)); + diagnosticsService + .setup((h) => + h.validatePythonPath( + TypeMoq.It.isValue(debugLauncherPython), + PythonPathSource.launchJson, + TypeMoq.It.isAny() + ) + ) + .returns(() => Promise.resolve(true)); + diagnosticsService + .setup((h) => + h.validatePythonPath( + TypeMoq.It.isValue(debugAdapterPython), + PythonPathSource.launchJson, + TypeMoq.It.isAny() + ) + ) + // Invalid + .returns(() => Promise.resolve(false)); + + const debugConfig = await resolveDebugConfiguration(workspaceFolder, { + ...launch, + redirectOutput: false, + python: pythonPath, + debugLauncherPython, + debugAdapterPython + }); + + diagnosticsService.verifyAll(); + expect(debugConfig).to.be.equal(undefined, 'Not undefined'); + }); + + test('Test validation of Python Path when launching debugger (with valid "python/debugAdapterPython/debugLauncherPython")', async () => { + const pythonPath = `PythonPath_${new Date().toString()}`; + const workspaceFolder = createMoqWorkspaceFolder(__dirname); + const pythonFile = 'xyz.py'; + setupIoc(pythonPath); + setupActiveEditor(pythonFile, PYTHON_LANGUAGE); + + diagnosticsService.reset(); + diagnosticsService + .setup((h) => + h.validatePythonPath( + TypeMoq.It.isValue(pythonPath), + PythonPathSource.launchJson, + TypeMoq.It.isAny() + ) ) .returns(() => Promise.resolve(true)) .verifiable(TypeMoq.Times.atLeastOnce());