Skip to content

Commit 37b83be

Browse files
Implement the high-level component API. (#13853)
This is where we define the top-level interface for the component. We also stub out our use of the component in the legacy IOC code. This is a prerequisite for using the injected component adapter in the extension.
1 parent 68253d0 commit 37b83be

File tree

11 files changed

+112
-17
lines changed

11 files changed

+112
-17
lines changed

src/client/pythonEnvironments/index.ts

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,60 @@
33

44
import * as vscode from 'vscode';
55
import { IServiceContainer, IServiceManager } from '../ioc/types';
6-
import { ILocator } from './base/locator';
6+
import { PythonEnvInfo } from './base/info';
7+
import { ILocator, PythonEnvsIterator, PythonLocatorQuery } from './base/locator';
8+
import { PythonEnvsChangedEvent } from './base/watcher';
79
import { ExtensionLocators, WorkspaceLocators } from './discovery/locators';
810
import { registerForIOC } from './legacyIOC';
911

12+
/**
13+
* Activate the Python environments component (during extension activation).'
14+
*/
1015
export function activate(serviceManager: IServiceManager, serviceContainer: IServiceContainer) {
11-
registerForIOC(serviceManager, serviceContainer);
16+
const [api, activateAPI] = createAPI();
17+
registerForIOC(serviceManager, serviceContainer, api);
18+
activateAPI();
19+
}
20+
21+
/**
22+
* The public API for the Python environments component.
23+
*
24+
* Note that this is composed of sub-components.
25+
*/
26+
export class PythonEnvironments implements ILocator {
27+
constructor(
28+
// These are the sub-components the full component is composed of:
29+
private readonly locators: ILocator
30+
) {}
31+
32+
public get onChanged(): vscode.Event<PythonEnvsChangedEvent> {
33+
return this.locators.onChanged;
34+
}
35+
36+
public iterEnvs(query?: PythonLocatorQuery): PythonEnvsIterator {
37+
return this.locators.iterEnvs(query);
38+
}
39+
40+
public async resolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
41+
return this.locators.resolveEnv(env);
42+
}
43+
}
1244

45+
/**
46+
* Initialize everything needed for the API and provide the API object.
47+
*
48+
* An activation function is also returned, which should be called soon.
49+
*/
50+
export function createAPI(): [PythonEnvironments, () => void] {
1351
const [locators, activateLocators] = initLocators();
14-
activateLocators();
15-
// We will pass the locators into the component API.
16-
// tslint:disable-next-line:no-unused-expression
17-
locators;
52+
53+
return [
54+
new PythonEnvironments(locators),
55+
() => {
56+
activateLocators();
57+
// Any other activation needed for the API will go here later.
58+
}
59+
];
1860
}
1961

2062
function initLocators(): [ExtensionLocators, () => void] {

src/client/pythonEnvironments/legacyIOC.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
import { injectable } from 'inversify';
45
import {
56
CONDA_ENV_FILE_SERVICE,
67
CONDA_ENV_SERVICE,
@@ -49,7 +50,32 @@ import {
4950
import { WorkspaceVirtualEnvWatcherService } from './discovery/locators/services/workspaceVirtualEnvWatcherService';
5051
import { EnvironmentInfoService, IEnvironmentInfoService } from './info/environmentInfoService';
5152

52-
export function registerForIOC(serviceManager: IServiceManager, serviceContainer: IServiceContainer): void {
53+
import { PythonEnvironments } from '.';
54+
55+
export const IComponentAdapter = Symbol('IComponentAdapter');
56+
export interface IComponentAdapter {
57+
// We will fill in the API separately.
58+
}
59+
60+
@injectable()
61+
class ComponentAdapter implements IComponentAdapter {
62+
constructor(
63+
// The adapter only wraps one thing: the component API.
64+
private readonly api: PythonEnvironments
65+
) {
66+
// For the moment we use this placeholder to exercise the property.
67+
if (this.api.onChanged) {
68+
this.api.onChanged((_event) => {
69+
// do nothing
70+
});
71+
}
72+
}
73+
}
74+
75+
export function registerForIOC(serviceManager: IServiceManager, serviceContainer: IServiceContainer, api: PythonEnvironments): void {
76+
const adapter = new ComponentAdapter(api);
77+
serviceManager.addSingletonInstance<IComponentAdapter>(IComponentAdapter, adapter);
78+
5379
serviceManager.addSingleton<IInterpreterLocatorHelper>(IInterpreterLocatorHelper, InterpreterLocatorHelper);
5480
serviceManager.addSingleton<IInterpreterLocatorService>(
5581
IInterpreterLocatorService,

src/test/datascience/dataScienceIocContainer.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ import { registerInterpreterTypes } from '../../client/interpreter/serviceRegist
375375
import { VirtualEnvironmentManager } from '../../client/interpreter/virtualEnvs';
376376
import { IVirtualEnvironmentManager } from '../../client/interpreter/virtualEnvs/types';
377377
import { ProposePylanceBanner } from '../../client/languageServices/proposeLanguageServerBanner';
378+
import { PythonEnvironments } from '../../client/pythonEnvironments';
378379
import { CacheableLocatorPromiseCache } from '../../client/pythonEnvironments/discovery/locators/services/cacheableLocatorService';
379380
import { InterpeterHashProviderFactory } from '../../client/pythonEnvironments/discovery/locators/services/hashProviderFactory';
380381
import { EnvironmentType, PythonEnvironment } from '../../client/pythonEnvironments/info';
@@ -436,6 +437,7 @@ export class DataScienceIocContainer extends UnitTestIocContainer {
436437
// tslint:disable-next-line:no-any
437438
public datascience!: TypeMoq.IMock<IDataScience>;
438439
public shouldMockJupyter: boolean;
440+
public readonly pythonEnvs: PythonEnvironments;
439441
private commandManager: MockCommandManager = new MockCommandManager();
440442
private setContexts: Record<string, boolean> = {};
441443
private contextSetEvent: EventEmitter<{ name: string; value: boolean }> = new EventEmitter<{
@@ -481,6 +483,7 @@ export class DataScienceIocContainer extends UnitTestIocContainer {
481483

482484
constructor(private readonly uiTest: boolean = false) {
483485
super();
486+
this.pythonEnvs = mock(PythonEnvironments);
484487
this.useVSCodeAPI = false;
485488
const isRollingBuild = process.env ? process.env.VSCODE_PYTHON_ROLLING !== undefined : false;
486489
this.shouldMockJupyter = !isRollingBuild;
@@ -1067,7 +1070,7 @@ export class DataScienceIocContainer extends UnitTestIocContainer {
10671070
when(this.kernelServiceMock.getKernelSpecs(anything(), anything())).thenResolve([]);
10681071
this.serviceManager.addSingletonInstance<KernelService>(KernelService, instance(this.kernelServiceMock));
10691072

1070-
registerForIOC(this.serviceManager, this.serviceContainer);
1073+
registerForIOC(this.serviceManager, this.serviceContainer, instance(this.pythonEnvs));
10711074

10721075
this.serviceManager.addSingleton<IInterpreterSecurityService>(
10731076
IInterpreterSecurityService,
@@ -1125,7 +1128,7 @@ export class DataScienceIocContainer extends UnitTestIocContainer {
11251128

11261129
// Make sure full interpreter services are available.
11271130
registerInterpreterTypes(this.serviceManager);
1128-
registerForIOC(this.serviceManager, this.serviceContainer);
1131+
registerForIOC(this.serviceManager, this.serviceContainer, instance(this.pythonEnvs));
11291132

11301133
// Rebind the interpreter display as we don't want to use the real one
11311134
this.serviceManager.rebindInstance<IInterpreterDisplay>(IInterpreterDisplay, interpreterDisplay.object);

src/test/format/extension.format.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
import * as fs from 'fs-extra';
66
import * as path from 'path';
7+
import { instance, mock } from 'ts-mockito';
78
import { CancellationTokenSource, Position, Uri, window, workspace } from 'vscode';
89
import { IProcessServiceFactory } from '../../client/common/process/types';
910
import { AutoPep8Formatter } from '../../client/formatters/autoPep8Formatter';
1011
import { BlackFormatter } from '../../client/formatters/blackFormatter';
1112
import { YapfFormatter } from '../../client/formatters/yapfFormatter';
13+
import { PythonEnvironments } from '../../client/pythonEnvironments';
1214
import { registerForIOC } from '../../client/pythonEnvironments/legacyIOC';
1315
import { isPythonVersionInProcess } from '../common';
1416
import { closeActiveWindows, initialize, initializeTest } from '../initialize';
@@ -35,6 +37,7 @@ let formattedAutoPep8 = '';
3537
// tslint:disable-next-line:max-func-body-length
3638
suite('Formatting - General', () => {
3739
let ioc: UnitTestIocContainer;
40+
let pythonEnvs: PythonEnvironments;
3841

3942
suiteSetup(async function () {
4043
// https://github.com/microsoft/vscode-python/issues/12564
@@ -60,6 +63,7 @@ suite('Formatting - General', () => {
6063

6164
setup(async () => {
6265
await initializeTest();
66+
pythonEnvs = mock(PythonEnvironments);
6367
initializeDI();
6468
});
6569
suiteTeardown(async () => {
@@ -87,7 +91,7 @@ suite('Formatting - General', () => {
8791
ioc.registerMockProcessTypes();
8892
ioc.registerMockInterpreterTypes();
8993

90-
registerForIOC(ioc.serviceManager, ioc.serviceContainer);
94+
registerForIOC(ioc.serviceManager, ioc.serviceContainer, instance(pythonEnvs));
9195
}
9296

9397
async function injectFormatOutput(outputFileName: string) {

src/test/pythonEnvironments/legacyIOC.unit.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
import { IPipEnvServiceHelper, IPythonInPathCommandProvider } from '../../client/interpreter/locators/types';
3131
import { ServiceContainer } from '../../client/ioc/container';
3232
import { ServiceManager } from '../../client/ioc/serviceManager';
33+
import { PythonEnvironments } from '../../client/pythonEnvironments';
3334
import { PythonInterpreterLocatorService } from '../../client/pythonEnvironments/discovery/locators';
3435
import { InterpreterLocatorHelper } from '../../client/pythonEnvironments/discovery/locators/helpers';
3536
import { InterpreterLocatorProgressService } from '../../client/pythonEnvironments/discovery/locators/progressService';
@@ -67,7 +68,8 @@ suite('Interpreters - Service Registry', () => {
6768
test('Registrations', () => {
6869
const serviceManager = mock(ServiceManager);
6970
const serviceContainer = mock(ServiceContainer);
70-
registerForIOC(instance(serviceManager), instance(serviceContainer));
71+
const api = mock(PythonEnvironments);
72+
registerForIOC(instance(serviceManager), instance(serviceContainer), instance(api));
7173
verify(serviceManager.addSingleton(IKnownSearchPathsForInterpreters, KnownSearchPathsForInterpreters)).once();
7274
verify(serviceManager.addSingleton(IVirtualEnvironmentsSearchPathProvider, GlobalVirtualEnvironmentsSearchPathProvider, 'global')).once();
7375
verify(serviceManager.addSingleton(IVirtualEnvironmentsSearchPathProvider, WorkspaceVirtualEnvironmentsSearchPathProvider, 'workspace')).once();

src/test/serviceRegistry.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import { ServiceContainer } from '../client/ioc/container';
5353
import { ServiceManager } from '../client/ioc/serviceManager';
5454
import { IServiceContainer, IServiceManager } from '../client/ioc/types';
5555
import { registerTypes as lintersRegisterTypes } from '../client/linters/serviceRegistry';
56+
import { PythonEnvironments } from '../client/pythonEnvironments';
5657
import { registerForIOC } from '../client/pythonEnvironments/legacyIOC';
5758
import { TEST_OUTPUT_CHANNEL } from '../client/testing/common/constants';
5859
import { registerTypes as unittestsRegisterTypes } from '../client/testing/serviceRegistry';
@@ -171,13 +172,15 @@ export class IocContainer {
171172

172173
public readonly serviceManager: IServiceManager;
173174
public readonly serviceContainer: IServiceContainer;
175+
public readonly pythonEnvs: PythonEnvironments;
174176

175177
private disposables: Disposable[] = [];
176178

177179
constructor() {
178180
const cont = new Container();
179181
this.serviceManager = new ServiceManager(cont);
180182
this.serviceContainer = new ServiceContainer(cont);
183+
this.pythonEnvs = mock(PythonEnvironments);
181184

182185
this.serviceManager.addSingletonInstance<IServiceContainer>(IServiceContainer, this.serviceContainer);
183186
this.serviceManager.addSingletonInstance<Disposable[]>(IDisposableRegistry, this.disposables);
@@ -299,7 +302,7 @@ export class IocContainer {
299302
public registerMockInterpreterTypes() {
300303
this.serviceManager.addSingleton<IInterpreterService>(IInterpreterService, InterpreterService);
301304
this.serviceManager.addSingleton<IRegistry>(IRegistry, RegistryImplementation);
302-
registerForIOC(this.serviceManager, this.serviceContainer);
305+
registerForIOC(this.serviceManager, this.serviceContainer, instance(this.pythonEnvs));
303306
}
304307

305308
public registerMockProcess() {

src/test/testing/nosetest/nosetest.disovery.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { EXTENSION_ROOT_DIR } from '../../../client/common/constants';
1010
import { IProcessServiceFactory } from '../../../client/common/process/types';
1111
import { ICondaService, IInterpreterService } from '../../../client/interpreter/contracts';
1212
import { InterpreterService } from '../../../client/interpreter/interpreterService';
13+
import { PythonEnvironments } from '../../../client/pythonEnvironments';
1314
import { CondaService } from '../../../client/pythonEnvironments/discovery/locators/services/condaService';
1415
import { registerForIOC } from '../../../client/pythonEnvironments/legacyIOC';
1516
import { CommandSource } from '../../../client/testing/common/constants';
@@ -38,6 +39,7 @@ const filesToDelete = [
3839
// tslint:disable-next-line:max-func-body-length
3940
suite('Unit Tests - nose - discovery with mocked process output', () => {
4041
let ioc: UnitTestIocContainer;
42+
let pythonEnvs: PythonEnvironments;
4143
const configTarget = IS_MULTI_ROOT_TEST
4244
? vscode.ConfigurationTarget.WorkspaceFolder
4345
: vscode.ConfigurationTarget.Workspace;
@@ -61,6 +63,7 @@ suite('Unit Tests - nose - discovery with mocked process output', () => {
6163
});
6264
setup(async () => {
6365
await initializeTest();
66+
pythonEnvs = mock(PythonEnvironments);
6467
initializeDI();
6568
});
6669
teardown(async () => {
@@ -82,7 +85,7 @@ suite('Unit Tests - nose - discovery with mocked process output', () => {
8285
instance(mock(InterpreterService))
8386
);
8487

85-
registerForIOC(ioc.serviceManager, ioc.serviceContainer);
88+
registerForIOC(ioc.serviceManager, ioc.serviceContainer, instance(pythonEnvs));
8689
ioc.serviceManager.rebindInstance<ICondaService>(ICondaService, instance(mock(CondaService)));
8790
}
8891

src/test/testing/pytest/pytest.discovery.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { IEnvironmentActivationService } from '../../../client/interpreter/activ
2323
import { ICondaService, IInterpreterService } from '../../../client/interpreter/contracts';
2424
import { InterpreterService } from '../../../client/interpreter/interpreterService';
2525
import { IServiceContainer } from '../../../client/ioc/types';
26+
import { PythonEnvironments } from '../../../client/pythonEnvironments';
2627
import { CondaService } from '../../../client/pythonEnvironments/discovery/locators/services/condaService';
2728
import { WindowsStoreInterpreter } from '../../../client/pythonEnvironments/discovery/locators/services/windowsStoreInterpreter';
2829
import { registerForIOC } from '../../../client/pythonEnvironments/legacyIOC';
@@ -60,6 +61,7 @@ Run the command `python <ExtensionDir>/pythonFiles/testing_tools/run_adapter.py
6061
// tslint:disable:max-func-body-length
6162
suite('Unit Tests - pytest - discovery with mocked process output', () => {
6263
let ioc: UnitTestIocContainer;
64+
let pythonEnvs: PythonEnvironments;
6365
const configTarget = IS_MULTI_ROOT_TEST
6466
? vscode.ConfigurationTarget.WorkspaceFolder
6567
: vscode.ConfigurationTarget.Workspace;
@@ -116,6 +118,7 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => {
116118
});
117119
setup(async () => {
118120
await initializeTest();
121+
pythonEnvs = mock(PythonEnvironments);
119122
initializeDI();
120123
});
121124
teardown(async () => {
@@ -137,7 +140,7 @@ suite('Unit Tests - pytest - discovery with mocked process output', () => {
137140
instance(mock(InterpreterService))
138141
);
139142
ioc.serviceManager.rebind<IPythonExecutionFactory>(IPythonExecutionFactory, ExecutionFactory);
140-
registerForIOC(ioc.serviceManager, ioc.serviceContainer);
143+
registerForIOC(ioc.serviceManager, ioc.serviceContainer, instance(pythonEnvs));
141144
ioc.serviceManager.rebindInstance<ICondaService>(ICondaService, instance(mock(CondaService)));
142145
}
143146

src/test/testing/pytest/pytest.run.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { IEnvironmentActivationService } from '../../../client/interpreter/activ
2424
import { ICondaService, IInterpreterService } from '../../../client/interpreter/contracts';
2525
import { InterpreterService } from '../../../client/interpreter/interpreterService';
2626
import { IServiceContainer } from '../../../client/ioc/types';
27+
import { PythonEnvironments } from '../../../client/pythonEnvironments';
2728
import { CondaService } from '../../../client/pythonEnvironments/discovery/locators/services/condaService';
2829
import { WindowsStoreInterpreter } from '../../../client/pythonEnvironments/discovery/locators/services/windowsStoreInterpreter';
2930
import { registerForIOC } from '../../../client/pythonEnvironments/legacyIOC';
@@ -383,6 +384,7 @@ async function testDiagnosticRelatedInformation(
383384

384385
suite('Unit Tests - pytest - run with mocked process output', () => {
385386
let ioc: UnitTestIocContainer;
387+
let pythonEnvs: PythonEnvironments;
386388
const configTarget = IS_MULTI_ROOT_TEST
387389
? vscode.ConfigurationTarget.WorkspaceFolder
388390
: vscode.ConfigurationTarget.Workspace;
@@ -460,7 +462,7 @@ suite('Unit Tests - pytest - run with mocked process output', () => {
460462
);
461463
ioc.serviceManager.rebind<IPythonExecutionFactory>(IPythonExecutionFactory, ExecutionFactory);
462464

463-
registerForIOC(ioc.serviceManager, ioc.serviceContainer);
465+
registerForIOC(ioc.serviceManager, ioc.serviceContainer, instance(pythonEnvs));
464466
ioc.serviceManager.rebindInstance<ICondaService>(ICondaService, instance(mock(CondaService)));
465467
}
466468

@@ -518,6 +520,7 @@ suite('Unit Tests - pytest - run with mocked process output', () => {
518520
// tslint:disable-next-line: no-invalid-this
519521
this.timeout(TEST_TIMEOUT * 2);
520522
await initializeTest();
523+
pythonEnvs = mock(PythonEnvironments);
521524
initializeDI();
522525
await injectTestDiscoveryOutput(scenario.discoveryOutput);
523526
await injectTestRunOutput(scenario.runOutput);

src/test/testing/unittest/unittest.discovery.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { EXTENSION_ROOT_DIR } from '../../../client/common/constants';
1111
import { IProcessServiceFactory } from '../../../client/common/process/types';
1212
import { ICondaService, IInterpreterService } from '../../../client/interpreter/contracts';
1313
import { InterpreterService } from '../../../client/interpreter/interpreterService';
14+
import { PythonEnvironments } from '../../../client/pythonEnvironments';
1415
import { CondaService } from '../../../client/pythonEnvironments/discovery/locators/services/condaService';
1516
import { registerForIOC } from '../../../client/pythonEnvironments/legacyIOC';
1617
import { CommandSource } from '../../../client/testing/common/constants';
@@ -29,6 +30,7 @@ const defaultUnitTestArgs = ['-v', '-s', '.', '-p', '*test*.py'];
2930
// tslint:disable-next-line:max-func-body-length
3031
suite('Unit Tests - unittest - discovery with mocked process output', () => {
3132
let ioc: UnitTestIocContainer;
33+
let pythonEnvs: PythonEnvironments;
3234
const rootDirectory = UNITTEST_TEST_FILES_PATH;
3335
const configTarget = IS_MULTI_ROOT_TEST ? ConfigurationTarget.WorkspaceFolder : ConfigurationTarget.Workspace;
3436

@@ -42,6 +44,7 @@ suite('Unit Tests - unittest - discovery with mocked process output', () => {
4244
await fs.remove(cachePath);
4345
}
4446
await initializeTest();
47+
pythonEnvs = mock(PythonEnvironments);
4548
initializeDI();
4649
});
4750
teardown(async () => {
@@ -62,7 +65,7 @@ suite('Unit Tests - unittest - discovery with mocked process output', () => {
6265
IInterpreterService,
6366
instance(mock(InterpreterService))
6467
);
65-
registerForIOC(ioc.serviceManager, ioc.serviceContainer);
68+
registerForIOC(ioc.serviceManager, ioc.serviceContainer, instance(pythonEnvs));
6669
ioc.serviceManager.rebindInstance<ICondaService>(ICondaService, instance(mock(CondaService)));
6770
}
6871

0 commit comments

Comments
 (0)