Skip to content

Commit 8dc7343

Browse files
activation: respect integration setting precedence (#1897)
## Summary - resolve `python.useEnvironmentsExtension` using workspace-folder, workspace, then user precedence - ignore contributed defaults when deciding whether explicit opt-out applies Fixes #1895 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7b69d86 commit 8dc7343

3 files changed

Lines changed: 124 additions & 25 deletions

File tree

‎src/extension.ts‎

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import {
3636
onDidChangeTerminalShellIntegration,
3737
withProgress,
3838
} from './common/window.apis';
39-
import { getConfiguration, getWorkspaceFolders } from './common/workspace.apis';
4039
import { createManagerReady } from './features/common/managerReady';
4140
import { AutoFindProjects } from './features/creators/autoFindProjects';
4241
import { ExistingProjects } from './features/creators/existingProjects';
@@ -80,7 +79,10 @@ import { PythonProjectManagerImpl } from './features/projectManager';
8079
import { reportIssue } from './features/reportIssue';
8180
import { getPythonApi, setPythonApi } from './extensionApi';
8281
import { registerCompletionProvider } from './features/settings/settingCompletions';
83-
import { migrateGlobalDefaultEnvManagerSetting } from './features/settings/settingHelpers';
82+
import {
83+
isEnvironmentsExtensionEnabled,
84+
migrateGlobalDefaultEnvManagerSetting,
85+
} from './features/settings/settingHelpers';
8486
import { setActivateMenuButtonContext } from './features/terminal/activateMenuButton';
8587
import { normalizeShellPath } from './features/terminal/shells/common/shellUtils';
8688
import { createShellEnvProviders, createShellStartupProviders } from './features/terminal/shells/providers';
@@ -124,29 +126,7 @@ import { registerPyenvFeatures } from './managers/pyenv/main';
124126
export async function activate(context: ExtensionContext): Promise<PythonEnvironmentApi | undefined> {
125127
// Only skip activation if user explicitly set useEnvironmentsExtension to false.
126128
// When disabled, the main Python extension handles environments instead (legacy mode).
127-
const config = getConfiguration('python');
128-
const inspection = config.inspect<boolean>('useEnvironmentsExtension');
129-
130-
// Check global and workspace-level explicit disables
131-
let explicitlyDisabled = inspection?.globalValue === false || inspection?.workspaceValue === false;
132-
133-
// Also check folder-scoped settings in multi-root workspaces
134-
// (inspect() on an unscoped config won't populate workspaceFolderValue reliably)
135-
if (!explicitlyDisabled) {
136-
const workspaceFolders = getWorkspaceFolders();
137-
if (workspaceFolders) {
138-
for (const folder of workspaceFolders) {
139-
const folderConfig = getConfiguration('python', folder.uri);
140-
const folderInspection = folderConfig.inspect<boolean>('useEnvironmentsExtension');
141-
if (folderInspection?.workspaceFolderValue === false) {
142-
explicitlyDisabled = true;
143-
break;
144-
}
145-
}
146-
}
147-
}
148-
149-
const useEnvironmentsExtension = !explicitlyDisabled;
129+
const useEnvironmentsExtension = isEnvironmentsExtensionEnabled();
150130
traceInfo(`Experiment Status: useEnvironmentsExtension setting set to ${useEnvironmentsExtension}`);
151131
if (!useEnvironmentsExtension) {
152132
traceWarn(

‎src/features/settings/settingHelpers.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,22 @@ export function getSettingWorkspaceScope<T>(section: string, key: string, scope?
989989
return undefined;
990990
}
991991

992+
/**
993+
* Determines whether the Python Environments extension should activate.
994+
*
995+
* Explicit values are resolved independently for each workspace folder using VS Code's
996+
* workspace-folder > workspace > user precedence. The extension activates when at least
997+
* one folder enables integration, or when no explicit value applies.
998+
*/
999+
export function isEnvironmentsExtensionEnabled(): boolean {
1000+
const workspaceFolders = workspaceApis.getWorkspaceFolders();
1001+
const scopes = workspaceFolders?.length ? workspaceFolders.map((folder) => folder.uri) : [undefined];
1002+
1003+
return scopes.some(
1004+
(scope) => getSettingWorkspaceScope<boolean>('python', 'useEnvironmentsExtension', scope) !== false,
1005+
);
1006+
}
1007+
9921008
/**
9931009
* Gets user-configured setting for user-scoped settings.
9941010
* Only checks globalValue (ignores defaultValue).
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import * as assert from 'assert';
5+
import * as sinon from 'sinon';
6+
import { Uri, WorkspaceConfiguration, WorkspaceFolder } from 'vscode';
7+
import * as workspaceApis from '../../../common/workspace.apis';
8+
import { isEnvironmentsExtensionEnabled } from '../../../features/settings/settingHelpers';
9+
10+
type SettingInspection = {
11+
defaultValue?: boolean;
12+
globalValue?: boolean;
13+
workspaceValue?: boolean;
14+
workspaceFolderValue?: boolean;
15+
};
16+
17+
function createConfiguration(inspection: SettingInspection | undefined): WorkspaceConfiguration {
18+
return {
19+
inspect: sinon.stub().withArgs('useEnvironmentsExtension').returns(inspection),
20+
} as unknown as WorkspaceConfiguration;
21+
}
22+
23+
function createWorkspaceFolder(name: string, index: number): WorkspaceFolder {
24+
return {
25+
uri: Uri.joinPath(Uri.file(process.cwd()), name),
26+
name,
27+
index,
28+
};
29+
}
30+
31+
suite('isEnvironmentsExtensionEnabled', () => {
32+
teardown(() => {
33+
sinon.restore();
34+
});
35+
36+
test('ignores a contributed default when no value is explicitly configured', () => {
37+
sinon.stub(workspaceApis, 'getWorkspaceFolders').returns(undefined);
38+
sinon
39+
.stub(workspaceApis, 'getConfiguration')
40+
.withArgs('python', undefined)
41+
.returns(createConfiguration({ defaultValue: false }));
42+
43+
assert.strictEqual(isEnvironmentsExtensionEnabled(), true);
44+
});
45+
46+
test('uses an explicit user false value when no workspace is open', () => {
47+
sinon.stub(workspaceApis, 'getWorkspaceFolders').returns(undefined);
48+
sinon
49+
.stub(workspaceApis, 'getConfiguration')
50+
.withArgs('python', undefined)
51+
.returns(createConfiguration({ globalValue: false }));
52+
53+
assert.strictEqual(isEnvironmentsExtensionEnabled(), false);
54+
});
55+
56+
test('uses a workspace true value over a user false value', () => {
57+
const workspaceFolder = createWorkspaceFolder('workspace', 0);
58+
sinon.stub(workspaceApis, 'getWorkspaceFolders').returns([workspaceFolder]);
59+
sinon
60+
.stub(workspaceApis, 'getConfiguration')
61+
.withArgs('python', workspaceFolder.uri)
62+
.returns(createConfiguration({ globalValue: false, workspaceValue: true }));
63+
64+
assert.strictEqual(isEnvironmentsExtensionEnabled(), true);
65+
});
66+
67+
test('uses a workspace false value over a user true value', () => {
68+
const workspaceFolder = createWorkspaceFolder('workspace', 0);
69+
sinon.stub(workspaceApis, 'getWorkspaceFolders').returns([workspaceFolder]);
70+
sinon
71+
.stub(workspaceApis, 'getConfiguration')
72+
.withArgs('python', workspaceFolder.uri)
73+
.returns(createConfiguration({ globalValue: true, workspaceValue: false }));
74+
75+
assert.strictEqual(isEnvironmentsExtensionEnabled(), false);
76+
});
77+
78+
test('uses a workspace-folder false value over a workspace true value', () => {
79+
const workspaceFolder = createWorkspaceFolder('workspace', 0);
80+
sinon.stub(workspaceApis, 'getWorkspaceFolders').returns([workspaceFolder]);
81+
sinon
82+
.stub(workspaceApis, 'getConfiguration')
83+
.withArgs('python', workspaceFolder.uri)
84+
.returns(createConfiguration({ workspaceValue: true, workspaceFolderValue: false }));
85+
86+
assert.strictEqual(isEnvironmentsExtensionEnabled(), false);
87+
});
88+
89+
test('activates when any workspace folder resolves to true', () => {
90+
const disabledFolder = createWorkspaceFolder('disabled', 0);
91+
const enabledFolder = createWorkspaceFolder('enabled', 1);
92+
sinon.stub(workspaceApis, 'getWorkspaceFolders').returns([disabledFolder, enabledFolder]);
93+
const getConfiguration = sinon.stub(workspaceApis, 'getConfiguration');
94+
getConfiguration
95+
.withArgs('python', disabledFolder.uri)
96+
.returns(createConfiguration({ workspaceValue: false }));
97+
getConfiguration
98+
.withArgs('python', enabledFolder.uri)
99+
.returns(createConfiguration({ workspaceValue: false, workspaceFolderValue: true }));
100+
101+
assert.strictEqual(isEnvironmentsExtensionEnabled(), true);
102+
});
103+
});

0 commit comments

Comments
 (0)