Skip to content

Commit 59213ca

Browse files
Use the component adapter in the extension.
1 parent 4ce9313 commit 59213ca

22 files changed

+182
-79
lines changed

src/client/application/diagnostics/checks/macPythonInterpreter.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,26 +95,27 @@ export class InvalidMacPythonInterpreterService extends BaseDiagnosticsService {
9595
return [];
9696
}
9797

98-
if (!this.helper.isMacDefaultPythonPath(settings.pythonPath)) {
98+
if (!(await this.helper.isMacDefaultPythonPath(settings.pythonPath))) {
9999
return [];
100100
}
101101
if (!currentInterpreter || currentInterpreter.envType !== EnvironmentType.Unknown) {
102102
return [];
103103
}
104104

105105
const interpreters = await this.interpreterService.getInterpreters(resource);
106-
if (interpreters.filter((i) => !this.helper.isMacDefaultPythonPath(i.path)).length === 0) {
107-
return [
108-
new InvalidMacPythonInterpreterDiagnostic(
109-
DiagnosticCodes.MacInterpreterSelectedAndNoOtherInterpretersDiagnostic,
110-
resource
111-
)
112-
];
106+
for (const info of interpreters) {
107+
if (!(await this.helper.isMacDefaultPythonPath(info.path))) {
108+
return [
109+
new InvalidMacPythonInterpreterDiagnostic(
110+
DiagnosticCodes.MacInterpreterSelectedAndHaveOtherInterpretersDiagnostic,
111+
resource
112+
)
113+
];
114+
}
113115
}
114-
115116
return [
116117
new InvalidMacPythonInterpreterDiagnostic(
117-
DiagnosticCodes.MacInterpreterSelectedAndHaveOtherInterpretersDiagnostic,
118+
DiagnosticCodes.MacInterpreterSelectedAndNoOtherInterpretersDiagnostic,
118119
resource
119120
)
120121
];

src/client/common/process/pythonExecutionFactory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export class PythonExecutionFactory implements IPythonExecutionFactory {
7171
processService,
7272
this.fileSystem,
7373
undefined,
74-
this.windowsStoreInterpreter.isWindowsStoreInterpreter(pythonPath)
74+
await this.windowsStoreInterpreter.isWindowsStoreInterpreter(pythonPath)
7575
);
7676
}
7777

src/client/interpreter/contracts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export const IInterpreterHelper = Symbol('IInterpreterHelper');
100100
export interface IInterpreterHelper {
101101
getActiveWorkspaceUri(resource: Resource): WorkspacePythonPath | undefined;
102102
getInterpreterInformation(pythonPath: string): Promise<undefined | Partial<PythonEnvironment>>;
103-
isMacDefaultPythonPath(pythonPath: string): Boolean;
103+
isMacDefaultPythonPath(pythonPath: string): Promise<boolean>;
104104
getInterpreterTypeDisplayName(interpreterType: EnvironmentType): string | undefined;
105105
getBestInterpreter(interpreters?: PythonEnvironment[]): PythonEnvironment | undefined;
106106
}

src/client/interpreter/helpers.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
PythonEnvironment,
1616
sortInterpreters
1717
} from '../pythonEnvironments/info';
18-
import { IInterpreterHelper } from './contracts';
18+
import { IComponentAdapter, IInterpreterHelper } from './contracts';
1919
import { IInterpreterHashProviderFactory } from './locators/types';
2020

2121
const EXPITY_DURATION = 24 * 60 * 60 * 1000;
@@ -44,12 +44,19 @@ export function isInterpreterLocatedInWorkspace(interpreter: PythonEnvironment,
4444
return interpreterPath.startsWith(resourcePath);
4545
}
4646

47+
// The parts of IComponentAdapter used here.
48+
interface IComponent {
49+
getInterpreterInformation(pythonPath: string): Promise<undefined | Partial<PythonEnvironment>>;
50+
isMacDefaultPythonPath(pythonPath: string): Promise<boolean | undefined>;
51+
}
52+
4753
@injectable()
4854
export class InterpreterHelper implements IInterpreterHelper {
4955
private readonly persistentFactory: IPersistentStateFactory;
5056
constructor(
5157
@inject(IServiceContainer) private serviceContainer: IServiceContainer,
52-
@inject(InterpeterHashProviderFactory) private readonly hashProviderFactory: IInterpreterHashProviderFactory
58+
@inject(InterpeterHashProviderFactory) private readonly hashProviderFactory: IInterpreterHashProviderFactory,
59+
@inject(IComponentAdapter) private readonly pyenvs: IComponent
5360
) {
5461
this.persistentFactory = this.serviceContainer.get<IPersistentStateFactory>(IPersistentStateFactory);
5562
}
@@ -78,6 +85,11 @@ export class InterpreterHelper implements IInterpreterHelper {
7885
}
7986
}
8087
public async getInterpreterInformation(pythonPath: string): Promise<undefined | Partial<PythonEnvironment>> {
88+
const found = await this.pyenvs.getInterpreterInformation(pythonPath);
89+
if (found !== undefined) {
90+
return found;
91+
}
92+
8193
const fileHash = await this.hashProviderFactory
8294
.create({ pythonPath })
8395
.then((provider) => provider.getInterpreterHash(pythonPath))
@@ -115,7 +127,11 @@ export class InterpreterHelper implements IInterpreterHelper {
115127
return;
116128
}
117129
}
118-
public isMacDefaultPythonPath(pythonPath: string) {
130+
public async isMacDefaultPythonPath(pythonPath: string): Promise<boolean> {
131+
const result = await this.pyenvs.isMacDefaultPythonPath(pythonPath);
132+
if (result !== undefined) {
133+
return result;
134+
}
119135
return isMacDefaultPythonPath(pythonPath);
120136
}
121137
public getInterpreterTypeDisplayName(interpreterType: EnvironmentType) {

src/client/interpreter/interpreterService.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { EnvironmentType, PythonEnvironment } from '../pythonEnvironments/info';
2525
import { captureTelemetry } from '../telemetry';
2626
import { EventName } from '../telemetry/constants';
2727
import {
28+
IComponentAdapter,
2829
IInterpreterDisplay,
2930
IInterpreterHelper,
3031
IInterpreterLocatorService,
@@ -40,6 +41,11 @@ export type GetInterpreterOptions = {
4041
onSuggestion?: boolean;
4142
};
4243

44+
// The parts of IComponentAdapter used here.
45+
interface IComponent {
46+
getInterpreterDetails(pythonPath: string): Promise<undefined | PythonEnvironment>;
47+
}
48+
4349
@injectable()
4450
export class InterpreterService implements Disposable, IInterpreterService {
4551
public get hasInterpreters(): Promise<boolean> {
@@ -70,7 +76,8 @@ export class InterpreterService implements Disposable, IInterpreterService {
7076

7177
constructor(
7278
@inject(IServiceContainer) private serviceContainer: IServiceContainer,
73-
@inject(InterpeterHashProviderFactory) private readonly hashProviderFactory: IInterpreterHashProviderFactory
79+
@inject(InterpeterHashProviderFactory) private readonly hashProviderFactory: IInterpreterHashProviderFactory,
80+
@inject(IComponentAdapter) private readonly pyenvs: IComponent
7481
) {
7582
this.locator = serviceContainer.get<IInterpreterLocatorService>(
7683
IInterpreterLocatorService,
@@ -160,6 +167,11 @@ export class InterpreterService implements Disposable, IInterpreterService {
160167
return this.getInterpreterDetails(fullyQualifiedPath, resource);
161168
}
162169
public async getInterpreterDetails(pythonPath: string, resource?: Uri): Promise<PythonEnvironment | undefined> {
170+
const info = await this.pyenvs.getInterpreterDetails(pythonPath);
171+
if (info !== undefined) {
172+
return info;
173+
}
174+
163175
// If we don't have the fully qualified path, then get it.
164176
if (path.basename(pythonPath) === pythonPath) {
165177
const pythonExecutionFactory = this.serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory);

src/client/interpreter/locators/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export interface IWindowsStoreInterpreter {
5353
* @returns {boolean}
5454
* @memberof WindowsStoreInterpreter
5555
*/
56-
isWindowsStoreInterpreter(pythonPath: string): boolean;
56+
isWindowsStoreInterpreter(pythonPath: string): Promise<boolean>;
5757
/**
5858
* Whether this is a python executable in a windows app store folder that is internal and can be hidden from users.
5959
*

src/client/pythonEnvironments/discovery/locators/index.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
CONDA_ENV_SERVICE,
1313
CURRENT_PATH_SERVICE,
1414
GLOBAL_VIRTUAL_ENV_SERVICE,
15+
IComponentAdapter,
1516
IInterpreterLocatorHelper,
1617
IInterpreterLocatorService,
1718
KNOWN_PATH_SERVICE,
@@ -171,6 +172,12 @@ function matchURI(uri: Uri, ...candidates: Uri[]): boolean {
171172
return false;
172173
}
173174

175+
// The parts of IComponentAdapter used here.
176+
interface IComponent {
177+
hasInterpreters: Promise<boolean> | undefined;
178+
getInterpreters(resource?: Uri, options?: GetInterpreterLocatorOptions): Promise<PythonEnvironment[] | undefined>;
179+
}
180+
174181
/**
175182
* Facilitates locating Python interpreters.
176183
*/
@@ -186,7 +193,10 @@ export class PythonInterpreterLocatorService implements IInterpreterLocatorServi
186193

187194
private readonly _hasInterpreters: Deferred<boolean>;
188195

189-
constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {
196+
constructor(
197+
@inject(IServiceContainer) private serviceContainer: IServiceContainer,
198+
@inject(IComponentAdapter) private readonly pyenvs: IComponent
199+
) {
190200
this._hasInterpreters = createDeferred<boolean>();
191201
serviceContainer.get<Disposable[]>(IDisposableRegistry).push(this);
192202
this.platform = serviceContainer.get<IPlatformService>(IPlatformService);
@@ -207,6 +217,10 @@ export class PythonInterpreterLocatorService implements IInterpreterLocatorServi
207217
}
208218

209219
public get hasInterpreters(): Promise<boolean> {
220+
const promise = this.pyenvs.hasInterpreters;
221+
if (promise !== undefined) {
222+
return promise;
223+
}
210224
return this._hasInterpreters.completed ? this._hasInterpreters.promise : Promise.resolve(false);
211225
}
212226

@@ -227,6 +241,10 @@ export class PythonInterpreterLocatorService implements IInterpreterLocatorServi
227241
*/
228242
@traceDecorators.verbose('Get Interpreters')
229243
public async getInterpreters(resource?: Uri, options?: GetInterpreterLocatorOptions): Promise<PythonEnvironment[]> {
244+
const envs = await this.pyenvs.getInterpreters(resource, options);
245+
if (envs !== undefined) {
246+
return envs;
247+
}
230248
const locators = this.getLocators(options);
231249
const promises = locators.map(async (provider) => provider.getInterpreters(resource));
232250
locators.forEach((locator) => {

src/client/pythonEnvironments/discovery/locators/services/condaService.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { IFileSystem, IPlatformService } from '../../../../common/platform/types
1313
import { IProcessServiceFactory } from '../../../../common/process/types';
1414
import { IConfigurationService, IDisposableRegistry, IPersistentStateFactory } from '../../../../common/types';
1515
import { cache } from '../../../../common/utils/decorators';
16-
import { ICondaService, IInterpreterLocatorService, WINDOWS_REGISTRY_SERVICE } from '../../../../interpreter/contracts';
16+
import { IComponentAdapter, ICondaService, IInterpreterLocatorService, WINDOWS_REGISTRY_SERVICE } from '../../../../interpreter/contracts';
1717
import { EnvironmentType, PythonEnvironment } from '../../../info';
1818
import { CondaEnvironmentInfo, CondaInfo } from './conda';
1919
import { parseCondaEnvFileContents } from './condaHelper';
@@ -49,6 +49,12 @@ export const CondaLocationsGlobWin = `{${condaGlobPathsForWindows.join(',')}}`;
4949

5050
export const CondaGetEnvironmentPrefix = 'Outputting Environment Now...';
5151

52+
// The parts of IComponentAdapter used here.
53+
interface IComponent {
54+
isCondaEnvironment(interpreterPath: string): Promise<boolean | undefined>;
55+
getCondaEnvironment(interpreterPath: string): Promise<CondaEnvironmentInfo | undefined>;
56+
}
57+
5258
/**
5359
* A wrapper around a conda installation.
5460
*/
@@ -66,6 +72,7 @@ export class CondaService implements ICondaService {
6672
@inject(IConfigurationService) private configService: IConfigurationService,
6773
@inject(IDisposableRegistry) private disposableRegistry: IDisposableRegistry,
6874
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService,
75+
@inject(IComponentAdapter) private readonly pyenvs: IComponent,
6976
@inject(IInterpreterLocatorService)
7077
@named(WINDOWS_REGISTRY_SERVICE)
7178
@optional()
@@ -183,6 +190,10 @@ export class CondaService implements ICondaService {
183190
* @memberof CondaService
184191
*/
185192
public async isCondaEnvironment(interpreterPath: string): Promise<boolean> {
193+
const result = await this.pyenvs.isCondaEnvironment(interpreterPath);
194+
if (result !== undefined) {
195+
return result;
196+
}
186197
const dir = path.dirname(interpreterPath);
187198
const { isWindows } = this.platform;
188199
const condaMetaDirectory = isWindows ? path.join(dir, 'conda-meta') : path.join(dir, '..', 'conda-meta');
@@ -193,6 +204,10 @@ export class CondaService implements ICondaService {
193204
* Return (env name, interpreter filename) for the interpreter.
194205
*/
195206
public async getCondaEnvironment(interpreterPath: string): Promise<{ name: string; path: string } | undefined> {
207+
const found = await this.pyenvs.getCondaEnvironment(interpreterPath);
208+
if (found !== undefined) {
209+
return found;
210+
}
196211
const isCondaEnv = await this.isCondaEnvironment(interpreterPath);
197212
if (!isCondaEnv) {
198213
return;

src/client/pythonEnvironments/discovery/locators/services/hashProviderFactory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export class InterpeterHashProviderFactory implements IInterpreterHashProviderFa
2828
? options.pythonPath
2929
: this.configService.getSettings(options.resource).pythonPath;
3030

31-
return this.windowsStoreInterpreter.isWindowsStoreInterpreter(pythonPath)
31+
return (await this.windowsStoreInterpreter.isWindowsStoreInterpreter(pythonPath))
3232
? this.windowsStoreHashProvider
3333
: this.hashProvider;
3434
}

src/client/pythonEnvironments/discovery/locators/services/windowsRegistryService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ export class WindowsRegistryService extends CacheableLocatorService {
186186
// Give preference to what we have retrieved from getInterpreterInformation.
187187
version: details.version || parsePythonVersion(version),
188188
companyDisplayName: interpreterInfo.companyDisplayName,
189-
envType: this.windowsStoreInterpreter.isWindowsStoreInterpreter(executablePath)
189+
envType: (await this.windowsStoreInterpreter.isWindowsStoreInterpreter(executablePath))
190190
? EnvironmentType.WindowsStore
191191
: EnvironmentType.Unknown,
192192
} as PythonEnvironment;

src/client/pythonEnvironments/discovery/locators/services/windowsStoreInterpreter.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { traceDecorators } from '../../../../common/logger';
99
import { IFileSystem } from '../../../../common/platform/types';
1010
import { IPythonExecutionFactory } from '../../../../common/process/types';
1111
import { IPersistentStateFactory } from '../../../../common/types';
12+
import { IComponentAdapter } from '../../../../interpreter/contracts';
1213
import { IInterpreterHashProvider, IWindowsStoreInterpreter } from '../../../../interpreter/locators/types';
1314
import { IServiceContainer } from '../../../../ioc/types';
1415

@@ -31,6 +32,11 @@ export function isRestrictedWindowsStoreInterpreterPath(pythonPath: string): boo
3132
);
3233
}
3334

35+
// The parts of IComponentAdapter used here.
36+
interface IComponent {
37+
isWindowsStoreInterpreter(pythonPath: string): Promise<boolean | undefined>;
38+
}
39+
3440
/**
3541
* The default location of Windows apps are `%ProgramFiles%\WindowsApps`.
3642
* (https://www.samlogic.net/articles/windows-8-windowsapps-folder.htm)
@@ -60,6 +66,7 @@ export class WindowsStoreInterpreter implements IWindowsStoreInterpreter, IInter
6066
@inject(IServiceContainer) private readonly serviceContainer: IServiceContainer,
6167
@inject(IPersistentStateFactory) private readonly persistentFactory: IPersistentStateFactory,
6268
@inject(IFileSystem) private readonly fs: IFileSystem,
69+
@inject(IComponentAdapter) private readonly pyenvs: IComponent
6370
) {}
6471

6572
/**
@@ -69,7 +76,11 @@ export class WindowsStoreInterpreter implements IWindowsStoreInterpreter, IInter
6976
* @returns {boolean}
7077
* @memberof WindowsStoreInterpreter
7178
*/
72-
public isWindowsStoreInterpreter(pythonPath: string): boolean {
79+
public async isWindowsStoreInterpreter(pythonPath: string): Promise<boolean> {
80+
const result = await this.pyenvs.isWindowsStoreInterpreter(pythonPath);
81+
if (result !== undefined) {
82+
return result;
83+
}
7384
const pythonPathToCompare = pythonPath.toUpperCase().replace(/\//g, '\\');
7485
return (
7586
pythonPathToCompare.includes('\\Microsoft\\WindowsApps\\'.toUpperCase())

src/client/pythonEnvironments/legacyIOC.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -147,18 +147,6 @@ class ComponentAdapter implements IComponentAdapter {
147147

148148
// IInterpreterService
149149

150-
public get hasInterpreters(): Promise<boolean> | undefined {
151-
if (!this.enabled) {
152-
return undefined;
153-
}
154-
const iterator = this.api.iterEnvs();
155-
return iterator.next().then((res) => {
156-
return !res.done;
157-
});
158-
}
159-
160-
//public async getInterpreters(_resource?: vscode.Uri, _options?: GetInterpreterOptions): Promise<PythonEnvironment[]>;
161-
162150
public async getInterpreterDetails(pythonPath: string, _resource?: vscode.Uri): Promise<undefined | PythonEnvironment> {
163151
if (!this.enabled) {
164152
return undefined;
@@ -216,6 +204,16 @@ class ComponentAdapter implements IComponentAdapter {
216204

217205
// IInterpreterLocatorService
218206

207+
public get hasInterpreters(): Promise<boolean> | undefined {
208+
if (!this.enabled) {
209+
return undefined;
210+
}
211+
const iterator = this.api.iterEnvs();
212+
return iterator.next().then((res) => {
213+
return !res.done;
214+
});
215+
}
216+
219217
public async getInterpreters(
220218
resource?: vscode.Uri,
221219
_options?: GetInterpreterLocatorOptions

src/test/application/diagnostics/checks/macPythonInterpreter.unit.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => {
220220
.verifiable(typemoq.Times.once());
221221
helper
222222
.setup((i) => i.isMacDefaultPythonPath(typemoq.It.isAny()))
223-
.returns(() => false)
223+
.returns(() => Promise.resolve(false))
224224
.verifiable(typemoq.Times.once());
225225

226226
const diagnostics = await diagnosticService.diagnose(undefined);
@@ -251,7 +251,7 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => {
251251
.verifiable(typemoq.Times.once());
252252
helper
253253
.setup((i) => i.isMacDefaultPythonPath(typemoq.It.isValue(pythonPath)))
254-
.returns(() => true)
254+
.returns(() => Promise.resolve(true))
255255
.verifiable(typemoq.Times.atLeastOnce());
256256

257257
const diagnostics = await diagnosticService.diagnose(undefined);
@@ -291,11 +291,11 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => {
291291
.verifiable(typemoq.Times.once());
292292
helper
293293
.setup((i) => i.isMacDefaultPythonPath(typemoq.It.isValue(pythonPath)))
294-
.returns(() => true)
294+
.returns(() => Promise.resolve(true))
295295
.verifiable(typemoq.Times.atLeastOnce());
296296
helper
297297
.setup((i) => i.isMacDefaultPythonPath(typemoq.It.isValue(nonMacStandardInterpreter)))
298-
.returns(() => false)
298+
.returns(() => Promise.resolve(false))
299299
.verifiable(typemoq.Times.atLeastOnce());
300300
interpreterService
301301
.setup((i) => i.getActiveInterpreter(typemoq.It.isAny()))

0 commit comments

Comments
 (0)