Skip to content

Commit

Permalink
Use the component adapter in the extension.
Browse files Browse the repository at this point in the history
  • Loading branch information
ericsnowcurrently committed Sep 16, 2020
1 parent c04c6f5 commit f15be0e
Show file tree
Hide file tree
Showing 22 changed files with 183 additions and 77 deletions.
21 changes: 11 additions & 10 deletions src/client/application/diagnostics/checks/macPythonInterpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,26 +95,27 @@ export class InvalidMacPythonInterpreterService extends BaseDiagnosticsService {
return [];
}

if (!this.helper.isMacDefaultPythonPath(settings.pythonPath)) {
if (!(await this.helper.isMacDefaultPythonPath(settings.pythonPath))) {
return [];
}
if (!currentInterpreter || currentInterpreter.envType !== EnvironmentType.Unknown) {
return [];
}

const interpreters = await this.interpreterService.getInterpreters(resource);
if (interpreters.filter((i) => !this.helper.isMacDefaultPythonPath(i.path)).length === 0) {
return [
new InvalidMacPythonInterpreterDiagnostic(
DiagnosticCodes.MacInterpreterSelectedAndNoOtherInterpretersDiagnostic,
resource
)
];
for (const info of interpreters) {
if (!(await this.helper.isMacDefaultPythonPath(info.path))) {
return [
new InvalidMacPythonInterpreterDiagnostic(
DiagnosticCodes.MacInterpreterSelectedAndHaveOtherInterpretersDiagnostic,
resource
)
];
}
}

return [
new InvalidMacPythonInterpreterDiagnostic(
DiagnosticCodes.MacInterpreterSelectedAndHaveOtherInterpretersDiagnostic,
DiagnosticCodes.MacInterpreterSelectedAndNoOtherInterpretersDiagnostic,
resource
)
];
Expand Down
2 changes: 1 addition & 1 deletion src/client/common/process/pythonExecutionFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class PythonExecutionFactory implements IPythonExecutionFactory {
processService,
this.fileSystem,
undefined,
this.windowsStoreInterpreter.isWindowsStoreInterpreter(pythonPath)
await this.windowsStoreInterpreter.isWindowsStoreInterpreter(pythonPath)
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/client/interpreter/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export const IInterpreterHelper = Symbol('IInterpreterHelper');
export interface IInterpreterHelper {
getActiveWorkspaceUri(resource: Resource): WorkspacePythonPath | undefined;
getInterpreterInformation(pythonPath: string): Promise<undefined | Partial<PythonEnvironment>>;
isMacDefaultPythonPath(pythonPath: string): Boolean;
isMacDefaultPythonPath(pythonPath: string): Promise<boolean>;
getInterpreterTypeDisplayName(interpreterType: EnvironmentType): string | undefined;
getBestInterpreter(interpreters?: PythonEnvironment[]): PythonEnvironment | undefined;
}
Expand Down
22 changes: 19 additions & 3 deletions src/client/interpreter/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
PythonEnvironment,
sortInterpreters
} from '../pythonEnvironments/info';
import { IInterpreterHelper } from './contracts';
import { IComponentAdapter, IInterpreterHelper } from './contracts';
import { IInterpreterHashProviderFactory } from './locators/types';

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

// The parts of IComponentAdapter used here.
interface IComponent {
getInterpreterInformation(pythonPath: string): Promise<undefined | Partial<PythonEnvironment>>;
isMacDefaultPythonPath(pythonPath: string): Promise<boolean | undefined>;
}

@injectable()
export class InterpreterHelper implements IInterpreterHelper {
private readonly persistentFactory: IPersistentStateFactory;
constructor(
@inject(IServiceContainer) private serviceContainer: IServiceContainer,
@inject(InterpeterHashProviderFactory) private readonly hashProviderFactory: IInterpreterHashProviderFactory
@inject(InterpeterHashProviderFactory) private readonly hashProviderFactory: IInterpreterHashProviderFactory,
@inject(IComponentAdapter) private readonly pyenvs: IComponent
) {
this.persistentFactory = this.serviceContainer.get<IPersistentStateFactory>(IPersistentStateFactory);
}
Expand Down Expand Up @@ -78,6 +85,11 @@ export class InterpreterHelper implements IInterpreterHelper {
}
}
public async getInterpreterInformation(pythonPath: string): Promise<undefined | Partial<PythonEnvironment>> {
const found = await this.pyenvs.getInterpreterInformation(pythonPath);
if (found !== undefined) {
return found;
}

const fileHash = await this.hashProviderFactory
.create({ pythonPath })
.then((provider) => provider.getInterpreterHash(pythonPath))
Expand Down Expand Up @@ -115,7 +127,11 @@ export class InterpreterHelper implements IInterpreterHelper {
return;
}
}
public isMacDefaultPythonPath(pythonPath: string) {
public async isMacDefaultPythonPath(pythonPath: string): Promise<boolean> {
const result = await this.pyenvs.isMacDefaultPythonPath(pythonPath);
if (result !== undefined) {
return result;
}
return isMacDefaultPythonPath(pythonPath);
}
public getInterpreterTypeDisplayName(interpreterType: EnvironmentType) {
Expand Down
14 changes: 13 additions & 1 deletion src/client/interpreter/interpreterService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { EnvironmentType, PythonEnvironment } from '../pythonEnvironments/info';
import { captureTelemetry } from '../telemetry';
import { EventName } from '../telemetry/constants';
import {
IComponentAdapter,
IInterpreterDisplay,
IInterpreterHelper,
IInterpreterLocatorService,
Expand All @@ -40,6 +41,11 @@ export type GetInterpreterOptions = {
onSuggestion?: boolean;
};

// The parts of IComponentAdapter used here.
interface IComponent {
getInterpreterDetails(pythonPath: string): Promise<undefined | PythonEnvironment>;
}

@injectable()
export class InterpreterService implements Disposable, IInterpreterService {
public get hasInterpreters(): Promise<boolean> {
Expand Down Expand Up @@ -70,7 +76,8 @@ export class InterpreterService implements Disposable, IInterpreterService {

constructor(
@inject(IServiceContainer) private serviceContainer: IServiceContainer,
@inject(InterpeterHashProviderFactory) private readonly hashProviderFactory: IInterpreterHashProviderFactory
@inject(InterpeterHashProviderFactory) private readonly hashProviderFactory: IInterpreterHashProviderFactory,
@inject(IComponentAdapter) private readonly pyenvs: IComponent
) {
this.locator = serviceContainer.get<IInterpreterLocatorService>(
IInterpreterLocatorService,
Expand Down Expand Up @@ -160,6 +167,11 @@ export class InterpreterService implements Disposable, IInterpreterService {
return this.getInterpreterDetails(fullyQualifiedPath, resource);
}
public async getInterpreterDetails(pythonPath: string, resource?: Uri): Promise<PythonEnvironment | undefined> {
const info = await this.pyenvs.getInterpreterDetails(pythonPath);
if (info !== undefined) {
return info;
}

// If we don't have the fully qualified path, then get it.
if (path.basename(pythonPath) === pythonPath) {
const pythonExecutionFactory = this.serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory);
Expand Down
2 changes: 1 addition & 1 deletion src/client/interpreter/locators/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export interface IWindowsStoreInterpreter {
* @returns {boolean}
* @memberof WindowsStoreInterpreter
*/
isWindowsStoreInterpreter(pythonPath: string): boolean;
isWindowsStoreInterpreter(pythonPath: string): Promise<boolean>;
/**
* Whether this is a python executable in a windows app store folder that is internal and can be hidden from users.
*
Expand Down
23 changes: 21 additions & 2 deletions src/client/pythonEnvironments/discovery/locators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
CONDA_ENV_SERVICE,
CURRENT_PATH_SERVICE,
GLOBAL_VIRTUAL_ENV_SERVICE,
IComponentAdapter,
IInterpreterLocatorHelper,
IInterpreterLocatorService,
KNOWN_PATH_SERVICE,
Expand Down Expand Up @@ -181,6 +182,12 @@ function matchURI(uri: Uri, ...candidates: Uri[]): boolean {
return false;
}

// The parts of IComponentAdapter used here.
interface IComponent {
hasInterpreters: Promise<boolean | undefined>;
getInterpreters(resource?: Uri, options?: GetInterpreterLocatorOptions): Promise<PythonEnvironment[] | undefined>;
}

/**
* Facilitates locating Python interpreters.
*/
Expand All @@ -196,7 +203,10 @@ export class PythonInterpreterLocatorService implements IInterpreterLocatorServi

private readonly _hasInterpreters: Deferred<boolean>;

constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {
constructor(
@inject(IServiceContainer) private serviceContainer: IServiceContainer,
@inject(IComponentAdapter) private readonly pyenvs: IComponent
) {
this._hasInterpreters = createDeferred<boolean>();
serviceContainer.get<Disposable[]>(IDisposableRegistry).push(this);
this.platform = serviceContainer.get<IPlatformService>(IPlatformService);
Expand All @@ -217,7 +227,12 @@ export class PythonInterpreterLocatorService implements IInterpreterLocatorServi
}

public get hasInterpreters(): Promise<boolean> {
return this._hasInterpreters.completed ? this._hasInterpreters.promise : Promise.resolve(false);
return this.pyenvs.hasInterpreters.then((res) => {
if (res !== undefined) {
return res;
}
return this._hasInterpreters.completed ? this._hasInterpreters.promise : Promise.resolve(false);
});
}

/**
Expand All @@ -237,6 +252,10 @@ export class PythonInterpreterLocatorService implements IInterpreterLocatorServi
*/
@traceDecorators.verbose('Get Interpreters')
public async getInterpreters(resource?: Uri, options?: GetInterpreterLocatorOptions): Promise<PythonEnvironment[]> {
const envs = await this.pyenvs.getInterpreters(resource, options);
if (envs !== undefined) {
return envs;
}
const locators = this.getLocators(options);
const promises = locators.map(async (provider) => provider.getInterpreters(resource));
locators.forEach((locator) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { IFileSystem, IPlatformService } from '../../../../common/platform/types
import { IProcessServiceFactory } from '../../../../common/process/types';
import { IConfigurationService, IDisposableRegistry, IPersistentStateFactory } from '../../../../common/types';
import { cache } from '../../../../common/utils/decorators';
import { ICondaService, IInterpreterLocatorService, WINDOWS_REGISTRY_SERVICE } from '../../../../interpreter/contracts';
import { IComponentAdapter, ICondaService, IInterpreterLocatorService, WINDOWS_REGISTRY_SERVICE } from '../../../../interpreter/contracts';
import { EnvironmentType, PythonEnvironment } from '../../../info';
import { CondaEnvironmentInfo, CondaInfo } from './conda';
import { parseCondaEnvFileContents } from './condaHelper';
Expand Down Expand Up @@ -49,6 +49,12 @@ export const CondaLocationsGlobWin = `{${condaGlobPathsForWindows.join(',')}}`;

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

// The parts of IComponentAdapter used here.
interface IComponent {
isCondaEnvironment(interpreterPath: string): Promise<boolean | undefined>;
getCondaEnvironment(interpreterPath: string): Promise<CondaEnvironmentInfo | undefined>;
}

/**
* A wrapper around a conda installation.
*/
Expand All @@ -66,6 +72,7 @@ export class CondaService implements ICondaService {
@inject(IConfigurationService) private configService: IConfigurationService,
@inject(IDisposableRegistry) private disposableRegistry: IDisposableRegistry,
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService,
@inject(IComponentAdapter) private readonly pyenvs: IComponent,
@inject(IInterpreterLocatorService)
@named(WINDOWS_REGISTRY_SERVICE)
@optional()
Expand Down Expand Up @@ -183,6 +190,10 @@ export class CondaService implements ICondaService {
* @memberof CondaService
*/
public async isCondaEnvironment(interpreterPath: string): Promise<boolean> {
const result = await this.pyenvs.isCondaEnvironment(interpreterPath);
if (result !== undefined) {
return result;
}
const dir = path.dirname(interpreterPath);
const { isWindows } = this.platform;
const condaMetaDirectory = isWindows ? path.join(dir, 'conda-meta') : path.join(dir, '..', 'conda-meta');
Expand All @@ -193,6 +204,10 @@ export class CondaService implements ICondaService {
* Return (env name, interpreter filename) for the interpreter.
*/
public async getCondaEnvironment(interpreterPath: string): Promise<{ name: string; path: string } | undefined> {
const found = await this.pyenvs.getCondaEnvironment(interpreterPath);
if (found !== undefined) {
return found;
}
const isCondaEnv = await this.isCondaEnvironment(interpreterPath);
if (!isCondaEnv) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class InterpeterHashProviderFactory implements IInterpreterHashProviderFa
? options.pythonPath
: this.configService.getSettings(options.resource).pythonPath;

return this.windowsStoreInterpreter.isWindowsStoreInterpreter(pythonPath)
return (await this.windowsStoreInterpreter.isWindowsStoreInterpreter(pythonPath))
? this.windowsStoreHashProvider
: this.hashProvider;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export class WindowsRegistryService extends CacheableLocatorService {
// Give preference to what we have retrieved from getInterpreterInformation.
version: details.version || parsePythonVersion(version),
companyDisplayName: interpreterInfo.companyDisplayName,
envType: this.windowsStoreInterpreter.isWindowsStoreInterpreter(executablePath)
envType: (await this.windowsStoreInterpreter.isWindowsStoreInterpreter(executablePath))
? EnvironmentType.WindowsStore
: EnvironmentType.Unknown,
} as PythonEnvironment;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { traceDecorators } from '../../../../common/logger';
import { IFileSystem } from '../../../../common/platform/types';
import { IPythonExecutionFactory } from '../../../../common/process/types';
import { IPersistentStateFactory } from '../../../../common/types';
import { IComponentAdapter } from '../../../../interpreter/contracts';
import { IInterpreterHashProvider, IWindowsStoreInterpreter } from '../../../../interpreter/locators/types';
import { IServiceContainer } from '../../../../ioc/types';

Expand All @@ -31,6 +32,11 @@ export function isRestrictedWindowsStoreInterpreterPath(pythonPath: string): boo
);
}

// The parts of IComponentAdapter used here.
interface IComponent {
isWindowsStoreInterpreter(pythonPath: string): Promise<boolean | undefined>;
}

/**
* The default location of Windows apps are `%ProgramFiles%\WindowsApps`.
* (https://www.samlogic.net/articles/windows-8-windowsapps-folder.htm)
Expand Down Expand Up @@ -60,6 +66,7 @@ export class WindowsStoreInterpreter implements IWindowsStoreInterpreter, IInter
@inject(IServiceContainer) private readonly serviceContainer: IServiceContainer,
@inject(IPersistentStateFactory) private readonly persistentFactory: IPersistentStateFactory,
@inject(IFileSystem) private readonly fs: IFileSystem,
@inject(IComponentAdapter) private readonly pyenvs: IComponent
) {}

/**
Expand All @@ -69,7 +76,11 @@ export class WindowsStoreInterpreter implements IWindowsStoreInterpreter, IInter
* @returns {boolean}
* @memberof WindowsStoreInterpreter
*/
public isWindowsStoreInterpreter(pythonPath: string): boolean {
public async isWindowsStoreInterpreter(pythonPath: string): Promise<boolean> {
const result = await this.pyenvs.isWindowsStoreInterpreter(pythonPath);
if (result !== undefined) {
return result;
}
const pythonPathToCompare = pythonPath.toUpperCase().replace(/\//g, '\\');
return (
pythonPathToCompare.includes('\\Microsoft\\WindowsApps\\'.toUpperCase())
Expand Down
18 changes: 9 additions & 9 deletions src/client/pythonEnvironments/legacyIOC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,6 @@ class ComponentAdapter implements IComponentAdapter {

// IInterpreterService

// A result of `undefined` means "Fall back to the old code!"
public get hasInterpreters(): Promise<boolean | undefined> {
if (!this.enabled) {
return Promise.resolve(undefined);
}
const iterator = this.api.iterEnvs();
return iterator.next().then((res) => !res.done);
}

// We use the same getInterpreters() here as for IInterpreterLocatorService.

// A result of `undefined` means "Fall back to the old code!"
Expand Down Expand Up @@ -274,6 +265,15 @@ class ComponentAdapter implements IComponentAdapter {

// IInterpreterLocatorService

// A result of `undefined` means "Fall back to the old code!"
public get hasInterpreters(): Promise<boolean | undefined> {
if (!this.enabled) {
return Promise.resolve(undefined);
}
const iterator = this.api.iterEnvs();
return iterator.next().then((res) => !res.done);
}

// A result of `undefined` means "Fall back to the old code!"
public async getInterpreters(
resource?: vscode.Uri,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => {
.verifiable(typemoq.Times.once());
helper
.setup((i) => i.isMacDefaultPythonPath(typemoq.It.isAny()))
.returns(() => false)
.returns(() => Promise.resolve(false))
.verifiable(typemoq.Times.once());

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

const diagnostics = await diagnosticService.diagnose(undefined);
Expand Down Expand Up @@ -291,11 +291,11 @@ suite('Application Diagnostics - Checks Mac Python Interpreter', () => {
.verifiable(typemoq.Times.once());
helper
.setup((i) => i.isMacDefaultPythonPath(typemoq.It.isValue(pythonPath)))
.returns(() => true)
.returns(() => Promise.resolve(true))
.verifiable(typemoq.Times.atLeastOnce());
helper
.setup((i) => i.isMacDefaultPythonPath(typemoq.It.isValue(nonMacStandardInterpreter)))
.returns(() => false)
.returns(() => Promise.resolve(false))
.verifiable(typemoq.Times.atLeastOnce());
interpreterService
.setup((i) => i.getActiveInterpreter(typemoq.It.isAny()))
Expand Down
Loading

0 comments on commit f15be0e

Please sign in to comment.