Skip to content

Commit a27e0c4

Browse files
author
Kartik Raj
authored
Added support for A/B testing and staged rollouts of new functionality (#5530)
* Added functionality * Added functionality * News entry * Code reviews * Addressed more comments * Added LS-enabled exp * Added tests for experiments * Corrected check and tests * Updated tests * Added inExperiment tests * Fixed bugs and updated tests * Send telemetry and log experiment group * Code reviews * More reviews * Accept suggestions * Send telemetry for the control group appropriately * Updated tests: * Ensure experiment groups are logged in the beginning * Modified decorator * Code reviews * Added docstrings * Minor fixing * code reviews... more coming * Reverted suggestion and added tests: * Add more doc strings * Refactoring and read config file * Added docstrings... * Simplified stuff * Local experiments file docstring * Updated tests * Added more tests * Added check for if experiments are valid * Added tests for registering IOCs * Move HTTP Client * Added tests * More tests * Refactored tests * Added crypto tests * Added httpClient.getJSON() tests * Apply quotes * Fix CI * Commit experiments file * Indent tests * Fix launch.json
1 parent 30158f7 commit a27e0c4

28 files changed

+2104
-443
lines changed

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,4 @@
189189
]
190190
}
191191
]
192-
}
192+
}

experiments.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[
2+
{}
3+
]

news/1 Enhancements/5042.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add support for A/B testing and staged rollouts of new functionality

package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
"DataScience.importingFormat": "Importing {0}",
103103
"DataScience.startingJupyter": "Starting Jupyter server",
104104
"DataScience.connectingToJupyter": "Connecting to Jupyter server",
105+
"Experiments.inGroup": "User belongs to experiment group '{0}'",
105106
"Interpreters.RefreshingInterpreters": "Refreshing Python Interpreters",
106107
"Interpreters.LoadingInterpreters": "Loading Python Interpreters",
107108
"Common.doNotShowAgain": "Do not show again",

src/client/activation/activationManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class ExtensionActivationManager implements IExtensionActivationManager {
2626
@inject(IInterpreterAutoSelectionService) private readonly autoSelection: IInterpreterAutoSelectionService,
2727
@inject(IApplicationDiagnostics) private readonly appDiagnostics: IApplicationDiagnostics,
2828
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService
29-
) {}
29+
) { }
3030

3131
public dispose() {
3232
while (this.disposables.length > 0) {

src/client/activation/activationService.ts

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import { LSNotSupportedDiagnosticServiceId } from '../application/diagnostics/ch
99
import { IDiagnosticsService } from '../application/diagnostics/types';
1010
import { IApplicationShell, ICommandManager, IWorkspaceService } from '../common/application/types';
1111
import { STANDARD_OUTPUT_CHANNEL } from '../common/constants';
12+
import { LSControl, LSEnabled } from '../common/experimentGroups';
1213
import '../common/extensions';
13-
import { IConfigurationService, IDisposableRegistry, IOutputChannel, IPersistentStateFactory, IPythonSettings, Resource } from '../common/types';
14+
import { traceError } from '../common/logger';
15+
import { IConfigurationService, IDisposableRegistry, IExperimentsManager, IOutputChannel, IPersistentStateFactory, IPythonSettings, Resource } from '../common/types';
1416
import { swallowExceptions } from '../common/utils/decorators';
1517
import { IServiceContainer } from '../ioc/types';
1618
import { sendTelemetryEvent } from '../telemetry';
@@ -33,7 +35,8 @@ export class LanguageServerExtensionActivationService implements IExtensionActiv
3335
private resource!: Resource;
3436

3537
constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer,
36-
@inject(IPersistentStateFactory) private stateFactory: IPersistentStateFactory) {
38+
@inject(IPersistentStateFactory) private stateFactory: IPersistentStateFactory,
39+
@inject(IExperimentsManager) private readonly abExperiments: IExperimentsManager) {
3740
this.workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
3841
this.output = this.serviceContainer.get<OutputChannel>(IOutputChannel, STANDARD_OUTPUT_CHANNEL);
3942
this.appShell = this.serviceContainer.get<IApplicationShell>(IApplicationShell);
@@ -113,6 +116,39 @@ export class LanguageServerExtensionActivationService implements IExtensionActiv
113116
sendTelemetryEvent(EventName.PYTHON_LANGUAGE_SERVER_SWITCHED, undefined, { change: message });
114117
}
115118
}
119+
120+
/**
121+
* Checks if user has not manually set `jediEnabled` setting
122+
* @param resource
123+
* @returns `true` if user has NOT manually added the setting and is using default configuration, `false` if user has `jediEnabled` setting added
124+
*/
125+
public isJediUsingDefaultConfiguration(resource?: Uri): boolean {
126+
const settings = this.workspaceService.getConfiguration('python', resource).inspect<boolean>('jediEnabled');
127+
if (!settings) {
128+
traceError('WorkspaceConfiguration.inspect returns `undefined` for setting `python.jediEnabled`');
129+
return false;
130+
}
131+
return (settings.globalValue === undefined && settings.workspaceValue === undefined && settings.workspaceFolderValue === undefined);
132+
}
133+
134+
/**
135+
* Checks if user is using Jedi as intellisense
136+
* @returns `true` if user is using jedi, `false` if user is using language server
137+
*/
138+
public useJedi(): boolean {
139+
if (this.isJediUsingDefaultConfiguration()) {
140+
if (this.abExperiments.inExperiment(LSEnabled)) {
141+
return false;
142+
}
143+
// Send telemetry if user is in control group
144+
this.abExperiments.sendTelemetryIfInExperiment(LSControl);
145+
}
146+
const configurationService = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
147+
const enabled = configurationService.getSettings(this.resource).jediEnabled;
148+
this.trackLangaugeServerSwitch(enabled).ignoreErrors();
149+
return enabled;
150+
}
151+
116152
protected onWorkspaceFoldersChanged() {
117153
//If an activated workspace folder was removed, dispose its activator
118154
const workspaceKeys = this.workspaceService.workspaceFolders!.map(workspaceFolder => this.getWorkspacePathKey(workspaceFolder.uri));
@@ -153,12 +189,6 @@ export class LanguageServerExtensionActivationService implements IExtensionActiv
153189
this.serviceContainer.get<ICommandManager>(ICommandManager).executeCommand('workbench.action.reloadWindow');
154190
}
155191
}
156-
private useJedi(): boolean {
157-
const configurationService = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
158-
const enabled = configurationService.getSettings(this.resource).jediEnabled;
159-
this.trackLangaugeServerSwitch(enabled).ignoreErrors();
160-
return enabled;
161-
}
162192
private getWorkspacePathKey(resource: Resource): string {
163193
return this.workspaceService.getWorkspaceFolderIdentifier(resource, workspacePathNameForGlobalWorkspaces);
164194
}

0 commit comments

Comments
 (0)