Skip to content

Commit 2d58111

Browse files
Gate debug per-file interpreter resolution behind useEnvExtension()
The debug resolver now resolves \/\ to a program URI and looks up the interpreter for that file. To avoid changing behavior for users who are not using the environments extension (e.g. multi-root debugging of a file that lives in a different folder than the launch config), only take the program-scoped path when useEnvExtension() is true; otherwise fall back to the historical workspace-folder resolution. Environments-extension users still get per-file (inline-script) resolution. Stub useEnvExtension() in the existing program-scoped resolver tests and add a test covering the gated fallback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 2d8bc7e commit 2d58111

2 files changed

Lines changed: 38 additions & 0 deletions

File tree

  • src
    • client/debugger/extension/configuration/resolvers
    • test/debugger/extension/configuration/resolvers

src/client/debugger/extension/configuration/resolvers/base.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
getWorkspaceFolder as getVSCodeWorkspaceFolder,
1414
getWorkspaceFolders,
1515
} from '../../../../common/vscodeApis/workspaceApis';
16+
import { useEnvExtension } from '../../../../envExt/api.internal';
1617
import { IInterpreterService } from '../../../../interpreter/contracts';
1718
import { AttachRequestArguments, DebugOptions, LaunchRequestArguments, PathMapping } from '../../../types';
1819
import { PythonPathSource } from '../../types';
@@ -171,6 +172,14 @@ export abstract class BaseConfigurationResolver<T extends DebugConfiguration>
171172
workspaceFolder: Uri | undefined,
172173
debugConfiguration: LaunchRequestArguments,
173174
) {
175+
// Program-scoped (per-file) interpreter resolution only applies when the environments
176+
// extension owns interpreter resolution. Without it, preserve the historical behavior of
177+
// resolving the interpreter from the launch workspace folder, so users who are not using
178+
// the environments extension (e.g. multi-root debugging of another folder's file) see no
179+
// change.
180+
if (!useEnvExtension()) {
181+
return this.interpreterService.getActiveInterpreter(workspaceFolder);
182+
}
174183
let configuredProgram = debugConfiguration.program === '${file}' ? getProgram() : debugConfiguration.program;
175184
let programWorkspaceFolder = workspaceFolder;
176185
if (configuredProgram) {

src/test/debugger/extension/configuration/resolvers/base.unit.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { IInterpreterService } from '../../../../../client/interpreter/contracts
1818
import { PythonEnvironment } from '../../../../../client/pythonEnvironments/info';
1919
import * as workspaceApis from '../../../../../client/common/vscodeApis/workspaceApis';
2020
import * as helper from '../../../../../client/debugger/extension/configuration/resolvers/helper';
21+
import * as extapi from '../../../../../client/envExt/api.internal';
2122

2223
suite('Debugging - Config Resolver', () => {
2324
class BaseResolver extends BaseConfigurationResolver<AttachRequestArguments | LaunchRequestArguments> {
@@ -70,6 +71,7 @@ suite('Debugging - Config Resolver', () => {
7071
let getWorkspaceFoldersStub: sinon.SinonStub;
7172
let getWorkspaceFolderStub: sinon.SinonStub;
7273
let getProgramStub: sinon.SinonStub;
74+
let useEnvExtensionStub: sinon.SinonStub;
7375

7476
setup(() => {
7577
configurationService = mock(ConfigurationService);
@@ -78,6 +80,7 @@ suite('Debugging - Config Resolver', () => {
7880
getWorkspaceFoldersStub = sinon.stub(workspaceApis, 'getWorkspaceFolders');
7981
getWorkspaceFolderStub = sinon.stub(workspaceApis, 'getWorkspaceFolder');
8082
getProgramStub = sinon.stub(helper, 'getProgram');
83+
useEnvExtensionStub = sinon.stub(extapi, 'useEnvExtension').returns(false);
8184
});
8285
teardown(() => {
8386
sinon.restore();
@@ -290,6 +293,7 @@ suite('Debugging - Config Resolver', () => {
290293
});
291294

292295
test('uses one exact program lookup for command-valued pythonPath and python', async () => {
296+
useEnvExtensionStub.returns(true);
293297
const workspaceUri = Uri.file(path.resolve('workspace'));
294298
const programPath = path.join(workspaceUri.fsPath, 'script.py');
295299
const workspacePython = path.resolve('workspace-env', 'python');
@@ -318,6 +322,7 @@ suite('Debugging - Config Resolver', () => {
318322
});
319323

320324
test('falls back to the workspace interpreter when exact program lookup has no environment', async () => {
325+
useEnvExtensionStub.returns(true);
321326
const workspaceUri = Uri.file(path.resolve('workspace'));
322327
const programPath = path.join(workspaceUri.fsPath, 'script.py');
323328
const workspacePython = path.resolve('workspace-env', 'python');
@@ -336,6 +341,7 @@ suite('Debugging - Config Resolver', () => {
336341
});
337342

338343
test('resolves a named workspace-folder program before exact interpreter lookup', async () => {
344+
useEnvExtensionStub.returns(true);
339345
const launchWorkspaceUri = Uri.file(path.resolve('workspace-a'));
340346
const programWorkspaceUri = Uri.file(path.resolve('workspace-b'));
341347
const programPath = path.join(programWorkspaceUri.fsPath, 'script.py');
@@ -360,6 +366,7 @@ suite('Debugging - Config Resolver', () => {
360366
});
361367

362368
test('does not mark a program interpreter that matches the workspace interpreter', async () => {
369+
useEnvExtensionStub.returns(true);
363370
const workspaceUri = Uri.file(path.resolve('workspace'));
364371
const programPath = path.join(workspaceUri.fsPath, 'script.py');
365372
const pythonPath = path.resolve('env', 'python');
@@ -374,6 +381,28 @@ suite('Debugging - Config Resolver', () => {
374381
expect(config).to.not.have.property('__pythonIsProgramInterpreter');
375382
});
376383

384+
test('does not use the program interpreter when the environments extension is not in use', async () => {
385+
useEnvExtensionStub.returns(false);
386+
const workspaceUri = Uri.file(path.resolve('workspace-a'));
387+
const programWorkspaceUri = Uri.file(path.resolve('workspace-b'));
388+
const programPath = path.join(programWorkspaceUri.fsPath, 'script.py');
389+
const workspacePython = path.resolve('workspace-env', 'python');
390+
const config = { program: programPath };
391+
when(interpreterService.getActiveInterpreter(anything(), anything())).thenResolve({
392+
path: path.resolve('program-env', 'python'),
393+
} as PythonEnvironment);
394+
when(interpreterService.getActiveInterpreter(anything())).thenResolve({
395+
path: workspacePython,
396+
} as PythonEnvironment);
397+
398+
await resolver.resolveAndUpdatePythonPath(workspaceUri, config as LaunchRequestArguments);
399+
400+
expect(config).to.have.property('python', workspacePython);
401+
expect(config).to.not.have.property('__pythonIsProgramInterpreter');
402+
verify(interpreterService.getActiveInterpreter(anything(), anything())).never();
403+
verify(interpreterService.getActiveInterpreter(anything())).once();
404+
});
405+
377406
const localHostTestMatrix: Record<string, boolean> = {
378407
localhost: true,
379408
'127.0.0.1': true,

0 commit comments

Comments
 (0)