diff --git a/data/.vscode/settings.json b/data/.vscode/settings.json index 99acc159fcaa..6f329d0777a4 100644 --- a/data/.vscode/settings.json +++ b/data/.vscode/settings.json @@ -1,3 +1,3 @@ { - "python.pythonPath": "/usr/bin/python3" + "python.defaultInterpreterPath": "/usr/bin/python3" } diff --git a/news/3 Code Health/17977.md b/news/3 Code Health/17977.md new file mode 100644 index 000000000000..d7052d234343 --- /dev/null +++ b/news/3 Code Health/17977.md @@ -0,0 +1 @@ +Remove `python.pythonPath` setting and `pythonDeprecatePythonPath` experiment. diff --git a/package.json b/package.json index 03ed79e7e75f..69364ca92b12 100644 --- a/package.json +++ b/package.json @@ -455,7 +455,7 @@ }, "python.defaultInterpreterPath": { "default": "python", - "description": "Path to Python, you can use a custom version of Python by modifying this setting to include the full path. This default setting is used as a fallback if no interpreter is selected for the workspace. The extension will also not set nor change the value of this setting, it will only read from it.", + "description": "Path to default Python to use when extension loads up for the first time, no longer used once an interpreter is selected for the workspace. See https://aka.ms/AAfekmf to understand when this is used.", "scope": "machine-overridable", "type": "string" }, @@ -982,12 +982,6 @@ "scope": "machine-overridable", "type": "string" }, - "python.pythonPath": { - "default": "python", - "description": "(DEPRECATED: Note this setting is not used when in pythonDeprecatePythonPath experiment) Path to Python, you can use a custom version of Python by modifying this setting to include the full path.", - "scope": "machine-overridable", - "type": "string" - }, "python.sortImports.args": { "default": [], "description": "Arguments passed in. Each argument is a separate item in the array.", diff --git a/src/client/activation/activationManager.ts b/src/client/activation/activationManager.ts index bfd9352f8183..c19383837bac 100644 --- a/src/client/activation/activationManager.ts +++ b/src/client/activation/activationManager.ts @@ -8,9 +8,8 @@ import { TextDocument } from 'vscode'; import { IApplicationDiagnostics } from '../application/types'; import { IActiveResourceService, IDocumentManager, IWorkspaceService } from '../common/application/types'; import { PYTHON_LANGUAGE } from '../common/constants'; -import { DeprecatePythonPath } from '../common/experiments/groups'; import { IFileSystem } from '../common/platform/types'; -import { IDisposable, IExperimentService, IInterpreterPathService, Resource } from '../common/types'; +import { IDisposable, Resource } from '../common/types'; import { Deferred } from '../common/utils/async'; import { IInterpreterAutoSelectionService } from '../interpreter/autoSelection/types'; import { traceDecoratorError } from '../logging'; @@ -37,8 +36,6 @@ export class ExtensionActivationManager implements IExtensionActivationManager { @inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService, @inject(IFileSystem) private readonly fileSystem: IFileSystem, @inject(IActiveResourceService) private readonly activeResourceService: IActiveResourceService, - @inject(IExperimentService) private readonly experiments: IExperimentService, - @inject(IInterpreterPathService) private readonly interpreterPathService: IInterpreterPathService, ) { if (!this.workspaceService.isTrusted) { this.activationServices = this.activationServices.filter( @@ -90,9 +87,6 @@ export class ExtensionActivationManager implements IExtensionActivationManager { if (this.workspaceService.isTrusted) { // Do not interact with interpreters in a untrusted workspace. - if (this.experiments.inExperimentSync(DeprecatePythonPath.experiment)) { - await this.interpreterPathService.copyOldInterpreterStorageValuesToNew(resource); - } await this.autoSelection.autoSelectInterpreter(resource); } await sendActivationTelemetry(this.fileSystem, this.workspaceService, resource); diff --git a/src/client/activation/jedi/languageServerProxy.ts b/src/client/activation/jedi/languageServerProxy.ts index 67d6c5a60919..ca7136bf16f7 100644 --- a/src/client/activation/jedi/languageServerProxy.ts +++ b/src/client/activation/jedi/languageServerProxy.ts @@ -11,8 +11,7 @@ import { } from 'vscode-languageclient/node'; import { ChildProcess } from 'child_process'; -import { DeprecatePythonPath } from '../../common/experiments/groups'; -import { IExperimentService, IInterpreterPathService, Resource } from '../../common/types'; +import { IInterpreterPathService, Resource } from '../../common/types'; import { PythonEnvironment } from '../../pythonEnvironments/info'; import { captureTelemetry } from '../../telemetry'; import { EventName } from '../../telemetry/constants'; @@ -37,7 +36,6 @@ export class JediLanguageServerProxy implements ILanguageServerProxy { constructor( @inject(ILanguageClientFactory) private readonly factory: ILanguageClientFactory, - @inject(IExperimentService) private readonly experiments: IExperimentService, @inject(IInterpreterPathService) private readonly interpreterPathService: IInterpreterPathService, ) {} @@ -148,18 +146,16 @@ export class JediLanguageServerProxy implements ILanguageServerProxy { const progressReporting = new ProgressReporting(this.languageClient!); this.disposables.push(progressReporting); - if (this.experiments.inExperimentSync(DeprecatePythonPath.experiment)) { - this.disposables.push( - this.interpreterPathService.onDidChange(() => { - // Manually send didChangeConfiguration in order to get the server to re-query - // the workspace configurations (to then pick up pythonPath set in the middleware). - // This is needed as interpreter changes via the interpreter path service happen - // outside of VS Code's settings (which would mean VS Code sends the config updates itself). - this.languageClient!.sendNotification(DidChangeConfigurationNotification.type, { - settings: null, - }); - }), - ); - } + this.disposables.push( + this.interpreterPathService.onDidChange(() => { + // Manually send didChangeConfiguration in order to get the server to re-query + // the workspace configurations (to then pick up pythonPath set in the middleware). + // This is needed as interpreter changes via the interpreter path service happen + // outside of VS Code's settings (which would mean VS Code sends the config updates itself). + this.languageClient!.sendNotification(DidChangeConfigurationNotification.type, { + settings: null, + }); + }), + ); } } diff --git a/src/client/activation/node/languageServerProxy.ts b/src/client/activation/node/languageServerProxy.ts index 005c2719a1ec..8d58c39e688c 100644 --- a/src/client/activation/node/languageServerProxy.ts +++ b/src/client/activation/node/languageServerProxy.ts @@ -11,7 +11,6 @@ import { State, } from 'vscode-languageclient/node'; -import { DeprecatePythonPath } from '../../common/experiments/groups'; import { IConfigurationService, IExperimentService, IInterpreterPathService, Resource } from '../../common/types'; import { createDeferred, Deferred, sleep } from '../../common/utils/async'; import { noop } from '../../common/utils/misc'; @@ -177,20 +176,17 @@ export class NodeLanguageServerProxy implements ILanguageServerProxy { const progressReporting = new ProgressReporting(this.languageClient!); this.disposables.push(progressReporting); - if (this.experimentService.inExperimentSync(DeprecatePythonPath.experiment)) { - this.disposables.push( - this.interpreterPathService.onDidChange(() => { - // Manually send didChangeConfiguration in order to get the server to requery - // the workspace configurations (to then pick up pythonPath set in the middleware). - // This is needed as interpreter changes via the interpreter path service happen - // outside of VS Code's settings (which would mean VS Code sends the config updates itself). - this.languageClient!.sendNotification(DidChangeConfigurationNotification.type, { - settings: null, - }); - }), - ); - } - + this.disposables.push( + this.interpreterPathService.onDidChange(() => { + // Manually send didChangeConfiguration in order to get the server to requery + // the workspace configurations (to then pick up pythonPath set in the middleware). + // This is needed as interpreter changes via the interpreter path service happen + // outside of VS Code's settings (which would mean VS Code sends the config updates itself). + this.languageClient!.sendNotification(DidChangeConfigurationNotification.type, { + settings: null, + }); + }), + ); this.disposables.push( this.environmentService.onDidEnvironmentVariablesChange(() => { this.languageClient!.sendNotification(DidChangeConfigurationNotification.type, { diff --git a/src/client/application/diagnostics/checks/macPythonInterpreter.ts b/src/client/application/diagnostics/checks/macPythonInterpreter.ts index f18a58df594f..0e1e65300d09 100644 --- a/src/client/application/diagnostics/checks/macPythonInterpreter.ts +++ b/src/client/application/diagnostics/checks/macPythonInterpreter.ts @@ -3,15 +3,12 @@ // eslint-disable-next-line max-classes-per-file import { inject, injectable } from 'inversify'; -import { ConfigurationChangeEvent, DiagnosticSeverity, Uri } from 'vscode'; -import { IWorkspaceService } from '../../../common/application/types'; -import { DeprecatePythonPath } from '../../../common/experiments/groups'; +import { DiagnosticSeverity } from 'vscode'; import '../../../common/extensions'; import { IPlatformService } from '../../../common/platform/types'; import { IConfigurationService, IDisposableRegistry, - IExperimentService, IInterpreterPathService, InterpreterConfigurationScope, Resource, @@ -148,40 +145,15 @@ export class InvalidMacPythonInterpreterService extends BaseDiagnosticsService { } protected addPythonPathChangedHandler(): void { - const workspaceService = this.serviceContainer.get(IWorkspaceService); const disposables = this.serviceContainer.get(IDisposableRegistry); const interpreterPathService = this.serviceContainer.get(IInterpreterPathService); - const experiments = this.serviceContainer.get(IExperimentService); - if (experiments.inExperimentSync(DeprecatePythonPath.experiment)) { - disposables.push(interpreterPathService.onDidChange((i) => this.onDidChangeConfiguration(undefined, i))); - } - disposables.push(workspaceService.onDidChangeConfiguration(this.onDidChangeConfiguration.bind(this))); + disposables.push(interpreterPathService.onDidChange((i) => this.onDidChangeConfiguration(i))); } protected async onDidChangeConfiguration( - event?: ConfigurationChangeEvent, - interpreterConfigurationScope?: InterpreterConfigurationScope, + interpreterConfigurationScope: InterpreterConfigurationScope, ): Promise { - let workspaceUri: Resource; - if (event) { - const workspaceService = this.serviceContainer.get(IWorkspaceService); - const workspacesUris: (Uri | undefined)[] = workspaceService.hasWorkspaceFolders - ? workspaceService.workspaceFolders!.map((workspace) => workspace.uri) - : [undefined]; - const workspaceUriIndex = workspacesUris.findIndex((uri) => - event.affectsConfiguration('python.pythonPath', uri), - ); - if (workspaceUriIndex === -1) { - return; - } - workspaceUri = workspacesUris[workspaceUriIndex]; - } else if (interpreterConfigurationScope) { - workspaceUri = interpreterConfigurationScope.uri; - } else { - throw new Error( - 'One of `interpreterConfigurationScope` or `event` should be defined when calling `onDidChangeConfiguration`.', - ); - } + const workspaceUri = interpreterConfigurationScope.uri; // Lets wait, for more changes, dirty simple throttling. if (this.timeOut && typeof this.timeOut !== 'number') { clearTimeout(this.timeOut); diff --git a/src/client/application/diagnostics/checks/pythonPathDeprecated.ts b/src/client/application/diagnostics/checks/pythonPathDeprecated.ts index 73c292ef3515..1cb516cd79a9 100644 --- a/src/client/application/diagnostics/checks/pythonPathDeprecated.ts +++ b/src/client/application/diagnostics/checks/pythonPathDeprecated.ts @@ -5,8 +5,7 @@ import { inject, named } from 'inversify'; import { DiagnosticSeverity } from 'vscode'; import { IWorkspaceService } from '../../../common/application/types'; -import { DeprecatePythonPath } from '../../../common/experiments/groups'; -import { IDisposableRegistry, IExperimentService, Resource } from '../../../common/types'; +import { IDisposableRegistry, Resource } from '../../../common/types'; import { Common, Diagnostics } from '../../../common/utils/localize'; import { IServiceContainer } from '../../../ioc/types'; import { BaseDiagnostic, BaseDiagnosticsService } from '../base'; @@ -44,10 +43,6 @@ export class PythonPathDeprecatedDiagnosticService extends BaseDiagnosticsServic } public async diagnose(resource: Resource): Promise { - const experiments = this.serviceContainer.get(IExperimentService); - if (!experiments.inExperimentSync(DeprecatePythonPath.experiment)) { - return []; - } const setting = this.workspaceService.getConfiguration('python', resource).inspect('pythonPath'); if (!setting) { return []; diff --git a/src/client/application/diagnostics/checks/upgradeCodeRunner.ts b/src/client/application/diagnostics/checks/upgradeCodeRunner.ts index 89a57f189746..2c9b68fc48b3 100644 --- a/src/client/application/diagnostics/checks/upgradeCodeRunner.ts +++ b/src/client/application/diagnostics/checks/upgradeCodeRunner.ts @@ -6,8 +6,7 @@ import { inject, named } from 'inversify'; import { DiagnosticSeverity } from 'vscode'; import { IWorkspaceService } from '../../../common/application/types'; import { CODE_RUNNER_EXTENSION_ID } from '../../../common/constants'; -import { DeprecatePythonPath } from '../../../common/experiments/groups'; -import { IDisposableRegistry, IExperimentService, IExtensions, Resource } from '../../../common/types'; +import { IDisposableRegistry, IExtensions, Resource } from '../../../common/types'; import { Common, Diagnostics } from '../../../common/utils/localize'; import { IServiceContainer } from '../../../ioc/types'; import { BaseDiagnostic, BaseDiagnosticsService } from '../base'; @@ -51,10 +50,6 @@ export class UpgradeCodeRunnerDiagnosticService extends BaseDiagnosticsService { if (this._diagnosticReturned) { return []; } - const experiments = this.serviceContainer.get(IExperimentService); - if (!experiments.inExperimentSync(DeprecatePythonPath.experiment)) { - return []; - } const extension = this.extensions.getExtension(CODE_RUNNER_EXTENSION_ID); if (!extension) { return []; diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index a37160cdad66..893021a7f73d 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -23,14 +23,12 @@ import { ITestingSettings } from '../testing/configuration/types'; import { IWorkspaceService } from './application/types'; import { WorkspaceService } from './application/workspace'; import { DEFAULT_INTERPRETER_SETTING, isTestExecution } from './constants'; -import { DeprecatePythonPath } from './experiments/groups'; import { ExtensionChannels } from './insidersBuild/types'; import { IS_WINDOWS } from './platform/constants'; import { IAutoCompleteSettings, IDefaultLanguageServer, IExperiments, - IExperimentService, IFormattingSettings, IInterpreterPathService, ILintingSettings, @@ -146,10 +144,9 @@ export class PythonSettings implements IPythonSettings { constructor( workspaceFolder: Resource, private readonly interpreterAutoSelectionService: IInterpreterAutoSelectionProxyService, - workspace?: IWorkspaceService, - private readonly experimentsManager?: IExperimentService, - private readonly interpreterPathService?: IInterpreterPathService, - private readonly defaultLS?: IDefaultLanguageServer, + workspace: IWorkspaceService, + private readonly interpreterPathService: IInterpreterPathService, + private readonly defaultLS: IDefaultLanguageServer | undefined, ) { this.workspace = workspace || new WorkspaceService(); this.workspaceRoot = workspaceFolder; @@ -159,10 +156,9 @@ export class PythonSettings implements IPythonSettings { public static getInstance( resource: Uri | undefined, interpreterAutoSelectionService: IInterpreterAutoSelectionProxyService, - workspace?: IWorkspaceService, - experimentsManager?: IExperimentService, - interpreterPathService?: IInterpreterPathService, - defaultLS?: IDefaultLanguageServer, + workspace: IWorkspaceService, + interpreterPathService: IInterpreterPathService, + defaultLS: IDefaultLanguageServer | undefined, ): PythonSettings { workspace = workspace || new WorkspaceService(); const workspaceFolderUri = PythonSettings.getSettingsUriAndTarget(resource, workspace).uri; @@ -173,7 +169,6 @@ export class PythonSettings implements IPythonSettings { workspaceFolderUri, interpreterAutoSelectionService, workspace, - experimentsManager, interpreterPathService, defaultLS, ); @@ -237,7 +232,7 @@ export class PythonSettings implements IPythonSettings { const workspaceRoot = this.workspaceRoot?.fsPath; const systemVariables: SystemVariables = new SystemVariables(undefined, workspaceRoot, this.workspace); - this.pythonPath = this.getPythonPath(pythonSettings, systemVariables, workspaceRoot); + this.pythonPath = this.getPythonPath(systemVariables, workspaceRoot); const defaultInterpreterPath = systemVariables.resolveAny(pythonSettings.get('defaultInterpreterPath')); this.defaultInterpreterPath = defaultInterpreterPath || DEFAULT_INTERPRETER_SETTING; @@ -561,24 +556,8 @@ export class PythonSettings implements IPythonSettings { this.changed.fire(); } - private getPythonPath( - pythonSettings: WorkspaceConfiguration, - systemVariables: SystemVariables, - workspaceRoot: string | undefined, - ) { - /** - * Note that while calling `IExperimentsManager.inExperiment()`, we assume `IExperimentsManager.activate()` is already called. - * That's not true here, as this method is often called in the constructor,which runs before `.activate()` methods. - * But we can still use it here for this particular experiment. Reason being that this experiment only changes - * `pythonPath` setting, and I've checked that `pythonPath` setting is not accessed anywhere in the constructor. - */ - const inExperiment = this.experimentsManager?.inExperimentSync(DeprecatePythonPath.experiment); - // Use the interpreter path service if in the experiment otherwise use the normal settings - this.pythonPath = systemVariables.resolveAny( - inExperiment && this.interpreterPathService - ? this.interpreterPathService.get(this.workspaceRoot) - : pythonSettings.get('pythonPath'), - )!; + private getPythonPath(systemVariables: SystemVariables, workspaceRoot: string | undefined) { + this.pythonPath = systemVariables.resolveAny(this.interpreterPathService.get(this.workspaceRoot))!; if ( !process.env.CI_DISABLE_AUTO_SELECTION && (this.pythonPath.length === 0 || this.pythonPath === 'python') && diff --git a/src/client/common/configuration/service.ts b/src/client/common/configuration/service.ts index 5fb87a0280fb..32e8cc951089 100644 --- a/src/client/common/configuration/service.ts +++ b/src/client/common/configuration/service.ts @@ -8,14 +8,7 @@ import { IServiceContainer } from '../../ioc/types'; import { IWorkspaceService } from '../application/types'; import { PythonSettings } from '../configSettings'; import { isUnitTestExecution } from '../constants'; -import { DeprecatePythonPath } from '../experiments/groups'; -import { - IConfigurationService, - IDefaultLanguageServer, - IExperimentService, - IInterpreterPathService, - IPythonSettings, -} from '../types'; +import { IConfigurationService, IDefaultLanguageServer, IInterpreterPathService, IPythonSettings } from '../types'; @injectable() export class ConfigurationService implements IConfigurationService { @@ -30,13 +23,11 @@ export class ConfigurationService implements IConfigurationService { IInterpreterAutoSelectionService, ); const interpreterPathService = this.serviceContainer.get(IInterpreterPathService); - const experiments = this.serviceContainer.get(IExperimentService); const defaultLS = this.serviceContainer.tryGet(IDefaultLanguageServer); return PythonSettings.getInstance( resource, InterpreterAutoSelectionService, this.workspaceService, - experiments, interpreterPathService, defaultLS, ); @@ -49,9 +40,7 @@ export class ConfigurationService implements IConfigurationService { resource?: Uri, configTarget?: ConfigurationTarget, ): Promise { - const experiments = this.serviceContainer.get(IExperimentService); const interpreterPathService = this.serviceContainer.get(IInterpreterPathService); - const inExperiment = experiments.inExperimentSync(DeprecatePythonPath.experiment); const defaultSetting = { uri: resource, target: configTarget || ConfigurationTarget.WorkspaceFolder, @@ -64,7 +53,7 @@ export class ConfigurationService implements IConfigurationService { const configSection = this.workspaceService.getConfiguration(section, settingsInfo.uri); const currentValue = - inExperiment && section === 'python' && setting === 'pythonPath' + section === 'python' && setting === 'pythonPath' ? interpreterPathService.inspect(settingsInfo.uri) : configSection.inspect(setting); @@ -77,10 +66,8 @@ export class ConfigurationService implements IConfigurationService { return; } if (section === 'python' && setting === 'pythonPath') { - if (inExperiment) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - await interpreterPathService.update(settingsInfo.uri, configTarget, value as any); - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await interpreterPathService.update(settingsInfo.uri, configTarget, value as any); } else { await configSection.update(setting, value, configTarget); await this.verifySetting(configSection, configTarget, setting, value); diff --git a/src/client/common/experiments/groups.ts b/src/client/common/experiments/groups.ts index 1a06bd58f0d7..184c79875df1 100644 --- a/src/client/common/experiments/groups.ts +++ b/src/client/common/experiments/groups.ts @@ -3,9 +3,14 @@ export enum ShowExtensionSurveyPrompt { experiment = 'pythonSurveyNotification', } -/* - * Experiment to check whether the extension should deprecate `python.pythonPath` setting - */ -export enum DeprecatePythonPath { - experiment = 'pythonDeprecatePythonPath', +// Feature flag for 'Python: Launch TensorBoard' feature +export enum NativeTensorBoard { + experiment = 'pythonTensorboardExperiment', +} + +// Feature gate to control whether we install the PyTorch profiler package +// torch.profiler release is being delayed till end of March. This allows us +// to turn on the profiler plugin install functionality between releases +export enum TorchProfiler { + experiment = 'PythonPyTorchProfiler', } diff --git a/src/client/common/interpreterPathProxyService.ts b/src/client/common/interpreterPathProxyService.ts deleted file mode 100644 index f1044747a1d0..000000000000 --- a/src/client/common/interpreterPathProxyService.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { inject, injectable } from 'inversify'; -import { IWorkspaceService } from './application/types'; -import { DeprecatePythonPath } from './experiments/groups'; -import { IExperimentService, IInterpreterPathProxyService, IInterpreterPathService, Resource } from './types'; -import { SystemVariables } from './variables/systemVariables'; - -@injectable() -export class InterpreterPathProxyService implements IInterpreterPathProxyService { - constructor( - @inject(IInterpreterPathService) private readonly interpreterPathService: IInterpreterPathService, - @inject(IExperimentService) private readonly experiment: IExperimentService, - @inject(IWorkspaceService) private readonly workspace: IWorkspaceService, - ) {} - - public get(resource: Resource): string { - const systemVariables = new SystemVariables( - undefined, - this.workspace.getWorkspaceFolder(resource)?.uri.fsPath, - this.workspace, - ); - const pythonSettings = this.workspace.getConfiguration('python', resource); - return systemVariables.resolveAny( - this.experiment.inExperimentSync(DeprecatePythonPath.experiment) - ? this.interpreterPathService.get(resource) - : pythonSettings.get('pythonPath'), - )!; - } -} diff --git a/src/client/common/interpreterPathService.ts b/src/client/common/interpreterPathService.ts index d07c1a966c9a..cf31eaa37d44 100644 --- a/src/client/common/interpreterPathService.ts +++ b/src/client/common/interpreterPathService.ts @@ -22,10 +22,8 @@ import { IPythonSettings, Resource, } from './types'; +import { SystemVariables } from './variables/systemVariables'; -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(); @@ -71,9 +69,8 @@ export class InterpreterPathService implements IInterpreterPathService { undefined, ); } - const defaultInterpreterPath = this.workspaceService - .getConfiguration('python', resource)! - .inspect('defaultInterpreterPath')!; + const defaultInterpreterPath: InspectInterpreterSettingType = + this.workspaceService.getConfiguration('python', resource)?.inspect('defaultInterpreterPath') ?? {}; return { globalValue: defaultInterpreterPath.globalValue, workspaceFolderValue: workspaceFolderSetting?.value || defaultInterpreterPath.workspaceFolderValue, @@ -83,12 +80,17 @@ export class InterpreterPathService implements IInterpreterPathService { public get(resource: Resource): string { const settings = this.inspect(resource); - return ( + const value = settings.workspaceFolderValue || settings.workspaceValue || settings.globalValue || - (isTestExecution() ? CI_PYTHON_PATH : 'python') + (isTestExecution() ? CI_PYTHON_PATH : 'python'); + const systemVariables = new SystemVariables( + undefined, + this.workspaceService.getWorkspaceFolder(resource)?.uri.fsPath, + this.workspaceService, ); + return systemVariables.resolveAny(value)!; } public async update( @@ -139,66 +141,4 @@ export class InterpreterPathService implements IInterpreterPathService { } return settingKey; } - - public async copyOldInterpreterStorageValuesToNew(resource: Resource): Promise { - resource = PythonSettings.getSettingsUriAndTarget(resource, this.workspaceService).uri; - const oldSettings = this.workspaceService.getConfiguration('python', resource).inspect('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 { - // Copy workspace folder setting into the new storage if it hasn't been copied already - const workspaceFolderKey = this.workspaceService.getWorkspaceFolderIdentifier(resource, ''); - if (workspaceFolderKey === '') { - // No workspace folder is opened, simply return. - return; - } - const flaggedWorkspaceFolderKeysStorage = this.persistentStateFactory.createGlobalPersistentState( - 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 { - // 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( - 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( - isGlobalSettingCopiedKey, - false, - ); - const shouldUpdateGlobalSetting = !isGlobalSettingCopiedStorage.value; - if (shouldUpdateGlobalSetting) { - await this.update(undefined, ConfigurationTarget.Global, value); - await isGlobalSettingCopiedStorage.updateValue(true); - } - } } diff --git a/src/client/common/persistentState.ts b/src/client/common/persistentState.ts index c72e3ab59e21..6c8a3ff64412 100644 --- a/src/client/common/persistentState.ts +++ b/src/client/common/persistentState.ts @@ -78,11 +78,11 @@ export class PersistentStateFactory implements IPersistentStateFactory, IExtensi constructor( @inject(IMemento) @named(GLOBAL_MEMENTO) private globalState: Memento, @inject(IMemento) @named(WORKSPACE_MEMENTO) private workspaceState: Memento, - @inject(ICommandManager) private cmdManager: ICommandManager, + @inject(ICommandManager) private cmdManager?: ICommandManager, ) {} public async activate(): Promise { - this.cmdManager.registerCommand(Commands.ClearStorage, this.cleanAllPersistentStates.bind(this)); + this.cmdManager?.registerCommand(Commands.ClearStorage, this.cleanAllPersistentStates.bind(this)); const globalKeysStorageDeprecated = this.createGlobalPersistentState(GLOBAL_PERSISTENT_KEYS_DEPRECATED, []); const workspaceKeysStorageDeprecated = this.createWorkspacePersistentState( WORKSPACE_PERSISTENT_KEYS_DEPRECATED, diff --git a/src/client/common/process/pythonExecutionFactory.ts b/src/client/common/process/pythonExecutionFactory.ts index f48a526c23bb..51d4420c2468 100644 --- a/src/client/common/process/pythonExecutionFactory.ts +++ b/src/client/common/process/pythonExecutionFactory.ts @@ -11,7 +11,7 @@ import { CondaEnvironmentInfo } from '../../pythonEnvironments/common/environmen import { sendTelemetryEvent } from '../../telemetry'; import { EventName } from '../../telemetry/constants'; import { IFileSystem } from '../platform/types'; -import { IConfigurationService, IDisposableRegistry, IInterpreterPathProxyService } from '../types'; +import { IConfigurationService, IDisposableRegistry, IInterpreterPathService } from '../types'; import { ProcessService } from './proc'; import { createCondaEnv, createPythonEnv, createWindowsStoreEnv } from './pythonEnvironment'; import { createPythonProcessService } from './pythonProcess'; @@ -49,7 +49,7 @@ export class PythonExecutionFactory implements IPythonExecutionFactory { @inject(IBufferDecoder) private readonly decoder: IBufferDecoder, @inject(IComponentAdapter) private readonly pyenvs: IComponentAdapter, @inject(IInterpreterAutoSelectionService) private readonly autoSelection: IInterpreterAutoSelectionService, - @inject(IInterpreterPathProxyService) private readonly interpreterPathExpHelper: IInterpreterPathProxyService, + @inject(IInterpreterPathService) private readonly interpreterPathExpHelper: IInterpreterPathService, ) { // Acquire other objects here so that if we are called during dispose they are available. this.disposables = this.serviceContainer.get(IDisposableRegistry); diff --git a/src/client/common/serviceRegistry.ts b/src/client/common/serviceRegistry.ts index 2a2c36211bc7..23e2a1162b84 100644 --- a/src/client/common/serviceRegistry.ts +++ b/src/client/common/serviceRegistry.ts @@ -19,7 +19,6 @@ import { IToolExecutionPath, IsWindows, ToolExecutionPath, - IInterpreterPathProxyService, } from './types'; import { IServiceManager } from '../ioc/types'; import { JupyterExtensionDependencyManager } from '../jupyter/jupyterExtensionDependencyManager'; @@ -110,17 +109,12 @@ import { import { IMultiStepInputFactory, MultiStepInputFactory } from './utils/multiStepInput'; import { Random } from './utils/random'; -import { InterpreterPathProxyService } from './interpreterPathProxyService'; import { ContextKeyManager } from './application/contextKeyManager'; export function registerTypes(serviceManager: IServiceManager): void { serviceManager.addSingletonInstance(IsWindows, IS_WINDOWS); serviceManager.addSingleton(IActiveResourceService, ActiveResourceService); - serviceManager.addSingleton( - IInterpreterPathProxyService, - InterpreterPathProxyService, - ); serviceManager.addSingleton(IInterpreterPathService, InterpreterPathService); serviceManager.addSingleton(IExtensions, Extensions); serviceManager.addSingleton(IRandom, Random); diff --git a/src/client/common/types.ts b/src/client/common/types.ts index f71ebf890645..a3b3abcf083b 100644 --- a/src/client/common/types.ts +++ b/src/client/common/types.ts @@ -470,15 +470,6 @@ export interface IInterpreterPathService { get(resource: Resource): string; inspect(resource: Resource): InspectInterpreterSettingType; update(resource: Resource, configTarget: ConfigurationTarget, value: string | undefined): Promise; - copyOldInterpreterStorageValuesToNew(resource: Uri | undefined): Promise; -} - -/** - * Interface used to access current Interpreter Path - */ -export const IInterpreterPathProxyService = Symbol('IInterpreterPathProxyService'); -export interface IInterpreterPathProxyService { - get(resource: Resource): string; } export type DefaultLSType = LanguageServerType.Jedi | LanguageServerType.Node; diff --git a/src/client/interpreter/configuration/pythonPathUpdaterServiceFactory.ts b/src/client/interpreter/configuration/pythonPathUpdaterServiceFactory.ts index 2bd97beaa39c..ff42f53bcb5b 100644 --- a/src/client/interpreter/configuration/pythonPathUpdaterServiceFactory.ts +++ b/src/client/interpreter/configuration/pythonPathUpdaterServiceFactory.ts @@ -1,8 +1,6 @@ import { inject, injectable } from 'inversify'; import { Uri } from 'vscode'; -import { IWorkspaceService } from '../../common/application/types'; -import { DeprecatePythonPath } from '../../common/experiments/groups'; -import { IExperimentService, IInterpreterPathService } from '../../common/types'; +import { IInterpreterPathService } from '../../common/types'; import { IServiceContainer } from '../../ioc/types'; import { GlobalPythonPathUpdaterService } from './services/globalUpdaterService'; import { WorkspaceFolderPythonPathUpdaterService } from './services/workspaceFolderUpdaterService'; @@ -11,36 +9,17 @@ import { IPythonPathUpdaterService, IPythonPathUpdaterServiceFactory } from './t @injectable() export class PythonPathUpdaterServiceFactory implements IPythonPathUpdaterServiceFactory { - private readonly inDeprecatePythonPathExperiment: boolean; - private readonly workspaceService: IWorkspaceService; private readonly interpreterPathService: IInterpreterPathService; constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) { - const experiments = serviceContainer.get(IExperimentService); - this.workspaceService = serviceContainer.get(IWorkspaceService); this.interpreterPathService = serviceContainer.get(IInterpreterPathService); - this.inDeprecatePythonPathExperiment = experiments.inExperimentSync(DeprecatePythonPath.experiment); } public getGlobalPythonPathConfigurationService(): IPythonPathUpdaterService { - return new GlobalPythonPathUpdaterService( - this.inDeprecatePythonPathExperiment, - this.workspaceService, - this.interpreterPathService, - ); + return new GlobalPythonPathUpdaterService(this.interpreterPathService); } public getWorkspacePythonPathConfigurationService(wkspace: Uri): IPythonPathUpdaterService { - return new WorkspacePythonPathUpdaterService( - wkspace, - this.inDeprecatePythonPathExperiment, - this.workspaceService, - this.interpreterPathService, - ); + return new WorkspacePythonPathUpdaterService(wkspace, this.interpreterPathService); } public getWorkspaceFolderPythonPathConfigurationService(workspaceFolder: Uri): IPythonPathUpdaterService { - return new WorkspaceFolderPythonPathUpdaterService( - workspaceFolder, - this.inDeprecatePythonPathExperiment, - this.workspaceService, - this.interpreterPathService, - ); + return new WorkspaceFolderPythonPathUpdaterService(workspaceFolder, this.interpreterPathService); } } diff --git a/src/client/interpreter/configuration/services/globalUpdaterService.ts b/src/client/interpreter/configuration/services/globalUpdaterService.ts index 9070feaf7c87..1cf2a7cc478f 100644 --- a/src/client/interpreter/configuration/services/globalUpdaterService.ts +++ b/src/client/interpreter/configuration/services/globalUpdaterService.ts @@ -1,27 +1,15 @@ import { ConfigurationTarget } from 'vscode'; -import { IWorkspaceService } from '../../../common/application/types'; import { IInterpreterPathService } from '../../../common/types'; import { IPythonPathUpdaterService } from '../types'; export class GlobalPythonPathUpdaterService implements IPythonPathUpdaterService { - constructor( - private readonly inDeprecatePythonPathExperiment: boolean, - private readonly workspaceService: IWorkspaceService, - private readonly interpreterPathService: IInterpreterPathService, - ) {} + constructor(private readonly interpreterPathService: IInterpreterPathService) {} public async updatePythonPath(pythonPath: string | undefined): Promise { - const pythonConfig = this.workspaceService.getConfiguration('python'); - const pythonPathValue = this.inDeprecatePythonPathExperiment - ? this.interpreterPathService.inspect(undefined) - : pythonConfig.inspect('pythonPath')!; + const pythonPathValue = this.interpreterPathService.inspect(undefined); if (pythonPathValue && pythonPathValue.globalValue === pythonPath) { return; } - if (this.inDeprecatePythonPathExperiment) { - await this.interpreterPathService.update(undefined, ConfigurationTarget.Global, pythonPath); - } else { - await pythonConfig.update('pythonPath', pythonPath, true); - } + await this.interpreterPathService.update(undefined, ConfigurationTarget.Global, pythonPath); } } diff --git a/src/client/interpreter/configuration/services/workspaceFolderUpdaterService.ts b/src/client/interpreter/configuration/services/workspaceFolderUpdaterService.ts index b932f2abf1fa..088d7129f3bf 100644 --- a/src/client/interpreter/configuration/services/workspaceFolderUpdaterService.ts +++ b/src/client/interpreter/configuration/services/workspaceFolderUpdaterService.ts @@ -1,21 +1,12 @@ import * as path from 'path'; import { ConfigurationTarget, Uri } from 'vscode'; -import { IWorkspaceService } from '../../../common/application/types'; import { IInterpreterPathService } from '../../../common/types'; import { IPythonPathUpdaterService } from '../types'; export class WorkspaceFolderPythonPathUpdaterService implements IPythonPathUpdaterService { - constructor( - private workspaceFolder: Uri, - private readonly inDeprecatePythonPathExperiment: boolean, - private readonly workspaceService: IWorkspaceService, - private readonly interpreterPathService: IInterpreterPathService, - ) {} + constructor(private workspaceFolder: Uri, private readonly interpreterPathService: IInterpreterPathService) {} public async updatePythonPath(pythonPath: string | undefined): Promise { - const pythonConfig = this.workspaceService.getConfiguration('python', this.workspaceFolder); - const pythonPathValue = this.inDeprecatePythonPathExperiment - ? this.interpreterPathService.inspect(this.workspaceFolder) - : pythonConfig.inspect('pythonPath')!; + const pythonPathValue = this.interpreterPathService.inspect(this.workspaceFolder); if (pythonPathValue && pythonPathValue.workspaceFolderValue === pythonPath) { return; @@ -23,14 +14,6 @@ export class WorkspaceFolderPythonPathUpdaterService implements IPythonPathUpdat if (pythonPath && pythonPath.startsWith(this.workspaceFolder.fsPath)) { pythonPath = path.relative(this.workspaceFolder.fsPath, pythonPath); } - if (this.inDeprecatePythonPathExperiment) { - await this.interpreterPathService.update( - this.workspaceFolder, - ConfigurationTarget.WorkspaceFolder, - pythonPath, - ); - } else { - await pythonConfig.update('pythonPath', pythonPath, ConfigurationTarget.WorkspaceFolder); - } + await this.interpreterPathService.update(this.workspaceFolder, ConfigurationTarget.WorkspaceFolder, pythonPath); } } diff --git a/src/client/interpreter/configuration/services/workspaceUpdaterService.ts b/src/client/interpreter/configuration/services/workspaceUpdaterService.ts index 3cd3270a0caf..7e99dc58fb7f 100644 --- a/src/client/interpreter/configuration/services/workspaceUpdaterService.ts +++ b/src/client/interpreter/configuration/services/workspaceUpdaterService.ts @@ -1,21 +1,12 @@ import * as path from 'path'; import { ConfigurationTarget, Uri } from 'vscode'; -import { IWorkspaceService } from '../../../common/application/types'; import { IInterpreterPathService } from '../../../common/types'; import { IPythonPathUpdaterService } from '../types'; export class WorkspacePythonPathUpdaterService implements IPythonPathUpdaterService { - constructor( - private workspace: Uri, - private readonly inDeprecatePythonPathExperiment: boolean, - private readonly workspaceService: IWorkspaceService, - private readonly interpreterPathService: IInterpreterPathService, - ) {} + constructor(private workspace: Uri, private readonly interpreterPathService: IInterpreterPathService) {} public async updatePythonPath(pythonPath: string | undefined): Promise { - const pythonConfig = this.workspaceService.getConfiguration('python', this.workspace); - const pythonPathValue = this.inDeprecatePythonPathExperiment - ? this.interpreterPathService.inspect(this.workspace) - : pythonConfig.inspect('pythonPath')!; + const pythonPathValue = this.interpreterPathService.inspect(this.workspace); if (pythonPathValue && pythonPathValue.workspaceValue === pythonPath) { return; @@ -23,10 +14,6 @@ export class WorkspacePythonPathUpdaterService implements IPythonPathUpdaterServ if (pythonPath && pythonPath.startsWith(this.workspace.fsPath)) { pythonPath = path.relative(this.workspace.fsPath, pythonPath); } - if (this.inDeprecatePythonPathExperiment) { - await this.interpreterPathService.update(this.workspace, ConfigurationTarget.Workspace, pythonPath); - } else { - await pythonConfig.update('pythonPath', pythonPath, false); - } + await this.interpreterPathService.update(this.workspace, ConfigurationTarget.Workspace, pythonPath); } } diff --git a/src/client/interpreter/interpreterService.ts b/src/client/interpreter/interpreterService.ts index eb93042a978f..cd6f46314977 100644 --- a/src/client/interpreter/interpreterService.ts +++ b/src/client/interpreter/interpreterService.ts @@ -1,15 +1,9 @@ import { inject, injectable } from 'inversify'; import { Disposable, Event, EventEmitter, Uri } from 'vscode'; import '../common/extensions'; -import { IDocumentManager, IWorkspaceService } from '../common/application/types'; -import { DeprecatePythonPath } from '../common/experiments/groups'; +import { IDocumentManager } from '../common/application/types'; import { IPythonExecutionFactory } from '../common/process/types'; -import { - IConfigurationService, - IDisposableRegistry, - IExperimentService, - IInterpreterPathService, -} from '../common/types'; +import { IConfigurationService, IDisposableRegistry, IInterpreterPathService } from '../common/types'; import { IServiceContainer } from '../ioc/types'; import { PythonEnvironment } from '../pythonEnvironments/info'; import { @@ -65,8 +59,6 @@ export class InterpreterService implements Disposable, IInterpreterService { private readonly interpreterPathService: IInterpreterPathService; - private readonly experimentsManager: IExperimentService; - private readonly didChangeInterpreterEmitter = new EventEmitter(); private readonly didChangeInterpreterInformation = new EventEmitter(); @@ -77,7 +69,6 @@ export class InterpreterService implements Disposable, IInterpreterService { ) { this.configService = this.serviceContainer.get(IConfigurationService); this.interpreterPathService = this.serviceContainer.get(IInterpreterPathService); - this.experimentsManager = this.serviceContainer.get(IExperimentService); this.onDidChangeInterpreters = pyenvs.onChanged; } @@ -94,28 +85,13 @@ export class InterpreterService implements Disposable, IInterpreterService { e && e.document ? this.refresh(e.document.uri) : undefined, ), ); - const workspaceService = this.serviceContainer.get(IWorkspaceService); const pySettings = this.configService.getSettings(); this._pythonPathSetting = pySettings.pythonPath; - if (this.experimentsManager.inExperimentSync(DeprecatePythonPath.experiment)) { - disposables.push( - this.interpreterPathService.onDidChange((i) => { - this._onConfigChanged(i.uri); - }), - ); - } else { - const workspacesUris: (Uri | undefined)[] = workspaceService.hasWorkspaceFolders - ? workspaceService.workspaceFolders!.map((workspace) => workspace.uri) - : [undefined]; - const disposable = workspaceService.onDidChangeConfiguration((e) => { - const workspaceUriIndex = workspacesUris.findIndex((uri) => - e.affectsConfiguration('python.pythonPath', uri), - ); - const workspaceUri = workspaceUriIndex === -1 ? undefined : workspacesUris[workspaceUriIndex]; - this._onConfigChanged(workspaceUri); - }); - disposables.push(disposable); - } + disposables.push( + this.interpreterPathService.onDidChange((i) => { + this._onConfigChanged(i.uri); + }), + ); } public getInterpreters(resource?: Uri): PythonEnvironment[] { diff --git a/src/client/startupTelemetry.ts b/src/client/startupTelemetry.ts index b9dff5974492..b85c6c177f34 100644 --- a/src/client/startupTelemetry.ts +++ b/src/client/startupTelemetry.ts @@ -3,15 +3,8 @@ import { IWorkspaceService } from './common/application/types'; import { isTestExecution } from './common/constants'; -import { DeprecatePythonPath } from './common/experiments/groups'; import { ITerminalHelper } from './common/terminal/types'; -import { - IConfigurationService, - IExperimentService, - IInterpreterPathService, - InspectInterpreterSettingType, - Resource, -} from './common/types'; +import { IConfigurationService, IInterpreterPathService, Resource } from './common/types'; import { IStopWatch } from './common/utils/stopWatch'; import { IInterpreterAutoSelectionService } from './interpreter/autoSelection/types'; import { ICondaService, IInterpreterService } from './interpreter/contracts'; @@ -73,15 +66,8 @@ function isUsingGlobalInterpreterInWorkspace(currentPythonPath: string, serviceC } export function hasUserDefinedPythonPath(resource: Resource, serviceContainer: IServiceContainer) { - const abExperiments = serviceContainer.get(IExperimentService); - const workspaceService = serviceContainer.get(IWorkspaceService); const interpreterPathService = serviceContainer.get(IInterpreterPathService); - let settings: InspectInterpreterSettingType; - if (abExperiments.inExperimentSync(DeprecatePythonPath.experiment)) { - settings = interpreterPathService.inspect(resource); - } else { - settings = workspaceService.getConfiguration('python', resource)!.inspect('pythonPath')!; - } + let settings = interpreterPathService.inspect(resource); return (settings.workspaceFolderValue && settings.workspaceFolderValue !== 'python') || (settings.workspaceValue && settings.workspaceValue !== 'python') || (settings.globalValue && settings.globalValue !== 'python') diff --git a/src/test/activation/activationManager.unit.test.ts b/src/test/activation/activationManager.unit.test.ts index 8b73247bfae4..9188cb74ac15 100644 --- a/src/test/activation/activationManager.unit.test.ts +++ b/src/test/activation/activationManager.unit.test.ts @@ -16,11 +16,9 @@ import { ActiveResourceService } from '../../client/common/application/activeRes import { IActiveResourceService, IDocumentManager, IWorkspaceService } from '../../client/common/application/types'; import { WorkspaceService } from '../../client/common/application/workspace'; import { PYTHON_LANGUAGE } from '../../client/common/constants'; -import { DeprecatePythonPath } from '../../client/common/experiments/groups'; -import { ExperimentService } from '../../client/common/experiments/service'; import { FileSystem } from '../../client/common/platform/fileSystem'; import { IFileSystem } from '../../client/common/platform/types'; -import { IDisposable, IExperimentService, IInterpreterPathService } from '../../client/common/types'; +import { IDisposable, IInterpreterPathService } from '../../client/common/types'; import { IInterpreterAutoSelectionService } from '../../client/interpreter/autoSelection/types'; import * as EnvFileTelemetry from '../../client/telemetry/envFileTelemetry'; import { sleep } from '../core'; @@ -47,12 +45,10 @@ suite('Activation Manager', () => { let activeResourceService: IActiveResourceService; let documentManager: typemoq.IMock; let interpreterPathService: typemoq.IMock; - let experiments: IExperimentService; let activationService1: IExtensionActivationService; let activationService2: IExtensionActivationService; let fileSystem: IFileSystem; setup(() => { - experiments = mock(ExperimentService); interpreterPathService = typemoq.Mock.ofType(); workspaceService = mock(WorkspaceService); activeResourceService = mock(ActiveResourceService); @@ -84,8 +80,6 @@ suite('Activation Manager', () => { instance(workspaceService), instance(fileSystem), instance(activeResourceService), - instance(experiments), - interpreterPathService.object, ); sinon.stub(EnvFileTelemetry, 'sendActivationTelemetry').resolves(); @@ -127,8 +121,6 @@ suite('Activation Manager', () => { instance(workspaceService), instance(fileSystem), instance(activeResourceService), - instance(experiments), - interpreterPathService.object, ); await managerTest.activateWorkspace(resource); @@ -170,8 +162,6 @@ suite('Activation Manager', () => { instance(workspaceService), instance(fileSystem), instance(activeResourceService), - instance(experiments), - interpreterPathService.object, ); await managerTest.activateWorkspace(resource); @@ -337,32 +327,6 @@ suite('Activation Manager', () => { verify(activationService2.activate(resource)).once(); }); - test('If in Deprecate PythonPath experiment, method activateWorkspace() will copy old interpreter storage values to new', async () => { - const resource = Uri.parse('two'); - when(activationService1.activate(resource)).thenResolve(); - when(activationService2.activate(resource)).thenResolve(); - - when(experiments.inExperimentSync(DeprecatePythonPath.experiment)).thenReturn(true); - interpreterPathService - .setup((i) => i.copyOldInterpreterStorageValuesToNew(resource)) - .returns(() => Promise.resolve()) - .verifiable(typemoq.Times.once()); - autoSelection - .setup((a) => a.autoSelectInterpreter(resource)) - .returns(() => Promise.resolve()) - .verifiable(typemoq.Times.once()); - appDiagnostics - .setup((a) => a.performPreStartupHealthCheck(resource)) - .returns(() => Promise.resolve()) - .verifiable(typemoq.Times.once()); - - await managerTest.activateWorkspace(resource); - - interpreterPathService.verifyAll(); - verify(activationService1.activate(resource)).once(); - verify(activationService2.activate(resource)).once(); - }); - test("The same workspace isn't activated more than once", async () => { const resource = Uri.parse('two'); when(activationService1.activate(resource)).thenResolve(); @@ -501,10 +465,8 @@ suite('Activation Manager', () => { let managerTest: ExtensionActivationManager; const resource = Uri.parse('a'); let interpreterPathService: typemoq.IMock; - let experiments: IExperimentService; setup(() => { - experiments = mock(ExperimentService); workspaceService = mock(WorkspaceService); activeResourceService = mock(ActiveResourceService); appDiagnostics = typemoq.Mock.ofType(); @@ -541,8 +503,6 @@ suite('Activation Manager', () => { instance(workspaceService), instance(fileSystem), instance(activeResourceService), - instance(experiments), - interpreterPathService.object, ); }); diff --git a/src/test/application/diagnostics/checks/macPythonInterpreter.unit.test.ts b/src/test/application/diagnostics/checks/macPythonInterpreter.unit.test.ts index 2d0e69c4ef51..5421cbcf99ee 100644 --- a/src/test/application/diagnostics/checks/macPythonInterpreter.unit.test.ts +++ b/src/test/application/diagnostics/checks/macPythonInterpreter.unit.test.ts @@ -3,9 +3,8 @@ 'use strict'; -import { assert, expect } from 'chai'; +import { expect } from 'chai'; import * as typemoq from 'typemoq'; -import { ConfigurationChangeEvent, Uri } from 'vscode'; import { BaseDiagnosticsService } from '../../../../client/application/diagnostics/base'; import { InvalidMacPythonInterpreterDiagnostic, @@ -27,16 +26,13 @@ import { } from '../../../../client/application/diagnostics/types'; import { CommandsWithoutArgs } from '../../../../client/common/application/commands'; import { IWorkspaceService } from '../../../../client/common/application/types'; -import { DeprecatePythonPath } from '../../../../client/common/experiments/groups'; import { IPlatformService } from '../../../../client/common/platform/types'; import { IConfigurationService, IDisposableRegistry, - IExperimentService, IInterpreterPathService, InterpreterConfigurationScope, IPythonSettings, - Resource, } from '../../../../client/common/types'; import { sleep } from '../../../../client/common/utils/async'; import { noop } from '../../../../client/common/utils/misc'; @@ -53,6 +49,7 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => { let platformService: typemoq.IMock; let helper: typemoq.IMock; let filterService: typemoq.IMock; + let interpreterPathService: typemoq.IMock; const pythonPath = 'My Python Path in Settings'; let serviceContainer: typemoq.IMock; function createContainer() { @@ -93,6 +90,10 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => { .setup((s) => s.get(typemoq.It.isValue(IDiagnosticFilterService))) .returns(() => filterService.object); + interpreterPathService = typemoq.Mock.ofType(); + serviceContainer + .setup((s) => s.get(typemoq.It.isValue(IInterpreterPathService))) + .returns(() => interpreterPathService.object); platformService .setup((p) => p.isMac) .returns(() => true) @@ -460,50 +461,8 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => { expect(invoked).to.be.equal(true, 'Not invoked'); }); - test('Event Handler is registered and invoked', async () => { - let invoked = false; - let callbackHandler!: (e: ConfigurationChangeEvent) => Promise; - const workspaceService = { - onDidChangeConfiguration: (cb: (e: ConfigurationChangeEvent) => Promise) => - (callbackHandler = cb), - } as any; - const serviceContainerObject = createContainer(); - - serviceContainer.setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))).returns(() => workspaceService); - const experiments = typemoq.Mock.ofType(); - serviceContainer - .setup((s) => s.get(typemoq.It.isValue(IExperimentService))) - .returns(() => experiments.object); - experiments.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); - diagnosticService = new (class extends InvalidMacPythonInterpreterService { - protected async onDidChangeConfiguration(_event: ConfigurationChangeEvent) { - invoked = true; - } - })(serviceContainerObject, undefined as any, [], undefined as any, undefined as any); - - await callbackHandler({} as any); - expect(invoked).to.be.equal(true, 'Not invoked'); - }); - test('Event Handler is registered and not invoked', async () => { - let invoked = false; - const workspaceService = { onDidChangeConfiguration: noop } as any; - const serviceContainerObject = createContainer(); - serviceContainer.setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))).returns(() => workspaceService); - const experiments = typemoq.Mock.ofType(); - serviceContainer - .setup((s) => s.get(typemoq.It.isValue(IExperimentService))) - .returns(() => experiments.object); - experiments.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); - diagnosticService = new (class extends InvalidMacPythonInterpreterService { - protected async onDidChangeConfiguration(_event: ConfigurationChangeEvent) { - invoked = true; - } - })(serviceContainerObject, undefined as any, [], undefined as any, undefined as any); - - expect(invoked).to.be.equal(false, 'Not invoked'); - }); - test('Diagnostics are checked with Config change event uri when path changes and event is passed', async () => { - const event = typemoq.Mock.ofType(); + test('Diagnostics are checked with correct interpreter config uri when path changes', async () => { + const event = typemoq.Mock.ofType(); const workspaceService = typemoq.Mock.ofType(); const serviceContainerObject = createContainer(); let diagnoseInvocationCount = 0; @@ -518,11 +477,7 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => { serviceContainer .setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); - const experiments = typemoq.Mock.ofType(); - serviceContainer - .setup((s) => s.get(typemoq.It.isValue(IExperimentService))) - .returns(() => experiments.object); - experiments.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); + const diagnosticSvc = new (class extends InvalidMacPythonInterpreterService { constructor( arg1: IServiceContainer, @@ -533,7 +488,8 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => { super(arg1, arg2, [], arg3, arg4); this.changeThrottleTimeout = 1; } - public onDidChangeConfigurationEx = (e: ConfigurationChangeEvent) => super.onDidChangeConfiguration(e); + public onDidChangeConfigurationEx = (e: InterpreterConfigurationScope) => + super.onDidChangeConfiguration(e); public diagnose(): Promise { diagnoseInvocationCount += 1; return Promise.resolve(); @@ -545,11 +501,6 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => { typemoq.Mock.ofType().object, ); - event - .setup((e) => e.affectsConfiguration(typemoq.It.isValue('python.pythonPath'), typemoq.It.isAny())) - .returns(() => true) - .verifiable(typemoq.Times.atLeastOnce()); - await diagnosticSvc.onDidChangeConfigurationEx(event.object); event.verifyAll(); await sleep(100); @@ -560,91 +511,8 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => { expect(diagnoseInvocationCount).to.be.equal(2, 'Not invoked'); }); - test('Diagnostics are checked with correct interpreter config uri when path changes and only config uri is passed', async () => { - const configUri = Uri.parse('i'); - const interpreterConfigurationScope = typemoq.Mock.ofType(); - interpreterConfigurationScope.setup((i) => i.uri).returns(() => Uri.parse('i')); - const workspaceService = typemoq.Mock.ofType(); - const serviceContainerObject = createContainer(); - let diagnoseInvocationCount = 0; - serviceContainer - .setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))) - .returns(() => workspaceService.object); - const experiments = typemoq.Mock.ofType(); - serviceContainer - .setup((s) => s.get(typemoq.It.isValue(IExperimentService))) - .returns(() => experiments.object); - experiments.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); - const diagnosticSvc = new (class extends InvalidMacPythonInterpreterService { - constructor( - arg1: IServiceContainer, - arg2: IInterpreterService, - arg3: IPlatformService, - arg4: IInterpreterHelper, - ) { - super(arg1, arg2, [], arg3, arg4); - this.changeThrottleTimeout = 1; - } - public onDidChangeConfigurationEx = (e?: ConfigurationChangeEvent, i?: InterpreterConfigurationScope) => - super.onDidChangeConfiguration(e, i); - public diagnose(resource: Resource): Promise { - diagnoseInvocationCount += 1; - assert.deepEqual(resource, configUri); - return Promise.resolve(); - } - })( - serviceContainerObject, - typemoq.Mock.ofType().object, - typemoq.Mock.ofType().object, - typemoq.Mock.ofType().object, - ); - - await diagnosticSvc.onDidChangeConfigurationEx(undefined, interpreterConfigurationScope.object); - await sleep(100); - expect(diagnoseInvocationCount).to.be.equal(1, 'Not invoked'); - - await diagnosticSvc.onDidChangeConfigurationEx(undefined, interpreterConfigurationScope.object); - await sleep(100); - expect(diagnoseInvocationCount).to.be.equal(2, 'Not invoked'); - }); - - test('Diagnostics throws error when none of config uri or config change event uri is passed', async () => { - const workspaceService = typemoq.Mock.ofType(); - const serviceContainerObject = createContainer(); - serviceContainer - .setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))) - .returns(() => workspaceService.object); - const experiments = typemoq.Mock.ofType(); - serviceContainer - .setup((s) => s.get(typemoq.It.isValue(IExperimentService))) - .returns(() => experiments.object); - experiments.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); - const diagnosticSvc = new (class extends InvalidMacPythonInterpreterService { - constructor( - arg1: IServiceContainer, - arg2: IInterpreterService, - arg3: IPlatformService, - arg4: IInterpreterHelper, - ) { - super(arg1, arg2, [], arg3, arg4); - this.changeThrottleTimeout = 1; - } - public onDidChangeConfigurationEx = (e?: ConfigurationChangeEvent, i?: InterpreterConfigurationScope) => - super.onDidChangeConfiguration(e, i); - })( - serviceContainerObject, - typemoq.Mock.ofType().object, - typemoq.Mock.ofType().object, - typemoq.Mock.ofType().object, - ); - - await expect(diagnosticSvc.onDidChangeConfigurationEx(undefined, undefined)).to.eventually.be.rejectedWith( - Error, - ); - }); - test('Diagnostics are checked and throttled when path changes', async () => { - const event = typemoq.Mock.ofType(); + const event = typemoq.Mock.ofType(); const workspaceService = typemoq.Mock.ofType(); const serviceContainerObject = createContainer(); let diagnoseInvocationCount = 0; @@ -659,11 +527,7 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => { serviceContainer .setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))) .returns(() => workspaceService.object); - const experiments = typemoq.Mock.ofType(); - serviceContainer - .setup((s) => s.get(typemoq.It.isValue(IExperimentService))) - .returns(() => experiments.object); - experiments.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); + const diagnosticSvc = new (class extends InvalidMacPythonInterpreterService { constructor( arg1: IServiceContainer, @@ -674,7 +538,8 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => { super(arg1, arg2, [], arg3, arg4); this.changeThrottleTimeout = 100; } - public onDidChangeConfigurationEx = (e: ConfigurationChangeEvent) => super.onDidChangeConfiguration(e); + public onDidChangeConfigurationEx = (e: InterpreterConfigurationScope) => + super.onDidChangeConfiguration(e); public diagnose(): Promise { diagnoseInvocationCount += 1; return Promise.resolve(); @@ -686,52 +551,39 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => { typemoq.Mock.ofType().object, ); - event - .setup((e) => e.affectsConfiguration(typemoq.It.isValue('python.pythonPath'), typemoq.It.isAny())) - .returns(() => true) - .verifiable(typemoq.Times.atLeastOnce()); - await diagnosticSvc.onDidChangeConfigurationEx(event.object); await diagnosticSvc.onDidChangeConfigurationEx(event.object); await diagnosticSvc.onDidChangeConfigurationEx(event.object); await diagnosticSvc.onDidChangeConfigurationEx(event.object); await diagnosticSvc.onDidChangeConfigurationEx(event.object); await sleep(500); - event.verifyAll(); expect(diagnoseInvocationCount).to.be.equal(1, 'Not invoked'); }); - test('Ensure event Handler is registered correctly if in Deprecate Python path experiment', async () => { + test('Ensure event Handler is registered correctly', async () => { let interpreterPathServiceHandler: Function; + let invoked = false; const workspaceService = { onDidChangeConfiguration: noop } as any; const serviceContainerObject = createContainer(); - const interpreterPathService = typemoq.Mock.ofType(); interpreterPathService .setup((d) => d.onDidChange(typemoq.It.isAny(), typemoq.It.isAny())) .callback((cb) => (interpreterPathServiceHandler = cb)) .returns(() => { return { dispose: noop }; }); - serviceContainer - .setup((s) => s.get(typemoq.It.isValue(IInterpreterPathService))) - .returns(() => interpreterPathService.object); serviceContainer.setup((s) => s.get(typemoq.It.isValue(IWorkspaceService))).returns(() => workspaceService); - const experiments = typemoq.Mock.ofType(); - serviceContainer - .setup((s) => s.get(typemoq.It.isValue(IExperimentService))) - .returns(() => experiments.object); - experiments.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); - diagnosticService = new (class extends InvalidMacPythonInterpreterService {})( - serviceContainerObject, - undefined as any, - [], - undefined as any, - undefined as any, - ); + + diagnosticService = new (class extends InvalidMacPythonInterpreterService { + protected async onDidChangeConfiguration(_i: InterpreterConfigurationScope) { + invoked = true; + } + })(serviceContainerObject, undefined as any, [], undefined as any, undefined as any); expect(interpreterPathServiceHandler!).to.not.equal(undefined, 'Handler not set'); + await interpreterPathServiceHandler!({} as any); + expect(invoked).to.be.equal(true, 'Not invoked'); }); }); }); diff --git a/src/test/application/diagnostics/checks/pythonPathDeprecated.unit.test.ts b/src/test/application/diagnostics/checks/pythonPathDeprecated.unit.test.ts index efe0a406eeac..82495623af3b 100644 --- a/src/test/application/diagnostics/checks/pythonPathDeprecated.unit.test.ts +++ b/src/test/application/diagnostics/checks/pythonPathDeprecated.unit.test.ts @@ -26,7 +26,6 @@ import { IDiagnosticHandlerService, } from '../../../../client/application/diagnostics/types'; import { IWorkspaceService } from '../../../../client/common/application/types'; -import { DeprecatePythonPath } from '../../../../client/common/experiments/groups'; import { IDisposableRegistry, IExperimentService, Resource } from '../../../../client/common/types'; import { Common, Diagnostics } from '../../../../client/common/utils/localize'; import { IServiceContainer } from '../../../../client/ioc/types'; @@ -227,31 +226,7 @@ suite('Application Diagnostics - Python Path Deprecated', () => { commandFactory.verifyAll(); }); - test('If not in DeprecatePythonPath experiment, empty diagnostics is returned', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); - const workspaceConfig = typemoq.Mock.ofType(); - workspaceService - .setup((w) => w.getConfiguration('python', resource)) - .returns(() => workspaceConfig.object) - .verifiable(typemoq.Times.never()); - workspaceConfig - .setup((w) => w.inspect('pythonPath')) - .returns( - () => - ({ - workspaceFolderValue: 'workspaceFolderValue', - workspaceValue: 'workspaceValue', - } as any), - ); - - const diagnostics = await diagnosticService.diagnose(resource); - assert.deepEqual(diagnostics, []); - - workspaceService.verifyAll(); - }); - test('If a workspace is opened and only workspace value is set, diagnostic with appropriate message is returned', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); const workspaceConfig = typemoq.Mock.ofType(); workspaceService.setup((w) => w.workspaceFile).returns(() => Uri.parse('path/to/workspaceFile')); workspaceService @@ -276,7 +251,6 @@ suite('Application Diagnostics - Python Path Deprecated', () => { }); test('If folder is directly opened and workspace folder value is set, diagnostic with appropriate message is returned', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); const workspaceConfig = typemoq.Mock.ofType(); workspaceService.setup((w) => w.workspaceFile).returns(() => undefined); workspaceService @@ -302,7 +276,6 @@ suite('Application Diagnostics - Python Path Deprecated', () => { }); test('If a workspace is opened and both workspace folder value & workspace value is set, diagnostic with appropriate message is returned', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); const workspaceConfig = typemoq.Mock.ofType(); workspaceService.setup((w) => w.workspaceFile).returns(() => Uri.parse('path/to/workspaceFile')); workspaceService @@ -328,7 +301,6 @@ suite('Application Diagnostics - Python Path Deprecated', () => { }); test('Otherwise an empty diagnostic is returned', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); const workspaceConfig = typemoq.Mock.ofType(); workspaceService.setup((w) => w.workspaceFile).returns(() => Uri.parse('path/to/workspaceFile')); workspaceService diff --git a/src/test/application/diagnostics/checks/upgradeCodeRunner.unit.test.ts b/src/test/application/diagnostics/checks/upgradeCodeRunner.unit.test.ts index 4240186bd04a..86b32668d276 100644 --- a/src/test/application/diagnostics/checks/upgradeCodeRunner.unit.test.ts +++ b/src/test/application/diagnostics/checks/upgradeCodeRunner.unit.test.ts @@ -26,7 +26,6 @@ import { } from '../../../../client/application/diagnostics/types'; import { IWorkspaceService } from '../../../../client/common/application/types'; import { CODE_RUNNER_EXTENSION_ID } from '../../../../client/common/constants'; -import { DeprecatePythonPath } from '../../../../client/common/experiments/groups'; import { IDisposableRegistry, IExperimentService, IExtensions, Resource } from '../../../../client/common/types'; import { Common, Diagnostics } from '../../../../client/common/utils/localize'; import { IServiceContainer } from '../../../../client/ioc/types'; @@ -225,16 +224,7 @@ suite('Application Diagnostics - Upgrade Code Runner', () => { assert.deepEqual(diagnostics, []); }); - test('If not in DeprecatePythonPath experiment, empty diagnostics is returned', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); - - const diagnostics = await diagnosticService.diagnose(resource); - - assert.deepEqual(diagnostics, []); - }); - test('If Code Runner extension is not installed, empty diagnostics is returned', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); extensions.setup((e) => e.getExtension(CODE_RUNNER_EXTENSION_ID)).returns(() => undefined); const diagnostics = await diagnosticService.diagnose(resource); @@ -243,7 +233,6 @@ suite('Application Diagnostics - Upgrade Code Runner', () => { }); test('If Code Runner extension is installed but the appropriate feature flag is set in package.json, empty diagnostics is returned', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); const extension = typemoq.Mock.ofType>(); extensions.setup((e) => e.getExtension(CODE_RUNNER_EXTENSION_ID)).returns(() => extension.object); extension @@ -264,7 +253,6 @@ suite('Application Diagnostics - Upgrade Code Runner', () => { }); test('If old version of Code Runner extension is installed but setting `code-runner.executorMap.python` is not set, empty diagnostics is returned', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); const workspaceConfig = typemoq.Mock.ofType(); const extension = typemoq.Mock.ofType>(); extensions.setup((e) => e.getExtension(CODE_RUNNER_EXTENSION_ID)).returns(() => extension.object); @@ -282,7 +270,6 @@ suite('Application Diagnostics - Upgrade Code Runner', () => { }); test('If old version of Code Runner extension is installed but $pythonPath is not being used, empty diagnostics is returned', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); const workspaceConfig = typemoq.Mock.ofType(); const extension = typemoq.Mock.ofType>(); extensions.setup((e) => e.getExtension(CODE_RUNNER_EXTENSION_ID)).returns(() => extension.object); @@ -300,7 +287,6 @@ suite('Application Diagnostics - Upgrade Code Runner', () => { }); test('If old version of Code Runner extension is installed and $pythonPath is being used, diagnostic with appropriate message is returned', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); const workspaceConfig = typemoq.Mock.ofType(); const extension = typemoq.Mock.ofType>(); extensions.setup((e) => e.getExtension(CODE_RUNNER_EXTENSION_ID)).returns(() => extension.object); diff --git a/src/test/common.ts b/src/test/common.ts index 6a0738303a77..df419b494241 100644 --- a/src/test/common.ts +++ b/src/test/common.ts @@ -2,6 +2,8 @@ // Licensed under the MIT License. 'use strict'; +// IMPORTANT: Do not import anything from the 'client' folder in this file as that folder is not available during smoke tests. + import * as assert from 'assert'; import * as fs from 'fs-extra'; import * as glob from 'glob'; @@ -10,9 +12,8 @@ import { coerce, SemVer } from 'semver'; import { ConfigurationTarget, Event, TextDocument, Uri } from 'vscode'; import { IExtensionApi } from '../client/api'; import { IProcessService } from '../client/common/process/types'; -import { IDisposable, IPythonSettings, Resource } from '../client/common/types'; +import { IDisposable } from '../client/common/types'; import { IServiceContainer, IServiceManager } from '../client/ioc/types'; -import { PythonEnvironment } from '../client/pythonEnvironments/info'; import { EXTENSION_ROOT_DIR_FOR_TESTS, IS_MULTI_ROOT_TEST, IS_PERF_TEST, IS_SMOKE_TEST } from './constants'; import { noop, sleep } from './core'; @@ -36,7 +37,7 @@ export enum OSType { } export type PythonSettingKeys = - | 'pythonPath' + | 'defaultInterpreterPath' | 'languageServer' | 'linting.lintOnSave' | 'linting.enabled' @@ -151,28 +152,6 @@ function getWorkspaceRoot() { return workspaceFolder ? workspaceFolder.uri : vscode.workspace.workspaceFolders[0].uri; } -export function getExtensionSettings(resource: Uri | undefined): IPythonSettings { - const vscode = require('vscode') as typeof import('vscode'); - class AutoSelectionService { - get onDidChangeAutoSelectedInterpreter(): Event { - return new vscode.EventEmitter().event; - } - public autoSelectInterpreter(_resource: Resource): Promise { - return Promise.resolve(); - } - public getAutoSelectedInterpreter(_resource: Resource): PythonEnvironment | undefined { - return; - } - public async setWorkspaceInterpreter( - _resource: Uri, - _interpreter: PythonEnvironment | undefined, - ): Promise { - return; - } - } - const pythonSettings = require('../client/common/configSettings') as typeof import('../client/common/configSettings'); - return pythonSettings.PythonSettings.getInstance(resource, new AutoSelectionService()); -} export function retryAsync(this: any, wrapped: Function, retryCount: number = 2) { return async (...args: any[]) => { return new Promise((resolve, reject) => { @@ -222,11 +201,11 @@ async function setPythonPathInWorkspace( } const resourceUri = typeof resource === 'string' ? vscode.Uri.file(resource) : resource; const settings = vscode.workspace.getConfiguration('python', resourceUri || null); - const value = settings.inspect('pythonPath'); + const value = settings.inspect('defaultInterpreterPath'); const prop: 'workspaceFolderValue' | 'workspaceValue' = config === vscode.ConfigurationTarget.Workspace ? 'workspaceValue' : 'workspaceFolderValue'; if (value && value[prop] !== pythonPath) { - await settings.update('pythonPath', pythonPath, config); + await settings.update('defaultInterpreterPath', pythonPath, config); await disposePythonSettings(); } } @@ -234,7 +213,7 @@ async function restoreGlobalPythonPathSetting(): Promise { const vscode = require('vscode') as typeof import('vscode'); const pythonConfig = vscode.workspace.getConfiguration('python', (null as any) as Uri); await Promise.all([ - pythonConfig.update('pythonPath', undefined, true), + pythonConfig.update('defaultInterpreterPath', undefined, true), pythonConfig.update('defaultInterpreterPath', undefined, true), ]); await disposePythonSettings(); diff --git a/src/test/common/configSettings.test.ts b/src/test/common/configSettings.test.ts index 477aef03fc50..75c20f512bbe 100644 --- a/src/test/common/configSettings.test.ts +++ b/src/test/common/configSettings.test.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import * as vscode from 'vscode'; import { IS_WINDOWS } from '../../client/common/platform/constants'; import { SystemVariables } from '../../client/common/variables/systemVariables'; -import { getExtensionSettings } from '../common'; +import { getExtensionSettings } from '../extensionSettings'; import { initialize } from './../initialize'; const workspaceRoot = path.join(__dirname, '..', '..', '..', 'src', 'test'); diff --git a/src/test/common/configSettings/configSettings.pythonPath.unit.test.ts b/src/test/common/configSettings/configSettings.pythonPath.unit.test.ts index 06a58b65a053..b59ee34877a7 100644 --- a/src/test/common/configSettings/configSettings.pythonPath.unit.test.ts +++ b/src/test/common/configSettings/configSettings.pythonPath.unit.test.ts @@ -11,7 +11,6 @@ import * as typemoq from 'typemoq'; import { Uri, WorkspaceConfiguration } from 'vscode'; import { IWorkspaceService } from '../../../client/common/application/types'; import { PythonSettings } from '../../../client/common/configSettings'; -import { DeprecatePythonPath } from '../../../client/common/experiments/groups'; import { IExperimentService, IInterpreterPathService } from '../../../client/common/types'; import { noop } from '../../../client/common/utils/misc'; import { PythonEnvironment } from '../../../client/pythonEnvironments/info'; @@ -57,50 +56,62 @@ suite('Python Settings - pythonPath', () => { sinon.restore(); }); - test('Python Path from settings.json is used', () => { - configSettings = new CustomPythonSettings(undefined, new MockAutoSelectionService()); + test('Python Path from settings is used', () => { const pythonPath = 'This is the python Path'; - pythonSettings - .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) - .returns(() => pythonPath) - .verifiable(typemoq.Times.atLeast(1)); + interpreterPathService.setup((p) => p.get(typemoq.It.isAny())).returns(() => pythonPath); + configSettings = new CustomPythonSettings( + undefined, + new MockAutoSelectionService(), + workspaceService.object, + interpreterPathService.object, + undefined, + ); configSettings.update(pythonSettings.object); expect(configSettings.pythonPath).to.be.equal(pythonPath); }); - test("Python Path from settings.json is used and relative path starting with '~' will be resolved from home directory", () => { - configSettings = new CustomPythonSettings(undefined, new MockAutoSelectionService()); + test("Python Path from settings is used and relative path starting with '~' will be resolved from home directory", () => { const pythonPath = `~${path.sep}This is the python Path`; - pythonSettings - .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) - .returns(() => pythonPath) - .verifiable(typemoq.Times.atLeast(1)); + interpreterPathService.setup((p) => p.get(typemoq.It.isAny())).returns(() => pythonPath); + configSettings = new CustomPythonSettings( + undefined, + new MockAutoSelectionService(), + workspaceService.object, + interpreterPathService.object, + undefined, + ); configSettings.update(pythonSettings.object); expect(configSettings.pythonPath).to.be.equal(untildify(pythonPath)); }); - test("Python Path from settings.json is used and relative path starting with '.' will be resolved from workspace folder", () => { - const workspaceFolderUri = Uri.file(__dirname); - configSettings = new CustomPythonSettings(workspaceFolderUri, new MockAutoSelectionService()); + test("Python Path from settings is used and relative path starting with '.' will be resolved from workspace folder", () => { const pythonPath = `.${path.sep}This is the python Path`; - pythonSettings - .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) - .returns(() => pythonPath) - .verifiable(typemoq.Times.atLeast(1)); + interpreterPathService.setup((p) => p.get(typemoq.It.isAny())).returns(() => pythonPath); + const workspaceFolderUri = Uri.file(__dirname); + configSettings = new CustomPythonSettings( + workspaceFolderUri, + new MockAutoSelectionService(), + workspaceService.object, + interpreterPathService.object, + undefined, + ); configSettings.update(pythonSettings.object); expect(configSettings.pythonPath).to.be.equal(path.resolve(workspaceFolderUri.fsPath, pythonPath)); }); - test('Python Path from settings.json is used and ${workspacecFolder} value will be resolved from workspace folder', () => { - const workspaceFolderUri = Uri.file(__dirname); - configSettings = new CustomPythonSettings(workspaceFolderUri, new MockAutoSelectionService()); + test('Python Path from settings is used and ${workspacecFolder} value will be resolved from workspace folder', () => { const workspaceFolderToken = '${workspaceFolder}'; const pythonPath = `${workspaceFolderToken}${path.sep}This is the python Path`; - pythonSettings - .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) - .returns(() => pythonPath) - .verifiable(typemoq.Times.atLeast(1)); + interpreterPathService.setup((p) => p.get(typemoq.It.isAny())).returns(() => pythonPath); + const workspaceFolderUri = Uri.file(__dirname); + configSettings = new CustomPythonSettings( + workspaceFolderUri, + new MockAutoSelectionService(), + workspaceService.object, + interpreterPathService.object, + undefined, + ); configSettings.update(pythonSettings.object); expect(configSettings.pythonPath).to.be.equal(path.join(workspaceFolderUri.fsPath, 'This is the python Path')); @@ -108,12 +119,15 @@ suite('Python Settings - pythonPath', () => { test("If we don't have a custom python path and no auto selected interpreters, then use default", () => { const workspaceFolderUri = Uri.file(__dirname); const selectionService = mock(MockAutoSelectionService); - configSettings = new CustomPythonSettings(workspaceFolderUri, instance(selectionService)); const pythonPath = 'python'; - pythonSettings - .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) - .returns(() => pythonPath) - .verifiable(typemoq.Times.atLeast(1)); + interpreterPathService.setup((p) => p.get(typemoq.It.isAny())).returns(() => pythonPath); + configSettings = new CustomPythonSettings( + workspaceFolderUri, + instance(selectionService), + workspaceService.object, + interpreterPathService.object, + undefined, + ); configSettings.update(pythonSettings.object); expect(configSettings.pythonPath).to.be.equal('python'); @@ -125,11 +139,14 @@ suite('Python Settings - pythonPath', () => { const selectionService = mock(MockAutoSelectionService); when(selectionService.getAutoSelectedInterpreter(workspaceFolderUri)).thenReturn(interpreter); when(selectionService.setWorkspaceInterpreter(workspaceFolderUri, anything())).thenResolve(); - configSettings = new CustomPythonSettings(workspaceFolderUri, instance(selectionService)); - pythonSettings - .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) - .returns(() => 'python') - .verifiable(typemoq.Times.atLeast(1)); + interpreterPathService.setup((p) => p.get(typemoq.It.isAny())).returns(() => 'python'); + configSettings = new CustomPythonSettings( + workspaceFolderUri, + instance(selectionService), + workspaceService.object, + interpreterPathService.object, + undefined, + ); configSettings.update(pythonSettings.object); expect(configSettings.pythonPath).to.be.equal(pythonPath); @@ -142,11 +159,15 @@ suite('Python Settings - pythonPath', () => { const selectionService = mock(MockAutoSelectionService); when(selectionService.getAutoSelectedInterpreter(workspaceFolderUri)).thenReturn(interpreter); when(selectionService.setWorkspaceInterpreter(workspaceFolderUri, anything())).thenResolve(); - configSettings = new CustomPythonSettings(workspaceFolderUri, instance(selectionService)); - pythonSettings - .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) - .returns(() => 'python') - .verifiable(typemoq.Times.atLeast(1)); + interpreterPathService.setup((p) => p.get(typemoq.It.isAny())).returns(() => 'python'); + + configSettings = new CustomPythonSettings( + workspaceFolderUri, + instance(selectionService), + workspaceService.object, + interpreterPathService.object, + undefined, + ); configSettings.update(pythonSettings.object); expect(configSettings.pythonPath).to.be.equal(pythonPath); @@ -157,14 +178,21 @@ suite('Python Settings - pythonPath', () => { const workspaceFolderUri = Uri.file(__dirname); const selectionService = mock(MockAutoSelectionService); when(selectionService.getAutoSelectedInterpreter(workspaceFolderUri)).thenReturn(interpreter); - configSettings = new CustomPythonSettings(workspaceFolderUri, instance(selectionService)); - pythonSettings.setup((p) => p.get(typemoq.It.isValue('pythonPath'))).returns(() => 'custom'); + + configSettings = new CustomPythonSettings( + workspaceFolderUri, + instance(selectionService), + workspaceService.object, + interpreterPathService.object, + undefined, + ); + interpreterPathService.setup((i) => i.get(typemoq.It.isAny())).returns(() => 'custom'); pythonSettings.setup((p) => p.get(typemoq.It.isValue('defaultInterpreterPath'))).returns(() => 'python'); configSettings.update(pythonSettings.object); expect(configSettings.defaultInterpreterPath).to.be.equal(pythonPath); }); - test("If user is in Deprecate Python Path experiment and we don't have a custom python path, get the autoselected interpreter and use it if it's safe", () => { + test("If we don't have a custom python path, get the autoselected interpreter and use it if it's safe", () => { const resource = Uri.parse('a'); const pythonPath = path.join(__dirname, 'this is a python path that was auto selected'); const interpreter = { path: pythonPath } as PythonEnvironment; @@ -175,68 +203,12 @@ suite('Python Settings - pythonPath', () => { resource, instance(selectionService), workspaceService.object, - experimentsManager.object, interpreterPathService.object, + undefined, ); - experimentsManager - .setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)) - .returns(() => true) - .verifiable(typemoq.Times.once()); interpreterPathService.setup((i) => i.get(resource)).returns(() => 'python'); configSettings.update(pythonSettings.object); - expect(configSettings.pythonPath).to.be.equal(pythonPath); - experimentsManager.verifyAll(); - interpreterPathService.verifyAll(); - pythonSettings.verifyAll(); - }); - test('If user is in Deprecate Python Path experiment, use the new API to fetch Python Path', () => { - const resource = Uri.parse('a'); - configSettings = new CustomPythonSettings( - resource, - new MockAutoSelectionService(), - workspaceService.object, - experimentsManager.object, - interpreterPathService.object, - ); - const pythonPath = 'This is the new API python Path'; - pythonSettings.setup((p) => p.get(typemoq.It.isValue('pythonPath'))).verifiable(typemoq.Times.never()); - experimentsManager - .setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)) - .returns(() => true) - .verifiable(typemoq.Times.once()); - interpreterPathService - .setup((i) => i.get(resource)) - .returns(() => pythonPath) - .verifiable(typemoq.Times.once()); - configSettings.update(pythonSettings.object); - - expect(configSettings.pythonPath).to.be.equal(pythonPath); - experimentsManager.verifyAll(); - interpreterPathService.verifyAll(); - pythonSettings.verifyAll(); - }); - test('If user is not in Deprecate Python Path experiment, use the settings to fetch Python Path', () => { - const resource = Uri.parse('a'); - configSettings = new CustomPythonSettings( - resource, - new MockAutoSelectionService(), - workspaceService.object, - experimentsManager.object, - interpreterPathService.object, - ); - const pythonPath = 'This is the settings python Path'; - pythonSettings - .setup((p) => p.get(typemoq.It.isValue('pythonPath'))) - .returns(() => pythonPath) - .verifiable(typemoq.Times.atLeastOnce()); - experimentsManager - .setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)) - .returns(() => false) - .verifiable(typemoq.Times.once()); - interpreterPathService.setup((i) => i.get(resource)).verifiable(typemoq.Times.never()); - configSettings.update(pythonSettings.object); - expect(configSettings.pythonPath).to.be.equal(pythonPath); experimentsManager.verifyAll(); interpreterPathService.verifyAll(); diff --git a/src/test/common/configSettings/configSettings.unit.test.ts b/src/test/common/configSettings/configSettings.unit.test.ts index 71cf1299a560..f251907600fc 100644 --- a/src/test/common/configSettings/configSettings.unit.test.ts +++ b/src/test/common/configSettings/configSettings.unit.test.ts @@ -11,7 +11,10 @@ import * as TypeMoq from 'typemoq'; import untildify = require('untildify'); import { WorkspaceConfiguration } from 'vscode'; import { LanguageServerType } from '../../../client/activation/types'; +import { WorkspaceService } from '../../../client/common/application/workspace'; import { PythonSettings } from '../../../client/common/configSettings'; +import { InterpreterPathService } from '../../../client/common/interpreterPathService'; +import { PersistentStateFactory } from '../../../client/common/persistentState'; import { IAutoCompleteSettings, IExperiments, @@ -24,6 +27,7 @@ import { noop } from '../../../client/common/utils/misc'; import * as EnvFileTelemetry from '../../../client/telemetry/envFileTelemetry'; import { ITestingSettings } from '../../../client/testing/configuration/types'; import { MockAutoSelectionService } from '../../mocks/autoSelector'; +import { MockMemento } from '../../mocks/mementos'; suite('Python Settings', async () => { class CustomPythonSettings extends PythonSettings { @@ -40,8 +44,25 @@ suite('Python Settings', async () => { setup(() => { sinon.stub(EnvFileTelemetry, 'sendSettingTelemetry').returns(); config = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Loose); - expected = new CustomPythonSettings(undefined, new MockAutoSelectionService()); - settings = new CustomPythonSettings(undefined, new MockAutoSelectionService()); + + const workspaceService = new WorkspaceService(); + const workspaceMemento = new MockMemento(); + const globalMemento = new MockMemento(); + const persistentStateFactory = new PersistentStateFactory(globalMemento, workspaceMemento); + expected = new CustomPythonSettings( + undefined, + new MockAutoSelectionService(), + workspaceService, + new InterpreterPathService(persistentStateFactory, workspaceService, []), + undefined, + ); + settings = new CustomPythonSettings( + undefined, + new MockAutoSelectionService(), + workspaceService, + new InterpreterPathService(persistentStateFactory, workspaceService, []), + undefined, + ); expected.defaultInterpreterPath = 'python'; }); @@ -119,7 +140,6 @@ suite('Python Settings', async () => { suite('String settings', async () => { [ - 'pythonPath', 'venvPath', 'condaPath', 'pipenvPath', diff --git a/src/test/common/configuration/service.test.ts b/src/test/common/configuration/service.test.ts index a3bdd783f2ff..dbac5f25be45 100644 --- a/src/test/common/configuration/service.test.ts +++ b/src/test/common/configuration/service.test.ts @@ -4,7 +4,7 @@ import { expect } from 'chai'; import { workspace } from 'vscode'; import { IAsyncDisposableRegistry, IConfigurationService } from '../../../client/common/types'; import { IServiceContainer } from '../../../client/ioc/types'; -import { getExtensionSettings } from '../../common'; +import { getExtensionSettings } from '../../extensionSettings'; import { initialize } from '../../initialize'; suite('Configuration Service', () => { diff --git a/src/test/common/configuration/service.unit.test.ts b/src/test/common/configuration/service.unit.test.ts index db997d3f0c51..4b55af5f0ba7 100644 --- a/src/test/common/configuration/service.unit.test.ts +++ b/src/test/common/configuration/service.unit.test.ts @@ -9,8 +9,7 @@ import { ConfigurationTarget, Uri, WorkspaceConfiguration } from 'vscode'; import { IWorkspaceService } from '../../../client/common/application/types'; import { PythonSettings } from '../../../client/common/configSettings'; import { ConfigurationService } from '../../../client/common/configuration/service'; -import { DeprecatePythonPath } from '../../../client/common/experiments/groups'; -import { IExperimentService, IInterpreterPathService } from '../../../client/common/types'; +import { IInterpreterPathService } from '../../../client/common/types'; import { IInterpreterAutoSelectionService } from '../../../client/interpreter/autoSelection/types'; import { IServiceContainer } from '../../../client/ioc/types'; @@ -18,7 +17,6 @@ suite('Configuration Service', () => { const resource = Uri.parse('a'); let workspaceService: TypeMoq.IMock; let interpreterPathService: TypeMoq.IMock; - let experimentsManager: TypeMoq.IMock; let serviceContainer: TypeMoq.IMock; let configService: ConfigurationService; setup(() => { @@ -32,10 +30,8 @@ suite('Configuration Service', () => { })); interpreterPathService = TypeMoq.Mock.ofType(); serviceContainer = TypeMoq.Mock.ofType(); - experimentsManager = TypeMoq.Mock.ofType(); serviceContainer.setup((s) => s.get(IWorkspaceService)).returns(() => workspaceService.object); serviceContainer.setup((s) => s.get(IInterpreterPathService)).returns(() => interpreterPathService.object); - serviceContainer.setup((s) => s.get(IExperimentService)).returns(() => experimentsManager.object); configService = new ConfigurationService(serviceContainer.object); }); @@ -58,7 +54,6 @@ suite('Configuration Service', () => { }); test('Do not update global settings if global value is already equal to the new value', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); const workspaceConfig = setupConfigProvider(); workspaceConfig @@ -75,7 +70,6 @@ suite('Configuration Service', () => { }); test('Update global settings if global value is not equal to the new value', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); const workspaceConfig = setupConfigProvider(); workspaceConfig @@ -92,7 +86,6 @@ suite('Configuration Service', () => { }); test('Do not update workspace settings if workspace value is already equal to the new value', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); const workspaceConfig = setupConfigProvider(); workspaceConfig @@ -109,7 +102,6 @@ suite('Configuration Service', () => { }); test('Update workspace settings if workspace value is not equal to the new value', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); const workspaceConfig = setupConfigProvider(); workspaceConfig @@ -126,7 +118,6 @@ suite('Configuration Service', () => { }); test('Do not update workspace folder settings if workspace folder value is already equal to the new value', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); const workspaceConfig = setupConfigProvider(); workspaceConfig .setup((w) => w.inspect('setting')) @@ -148,7 +139,6 @@ suite('Configuration Service', () => { }); test('Update workspace folder settings if workspace folder value is not equal to the new value', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); const workspaceConfig = setupConfigProvider(); workspaceConfig .setup((w) => w.inspect('setting')) @@ -170,7 +160,6 @@ suite('Configuration Service', () => { }); test('If in Deprecate PythonPath experiment & setting to update is `python.pythonPath`, update settings using new API if stored value is not equal to the new value', async () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); interpreterPathService .setup((w) => w.inspect(resource)) diff --git a/src/test/common/installer.test.ts b/src/test/common/installer.test.ts index 0309b0df7bf7..bbcf1aa1e41e 100644 --- a/src/test/common/installer.test.ts +++ b/src/test/common/installer.test.ts @@ -97,7 +97,6 @@ import { IFileDownloader, IHttpClient, IInstaller, - IInterpreterPathProxyService, IInterpreterPathService, IPathUtils, IPersistentStateFactory, @@ -117,7 +116,6 @@ import { MockModuleInstaller } from '../mocks/moduleInstaller'; import { MockProcessService } from '../mocks/proc'; import { UnitTestIocContainer } from '../testing/serviceRegistry'; import { closeActiveWindows, initializeTest, IS_MULTI_ROOT_TEST } from '../initialize'; -import { InterpreterPathProxyService } from '../../client/common/interpreterPathProxyService'; suite('Installer', () => { let ioc: UnitTestIocContainer; @@ -189,10 +187,6 @@ suite('Installer', () => { ); ioc.serviceManager.addSingleton(IActiveResourceService, ActiveResourceService); ioc.serviceManager.addSingleton(IInterpreterPathService, InterpreterPathService); - ioc.serviceManager.addSingleton( - IInterpreterPathProxyService, - InterpreterPathProxyService, - ); ioc.serviceManager.addSingleton(IExtensions, Extensions); ioc.serviceManager.addSingleton(IRandom, Random); ioc.serviceManager.addSingleton(ITerminalServiceFactory, TerminalServiceFactory); diff --git a/src/test/common/interpreterPathProxyService.unit.test.ts b/src/test/common/interpreterPathProxyService.unit.test.ts deleted file mode 100644 index 1d7116335a66..000000000000 --- a/src/test/common/interpreterPathProxyService.unit.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { Uri, WorkspaceConfiguration } from 'vscode'; -import * as TypeMoq from 'typemoq'; -import { expect } from 'chai'; -import { InterpreterPathProxyService } from '../../client/common/interpreterPathProxyService'; -import { IExperimentService, IInterpreterPathProxyService, IInterpreterPathService } from '../../client/common/types'; -import { IWorkspaceService } from '../../client/common/application/types'; -import { DeprecatePythonPath } from '../../client/common/experiments/groups'; - -suite('Interpreter Path Proxy Service', async () => { - let interpreterPathProxyService: IInterpreterPathProxyService; - let workspaceService: TypeMoq.IMock; - let experiments: TypeMoq.IMock; - let interpreterPathService: TypeMoq.IMock; - const resource = Uri.parse('a'); - const interpreterPath = 'path/to/interpreter'; - setup(() => { - workspaceService = TypeMoq.Mock.ofType(); - experiments = TypeMoq.Mock.ofType(); - interpreterPathService = TypeMoq.Mock.ofType(); - workspaceService - .setup((w) => w.getWorkspaceFolder(resource)) - .returns(() => ({ - uri: resource, - name: 'Workspacefolder', - index: 0, - })); - interpreterPathProxyService = new InterpreterPathProxyService( - interpreterPathService.object, - experiments.object, - workspaceService.object, - ); - }); - - test('When in experiment, use interpreter path service to get setting value', () => { - experiments.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); - interpreterPathService.setup((i) => i.get(resource)).returns(() => interpreterPath); - const value = interpreterPathProxyService.get(resource); - expect(value).to.equal(interpreterPath); - }); - - test('When not in experiment, use workspace service to get setting value', () => { - experiments.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); - const workspaceConfig = TypeMoq.Mock.ofType(); - workspaceService.setup((i) => i.getConfiguration('python', resource)).returns(() => workspaceConfig.object); - workspaceConfig.setup((w) => w.get('pythonPath')).returns(() => interpreterPath); - const value = interpreterPathProxyService.get(resource); - expect(value).to.equal(interpreterPath); - }); -}); diff --git a/src/test/common/interpreterPathService.unit.test.ts b/src/test/common/interpreterPathService.unit.test.ts index 6a898b7b4028..4dbd6577b0a3 100644 --- a/src/test/common/interpreterPathService.unit.test.ts +++ b/src/test/common/interpreterPathService.unit.test.ts @@ -15,13 +15,7 @@ import { WorkspaceConfiguration, } from 'vscode'; import { IWorkspaceService } from '../../client/common/application/types'; -import { - defaultInterpreterPathSetting, - InterpreterPathService, - isGlobalSettingCopiedKey, - workspaceFolderKeysForWhichTheCopyIsDone_Key, - workspaceKeysForWhichTheCopyIsDone_Key, -} from '../../client/common/interpreterPathService'; +import { defaultInterpreterPathSetting, InterpreterPathService } from '../../client/common/interpreterPathService'; import { FileSystemPaths } from '../../client/common/platform/fs-paths'; import { InterpreterConfigurationScope, IPersistentState, IPersistentStateFactory } from '../../client/common/types'; import { createDeferred, sleep } from '../../client/common/utils/async'; @@ -54,184 +48,6 @@ suite('Interpreter Path Service', async () => { sinon.restore(); }); - test('Ensure execution of method copyOldInterpreterStorageValuesToNew() goes as expected', async () => { - const _copyWorkspaceFolderValueToNewStorage = sinon.stub( - InterpreterPathService.prototype, - '_copyWorkspaceFolderValueToNewStorage', - ); - const _copyWorkspaceValueToNewStorage = sinon.stub( - InterpreterPathService.prototype, - '_copyWorkspaceValueToNewStorage', - ); - const _moveGlobalSettingValueToNewStorage = sinon.stub( - InterpreterPathService.prototype, - '_moveGlobalSettingValueToNewStorage', - ); - const workspaceConfig = TypeMoq.Mock.ofType(); - workspaceService.setup((w) => w.getConfiguration('python', resource)).returns(() => workspaceConfig.object); - workspaceConfig - .setup((w) => w.inspect('pythonPath')) - .returns( - () => - ({ - globalValue: 'globalPythonPath', - workspaceFolderValue: 'workspaceFolderPythonPath', - workspaceValue: 'workspacePythonPath', - } as any), - ); - - interpreterPathService = new InterpreterPathService(persistentStateFactory.object, workspaceService.object, []); - await interpreterPathService.copyOldInterpreterStorageValuesToNew(resource); - - assert(_copyWorkspaceFolderValueToNewStorage.calledWith(resource, 'workspaceFolderPythonPath')); - assert(_copyWorkspaceValueToNewStorage.calledWith(resource, 'workspacePythonPath')); - assert(_moveGlobalSettingValueToNewStorage.calledWith('globalPythonPath')); - }); - - test('If the one-off transfer to new storage has not happened yet for the workspace folder, do it and record the transfer', async () => { - const update = sinon.stub(InterpreterPathService.prototype, 'update'); - const persistentState = TypeMoq.Mock.ofType>(); - workspaceService.setup((w) => w.getWorkspaceFolderIdentifier(resource, '')).returns(() => resource.fsPath); - persistentStateFactory - .setup((p) => p.createGlobalPersistentState(workspaceFolderKeysForWhichTheCopyIsDone_Key, [])) - .returns(() => persistentState.object); - persistentState.setup((p) => p.value).returns(() => ['...storedWorkspaceFolderKeys']); - persistentState - .setup((p) => p.updateValue([resource.fsPath, '...storedWorkspaceFolderKeys'])) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.once()); - - interpreterPathService = new InterpreterPathService(persistentStateFactory.object, workspaceService.object, []); - await interpreterPathService._copyWorkspaceFolderValueToNewStorage(resource, 'workspaceFolderPythonPath'); - - assert(update.calledWith(resource, ConfigurationTarget.WorkspaceFolder, 'workspaceFolderPythonPath')); - persistentState.verifyAll(); - }); - - test('If the one-off transfer to new storage has already happened for the workspace folder, do not update and simply return', async () => { - const update = sinon.stub(InterpreterPathService.prototype, 'update'); - const persistentState = TypeMoq.Mock.ofType>(); - workspaceService.setup((w) => w.getWorkspaceFolderIdentifier(resource, '')).returns(() => resource.fsPath); - persistentStateFactory - .setup((p) => p.createGlobalPersistentState(workspaceFolderKeysForWhichTheCopyIsDone_Key, [])) - .returns(() => persistentState.object); - persistentState.setup((p) => p.value).returns(() => [resource.fsPath, '...storedWorkspaceKeys']); - persistentState.setup((p) => p.updateValue(TypeMoq.It.isAny())).verifiable(TypeMoq.Times.never()); - - interpreterPathService = new InterpreterPathService(persistentStateFactory.object, workspaceService.object, []); - await interpreterPathService._copyWorkspaceFolderValueToNewStorage(resource, 'workspaceFolderPythonPath'); - - assert(update.notCalled); - persistentState.verifyAll(); - }); - - test('If no folder is opened, do not do the one-off transfer to new storage for the workspace folder', async () => { - const update = sinon.stub(InterpreterPathService.prototype, 'update'); - const persistentState = TypeMoq.Mock.ofType>(); - workspaceService.setup((w) => w.getWorkspaceFolderIdentifier(resource, '')).returns(() => ''); - persistentStateFactory - .setup((p) => p.createGlobalPersistentState(workspaceFolderKeysForWhichTheCopyIsDone_Key, [])) - .returns(() => persistentState.object); - persistentState.setup((p) => p.value).returns(() => ['...storedWorkspaceKeys']); - persistentState.setup((p) => p.updateValue(TypeMoq.It.isAny())).verifiable(TypeMoq.Times.never()); - - interpreterPathService = new InterpreterPathService(persistentStateFactory.object, workspaceService.object, []); - await interpreterPathService._copyWorkspaceFolderValueToNewStorage(resource, 'workspaceFolderPythonPath'); - - assert(update.notCalled); - persistentState.verifyAll(); - }); - - test('If the one-off transfer to new storage has not happened yet for the workspace, do it and record the transfer', async () => { - const workspaceFileUri = Uri.parse('path/to/workspaceFile'); - const expectedWorkspaceKey = fs.normCase(workspaceFileUri.fsPath); - const update = sinon.stub(InterpreterPathService.prototype, 'update'); - const persistentState = TypeMoq.Mock.ofType>(); - workspaceService.setup((w) => w.workspaceFile).returns(() => workspaceFileUri); - persistentStateFactory - .setup((p) => p.createGlobalPersistentState(workspaceKeysForWhichTheCopyIsDone_Key, [])) - .returns(() => persistentState.object); - persistentState.setup((p) => p.value).returns(() => ['...storedWorkspaceKeys']); - persistentState - .setup((p) => p.updateValue([expectedWorkspaceKey, '...storedWorkspaceKeys'])) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.once()); - - interpreterPathService = new InterpreterPathService(persistentStateFactory.object, workspaceService.object, []); - await interpreterPathService._copyWorkspaceValueToNewStorage(resource, 'workspacePythonPath'); - - assert(update.calledWith(resource, ConfigurationTarget.Workspace, 'workspacePythonPath')); - persistentState.verifyAll(); - }); - - test('If the one-off transfer to new storage has already happened for the workspace, do not update and simply return', async () => { - const workspaceFileUri = Uri.parse('path/to/workspaceFile'); - const expectedWorkspaceKey = fs.normCase(workspaceFileUri.fsPath); - const update = sinon.stub(InterpreterPathService.prototype, 'update'); - const persistentState = TypeMoq.Mock.ofType>(); - workspaceService.setup((w) => w.workspaceFile).returns(() => workspaceFileUri); - persistentStateFactory - .setup((p) => p.createGlobalPersistentState(workspaceKeysForWhichTheCopyIsDone_Key, [])) - .returns(() => persistentState.object); - persistentState.setup((p) => p.value).returns(() => [expectedWorkspaceKey, '...storedWorkspaceKeys']); - persistentState.setup((p) => p.updateValue(TypeMoq.It.isAny())).verifiable(TypeMoq.Times.never()); - - interpreterPathService = new InterpreterPathService(persistentStateFactory.object, workspaceService.object, []); - await interpreterPathService._copyWorkspaceValueToNewStorage(resource, 'workspacePythonPath'); - - assert(update.notCalled); - persistentState.verifyAll(); - }); - - test('Do not update workspace settings and if a folder is directly opened', async () => { - const update = sinon.stub(InterpreterPathService.prototype, 'update'); - const persistentState = TypeMoq.Mock.ofType>(); - workspaceService.setup((w) => w.workspaceFile).returns(() => undefined); - persistentStateFactory - .setup((p) => p.createGlobalPersistentState(workspaceKeysForWhichTheCopyIsDone_Key, [])) - .returns(() => persistentState.object); - persistentState.setup((p) => p.value).verifiable(TypeMoq.Times.never()); - persistentState.setup((p) => p.updateValue(TypeMoq.It.isAny())).verifiable(TypeMoq.Times.never()); - - interpreterPathService = new InterpreterPathService(persistentStateFactory.object, workspaceService.object, []); - await interpreterPathService._copyWorkspaceValueToNewStorage(resource, 'workspacePythonPath'); - - assert(update.notCalled); - persistentState.verifyAll(); - }); - - test('If the one-off transfer to new storage has not happened yet for the user setting, do it, record the transfer', async () => { - const update = sinon.stub(InterpreterPathService.prototype, 'update'); - const persistentState = TypeMoq.Mock.ofType>(); - persistentStateFactory - .setup((p) => p.createGlobalPersistentState(isGlobalSettingCopiedKey, false)) - .returns(() => persistentState.object); - persistentState.setup((p) => p.value).returns(() => false); - persistentState.setup((p) => p.updateValue(true)).verifiable(TypeMoq.Times.once()); - - interpreterPathService = new InterpreterPathService(persistentStateFactory.object, workspaceService.object, []); - await interpreterPathService._moveGlobalSettingValueToNewStorage('globalPythonPath'); - - assert(update.calledWith(undefined, ConfigurationTarget.Global, 'globalPythonPath')); - persistentState.verifyAll(); - }); - - test('If the one-off transfer to new storage has already happened for the user setting, do not update and simply return', async () => { - const update = sinon.stub(InterpreterPathService.prototype, 'update'); - const persistentState = TypeMoq.Mock.ofType>(); - persistentStateFactory - .setup((p) => p.createGlobalPersistentState(isGlobalSettingCopiedKey, false)) - .returns(() => persistentState.object); - persistentState.setup((p) => p.value).returns(() => true); - persistentState.setup((p) => p.updateValue(TypeMoq.It.isAny())).verifiable(TypeMoq.Times.never()); - - interpreterPathService = new InterpreterPathService(persistentStateFactory.object, workspaceService.object, []); - await interpreterPathService._moveGlobalSettingValueToNewStorage('globalPythonPath'); - - assert(update.notCalled); - persistentState.verifyAll(); - }); - test('Global settings are not updated if stored value is same as new value', async () => { const workspaceConfig = TypeMoq.Mock.ofType(); workspaceService.setup((w) => w.getConfiguration('python')).returns(() => workspaceConfig.object); diff --git a/src/test/common/moduleInstaller.test.ts b/src/test/common/moduleInstaller.test.ts index bbb550f04d4b..68c11c497c96 100644 --- a/src/test/common/moduleInstaller.test.ts +++ b/src/test/common/moduleInstaller.test.ts @@ -96,7 +96,6 @@ import { IFileDownloader, IHttpClient, IInstaller, - IInterpreterPathProxyService, IInterpreterPathService, IPathUtils, IPersistentStateFactory, @@ -118,7 +117,6 @@ import { MockModuleInstaller } from '../mocks/moduleInstaller'; import { MockProcessService } from '../mocks/proc'; import { UnitTestIocContainer } from '../testing/serviceRegistry'; import { closeActiveWindows, initializeTest } from '../initialize'; -import { InterpreterPathProxyService } from '../../client/common/interpreterPathProxyService'; chaiUse(chaiAsPromised); @@ -206,10 +204,6 @@ suite('Module Installer', () => { ioc.serviceManager.addSingleton(IActiveResourceService, ActiveResourceService); ioc.serviceManager.addSingleton(IInterpreterPathService, InterpreterPathService); - ioc.serviceManager.addSingleton( - IInterpreterPathProxyService, - InterpreterPathProxyService, - ); ioc.serviceManager.addSingleton(IExtensions, Extensions); ioc.serviceManager.addSingleton(IRandom, Random); ioc.serviceManager.addSingleton(IApplicationShell, ApplicationShell); diff --git a/src/test/common/process/proc.exec.test.ts b/src/test/common/process/proc.exec.test.ts index 205c5e53d84c..f198a71cca49 100644 --- a/src/test/common/process/proc.exec.test.ts +++ b/src/test/common/process/proc.exec.test.ts @@ -10,7 +10,8 @@ import { BufferDecoder } from '../../../client/common/process/decoder'; import { ProcessService } from '../../../client/common/process/proc'; import { StdErrError } from '../../../client/common/process/types'; import { OSType } from '../../../client/common/utils/platform'; -import { getExtensionSettings, isOs, isPythonVersion } from '../../common'; +import { isOs, isPythonVersion } from '../../common'; +import { getExtensionSettings } from '../../extensionSettings'; import { initialize } from './../../initialize'; use(chaiAsPromised); diff --git a/src/test/common/process/proc.observable.test.ts b/src/test/common/process/proc.observable.test.ts index 22a0481f0550..fe1b8f510c4c 100644 --- a/src/test/common/process/proc.observable.test.ts +++ b/src/test/common/process/proc.observable.test.ts @@ -7,7 +7,8 @@ import { CancellationTokenSource } from 'vscode'; import { BufferDecoder } from '../../../client/common/process/decoder'; import { ProcessService } from '../../../client/common/process/proc'; import { createDeferred } from '../../../client/common/utils/async'; -import { getExtensionSettings, isOs, OSType } from '../../common'; +import { isOs, OSType } from '../../common'; +import { getExtensionSettings } from '../../extensionSettings'; import { initialize } from './../../initialize'; use(chaiAsPromised); diff --git a/src/test/common/process/pythonExecutionFactory.unit.test.ts b/src/test/common/process/pythonExecutionFactory.unit.test.ts index 4177bbc5837c..1d099dee3445 100644 --- a/src/test/common/process/pythonExecutionFactory.unit.test.ts +++ b/src/test/common/process/pythonExecutionFactory.unit.test.ts @@ -24,7 +24,7 @@ import { IProcessServiceFactory, IPythonExecutionService, } from '../../../client/common/process/types'; -import { IConfigurationService, IDisposableRegistry, IInterpreterPathProxyService } from '../../../client/common/types'; +import { IConfigurationService, IDisposableRegistry, IInterpreterPathService } from '../../../client/common/types'; import { Architecture } from '../../../client/common/utils/platform'; import { EnvironmentActivationService } from '../../../client/interpreter/activation/service'; import { IEnvironmentActivationService } from '../../../client/interpreter/activation/types'; @@ -85,7 +85,7 @@ suite('Process - PythonExecutionFactory', () => { let pyenvs: IComponentAdapter; let executionService: typemoq.IMock; let autoSelection: IInterpreterAutoSelectionService; - let interpreterPathExpHelper: IInterpreterPathProxyService; + let interpreterPathExpHelper: IInterpreterPathService; setup(() => { bufferDecoder = mock(BufferDecoder); activationHelper = mock(EnvironmentActivationService); @@ -94,7 +94,7 @@ suite('Process - PythonExecutionFactory', () => { condaService = mock(CondaService); processLogger = mock(ProcessLogger); autoSelection = mock(); - interpreterPathExpHelper = mock(); + interpreterPathExpHelper = mock(); when(interpreterPathExpHelper.get(anything())).thenReturn('selected interpreter path'); pyenvs = mock(); diff --git a/src/test/common/process/pythonProc.simple.multiroot.test.ts b/src/test/common/process/pythonProc.simple.multiroot.test.ts index 1004ed02532c..495092268704 100644 --- a/src/test/common/process/pythonProc.simple.multiroot.test.ts +++ b/src/test/common/process/pythonProc.simple.multiroot.test.ts @@ -15,7 +15,8 @@ import { IConfigurationService } from '../../../client/common/types'; import { clearCache } from '../../../client/common/utils/cacheUtils'; import { OSType } from '../../../client/common/utils/platform'; import { IServiceContainer } from '../../../client/ioc/types'; -import { clearPythonPathInWorkspaceFolder, getExtensionSettings, isOs, isPythonVersion } from '../../common'; +import { clearPythonPathInWorkspaceFolder, isOs, isPythonVersion } from '../../common'; +import { getExtensionSettings } from '../../extensionSettings'; import { closeActiveWindows, initialize, initializeTest, IS_MULTI_ROOT_TEST } from './../../initialize'; use(chaiAsPromised); diff --git a/src/test/common/terminals/environmentActivationProviders/terminalActivation.testvirtualenvs.ts b/src/test/common/terminals/environmentActivationProviders/terminalActivation.testvirtualenvs.ts index 368657b0e8dd..a5e5ef6146dc 100644 --- a/src/test/common/terminals/environmentActivationProviders/terminalActivation.testvirtualenvs.ts +++ b/src/test/common/terminals/environmentActivationProviders/terminalActivation.testvirtualenvs.ts @@ -8,22 +8,18 @@ import * as fs from 'fs-extra'; import * as path from 'path'; import * as sinon from 'sinon'; import * as vscode from 'vscode'; -import { DeprecatePythonPath } from '../../../../client/common/experiments/groups'; import { FileSystem } from '../../../../client/common/platform/fileSystem'; -import { IExperimentService } from '../../../../client/common/types'; import { PYTHON_VIRTUAL_ENVS_LOCATION } from '../../../ciConstants'; import { PYTHON_PATH, resetGlobalInterpreterPathSetting, - restorePythonPathInWorkspaceRoot, setGlobalInterpreterPath, - setPythonPathInWorkspaceRoot, updateSetting, waitForCondition, } from '../../../common'; import { EXTENSION_ROOT_DIR_FOR_TESTS, TEST_TIMEOUT } from '../../../constants'; import { sleep } from '../../../core'; -import { initialize, initializeTest } from '../../../initialize'; +import { initializeTest } from '../../../initialize'; suite('Activation of Environments in Terminal', () => { const file = path.join( @@ -57,7 +53,6 @@ suite('Activation of Environments in Terminal', () => { }; let terminalSettings: any; let pythonSettings: any; - let experiments: IExperimentService; const sandbox = sinon.createSandbox(); suiteSetup(async () => { envPaths = await fs.readJson(envsLocation); @@ -66,7 +61,6 @@ suite('Activation of Environments in Terminal', () => { defaultShell.Windows = terminalSettings.inspect('integrated.defaultProfile.windows').globalValue; defaultShell.Linux = terminalSettings.inspect('integrated.defaultProfile.linux').globalValue; await terminalSettings.update('integrated.defaultProfile.linux', 'bash', vscode.ConfigurationTarget.Global); - experiments = (await initialize()).serviceContainer.get(IExperimentService); }); setup(async () => { @@ -113,11 +107,7 @@ suite('Activation of Environments in Terminal', () => { vscode.ConfigurationTarget.Global, ); await pythonSettings.update('condaPath', undefined, vscode.ConfigurationTarget.Global); - if (experiments.inExperimentSync(DeprecatePythonPath.experiment)) { - await resetGlobalInterpreterPathSetting(); - } else { - await restorePythonPathInWorkspaceRoot(); - } + await resetGlobalInterpreterPathSetting(); } /** @@ -158,11 +148,7 @@ suite('Activation of Environments in Terminal', () => { vscode.workspace.workspaceFolders![0].uri, vscode.ConfigurationTarget.WorkspaceFolder, ); - if (experiments.inExperimentSync(DeprecatePythonPath.experiment)) { - await setGlobalInterpreterPath(envPath); - } else { - await setPythonPathInWorkspaceRoot(envPath); - } + await setGlobalInterpreterPath(envPath); const content = await openTerminalAndAwaitCommandContent(waitTimeForActivation, file, outputFile, 5_000); expect(fileSystem.arePathsSame(content, envPath)).to.equal(true, 'Environment not activated'); } diff --git a/src/test/extensionSettings.ts b/src/test/extensionSettings.ts new file mode 100644 index 000000000000..d3e96c030a49 --- /dev/null +++ b/src/test/extensionSettings.ts @@ -0,0 +1,50 @@ +/* eslint-disable global-require */ +/* eslint-disable class-methods-use-this */ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { Event, Uri } from 'vscode'; +import { WorkspaceService } from '../client/common/application/workspace'; +import { InterpreterPathService } from '../client/common/interpreterPathService'; +import { PersistentStateFactory } from '../client/common/persistentState'; +import { IPythonSettings, Resource } from '../client/common/types'; +import { PythonEnvironment } from '../client/pythonEnvironments/info'; +import { MockMemento } from './mocks/mementos'; + +export function getExtensionSettings(resource: Uri | undefined): IPythonSettings { + const vscode = require('vscode') as typeof import('vscode'); + class AutoSelectionService { + get onDidChangeAutoSelectedInterpreter(): Event { + return new vscode.EventEmitter().event; + } + + public autoSelectInterpreter(_resource: Resource): Promise { + return Promise.resolve(); + } + + public getAutoSelectedInterpreter(_resource: Resource): PythonEnvironment | undefined { + return undefined; + } + + public async setWorkspaceInterpreter( + _resource: Uri, + _interpreter: PythonEnvironment | undefined, + ): Promise { + return undefined; + } + } + const pythonSettings = require('../client/common/configSettings') as typeof import('../client/common/configSettings'); + const workspaceService = new WorkspaceService(); + const workspaceMemento = new MockMemento(); + const globalMemento = new MockMemento(); + const persistentStateFactory = new PersistentStateFactory(globalMemento, workspaceMemento); + return pythonSettings.PythonSettings.getInstance( + resource, + new AutoSelectionService(), + workspaceService, + new InterpreterPathService(persistentStateFactory, workspaceService, []), + undefined, + ); +} diff --git a/src/test/format/format.helper.test.ts b/src/test/format/format.helper.test.ts index 623db8a10d94..50000f1af867 100644 --- a/src/test/format/format.helper.test.ts +++ b/src/test/format/format.helper.test.ts @@ -4,7 +4,7 @@ import { IConfigurationService, IFormattingSettings, Product } from '../../clien import * as EnumEx from '../../client/common/utils/enum'; import { FormatterHelper } from '../../client/formatters/helper'; import { FormatterId } from '../../client/formatters/types'; -import { getExtensionSettings } from '../common'; +import { getExtensionSettings } from '../extensionSettings'; import { initialize } from '../initialize'; import { UnitTestIocContainer } from '../testing/serviceRegistry'; diff --git a/src/test/interpreters/interpreterService.unit.test.ts b/src/test/interpreters/interpreterService.unit.test.ts index 0a7bbe7b6b47..8dc1557ccde1 100644 --- a/src/test/interpreters/interpreterService.unit.test.ts +++ b/src/test/interpreters/interpreterService.unit.test.ts @@ -10,13 +10,11 @@ import * as path from 'path'; import * as TypeMoq from 'typemoq'; import { ConfigurationTarget, Disposable, TextDocument, TextEditor, Uri, WorkspaceConfiguration } from 'vscode'; import { IDocumentManager, IWorkspaceService } from '../../client/common/application/types'; -import { DeprecatePythonPath } from '../../client/common/experiments/groups'; import { IFileSystem } from '../../client/common/platform/types'; import { IPythonExecutionFactory, IPythonExecutionService } from '../../client/common/process/types'; import { IConfigurationService, IDisposableRegistry, - IExperimentService, IInterpreterPathService, InterpreterConfigurationScope, IPersistentStateFactory, @@ -54,7 +52,6 @@ suite('Interpreters service', () => { let pythonExecutionService: TypeMoq.IMock; let configService: TypeMoq.IMock; let interpreterPathService: TypeMoq.IMock; - let experimentService: TypeMoq.IMock; let pythonSettings: TypeMoq.IMock; function setupSuite() { @@ -62,7 +59,6 @@ suite('Interpreters service', () => { serviceManager = new ServiceManager(cont); serviceContainer = new ServiceContainer(cont); - experimentService = TypeMoq.Mock.ofType(); interpreterPathService = TypeMoq.Mock.ofType(); updater = TypeMoq.Mock.ofType(); pyenvs = TypeMoq.Mock.ofType(); @@ -103,7 +99,6 @@ suite('Interpreters service', () => { ); serviceManager.addSingletonInstance(IWorkspaceService, workspace.object); serviceManager.addSingletonInstance(IFileSystem, fileSystem.object); - serviceManager.addSingletonInstance(IExperimentService, experimentService.object); serviceManager.addSingletonInstance( IInterpreterPathService, interpreterPathService.object, @@ -153,7 +148,6 @@ suite('Interpreters service', () => { const service = new InterpreterService(serviceContainer, pyenvs.object); const documentManager = TypeMoq.Mock.ofType(); - experimentService.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); workspace.setup((w) => w.hasWorkspaceFolders).returns(() => true); workspace.setup((w) => w.workspaceFolders).returns(() => [{ uri: '' }] as any); let activeTextEditorChangeHandler: (e: TextEditor | undefined) => any | undefined; @@ -180,7 +174,6 @@ suite('Interpreters service', () => { const service = new InterpreterService(serviceContainer, pyenvs.object); const documentManager = TypeMoq.Mock.ofType(); - experimentService.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); workspace.setup((w) => w.hasWorkspaceFolders).returns(() => true); workspace.setup((w) => w.workspaceFolders).returns(() => [{ uri: '' }] as any); let activeTextEditorChangeHandler: (e?: TextEditor | undefined) => any | undefined; @@ -202,7 +195,6 @@ suite('Interpreters service', () => { const service = new InterpreterService(serviceContainer, pyenvs.object); const documentManager = TypeMoq.Mock.ofType(); - experimentService.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); workspace.setup((w) => w.hasWorkspaceFolders).returns(() => true); workspace.setup((w) => w.workspaceFolders).returns(() => [{ uri: '' }] as any); let interpreterPathServiceHandler: (e: InterpreterConfigurationScope) => any | undefined = () => 0; diff --git a/src/test/interpreters/pythonPathUpdaterFactory.unit.test.ts b/src/test/interpreters/pythonPathUpdaterFactory.unit.test.ts index a7e782d1b7d5..a69bde4e0fa3 100644 --- a/src/test/interpreters/pythonPathUpdaterFactory.unit.test.ts +++ b/src/test/interpreters/pythonPathUpdaterFactory.unit.test.ts @@ -13,11 +13,9 @@ suite('Python Path Settings Updater', () => { let experimentsManager: TypeMoq.IMock; let interpreterPathService: TypeMoq.IMock; let updaterServiceFactory: IPythonPathUpdaterServiceFactory; - function setupMocks(inExperiment: boolean = false) { + function setupMocks() { serviceContainer = TypeMoq.Mock.ofType(); workspaceService = TypeMoq.Mock.ofType(); - experimentsManager = TypeMoq.Mock.ofType(); - experimentsManager.setup((e) => e.inExperimentSync(TypeMoq.It.isAny())).returns(() => inExperiment); interpreterPathService = TypeMoq.Mock.ofType(); serviceContainer .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))) @@ -38,301 +36,139 @@ suite('Python Path Settings Updater', () => { return workspaceConfig; } - suite('When not in Deprecate PythonPath experiment', async () => { - suite('Global', () => { - setup(() => setupMocks(false)); - test('Python Path should not be updated when current pythonPath is the same', async () => { - const updater = updaterServiceFactory.getGlobalPythonPathConfigurationService(); - const pythonPath = `xGlobalPythonPath${new Date().getMilliseconds()}`; - const workspaceConfig = setupConfigProvider(); - workspaceConfig - .setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))) - .returns(() => { - return { globalValue: pythonPath } as any; - }); - - await updater.updatePythonPath(pythonPath); - workspaceConfig.verify( - (w) => w.update(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), - TypeMoq.Times.never(), - ); - }); - test('Python Path should be updated when current pythonPath is different', async () => { - const updater = updaterServiceFactory.getGlobalPythonPathConfigurationService(); - const pythonPath = `xGlobalPythonPath${new Date().getMilliseconds()}`; - const workspaceConfig = setupConfigProvider(); - workspaceConfig.setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); - - await updater.updatePythonPath(pythonPath); - workspaceConfig.verify( - (w) => - w.update( - TypeMoq.It.isValue('pythonPath'), - TypeMoq.It.isValue(pythonPath), - TypeMoq.It.isValue(true), - ), - TypeMoq.Times.once(), - ); - }); + suite('Global', () => { + setup(() => setupMocks()); + test('Python Path should not be updated when current pythonPath is the same', async () => { + const pythonPath = `xGlobalPythonPath${new Date().getMilliseconds()}`; + interpreterPathService + .setup((i) => i.inspect(undefined)) + .returns(() => { + return { globalValue: pythonPath }; + }); + interpreterPathService + .setup((i) => i.update(undefined, ConfigurationTarget.Global, pythonPath)) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.never()); + + const updater = updaterServiceFactory.getGlobalPythonPathConfigurationService(); + await updater.updatePythonPath(pythonPath); + interpreterPathService.verifyAll(); }); - - suite('WorkspaceFolder', () => { - setup(() => setupMocks(false)); - test('Python Path should not be updated when current pythonPath is the same', async () => { - const workspaceFolderPath = path.join('user', 'desktop', 'development'); - const workspaceFolder = Uri.file(workspaceFolderPath); - const updater = updaterServiceFactory.getWorkspaceFolderPythonPathConfigurationService(workspaceFolder); - const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; - const workspaceConfig = setupConfigProvider(workspaceFolder); - workspaceConfig - .setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))) - .returns(() => { - return { workspaceFolderValue: pythonPath } as any; - }); - - await updater.updatePythonPath(pythonPath); - workspaceConfig.verify( - (w) => w.update(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), - TypeMoq.Times.never(), - ); - }); - test('Python Path should be updated when current pythonPath is different', async () => { - const workspaceFolderPath = path.join('user', 'desktop', 'development'); - const workspaceFolder = Uri.file(workspaceFolderPath); - const updater = updaterServiceFactory.getWorkspaceFolderPythonPathConfigurationService(workspaceFolder); - const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; - const workspaceConfig = setupConfigProvider(workspaceFolder); - workspaceConfig.setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); - - await updater.updatePythonPath(pythonPath); - workspaceConfig.verify( - (w) => - w.update( - TypeMoq.It.isValue('pythonPath'), - TypeMoq.It.isValue(pythonPath), - TypeMoq.It.isValue(ConfigurationTarget.WorkspaceFolder), - ), - TypeMoq.Times.once(), - ); - }); - test('Python Path should be truncated for worspace-relative paths', async () => { - const workspaceFolderPath = path.join('user', 'desktop', 'development'); - const workspaceFolder = Uri.file(workspaceFolderPath); - const updater = updaterServiceFactory.getWorkspaceFolderPythonPathConfigurationService(workspaceFolder); - const pythonPath = Uri.file(path.join(workspaceFolderPath, 'env', 'bin', 'python')).fsPath; - const expectedPythonPath = path.join('env', 'bin', 'python'); - const workspaceConfig = setupConfigProvider(workspaceFolder); - workspaceConfig.setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); - - await updater.updatePythonPath(pythonPath); - workspaceConfig.verify( - (w) => - w.update( - TypeMoq.It.isValue('pythonPath'), - TypeMoq.It.isValue(expectedPythonPath), - TypeMoq.It.isValue(ConfigurationTarget.WorkspaceFolder), - ), - TypeMoq.Times.once(), - ); - }); + test('Python Path should be updated when current pythonPath is different', async () => { + const pythonPath = `xGlobalPythonPath${new Date().getMilliseconds()}`; + interpreterPathService.setup((i) => i.inspect(undefined)).returns(() => ({})); + + interpreterPathService + .setup((i) => i.update(undefined, ConfigurationTarget.Global, pythonPath)) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.once()); + const updater = updaterServiceFactory.getGlobalPythonPathConfigurationService(); + await updater.updatePythonPath(pythonPath); + interpreterPathService.verifyAll(); }); - suite('Workspace (multiroot scenario)', () => { - setup(() => setupMocks(false)); - test('Python Path should not be updated when current pythonPath is the same', async () => { - const workspaceFolderPath = path.join('user', 'desktop', 'development'); - const workspaceFolder = Uri.file(workspaceFolderPath); - const updater = updaterServiceFactory.getWorkspacePythonPathConfigurationService(workspaceFolder); - const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; - const workspaceConfig = setupConfigProvider(workspaceFolder); - workspaceConfig - .setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))) - .returns(() => { - return { workspaceValue: pythonPath } as any; - }); - - await updater.updatePythonPath(pythonPath); - workspaceConfig.verify( - (w) => w.update(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), - TypeMoq.Times.never(), - ); - }); - test('Python Path should be updated when current pythonPath is different', async () => { - const workspaceFolderPath = path.join('user', 'desktop', 'development'); - const workspaceFolder = Uri.file(workspaceFolderPath); - const updater = updaterServiceFactory.getWorkspacePythonPathConfigurationService(workspaceFolder); - const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; - const workspaceConfig = setupConfigProvider(workspaceFolder); - workspaceConfig.setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); - - await updater.updatePythonPath(pythonPath); - workspaceConfig.verify( - (w) => - w.update( - TypeMoq.It.isValue('pythonPath'), - TypeMoq.It.isValue(pythonPath), - TypeMoq.It.isValue(false), - ), - TypeMoq.Times.once(), - ); - }); - test('Python Path should be truncated for workspace-relative paths', async () => { - const workspaceFolderPath = path.join('user', 'desktop', 'development'); - const workspaceFolder = Uri.file(workspaceFolderPath); - const updater = updaterServiceFactory.getWorkspacePythonPathConfigurationService(workspaceFolder); - const pythonPath = Uri.file(path.join(workspaceFolderPath, 'env', 'bin', 'python')).fsPath; - const expectedPythonPath = path.join('env', 'bin', 'python'); - const workspaceConfig = setupConfigProvider(workspaceFolder); - workspaceConfig.setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); + }); - await updater.updatePythonPath(pythonPath); - workspaceConfig.verify( - (w) => - w.update( - TypeMoq.It.isValue('pythonPath'), - TypeMoq.It.isValue(expectedPythonPath), - TypeMoq.It.isValue(false), - ), - TypeMoq.Times.once(), - ); - }); + suite('WorkspaceFolder', () => { + setup(() => setupMocks()); + test('Python Path should not be updated when current pythonPath is the same', async () => { + const workspaceFolderPath = path.join('user', 'desktop', 'development'); + const workspaceFolder = Uri.file(workspaceFolderPath); + const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; + interpreterPathService + .setup((i) => i.inspect(workspaceFolder)) + .returns(() => ({ + workspaceFolderValue: pythonPath, + })); + interpreterPathService + .setup((i) => i.update(workspaceFolder, ConfigurationTarget.WorkspaceFolder, pythonPath)) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.never()); + const updater = updaterServiceFactory.getWorkspaceFolderPythonPathConfigurationService(workspaceFolder); + await updater.updatePythonPath(pythonPath); + interpreterPathService.verifyAll(); + }); + test('Python Path should be updated when current pythonPath is different', async () => { + const workspaceFolderPath = path.join('user', 'desktop', 'development'); + const workspaceFolder = Uri.file(workspaceFolderPath); + const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; + interpreterPathService.setup((i) => i.inspect(workspaceFolder)).returns(() => ({})); + interpreterPathService + .setup((i) => i.update(workspaceFolder, ConfigurationTarget.WorkspaceFolder, pythonPath)) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.once()); + + const updater = updaterServiceFactory.getWorkspaceFolderPythonPathConfigurationService(workspaceFolder); + await updater.updatePythonPath(pythonPath); + interpreterPathService.verifyAll(); + }); + test('Python Path should be truncated for workspace-relative paths', async () => { + const workspaceFolderPath = path.join('user', 'desktop', 'development'); + const workspaceFolder = Uri.file(workspaceFolderPath); + const pythonPath = Uri.file(path.join(workspaceFolderPath, 'env', 'bin', 'python')).fsPath; + const expectedPythonPath = path.join('env', 'bin', 'python'); + const workspaceConfig = setupConfigProvider(workspaceFolder); + workspaceConfig.setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); + interpreterPathService.setup((i) => i.inspect(workspaceFolder)).returns(() => ({})); + interpreterPathService + .setup((i) => i.update(workspaceFolder, ConfigurationTarget.WorkspaceFolder, expectedPythonPath)) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.once()); + + const updater = updaterServiceFactory.getWorkspaceFolderPythonPathConfigurationService(workspaceFolder); + await updater.updatePythonPath(pythonPath); + interpreterPathService.verifyAll(); }); }); - - suite('When in Deprecate PythonPath experiment', async () => { - suite('Global', () => { - setup(() => setupMocks(true)); - test('Python Path should not be updated when current pythonPath is the same', async () => { - const pythonPath = `xGlobalPythonPath${new Date().getMilliseconds()}`; - interpreterPathService - .setup((i) => i.inspect(undefined)) - .returns(() => { - return { globalValue: pythonPath }; - }); - interpreterPathService - .setup((i) => i.update(undefined, ConfigurationTarget.Global, pythonPath)) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.never()); - - const updater = updaterServiceFactory.getGlobalPythonPathConfigurationService(); - await updater.updatePythonPath(pythonPath); - interpreterPathService.verifyAll(); - }); - test('Python Path should be updated when current pythonPath is different', async () => { - const pythonPath = `xGlobalPythonPath${new Date().getMilliseconds()}`; - interpreterPathService.setup((i) => i.inspect(undefined)).returns(() => ({})); - - interpreterPathService - .setup((i) => i.update(undefined, ConfigurationTarget.Global, pythonPath)) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.once()); - const updater = updaterServiceFactory.getGlobalPythonPathConfigurationService(); - await updater.updatePythonPath(pythonPath); - interpreterPathService.verifyAll(); - }); + suite('Workspace (multiroot scenario)', () => { + setup(() => setupMocks()); + test('Python Path should not be updated when current pythonPath is the same', async () => { + const workspaceFolderPath = path.join('user', 'desktop', 'development'); + const workspaceFolder = Uri.file(workspaceFolderPath); + const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; + interpreterPathService + .setup((i) => i.inspect(workspaceFolder)) + .returns(() => ({ workspaceValue: pythonPath })); + interpreterPathService + .setup((i) => i.update(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.never()); + + const updater = updaterServiceFactory.getWorkspacePythonPathConfigurationService(workspaceFolder); + await updater.updatePythonPath(pythonPath); + interpreterPathService.verifyAll(); }); + test('Python Path should be updated when current pythonPath is different', async () => { + const workspaceFolderPath = path.join('user', 'desktop', 'development'); + const workspaceFolder = Uri.file(workspaceFolderPath); + const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; - suite('WorkspaceFolder', () => { - setup(() => setupMocks(true)); - test('Python Path should not be updated when current pythonPath is the same', async () => { - const workspaceFolderPath = path.join('user', 'desktop', 'development'); - const workspaceFolder = Uri.file(workspaceFolderPath); - const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; - interpreterPathService - .setup((i) => i.inspect(workspaceFolder)) - .returns(() => ({ - workspaceFolderValue: pythonPath, - })); - interpreterPathService - .setup((i) => i.update(workspaceFolder, ConfigurationTarget.WorkspaceFolder, pythonPath)) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.never()); - const updater = updaterServiceFactory.getWorkspaceFolderPythonPathConfigurationService(workspaceFolder); - await updater.updatePythonPath(pythonPath); - interpreterPathService.verifyAll(); - }); - test('Python Path should be updated when current pythonPath is different', async () => { - const workspaceFolderPath = path.join('user', 'desktop', 'development'); - const workspaceFolder = Uri.file(workspaceFolderPath); - const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; - interpreterPathService.setup((i) => i.inspect(workspaceFolder)).returns(() => ({})); - interpreterPathService - .setup((i) => i.update(workspaceFolder, ConfigurationTarget.WorkspaceFolder, pythonPath)) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.once()); + interpreterPathService.setup((i) => i.inspect(workspaceFolder)).returns(() => ({})); + interpreterPathService + .setup((i) => i.update(workspaceFolder, ConfigurationTarget.Workspace, pythonPath)) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.once()); - const updater = updaterServiceFactory.getWorkspaceFolderPythonPathConfigurationService(workspaceFolder); - await updater.updatePythonPath(pythonPath); - interpreterPathService.verifyAll(); - }); - test('Python Path should be truncated for workspace-relative paths', async () => { - const workspaceFolderPath = path.join('user', 'desktop', 'development'); - const workspaceFolder = Uri.file(workspaceFolderPath); - const pythonPath = Uri.file(path.join(workspaceFolderPath, 'env', 'bin', 'python')).fsPath; - const expectedPythonPath = path.join('env', 'bin', 'python'); - const workspaceConfig = setupConfigProvider(workspaceFolder); - workspaceConfig.setup((w) => w.inspect(TypeMoq.It.isValue('pythonPath'))).returns(() => undefined); - interpreterPathService.setup((i) => i.inspect(workspaceFolder)).returns(() => ({})); - interpreterPathService - .setup((i) => i.update(workspaceFolder, ConfigurationTarget.WorkspaceFolder, expectedPythonPath)) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.once()); + const updater = updaterServiceFactory.getWorkspacePythonPathConfigurationService(workspaceFolder); + await updater.updatePythonPath(pythonPath); - const updater = updaterServiceFactory.getWorkspaceFolderPythonPathConfigurationService(workspaceFolder); - await updater.updatePythonPath(pythonPath); - interpreterPathService.verifyAll(); - }); + interpreterPathService.verifyAll(); }); - suite('Workspace (multiroot scenario)', () => { - setup(() => setupMocks(true)); - test('Python Path should not be updated when current pythonPath is the same', async () => { - const workspaceFolderPath = path.join('user', 'desktop', 'development'); - const workspaceFolder = Uri.file(workspaceFolderPath); - const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; - interpreterPathService - .setup((i) => i.inspect(workspaceFolder)) - .returns(() => ({ workspaceValue: pythonPath })); - interpreterPathService - .setup((i) => i.update(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.never()); - - const updater = updaterServiceFactory.getWorkspacePythonPathConfigurationService(workspaceFolder); - await updater.updatePythonPath(pythonPath); - interpreterPathService.verifyAll(); - }); - test('Python Path should be updated when current pythonPath is different', async () => { - const workspaceFolderPath = path.join('user', 'desktop', 'development'); - const workspaceFolder = Uri.file(workspaceFolderPath); - const pythonPath = `xWorkspaceFolderPythonPath${new Date().getMilliseconds()}`; - - interpreterPathService.setup((i) => i.inspect(workspaceFolder)).returns(() => ({})); - interpreterPathService - .setup((i) => i.update(workspaceFolder, ConfigurationTarget.Workspace, pythonPath)) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.once()); - - const updater = updaterServiceFactory.getWorkspacePythonPathConfigurationService(workspaceFolder); - await updater.updatePythonPath(pythonPath); - - interpreterPathService.verifyAll(); - }); - test('Python Path should be truncated for workspace-relative paths', async () => { - const workspaceFolderPath = path.join('user', 'desktop', 'development'); - const workspaceFolder = Uri.file(workspaceFolderPath); - const pythonPath = Uri.file(path.join(workspaceFolderPath, 'env', 'bin', 'python')).fsPath; - const expectedPythonPath = path.join('env', 'bin', 'python'); - - interpreterPathService.setup((i) => i.inspect(workspaceFolder)).returns(() => ({})); - interpreterPathService - .setup((i) => i.update(workspaceFolder, ConfigurationTarget.Workspace, expectedPythonPath)) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.once()); - - const updater = updaterServiceFactory.getWorkspacePythonPathConfigurationService(workspaceFolder); - await updater.updatePythonPath(pythonPath); - - interpreterPathService.verifyAll(); - }); + test('Python Path should be truncated for workspace-relative paths', async () => { + const workspaceFolderPath = path.join('user', 'desktop', 'development'); + const workspaceFolder = Uri.file(workspaceFolderPath); + const pythonPath = Uri.file(path.join(workspaceFolderPath, 'env', 'bin', 'python')).fsPath; + const expectedPythonPath = path.join('env', 'bin', 'python'); + + interpreterPathService.setup((i) => i.inspect(workspaceFolder)).returns(() => ({})); + interpreterPathService + .setup((i) => i.update(workspaceFolder, ConfigurationTarget.Workspace, expectedPythonPath)) + .returns(() => Promise.resolve()) + .verifiable(TypeMoq.Times.once()); + + const updater = updaterServiceFactory.getWorkspacePythonPathConfigurationService(workspaceFolder); + await updater.updatePythonPath(pythonPath); + + interpreterPathService.verifyAll(); }); }); }); diff --git a/src/test/linters/lint.functional.test.ts b/src/test/linters/lint.functional.test.ts index e0fc71aafa89..8a3592ac257c 100644 --- a/src/test/linters/lint.functional.test.ts +++ b/src/test/linters/lint.functional.test.ts @@ -26,7 +26,7 @@ import { IPythonExecutionFactory, IPythonToolExecutionService, } from '../../client/common/process/types'; -import { IConfigurationService, IDisposableRegistry, IInterpreterPathProxyService } from '../../client/common/types'; +import { IConfigurationService, IDisposableRegistry, IInterpreterPathService } from '../../client/common/types'; import { IEnvironmentVariablesProvider } from '../../client/common/variables/types'; import { IEnvironmentActivationService } from '../../client/interpreter/activation/types'; import { IComponentAdapter, ICondaService, IInterpreterService } from '../../client/interpreter/contracts'; @@ -715,7 +715,7 @@ class TestFixture extends BaseTestFixture { const pyenvs: IComponentAdapter = mock(); const autoSelection = mock(); - const interpreterPathExpHelper = mock(); + const interpreterPathExpHelper = mock(); when(interpreterPathExpHelper.get(anything())).thenReturn('selected interpreter path'); return new PythonExecutionFactory( diff --git a/src/test/startupTelemetry.unit.test.ts b/src/test/startupTelemetry.unit.test.ts index ef79039c75ba..a9af3adff9a5 100644 --- a/src/test/startupTelemetry.unit.test.ts +++ b/src/test/startupTelemetry.unit.test.ts @@ -5,9 +5,8 @@ import { expect } from 'chai'; import * as TypeMoq from 'typemoq'; -import { Uri, WorkspaceConfiguration } from 'vscode'; +import { Uri } from 'vscode'; import { IWorkspaceService } from '../client/common/application/types'; -import { DeprecatePythonPath } from '../client/common/experiments/groups'; import { IExperimentService, IInterpreterPathService } from '../client/common/types'; import { IServiceContainer } from '../client/ioc/types'; import { hasUserDefinedPythonPath } from '../client/startupTelemetry'; @@ -28,26 +27,12 @@ suite('Startup Telemetry - hasUserDefinedPythonPath()', async () => { serviceContainer.setup((s) => s.get(IInterpreterPathService)).returns(() => interpreterPathService.object); }); - function setupConfigProvider(): TypeMoq.IMock { - const workspaceConfig = TypeMoq.Mock.ofType(); - workspaceService - .setup((w) => w.getConfiguration(TypeMoq.It.isValue('python'), TypeMoq.It.isValue(resource))) - .returns(() => workspaceConfig.object); - return workspaceConfig; - } - [undefined, 'python'].forEach((globalValue) => { [undefined, 'python'].forEach((workspaceValue) => { [undefined, 'python'].forEach((workspaceFolderValue) => { test(`Return false if using settings equals {globalValue: ${globalValue}, workspaceValue: ${workspaceValue}, workspaceFolderValue: ${workspaceFolderValue}}`, () => { - experimentsManager - .setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)) - .returns(() => false); - const workspaceConfig = setupConfigProvider(); - - workspaceConfig - .setup((w) => w.inspect('pythonPath')) - + interpreterPathService + .setup((i) => i.inspect(resource)) .returns(() => ({ globalValue, workspaceValue, workspaceFolderValue } as any)); const result = hasUserDefinedPythonPath(resource, serviceContainer.object); expect(result).to.equal(false, 'Should be false'); @@ -57,21 +42,10 @@ suite('Startup Telemetry - hasUserDefinedPythonPath()', async () => { }); test('Return true if using setting value equals something else', () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => false); - const workspaceConfig = setupConfigProvider(); - - workspaceConfig.setup((w) => w.inspect('pythonPath')).returns(() => ({ globalValue: 'something else' } as any)); - const result = hasUserDefinedPythonPath(resource, serviceContainer.object); - expect(result).to.equal(true, 'Should be true'); - }); - - test('If in Deprecate PythonPath experiment, use the new API to inspect settings', () => { - experimentsManager.setup((e) => e.inExperimentSync(DeprecatePythonPath.experiment)).returns(() => true); interpreterPathService .setup((i) => i.inspect(resource)) - .returns(() => ({})) - .verifiable(TypeMoq.Times.once()); - hasUserDefinedPythonPath(resource, serviceContainer.object); - interpreterPathService.verifyAll(); + .returns(() => ({ globalValue: 'something else' } as any)); + const result = hasUserDefinedPythonPath(resource, serviceContainer.object); + expect(result).to.equal(true, 'Should be true'); }); });