diff --git a/src/extension/debugger/adapter/factory.ts b/src/extension/debugger/adapter/factory.ts index 2a8090ed..0e1a4b82 100644 --- a/src/extension/debugger/adapter/factory.ts +++ b/src/extension/debugger/adapter/factory.ts @@ -22,7 +22,6 @@ import { sendTelemetryEvent } from '../../telemetry'; import { Commands, EXTENSION_ROOT_DIR } from '../../common/constants'; import { Common, DebugConfigStrings, Interpreters } from '../../common/utils/localize'; import { IPersistentStateFactory } from '../../common/types'; -import { fileToCommandArgumentForPythonExt } from '../../common/stringUtils'; import { PythonEnvironment } from '../../envExtApi'; import { resolveEnvironment, getInterpreterDetails, runPythonExtensionCommand } from '../../common/python'; @@ -79,13 +78,12 @@ export class DebugAdapterDescriptorFactory implements IDebugAdapterDescriptorFac } let executable = command.shift() ?? 'python'; - - // Always ensure interpreter/command is quoted if necessary. Previously this was - // only done in the debugAdapterPath branch which meant that in the common case - // (using the built‑in adapter path) an interpreter path containing spaces would - // be passed unquoted, resulting in a fork/spawn failure on Windows. See bug - // report for details. - executable = fileToCommandArgumentForPythonExt(executable); + // DO NOT apply shell-style quoting here. + // The 'executable' path is passed to 'DebugAdapterExecutable', which internally + // uses 'child_process.spawn' in a non-shell environment. + // Manual quoting will cause the OS (especially Windows) to treat the quotes + // as part of the filename, leading to ENOENT. + // See regression reported in #1013 and analysis of #964. // "logToFile" is not handled directly by the adapter - instead, we need to pass // the corresponding CLI switch when spawning it. diff --git a/src/test/unittest/adapter/factory.unit.test.ts b/src/test/unittest/adapter/factory.unit.test.ts index 9c3a9e91..b68f7c25 100644 --- a/src/test/unittest/adapter/factory.unit.test.ts +++ b/src/test/unittest/adapter/factory.unit.test.ts @@ -304,28 +304,24 @@ suite('Debugging - Adapter Factory', () => { assert.deepStrictEqual(descriptor, debugExecutable); }); - test('Add quotes to interpreter path with spaces (default adapter path)', async () => { + test('Should not add quotes to interpreter path with spaces (default adapter path)', async () => { const session = createSession({}); const interpreterPathSpaces = 'path/to/python interpreter with spaces'; - const interpreterPathSpacesQuoted = `"${interpreterPathSpaces}"`; - const debugExecutable = new DebugAdapterExecutable(interpreterPathSpacesQuoted, [debugAdapterPath]); - + const debugExecutable = new DebugAdapterExecutable(interpreterPathSpaces, [debugAdapterPath]); getInterpreterDetailsStub.resolves({ path: [interpreterPathSpaces] }); const interpreterSpacePath: PythonEnvironment = createInterpreter(interpreterPathSpaces, '3.7.4-test'); // Add architecture for completeness. (interpreterSpacePath as any).architecture = Architecture.Unknown; resolveEnvironmentStub.withArgs(interpreterPathSpaces).resolves(interpreterSpacePath); const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable); - assert.deepStrictEqual(descriptor, debugExecutable); }); - test('Add quotes to interpreter path with spaces when debugAdapterPath is specified', async () => { + test('Should not add quotes to interpreter path with spaces when debugAdapterPath is specified', async () => { const customAdapterPath = 'custom/debug/adapter/customAdapterPath'; const session = createSession({ debugAdapterPath: customAdapterPath }); const interpreterPathSpaces = 'path/to/python interpreter with spaces'; - const interpreterPathSpacesQuoted = `"${interpreterPathSpaces}"`; - const debugExecutable = new DebugAdapterExecutable(interpreterPathSpacesQuoted, [customAdapterPath]); + const debugExecutable = new DebugAdapterExecutable(interpreterPathSpaces, [customAdapterPath]); getInterpreterDetailsStub.resolves({ path: [interpreterPathSpaces] }); const interpreterSpacePath: PythonEnvironment = createInterpreter(interpreterPathSpaces, '3.7.4-test');