Skip to content

Commit

Permalink
Adds low-level locators (microsoft#14655)
Browse files Browse the repository at this point in the history
* Provide factory function for posix locator

* Provide factory function for global virtual env

* Provide factory function for windos store and reg

* Refactor and add low-level locators

* Tweak interfaces

* clean up

* Simplify disposing locators

* Ignore dispose fails after logging

* Rebase with main and fix conflicts
  • Loading branch information
karthiknadig authored Nov 10, 2020
1 parent 7b275b4 commit 0d398f7
Show file tree
Hide file tree
Showing 23 changed files with 343 additions and 197 deletions.
2 changes: 1 addition & 1 deletion src/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ async function activateUnsafe(
const [serviceManager, serviceContainer] = initializeGlobals(context);
activatedServiceContainer = serviceContainer;
initializeCommon(context, serviceManager, serviceContainer);
initializeComponents(context, serviceManager, serviceContainer);
await initializeComponents(context, serviceManager, serviceContainer);
const { activationPromise } = await activateComponents(context, serviceManager, serviceContainer);

//===============================================
Expand Down
4 changes: 2 additions & 2 deletions src/client/extensionInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ export function initializeCommon(
// We will be pulling other code over from activateLegacy().
}

export function initializeComponents(
export async function initializeComponents(
_context: IExtensionContext,
serviceManager: IServiceManager,
serviceContainer: IServiceContainer
) {
activatePythonEnvironments(serviceManager, serviceContainer);
await activatePythonEnvironments(serviceManager, serviceContainer);
}
41 changes: 41 additions & 0 deletions src/client/pythonEnvironments/base/disableableLocator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { PythonEnvInfo } from './info';
import {
ILocator,
IPythonEnvsIterator,
NOOP_ITERATOR,
PythonLocatorQuery,
} from './locator';
import { DisableableEnvsWatcher } from './watchers';

/**
* A locator wrapper that can be disabled.
*
* If disabled, events emitted by the wrapped locator are discarded,
* `iterEnvs()` yields nothing, and `resolveEnv()` already returns
* `undefined`.
*/
export class DisableableLocator extends DisableableEnvsWatcher implements ILocator {
constructor(
// To wrapp more than one use `Locators`.
private readonly locator: ILocator,
) {
super(locator);
}

public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator {
if (!this.enabled) {
return NOOP_ITERATOR;
}
return this.locator.iterEnvs(query);
}

public async resolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
if (!this.enabled) {
return undefined;
}
return this.locator.resolveEnv(env);
}
}
5 changes: 4 additions & 1 deletion src/client/pythonEnvironments/base/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ export interface ILocator<E extends BasicPythonEnvsChangedEvent = PythonEnvsChan
resolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined>;
}

export interface IDisposableLocator<E extends BasicPythonEnvsChangedEvent = PythonEnvsChangedEvent>
extends ILocator<E>, IDisposable{}

interface IEmitter<E extends BasicPythonEnvsChangedEvent> {
fire(e: E): void;
}
Expand All @@ -184,7 +187,7 @@ interface IEmitter<E extends BasicPythonEnvsChangedEvent> {
* `BasicPythonEnvsChangedEvent`.
*/
abstract class LocatorBase<E extends BasicPythonEnvsChangedEvent = PythonEnvsChangedEvent>
implements IDisposable, ILocator<E> {
implements IDisposableLocator<E> {
public readonly onChanged: Event<E>;

protected readonly emitter: IEmitter<E>;
Expand Down
48 changes: 14 additions & 34 deletions src/client/pythonEnvironments/base/locators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
// Licensed under the MIT License.

import { EventEmitter } from 'vscode';
import { traceVerbose } from '../../common/logger';
import { chain } from '../../common/utils/async';
import { PythonEnvInfo } from './info';
import {
ILocator,
IDisposableLocator,
IPythonEnvsIterator,
NOOP_ITERATOR,
PythonEnvUpdatedEvent,
PythonLocatorQuery
PythonLocatorQuery,
} from './locator';
import { DisableableEnvsWatcher, PythonEnvsWatchers } from './watchers';
import { PythonEnvsWatchers } from './watchers';

/**
* Combine the `onUpdated` event of the given iterators into a single event.
Expand Down Expand Up @@ -48,10 +48,10 @@ export function combineIterators(iterators: IPythonEnvsIterator[]): IPythonEnvsI
*
* Events and iterator results are combined.
*/
export class Locators extends PythonEnvsWatchers implements ILocator {
export class Locators extends PythonEnvsWatchers implements IDisposableLocator {
constructor(
// The locators will be watched as well as iterated.
private readonly locators: ReadonlyArray<ILocator>
private readonly locators: ReadonlyArray<IDisposableLocator>,
) {
super(locators);
}
Expand All @@ -70,34 +70,14 @@ export class Locators extends PythonEnvsWatchers implements ILocator {
}
return undefined;
}
}

/**
* A locator wrapper that can be disabled.
*
* If disabled, events emitted by the wrapped locator are discarded,
* `iterEnvs()` yields nothing, and `resolveEnv()` already returns
* `undefined`.
*/
export class DisableableLocator extends DisableableEnvsWatcher implements ILocator {
constructor(
// To wrapp more than one use `Locators`.
private readonly locator: ILocator
) {
super(locator);
}

public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator {
if (!this.enabled) {
return NOOP_ITERATOR;
}
return this.locator.iterEnvs(query);
}

public async resolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
if (!this.enabled) {
return undefined;
}
return this.locator.resolveEnv(env);
public dispose(): void {
this.locators.forEach((locator) => {
try {
locator.dispose();
} catch (ex) {
traceVerbose(`Dispose failed for ${typeof locator} locator:`, ex);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { IEnvsCache } from '../../envsCache';
import { PythonEnvInfo } from '../../info';
import { getMinimalPartialInfo } from '../../info/env';
import {
ILocator,
IDisposableLocator,
IPythonEnvsIterator,
PythonLocatorQuery,
} from '../../locator';
Expand All @@ -21,7 +21,7 @@ import { pickBestEnv } from './reducingLocator';
/**
* A locator that stores the known environments in the given cache.
*/
export class CachingLocator implements ILocator {
export class CachingLocator implements IDisposableLocator {
public readonly onChanged: Event<PythonEnvsChangedEvent>;

private readonly watcher = new PythonEnvsWatcher();
Expand All @@ -34,7 +34,7 @@ export class CachingLocator implements ILocator {

constructor(
private readonly cache: IEnvsCache,
private readonly locator: ILocator,
private readonly locator: IDisposableLocator,
) {
this.onChanged = this.watcher.onChanged;
this.looper = new BackgroundRequestLooper({
Expand Down Expand Up @@ -76,6 +76,8 @@ export class CachingLocator implements ILocator {
public dispose(): void {
const waitUntilStopped = this.looper.stop();
waitUntilStopped.ignoreErrors();

this.locator.dispose();
}

public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator {
Expand Down
12 changes: 6 additions & 6 deletions src/client/pythonEnvironments/discovery/locators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ import {
WORKSPACE_VIRTUAL_ENV_SERVICE,
} from '../../../interpreter/contracts';
import { IServiceContainer } from '../../../ioc/types';
import { DisableableLocator } from '../../base/disableableLocator';
import { PythonEnvInfo } from '../../base/info';
import {
ILocator,
IDisposableLocator,
IPythonEnvsIterator,
Locator,
NOOP_ITERATOR,
PythonLocatorQuery,
} from '../../base/locator';
import {
combineIterators,
DisableableLocator,
Locators,
} from '../../base/locators';
import { PythonEnvironment } from '../../info';
Expand All @@ -48,16 +48,16 @@ import { GetInterpreterLocatorOptions } from './types';
export class ExtensionLocators extends Locators {
constructor(
// These are expected to be low-level locators (e.g. system).
nonWorkspace: ILocator[],
nonWorkspace: IDisposableLocator[],
// This is expected to be a locator wrapping any found in
// the workspace (i.e. WorkspaceLocators).
workspace: ILocator,
workspace: IDisposableLocator,
) {
super([...nonWorkspace, workspace]);
}
}

type WorkspaceLocatorFactory = (root: Uri) => ILocator[];
type WorkspaceLocatorFactory = (root: Uri) => IDisposableLocator[];

interface IWorkspaceFolders {
readonly roots: ReadonlyArray<Uri>;
Expand Down Expand Up @@ -144,7 +144,7 @@ export class WorkspaceLocators extends Locator {
// Drop the old one, if necessary.
this.removeRoot(root);
// Create the root's locator, wrapping each factory-generated locator.
const locators: ILocator[] = [];
const locators: IDisposableLocator[] = [];
this.factories.forEach((create) => {
locators.push(...create(root));
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ import * as path from 'path';
import { traceVerbose } from '../../../../common/logger';
import { chain, iterable } from '../../../../common/utils/async';
import {
getEnvironmentVariable, getOSType, getUserHomeDir, OSType
getEnvironmentVariable, getOSType, getUserHomeDir, OSType,
} from '../../../../common/utils/platform';
import { PythonEnvInfo, PythonEnvKind, UNKNOWN_PYTHON_VERSION } from '../../../base/info';
import { buildEnvInfo } from '../../../base/info/env';
import { IPythonEnvsIterator } from '../../../base/locator';
import { IDisposableLocator, IPythonEnvsIterator } from '../../../base/locator';
import { FSWatchingLocator } from '../../../base/locators/lowLevel/fsWatchingLocator';
import { findInterpretersInDir } from '../../../common/commonUtils';
import { getFileInfo, pathExists } from '../../../common/externalDependencies';
import { isPipenvEnvironment } from './pipEnvHelper';
import {
isVenvEnvironment,
isVirtualenvEnvironment,
isVirtualenvwrapperEnvironment
isVirtualenvwrapperEnvironment,
} from './virtualEnvironmentIdentifier';

const DEFAULT_SEARCH_DEPTH = 2;
Expand Down Expand Up @@ -79,7 +79,7 @@ async function getVirtualEnvKind(interpreterPath: string): Promise<PythonEnvKind
/**
* Finds and resolves virtual environments created in known global locations.
*/
export class GlobalVirtualEnvironmentLocator extends FSWatchingLocator {
class GlobalVirtualEnvironmentLocator extends FSWatchingLocator {
private virtualEnvKinds = [
PythonEnvKind.Venv,
PythonEnvKind.VirtualEnv,
Expand Down Expand Up @@ -172,3 +172,9 @@ export class GlobalVirtualEnvironmentLocator extends FSWatchingLocator {
return undefined;
}
}

export async function createGlobalVirtualEnvironmentLocator(searchDepth?: number): Promise<IDisposableLocator> {
const locator = new GlobalVirtualEnvironmentLocator(searchDepth);
await locator.initialize();
return locator;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import {
PythonEnvInfo, PythonEnvKind, PythonReleaseLevel, PythonVersion,
} from '../../../base/info';
import { parseVersion } from '../../../base/info/pythonVersion';
import { ILocator, IPythonEnvsIterator } from '../../../base/locator';
import { PythonEnvsWatcher } from '../../../base/watcher';
import {
IDisposableLocator, IPythonEnvsIterator, Locator,
} from '../../../base/locator';
import { getFileInfo, resolveSymbolicLink } from '../../../common/externalDependencies';
import { commonPosixBinPaths, isPosixPythonBin } from '../../../common/posixUtils';

Expand Down Expand Up @@ -39,7 +40,7 @@ async function getPythonBinFromKnownPaths(): Promise<string[]> {
return Array.from(pythonBins);
}

export class PosixKnownPathsLocator extends PythonEnvsWatcher implements ILocator {
class PosixKnownPathsLocator extends Locator {
private kind: PythonEnvKind = PythonEnvKind.OtherGlobal;

public iterEnvs(): IPythonEnvsIterator {
Expand Down Expand Up @@ -85,3 +86,8 @@ export class PosixKnownPathsLocator extends PythonEnvsWatcher implements ILocato
};
}
}

export function createPosixKnownPathsLocator(): Promise<IDisposableLocator> {
const locator = new PosixKnownPathsLocator();
return Promise.resolve(locator);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import {
PythonEnvInfo, PythonEnvKind,
} from '../../../base/info';
import { buildEnvInfo } from '../../../base/info/env';
import { ILocator, IPythonEnvsIterator } from '../../../base/locator';
import { PythonEnvsWatcher } from '../../../base/watcher';
import { IDisposableLocator, IPythonEnvsIterator, Locator } from '../../../base/locator';
import {
getEnvironmentDirFromPath, getInterpreterPathFromDir, getPythonVersionFromPath,
} from '../../../common/commonUtils';
Expand Down Expand Up @@ -294,7 +293,7 @@ async function* getPyenvEnvironments(): AsyncIterableIterator<PythonEnvInfo> {
}
}

export class PyenvLocator extends PythonEnvsWatcher implements ILocator {
class PyenvLocator extends Locator {
// eslint-disable-next-line class-methods-use-this
public iterEnvs(): IPythonEnvsIterator {
return getPyenvEnvironments();
Expand Down Expand Up @@ -329,3 +328,8 @@ export class PyenvLocator extends PythonEnvsWatcher implements ILocator {
return undefined;
}
}

export function createPyenvLocator(): Promise<IDisposableLocator> {
const locator = new PyenvLocator();
return Promise.resolve(locator);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import {
PythonEnvInfo, PythonEnvKind, PythonVersion, UNKNOWN_PYTHON_VERSION,
} from '../../../base/info';
import { parseVersion } from '../../../base/info/pythonVersion';
import { ILocator, IPythonEnvsIterator } from '../../../base/locator';
import { PythonEnvsWatcher } from '../../../base/watcher';
import {
IDisposableLocator, IPythonEnvsIterator, Locator,
} from '../../../base/locator';
import { getFileInfo } from '../../../common/externalDependencies';
import { getInterpreterDataFromRegistry, IRegistryInterpreterData, readRegistryKeys } from '../../../common/windowsUtils';

Expand Down Expand Up @@ -44,7 +45,7 @@ function getArchitecture(data:IRegistryInterpreterData) {
return arch;
}

export class WindowsRegistryLocator extends PythonEnvsWatcher implements ILocator {
class WindowsRegistryLocator extends Locator {
private kind:PythonEnvKind = PythonEnvKind.OtherGlobal;

public iterEnvs(): IPythonEnvsIterator {
Expand Down Expand Up @@ -93,3 +94,8 @@ export class WindowsRegistryLocator extends PythonEnvsWatcher implements ILocato
};
}
}

export function createWindowsRegistryLocator(): Promise<IDisposableLocator> {
const locator = new WindowsRegistryLocator();
return Promise.resolve(locator);
}
Loading

0 comments on commit 0d398f7

Please sign in to comment.