Skip to content

Commit a61b575

Browse files
eleanorjboydStellaHuang95
authored andcommitted
Prevent writing settings to global scope for environment and package managers (microsoft#1489)
prevents microsoft#1468. Now we no longer save env manager or package manage default values to user settings. If a user wants to save these they can but the extension should not as it could very easily cause disruption across a users projects
1 parent 251695a commit a61b575

3 files changed

Lines changed: 84 additions & 164 deletions

File tree

docs/managing-python-projects.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ You can set default managers that apply to all projects without explicit overrid
211211
}
212212
```
213213

214+
> **Important**: The extension never writes settings to the User (global) scope. All extension-managed settings are written at the Workspace or Workspace Folder level only. This prevents the extension from setting values that persist across unrelated projects and cause unexpected interference (see [#1468](https://github.com/microsoft/vscode-python-environments/issues/1468)). If a user wants a user-level default, they can set it manually in their User `settings.json`.
215+
214216
## Working with Multi-Root Workspaces
215217

216218
Multi-root workspaces contain multiple top-level folders. The extension handles these seamlessly:

src/features/settings/settingHelpers.ts

Lines changed: 48 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as path from 'path';
22
import { ConfigurationScope, ConfigurationTarget, Uri, WorkspaceConfiguration, WorkspaceFolder } from 'vscode';
33
import { PythonProject } from '../../api';
44
import { DEFAULT_ENV_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID } from '../../common/constants';
5-
import { traceError, traceInfo, traceWarn } from '../../common/logging';
5+
import { traceError, traceInfo, traceVerbose, traceWarn } from '../../common/logging';
66
import * as workspaceApis from '../../common/workspace.apis';
77
import { PythonProjectManager, PythonProjectSettings } from '../../internal.api';
88

@@ -81,8 +81,16 @@ export function getDefaultPkgManagerSetting(
8181
return defaultManager;
8282
}
8383

84+
function traceIgnoredGlobalManagerEdits(functionName: string, count: number): void {
85+
if (count > 0) {
86+
traceVerbose(
87+
`[${functionName}] Ignoring ${count} edit(s) without a project because python-envs does not persist manager defaults to User/global settings.`,
88+
);
89+
}
90+
}
91+
8492
export interface EditAllManagerSettings {
85-
// undefined means global
93+
// Edits without a project are ignored; python-envs does not persist manager defaults to User/global settings.
8694
project?: PythonProject;
8795
envManager: string;
8896
packageManager: string;
@@ -95,24 +103,25 @@ interface EditAllManagerSettingsInternal {
95103
export async function setAllManagerSettings(edits: EditAllManagerSettings[]): Promise<void> {
96104
const noWorkspace: EditAllManagerSettingsInternal[] = [];
97105
const workspaces = new Map<WorkspaceFolder, EditAllManagerSettingsInternal[]>();
98-
edits
99-
.filter((e) => !!e.project)
100-
.map((e) => e as EditAllManagerSettingsInternal)
101-
.forEach((e) => {
102-
const w = workspaceApis.getWorkspaceFolder(e.project.uri);
103-
if (w) {
104-
workspaces.set(w, [
105-
...(workspaces.get(w) || []),
106-
{ project: e.project, envManager: e.envManager, packageManager: e.packageManager },
107-
]);
108-
} else {
109-
noWorkspace.push({ project: e.project, envManager: e.envManager, packageManager: e.packageManager });
110-
}
111-
});
106+
const projectEdits = edits.filter((e): e is EditAllManagerSettingsInternal => !!e.project);
107+
traceIgnoredGlobalManagerEdits('setAllManagerSettings', edits.length - projectEdits.length);
108+
projectEdits.forEach((e) => {
109+
const w = workspaceApis.getWorkspaceFolder(e.project.uri);
110+
if (w) {
111+
workspaces.set(w, [
112+
...(workspaces.get(w) || []),
113+
{ project: e.project, envManager: e.envManager, packageManager: e.packageManager },
114+
]);
115+
} else {
116+
noWorkspace.push({ project: e.project, envManager: e.envManager, packageManager: e.packageManager });
117+
}
118+
});
112119

113120
noWorkspace.forEach((e) => {
114121
if (e.project) {
115-
traceInfo(`Unable to find workspace for ${e.project.uri.fsPath}, will use global settings for this.`);
122+
traceInfo(
123+
`Unable to find workspace for ${e.project.uri.fsPath}, skipping settings update because no User/global fallback is written.`,
124+
);
116125
}
117126
});
118127

@@ -195,23 +204,11 @@ export async function setAllManagerSettings(edits: EditAllManagerSettings[]): Pr
195204
}
196205
});
197206

198-
const config = workspaceApis.getConfiguration('python-envs', undefined);
199-
edits
200-
.filter((e) => !e.project)
201-
.forEach((e) => {
202-
if (config.get('defaultEnvManager') !== e.envManager) {
203-
promises.push(config.update('defaultEnvManager', e.envManager, ConfigurationTarget.Global));
204-
}
205-
if (config.get('defaultPackageManager') !== e.packageManager) {
206-
promises.push(config.update('defaultPackageManager', e.packageManager, ConfigurationTarget.Global));
207-
}
208-
});
209-
210207
await Promise.all(promises);
211208
}
212209

213210
export interface EditEnvManagerSettings {
214-
// undefined means global
211+
// Edits without a project are ignored; python-envs does not persist manager defaults to User/global settings.
215212
project?: PythonProject;
216213
envManager: string;
217214
}
@@ -222,17 +219,16 @@ interface EditEnvManagerSettingsInternal {
222219
export async function setEnvironmentManager(edits: EditEnvManagerSettings[]): Promise<void> {
223220
const noWorkspace: EditEnvManagerSettingsInternal[] = [];
224221
const workspaces = new Map<WorkspaceFolder, EditEnvManagerSettingsInternal[]>();
225-
edits
226-
.filter((e) => !!e.project)
227-
.map((e) => e as EditEnvManagerSettingsInternal)
228-
.forEach((e) => {
229-
const w = workspaceApis.getWorkspaceFolder(e.project.uri);
230-
if (w) {
231-
workspaces.set(w, [...(workspaces.get(w) || []), { project: e.project, envManager: e.envManager }]);
232-
} else {
233-
noWorkspace.push({ project: e.project, envManager: e.envManager });
234-
}
235-
});
222+
const projectEdits = edits.filter((e): e is EditEnvManagerSettingsInternal => !!e.project);
223+
traceIgnoredGlobalManagerEdits('setEnvironmentManager', edits.length - projectEdits.length);
224+
projectEdits.forEach((e) => {
225+
const w = workspaceApis.getWorkspaceFolder(e.project.uri);
226+
if (w) {
227+
workspaces.set(w, [...(workspaces.get(w) || []), { project: e.project, envManager: e.envManager }]);
228+
} else {
229+
noWorkspace.push({ project: e.project, envManager: e.envManager });
230+
}
231+
});
236232

237233
noWorkspace.forEach((e) => {
238234
if (e.project) {
@@ -269,20 +265,11 @@ export async function setEnvironmentManager(edits: EditEnvManagerSettings[]): Pr
269265
}
270266
});
271267

272-
const config = workspaceApis.getConfiguration('python-envs', undefined);
273-
edits
274-
.filter((e) => !e.project)
275-
.forEach((e) => {
276-
if (config.get('defaultEnvManager') !== e.envManager) {
277-
promises.push(config.update('defaultEnvManager', e.envManager, ConfigurationTarget.Global));
278-
}
279-
});
280-
281268
await Promise.all(promises);
282269
}
283270

284271
export interface EditPackageManagerSettings {
285-
// undefined means global
272+
// Edits without a project are ignored; python-envs does not persist manager defaults to User/global settings.
286273
project?: PythonProject;
287274
packageManager: string;
288275
}
@@ -293,20 +280,16 @@ interface EditPackageManagerSettingsInternal {
293280
export async function setPackageManager(edits: EditPackageManagerSettings[]): Promise<void> {
294281
const noWorkspace: EditPackageManagerSettingsInternal[] = [];
295282
const workspaces = new Map<WorkspaceFolder, EditPackageManagerSettingsInternal[]>();
296-
edits
297-
.filter((e) => !!e.project)
298-
.map((e) => e as EditPackageManagerSettingsInternal)
299-
.forEach((e) => {
300-
const w = workspaceApis.getWorkspaceFolder(e.project.uri);
301-
if (w) {
302-
workspaces.set(w, [
303-
...(workspaces.get(w) || []),
304-
{ project: e.project, packageManager: e.packageManager },
305-
]);
306-
} else {
307-
noWorkspace.push({ project: e.project, packageManager: e.packageManager });
308-
}
309-
});
283+
const projectEdits = edits.filter((e): e is EditPackageManagerSettingsInternal => !!e.project);
284+
traceIgnoredGlobalManagerEdits('setPackageManager', edits.length - projectEdits.length);
285+
projectEdits.forEach((e) => {
286+
const w = workspaceApis.getWorkspaceFolder(e.project.uri);
287+
if (w) {
288+
workspaces.set(w, [...(workspaces.get(w) || []), { project: e.project, packageManager: e.packageManager }]);
289+
} else {
290+
noWorkspace.push({ project: e.project, packageManager: e.packageManager });
291+
}
292+
});
310293

311294
noWorkspace.forEach((e) => {
312295
if (e.project) {
@@ -343,15 +326,6 @@ export async function setPackageManager(edits: EditPackageManagerSettings[]): Pr
343326
}
344327
});
345328

346-
const config = workspaceApis.getConfiguration('python-envs', undefined);
347-
edits
348-
.filter((e) => !e.project)
349-
.forEach((e) => {
350-
if (config.get('defaultPackageManager') !== e.packageManager) {
351-
promises.push(config.update('defaultPackageManager', e.packageManager, ConfigurationTarget.Global));
352-
}
353-
});
354-
355329
await Promise.all(promises);
356330
}
357331

0 commit comments

Comments
 (0)