Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/client/interpreter/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export interface IComponentAdapter {
// IInterpreterLocatorService (for WINDOWS_REGISTRY_SERVICE)
getWinRegInterpreters(resource: Resource): Promise<PythonEnvironment[]>;
// IInterpreterService
getInterpreterDetails(pythonPath: string): Promise<PythonEnvironment | undefined>;
getInterpreterDetails(pythonPath: string, _resource?: Uri): Promise<PythonEnvironment>;

// IInterpreterHelper
// Undefined is expected on this API, if the environment info retrieval fails.
Expand Down
3 changes: 0 additions & 3 deletions src/client/interpreter/interpreterService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,6 @@ export class InterpreterService implements Disposable, IInterpreterService {
): Promise<StoredPythonEnvironment | undefined> {
if (await inDiscoveryExperiment(this.experimentService)) {
const info = await this.pyenvs.getInterpreterDetails(pythonPath);
if (!info) {
return undefined;
}
if (!info.displayName) {
// Set display name for the environment returned by component if it's not set (this should eventually go away)
info.displayName = await this.getDisplayName(info, resource);
Expand Down
8 changes: 4 additions & 4 deletions src/client/pythonEnvironments/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { Event } from 'vscode';
import { Disposables, IDisposable } from '../common/utils/resourceLifecycle';
import { PythonEnvInfo } from './base/info';
import { IPythonEnvsIterator, IResolvingLocator, PythonLocatorQuery } from './base/locator';
import { ILocator, IPythonEnvsIterator, PythonLocatorQuery } from './base/locator';
import { GetLocatorFunc, LazyWrappingLocator } from './base/locators/common/wrappingLocator';
import { PythonEnvsChangedEvent } from './base/watcher';

Expand All @@ -13,10 +13,10 @@ import { PythonEnvsChangedEvent } from './base/watcher';
*
* Note that this is composed of sub-components.
*/
export class PythonEnvironments implements IResolvingLocator, IDisposable {
export class PythonEnvironments implements ILocator, IDisposable {
private readonly disposables = new Disposables();

private readonly locators: IResolvingLocator;
private readonly locators: ILocator;

constructor(
// These are factories for the sub-components the full component is composed of:
Expand All @@ -41,7 +41,7 @@ export class PythonEnvironments implements IResolvingLocator, IDisposable {
return this.locators.iterEnvs(query);
}

public async resolveEnv(env: string): Promise<PythonEnvInfo | undefined> {
public async resolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
return this.locators.resolveEnv(env);
}
}
6 changes: 3 additions & 3 deletions src/client/pythonEnvironments/base/info/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,14 +269,14 @@ export async function getMaxDerivedEnvInfo(minimal: PythonEnvInfo): Promise<Pyth
*
* The returned function is compatible with `Array.filter()`.
*/
export function getEnvMatcher(query: string): (env: string) => boolean {
export function getEnvMatcher(query: string | Partial<PythonEnvInfo>): (env: PythonEnvInfo) => boolean {
const executable = getEnvExecutable(query);
if (executable === '') {
// We could throw an exception error, but skipping it is fine.
return () => false;
}
function matchEnv(candidateExecutable: string): boolean {
return arePathsSame(executable, candidateExecutable);
function matchEnv(candidate: PythonEnvInfo): boolean {
return arePathsSame(executable, candidate.executable.filename);
}
return matchEnv;
}
Expand Down
25 changes: 17 additions & 8 deletions src/client/pythonEnvironments/base/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,24 @@ export interface ILocator<E extends BasicPythonEnvsChangedEvent = PythonEnvsChan
* @returns - the fast async iterator of Python envs, which may have incomplete info
*/
iterEnvs(query?: QueryForEvent<E>): IPythonEnvsIterator;
}

interface IResolver {
/**
* Find as much info about the given Python environment as possible.
* If executable passed is invalid, then `undefined` is returned.
* Find the given Python environment and fill in as much missing info as possible.
*
* If the locator can find the environment then the result is as
* much info about that env as the locator has. At the least this
* will include all the `PythonEnvBaseInfo` data. If a `PythonEnvInfo`
* was provided then the result will be a copy with any updates or
* extra info applied.
*
* @param env - the Python executable path to resolve more information about
* If the locator could not find the environment then `undefined`
* is returned.
*
* @param env - the Python executable path or partial env info to find and update
*/
resolveEnv(env: string): Promise<PythonEnvInfo | undefined>;
resolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined>;
}

export interface IResolvingLocator extends IResolver, ILocator {}

interface IEmitter<E extends PythonEnvsChangedEvent> {
fire(e: E): void;
}
Expand Down Expand Up @@ -189,6 +193,11 @@ abstract class LocatorBase<E extends BasicPythonEnvsChangedEvent = PythonEnvsCha

// eslint-disable-next-line class-methods-use-this
public abstract iterEnvs(query?: QueryForEvent<E>): IPythonEnvsIterator;

// eslint-disable-next-line class-methods-use-this
public async resolveEnv(_env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
return undefined;
}
}

/**
Expand Down
41 changes: 40 additions & 1 deletion src/client/pythonEnvironments/base/locatorUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { Uri } from 'vscode';
import { traceVerbose } from '../../common/logger';
import { createDeferred } from '../../common/utils/async';
import { getURIFilter } from '../../common/utils/misc';
import { IDisposable } from '../../common/utils/resourceLifecycle';
import { PythonEnvInfo } from './info';
import { getMaxDerivedEnvInfo } from './info/env';
import { getEnvMatcher, getMaxDerivedEnvInfo } from './info/env';
import { IPythonEnvsIterator, PythonEnvUpdatedEvent, PythonLocatorQuery } from './locator';

/**
Expand Down Expand Up @@ -164,3 +165,41 @@ export async function* iterAndUpdateEnvs(
notify(null);
}
}

/**
* Naively implement `ILocator.resolveEnv()` by searching through an iterator.
*/
export async function resolveEnvFromIterator(
env: string | Partial<PythonEnvInfo>,
iterator: IPythonEnvsIterator,
): Promise<PythonEnvInfo | undefined> {
let resolved: PythonEnvInfo | undefined;

const matchEnv = getEnvMatcher(env);

let listener: IDisposable | undefined;
const done = createDeferred<void>();
if (iterator.onUpdated !== undefined) {
listener = iterator.onUpdated((event: PythonEnvUpdatedEvent | null) => {
if (event === null) {
done.resolve();
} else if (!event.update || matchEnv(event.update)) {
resolved = event.update;
}
});
} else {
done.resolve();
}
for await (const iterated of iterator) {
if (matchEnv(iterated)) {
resolved = iterated;
}
}
await done.promise;

if (listener !== undefined) {
listener.dispose();
}

return resolved;
}
11 changes: 11 additions & 0 deletions src/client/pythonEnvironments/base/locators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import { chain } from '../../common/utils/async';
import { Disposables } from '../../common/utils/resourceLifecycle';
import { PythonEnvInfo } from './info';
import { ILocator, IPythonEnvsIterator, PythonEnvUpdatedEvent, PythonLocatorQuery } from './locator';
import { PythonEnvsWatchers } from './watchers';

Expand Down Expand Up @@ -58,4 +59,14 @@ export class Locators extends PythonEnvsWatchers implements ILocator {
const iterators = this.locators.map((loc) => loc.iterEnvs(query));
return combineIterators(iterators);
}

public async resolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
for (const locator of this.locators) {
const resolved = await locator.resolveEnv(env);
if (resolved !== undefined) {
return resolved;
}
}
return undefined;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import { createDeferred, Deferred } from '../../../../common/utils/async';
import { Disposables, IDisposable } from '../../../../common/utils/resourceLifecycle';
import { PythonEnvInfo } from '../../info';
import { IPythonEnvsIterator, Locator, PythonLocatorQuery } from '../../locator';

/**
Expand Down Expand Up @@ -37,11 +38,21 @@ export abstract class LazyResourceBasedLocator extends Locator implements IDispo
this.ensureWatchersReady().ignoreErrors();
}

public async resolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
await this.ensureResourcesReady();
return this.doResolveEnv(env);
}

/**
* The subclass implementation of iterEnvs().
*/
protected abstract doIterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator;

/**
* The subclass implementation of resolveEnv().
*/
protected abstract doResolveEnv(_env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined>;

/**
* This is where subclasses get their resources ready.
*
Expand Down Expand Up @@ -79,7 +90,7 @@ export abstract class LazyResourceBasedLocator extends Locator implements IDispo
// No watchers!
}

protected async ensureResourcesReady(): Promise<void> {
private async ensureResourcesReady(): Promise<void> {
if (this.resourcesReady !== undefined) {
await this.resourcesReady.promise;
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
import { Event } from 'vscode';
import { IDisposable } from '../../../../common/utils/resourceLifecycle';
import { PythonEnvInfo } from '../../info';
import { IPythonEnvsIterator, IResolvingLocator, PythonLocatorQuery } from '../../locator';
import { ILocator, IPythonEnvsIterator, PythonLocatorQuery } from '../../locator';
import { PythonEnvsChangedEvent, PythonEnvsWatcher } from '../../watcher';
import { LazyResourceBasedLocator } from './resourceBasedLocator';

export type GetLocatorFunc = () => Promise<IResolvingLocator & Partial<IDisposable>>;
export type GetLocatorFunc = () => Promise<ILocator & Partial<IDisposable>>;

/**
* A locator that wraps another.
Expand All @@ -20,7 +20,7 @@ export class LazyWrappingLocator extends LazyResourceBasedLocator {

private readonly watcher = new PythonEnvsWatcher();

private wrapped?: IResolvingLocator;
private wrapped?: ILocator;

constructor(private readonly getLocator: GetLocatorFunc) {
super();
Expand All @@ -31,7 +31,7 @@ export class LazyWrappingLocator extends LazyResourceBasedLocator {
yield* this.wrapped!.iterEnvs(query);
}

public async resolveEnv(env: string): Promise<PythonEnvInfo | undefined> {
protected async doResolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
return this.wrapped!.resolveEnv(env);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { logWarning } from '../../../../logging';
import { IEnvsCache } from '../../envsCache';
import { PythonEnvInfo } from '../../info';
import { getMinimalPartialInfo } from '../../info/env';
import { IPythonEnvsIterator, IResolvingLocator, PythonLocatorQuery } from '../../locator';
import { ILocator, IPythonEnvsIterator, PythonLocatorQuery } from '../../locator';
import { getEnvs, getQueryFilter } from '../../locatorUtils';
import { PythonEnvsChangedEvent, PythonEnvsWatcher } from '../../watcher';
import { LazyResourceBasedLocator } from '../common/resourceBasedLocator';
Expand All @@ -17,7 +17,7 @@ import { pickBestEnv } from './reducingLocator';
/**
* A locator that stores the known environments in the given cache.
*/
export class CachingLocator extends LazyResourceBasedLocator implements IResolvingLocator {
export class CachingLocator extends LazyResourceBasedLocator {
public readonly onChanged: Event<PythonEnvsChangedEvent>;

private readonly watcher = new PythonEnvsWatcher();
Expand All @@ -26,7 +26,7 @@ export class CachingLocator extends LazyResourceBasedLocator implements IResolvi

private refreshCache?: () => Promise<void>;

constructor(private readonly cache: IEnvsCache, private readonly locator: IResolvingLocator) {
constructor(private readonly cache: IEnvsCache, private readonly locator: ILocator) {
super();
this.onChanged = this.watcher.onChanged;
}
Expand All @@ -39,8 +39,7 @@ export class CachingLocator extends LazyResourceBasedLocator implements IResolvi
yield* this.iterFromCache(query);
}

public async resolveEnv(env: string): Promise<PythonEnvInfo | undefined> {
await this.ensureResourcesReady();
protected async doResolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
let matchingEnvs = this.filterMatchingEnvsFromCache(env);
if (matchingEnvs.length > 0) {
return pickBestEnv(matchingEnvs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { traceVerbose } from '../../../../common/logger';
import { PythonEnvInfo, PythonEnvKind } from '../../info';
import { areSameEnv, mergeEnvironments } from '../../info/env';
import { ILocator, IPythonEnvsIterator, PythonEnvUpdatedEvent, PythonLocatorQuery } from '../../locator';
import { getEnvs } from '../../locatorUtils';
import { PythonEnvsChangedEvent } from '../../watcher';

/**
Expand All @@ -19,6 +20,17 @@ export class PythonEnvsReducer implements ILocator {

constructor(private readonly parentLocator: ILocator) {}

public async resolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
const environments = await getEnvs(this.iterEnvs());
let environment: string | PythonEnvInfo | undefined = environments.find((e) => areSameEnv(e, env));
if (!environment) {
// It isn't one we've reduced, but fall back
// to the wrapped locator anyway.
environment = env;
}
return this.parentLocator.resolveEnv(environment);
}

public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator {
const didUpdate = new EventEmitter<PythonEnvUpdatedEvent | null>();
const incomingIterator = this.parentLocator.iterEnvs(query);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,14 @@ import { traceVerbose } from '../../../../common/logger';
import { IEnvironmentInfoService } from '../../../info/environmentInfoService';
import { PythonEnvInfo } from '../../info';
import { InterpreterInformation } from '../../info/interpreter';
import {
ILocator,
IPythonEnvsIterator,
IResolvingLocator,
PythonEnvUpdatedEvent,
PythonLocatorQuery,
} from '../../locator';
import { ILocator, IPythonEnvsIterator, PythonEnvUpdatedEvent, PythonLocatorQuery } from '../../locator';
import { PythonEnvsChangedEvent } from '../../watcher';
import { resolveEnv } from './resolverUtils';

/**
* Calls environment info service which runs `interpreterInfo.py` script on environments received
* from the parent locator. Uses information received to populate environments further and pass it on.
*/
export class PythonEnvsResolver implements IResolvingLocator {
export class PythonEnvsResolver implements ILocator {
public get onChanged(): Event<PythonEnvsChangedEvent> {
return this.parentLocator.onChanged;
}
Expand All @@ -31,8 +24,11 @@ export class PythonEnvsResolver implements IResolvingLocator {
private readonly environmentInfoService: IEnvironmentInfoService,
) {}

public async resolveEnv(executablePath: string): Promise<PythonEnvInfo | undefined> {
const environment = await resolveEnv(executablePath);
public async resolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
const environment = await this.parentLocator.resolveEnv(env);
if (!environment) {
return undefined;
}
const info = await this.environmentInfoService.getEnvironmentInfo(environment.executable.filename);
if (!info) {
return undefined;
Expand Down
Loading