Skip to content

Do a one-off transfer of existing values for python.pythonPath setting to new Interpreter storage if in DeprecatePythonPath experiment #11053

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/1 Enhancements/11052.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Do a one-off transfer of existing values for `python.pythonPath` setting to new Interpreter storage if in DeprecatePythonPath experiment.
6 changes: 6 additions & 0 deletions src/client/activation/activationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ export class ExtensionActivationManager implements IExtensionActivationManager {
return;
}
this.activatedWorkspaces.add(key);

if (this.experiments.inExperiment(DeprecatePythonPath.experiment)) {
await this.interpreterPathService.copyOldInterpreterStorageValuesToNew(resource);
}
this.experiments.sendTelemetryIfInExperiment(DeprecatePythonPath.control);

// Get latest interpreter list in the background.
this.interpreterService.getInterpreters(resource).ignoreErrors();

Expand Down
92 changes: 83 additions & 9 deletions src/client/common/interpreterPathService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ConfigurationChangeEvent, ConfigurationTarget, Event, EventEmitter, Uri
import { IWorkspaceService } from './application/types';
import { PythonSettings } from './configSettings';
import { isTestExecution } from './constants';
import { FileSystemPaths } from './platform/fs-paths';
import {
IDisposable,
IDisposableRegistry,
Expand All @@ -21,6 +22,9 @@ import {
Resource
} from './types';

export const workspaceKeysForWhichTheCopyIsDone_Key = 'workspaceKeysForWhichTheCopyIsDone_Key';
export const workspaceFolderKeysForWhichTheCopyIsDone_Key = 'workspaceFolderKeysForWhichTheCopyIsDone_Key';
export const isGlobalSettingCopiedKey = 'isGlobalSettingCopiedKey';
export const defaultInterpreterPathSetting: keyof IPythonSettings = 'defaultInterpreterPath';
const CI_PYTHON_PATH = getCIPythonPath();

Expand All @@ -32,13 +36,18 @@ export function getCIPythonPath(): string {
}
@injectable()
export class InterpreterPathService implements IInterpreterPathService {
public get onDidChange(): Event<InterpreterConfigurationScope> {
return this._didChangeInterpreterEmitter.event;
}
public _didChangeInterpreterEmitter = new EventEmitter<InterpreterConfigurationScope>();
private fileSystemPaths: FileSystemPaths;
constructor(
@inject(IPersistentStateFactory) private readonly persistentStateFactory: IPersistentStateFactory,
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService,
@inject(IDisposableRegistry) disposables: IDisposable[]
) {
disposables.push(this.workspaceService.onDidChangeConfiguration(this.onDidChangeConfiguration.bind(this)));
this.fileSystemPaths = FileSystemPaths.withDefaults();
}

public async onDidChangeConfiguration(event: ConfigurationChangeEvent) {
Expand Down Expand Up @@ -88,8 +97,11 @@ export class InterpreterPathService implements IInterpreterPathService {
resource = PythonSettings.getSettingsUriAndTarget(resource, this.workspaceService).uri;
if (configTarget === ConfigurationTarget.Global) {
const pythonConfig = this.workspaceService.getConfiguration('python');
await pythonConfig.update('defaultInterpreterPath', pythonPath, true);
this._didChangeInterpreterEmitter.fire({ uri: undefined, configTarget });
const globalValue = pythonConfig.inspect<string>('defaultInterpreterPath')!.globalValue;
if (globalValue !== pythonPath) {
await pythonConfig.update('defaultInterpreterPath', pythonPath, true);
this._didChangeInterpreterEmitter.fire({ uri: undefined, configTarget });
}
return;
}
if (!resource) {
Expand All @@ -100,12 +112,10 @@ export class InterpreterPathService implements IInterpreterPathService {
settingKey,
undefined
);
await persistentSetting.updateValue(pythonPath);
this._didChangeInterpreterEmitter.fire({ uri: resource, configTarget });
}

public get onDidChange(): Event<InterpreterConfigurationScope> {
return this._didChangeInterpreterEmitter.event;
if (persistentSetting.value !== pythonPath) {
await persistentSetting.updateValue(pythonPath);
this._didChangeInterpreterEmitter.fire({ uri: resource, configTarget });
}
}

public getSettingKey(
Expand All @@ -118,10 +128,74 @@ export class InterpreterPathService implements IInterpreterPathService {
settingKey = `WORKSPACE_FOLDER_INTERPRETER_PATH_${folderKey}`;
} else {
settingKey = this.workspaceService.workspaceFile
? `WORKSPACE_INTERPRETER_PATH_${this.workspaceService.workspaceFile.fsPath}`
? `WORKSPACE_INTERPRETER_PATH_${this.fileSystemPaths.normCase(
this.workspaceService.workspaceFile.fsPath
)}`
: // Only a single folder is opened, use fsPath of the folder as key
`WORKSPACE_FOLDER_INTERPRETER_PATH_${folderKey}`;
}
return settingKey;
}

public async copyOldInterpreterStorageValuesToNew(resource: Resource): Promise<void> {
resource = PythonSettings.getSettingsUriAndTarget(resource, this.workspaceService).uri;
const oldSettings = this.workspaceService.getConfiguration('python', resource).inspect<string>('pythonPath')!;
await Promise.all([
this._copyWorkspaceFolderValueToNewStorage(resource, oldSettings.workspaceFolderValue),
this._copyWorkspaceValueToNewStorage(resource, oldSettings.workspaceValue),
this._moveGlobalSettingValueToNewStorage(oldSettings.globalValue)
]);
}

public async _copyWorkspaceFolderValueToNewStorage(resource: Resource, value: string | undefined): Promise<void> {
// Copy workspace folder setting into the new storage if it hasn't been copied already
const workspaceFolderKey = this.workspaceService.getWorkspaceFolderIdentifier(resource);
const flaggedWorkspaceFolderKeysStorage = this.persistentStateFactory.createGlobalPersistentState<string[]>(
workspaceFolderKeysForWhichTheCopyIsDone_Key,
[]
);
const flaggedWorkspaceFolderKeys = flaggedWorkspaceFolderKeysStorage.value;
const shouldUpdateWorkspaceFolderSetting = !flaggedWorkspaceFolderKeys.includes(workspaceFolderKey);
if (shouldUpdateWorkspaceFolderSetting) {
await this.update(resource, ConfigurationTarget.WorkspaceFolder, value);
await flaggedWorkspaceFolderKeysStorage.updateValue([workspaceFolderKey, ...flaggedWorkspaceFolderKeys]);
}
}

public async _copyWorkspaceValueToNewStorage(resource: Resource, value: string | undefined): Promise<void> {
// Copy workspace setting into the new storage if it hasn't been copied already
const workspaceKey = this.workspaceService.workspaceFile
? this.fileSystemPaths.normCase(this.workspaceService.workspaceFile.fsPath)
: undefined;
if (!workspaceKey) {
return;
}
const flaggedWorkspaceKeysStorage = this.persistentStateFactory.createGlobalPersistentState<string[]>(
workspaceKeysForWhichTheCopyIsDone_Key,
[]
);
const flaggedWorkspaceKeys = flaggedWorkspaceKeysStorage.value;
const shouldUpdateWorkspaceSetting = !flaggedWorkspaceKeys.includes(workspaceKey);
if (shouldUpdateWorkspaceSetting) {
await this.update(resource, ConfigurationTarget.Workspace, value);
await flaggedWorkspaceKeysStorage.updateValue([workspaceKey, ...flaggedWorkspaceKeys]);
}
}

public async _moveGlobalSettingValueToNewStorage(value: string | undefined) {
// Move global setting into the new storage if it hasn't been moved already
const isGlobalSettingCopiedStorage = this.persistentStateFactory.createGlobalPersistentState<boolean>(
isGlobalSettingCopiedKey,
false
);
const shouldUpdateGlobalSetting = !isGlobalSettingCopiedStorage.value;
if (shouldUpdateGlobalSetting) {
await this.update(undefined, ConfigurationTarget.Global, value);
// Make sure to delete the original setting after copying it
await this.workspaceService
.getConfiguration('python')
.update('pythonPath', undefined, ConfigurationTarget.Global);
await isGlobalSettingCopiedStorage.updateValue(true);
}
}
}
1 change: 1 addition & 0 deletions src/client/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -632,4 +632,5 @@ export interface IInterpreterPathService {
get(resource: Resource): string;
inspect(resource: Resource): InspectInterpreterSettingType;
update(resource: Resource, configTarget: ConfigurationTarget, value: string | undefined): Promise<void>;
copyOldInterpreterStorageValuesToNew(resource: Uri | undefined): Promise<void>;
}
2 changes: 1 addition & 1 deletion src/client/interpreter/autoSelection/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
export const unsafeInterpreterPromptKey = 'unsafeInterpreterPromptKey';
export const unsafeInterpretersKey = 'unsafeInterpretersKey';
export const safeInterpretersKey = 'safeInterpretersKey';
export const flaggedWorkspacesKeysStorageKey = 'flaggedWorkspacesKeysStorageKey';
export const flaggedWorkspacesKeysStorageKey = 'flaggedWorkspacesKeysInterpreterSecurityStorageKey';
export const learnMoreOnInterpreterSecurityURI = 'https://aka.ms/AA7jfor';
Loading