Skip to content

Commit 919ea9e

Browse files
author
Kartik Raj
authored
Do not resolve inside iterEnvs in low-level locators (#16642)
* Log more details * Fix tests * Pluck resolving out of locators * Allow ILocator to accept additional return types for iterEnvs * Modify iterEnvs of Windows store locator to return only basic envs * Rename executable to executablePath * Factor out resolver for kind * Only do basic env collision resolving in reducer * Fix tests for resolver * Fix tests with reducer * Pluck out resolving in windows registry locator * Pluck out resolving in workspace locator * Pluck out resolving in global locator * Pluck out resolving in poetry locator * Pluck out resolving in custom virtualenv locator * Pluck out resolving in pyenv locator * Pluck out resolving in conda locator * Pluck out resolving in posix known paths locator * Pluck out resolving in windows path locator * Fix watcher tests * Change pythonEnvs index module to accept BasicEnvInfo * Remove duplicate assignment * Oops * Remove unnecessary API * Fix bug * Added resolver to resolve globally installed envs * Rename * Fix tests
1 parent d78ceea commit 919ea9e

44 files changed

Lines changed: 851 additions & 1909 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/client/common/utils/async.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export function createDeferredFromPromise<T>(promise: Promise<T>): Deferred<T> {
9999

100100
// iterators
101101

102-
interface IAsyncIterator<T> extends AsyncIterator<T, void>, Partial<AsyncIterable<T>> {}
102+
interface IAsyncIterator<T> extends AsyncIterator<T, void> {}
103103

104104
export interface IAsyncIterableIterator<T> extends IAsyncIterator<T>, AsyncIterable<T> {}
105105

src/client/pythonEnvironments/base/info/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ export enum PythonEnvKind {
2828
OtherVirtual = 'virt-other',
2929
}
3030

31+
export const virtualEnvKinds = [
32+
PythonEnvKind.Poetry,
33+
PythonEnvKind.Pipenv,
34+
PythonEnvKind.Venv,
35+
PythonEnvKind.VirtualEnvWrapper,
36+
PythonEnvKind.Conda,
37+
PythonEnvKind.VirtualEnv,
38+
];
3139
/**
3240
* Information about a file.
3341
*/

src/client/pythonEnvironments/base/locator.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,20 @@ import { BasicPythonEnvsChangedEvent, IPythonEnvsWatcher, PythonEnvsChangedEvent
1111
/**
1212
* A single update to a previously provided Python env object.
1313
*/
14-
export type PythonEnvUpdatedEvent = {
14+
export type PythonEnvUpdatedEvent<I = PythonEnvInfo> = {
1515
/**
1616
* The iteration index of The env info that was previously provided.
1717
*/
1818
index: number;
1919
/**
2020
* The env info that was previously provided.
2121
*/
22-
old?: PythonEnvInfo;
22+
old?: I;
2323
/**
2424
* The env info that replaces the old info.
2525
* Update is sent as `undefined` if we find out that the environment is no longer valid.
2626
*/
27-
update: PythonEnvInfo | undefined;
27+
update: I | undefined;
2828
};
2929

3030
/**
@@ -49,7 +49,7 @@ export type PythonEnvUpdatedEvent = {
4949
* Callers can usually ignore the update event entirely and rely on
5050
* the locator to provide sufficiently complete information.
5151
*/
52-
export interface IPythonEnvsIterator extends IAsyncIterableIterator<PythonEnvInfo> {
52+
export interface IPythonEnvsIterator<I = PythonEnvInfo> extends IAsyncIterableIterator<I> {
5353
/**
5454
* Provides possible updates for already-iterated envs.
5555
*
@@ -58,7 +58,7 @@ export interface IPythonEnvsIterator extends IAsyncIterableIterator<PythonEnvInf
5858
* If this property is not provided then it means the iterator does
5959
* not support updates.
6060
*/
61-
onUpdated?: Event<PythonEnvUpdatedEvent | null>;
61+
onUpdated?: Event<PythonEnvUpdatedEvent<I> | null>;
6262
}
6363

6464
/**
@@ -114,6 +114,8 @@ export type PythonLocatorQuery = BasicPythonLocatorQuery & {
114114

115115
type QueryForEvent<E> = E extends PythonEnvsChangedEvent ? PythonLocatorQuery : BasicPythonLocatorQuery;
116116

117+
export type BasicEnvInfo = { kind: PythonEnvKind; executablePath: string };
118+
117119
/**
118120
* A single Python environment locator.
119121
*
@@ -128,7 +130,7 @@ type QueryForEvent<E> = E extends PythonEnvsChangedEvent ? PythonLocatorQuery :
128130
* events emitted via `onChanged` do not need to provide information
129131
* for the specific environments that changed.
130132
*/
131-
export interface ILocator<E extends BasicPythonEnvsChangedEvent = PythonEnvsChangedEvent>
133+
export interface ILocator<I = PythonEnvInfo, E extends BasicPythonEnvsChangedEvent = PythonEnvsChangedEvent>
132134
extends IPythonEnvsWatcher<E> {
133135
/**
134136
* Iterate over the enviroments known tos this locator.
@@ -146,7 +148,7 @@ export interface ILocator<E extends BasicPythonEnvsChangedEvent = PythonEnvsChan
146148
* @param query - if provided, the locator will limit results to match
147149
* @returns - the fast async iterator of Python envs, which may have incomplete info
148150
*/
149-
iterEnvs(query?: QueryForEvent<E>): IPythonEnvsIterator;
151+
iterEnvs(query?: QueryForEvent<E>): IPythonEnvsIterator<I>;
150152
}
151153

152154
interface IResolver {
@@ -159,7 +161,7 @@ interface IResolver {
159161
resolveEnv(env: string): Promise<PythonEnvInfo | undefined>;
160162
}
161163

162-
export interface IResolvingLocator extends IResolver, ILocator {}
164+
export interface IResolvingLocator<I = PythonEnvInfo> extends IResolver, ILocator<I> {}
163165

164166
interface IEmitter<E extends PythonEnvsChangedEvent> {
165167
fire(e: E): void;
@@ -177,7 +179,8 @@ interface IEmitter<E extends PythonEnvsChangedEvent> {
177179
* should be used. Only in low-level cases should you consider using
178180
* `BasicPythonEnvsChangedEvent`.
179181
*/
180-
abstract class LocatorBase<E extends BasicPythonEnvsChangedEvent = PythonEnvsChangedEvent> implements ILocator<E> {
182+
abstract class LocatorBase<I = PythonEnvInfo, E extends BasicPythonEnvsChangedEvent = PythonEnvsChangedEvent>
183+
implements ILocator<I, E> {
181184
public readonly onChanged: Event<E>;
182185

183186
protected readonly emitter: IEmitter<E>;
@@ -188,7 +191,7 @@ abstract class LocatorBase<E extends BasicPythonEnvsChangedEvent = PythonEnvsCha
188191
}
189192

190193
// eslint-disable-next-line class-methods-use-this
191-
public abstract iterEnvs(query?: QueryForEvent<E>): IPythonEnvsIterator;
194+
public abstract iterEnvs(query?: QueryForEvent<E>): IPythonEnvsIterator<I>;
192195
}
193196

194197
/**
@@ -203,7 +206,7 @@ abstract class LocatorBase<E extends BasicPythonEnvsChangedEvent = PythonEnvsCha
203206
* Only in low-level cases should you consider subclassing `LocatorBase`
204207
* using `BasicPythonEnvsChangedEvent.
205208
*/
206-
export abstract class Locator extends LocatorBase {
209+
export abstract class Locator<I = PythonEnvInfo> extends LocatorBase<I> {
207210
constructor() {
208211
super(new PythonEnvsWatcher());
209212
}

src/client/pythonEnvironments/base/locatorUtils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,14 @@ function getSearchLocationFilters(query: PythonLocatorQuery): ((u: Uri) => boole
7474
*
7575
* This includes applying any received updates.
7676
*/
77-
export async function getEnvs(iterator: IPythonEnvsIterator): Promise<PythonEnvInfo[]> {
78-
const envs: (PythonEnvInfo | undefined)[] = [];
77+
export async function getEnvs<I = PythonEnvInfo>(iterator: IPythonEnvsIterator<I>): Promise<I[]> {
78+
const envs: (I | undefined)[] = [];
7979

8080
const updatesDone = createDeferred<void>();
8181
if (iterator.onUpdated === undefined) {
8282
updatesDone.resolve();
8383
} else {
84-
const listener = iterator.onUpdated((event: PythonEnvUpdatedEvent | null) => {
84+
const listener = iterator.onUpdated((event: PythonEnvUpdatedEvent<I> | null) => {
8585
if (event === null) {
8686
updatesDone.resolve();
8787
listener.dispose();

src/client/pythonEnvironments/base/locators.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,27 @@
33

44
import { chain } from '../../common/utils/async';
55
import { Disposables } from '../../common/utils/resourceLifecycle';
6+
import { PythonEnvInfo } from './info';
67
import { ILocator, IPythonEnvsIterator, PythonEnvUpdatedEvent, PythonLocatorQuery } from './locator';
78
import { PythonEnvsWatchers } from './watchers';
89

910
/**
1011
* Combine the `onUpdated` event of the given iterators into a single event.
1112
*/
12-
export function combineIterators(iterators: IPythonEnvsIterator[]): IPythonEnvsIterator {
13-
const result: IPythonEnvsIterator = chain(iterators);
13+
export function combineIterators<I>(iterators: IPythonEnvsIterator<I>[]): IPythonEnvsIterator<I> {
14+
const result: IPythonEnvsIterator<I> = chain(iterators);
1415
const events = iterators.map((it) => it.onUpdated).filter((v) => v);
1516
if (!events || events.length === 0) {
1617
// There are no sub-events, so we leave `onUpdated` undefined.
1718
return result;
1819
}
1920

2021
// eslint-disable-next-line @typescript-eslint/no-explicit-any
21-
result.onUpdated = (handleEvent: (e: PythonEnvUpdatedEvent | null) => any) => {
22+
result.onUpdated = (handleEvent: (e: PythonEnvUpdatedEvent<I> | null) => any) => {
2223
const disposables = new Disposables();
2324
let numActive = events.length;
2425
events.forEach((event) => {
25-
const disposable = event!((e: PythonEnvUpdatedEvent | null) => {
26+
const disposable = event!((e: PythonEnvUpdatedEvent<I> | null) => {
2627
// NOSONAR
2728
if (e === null) {
2829
numActive -= 1;
@@ -46,15 +47,15 @@ export function combineIterators(iterators: IPythonEnvsIterator[]): IPythonEnvsI
4647
*
4748
* Events and iterator results are combined.
4849
*/
49-
export class Locators extends PythonEnvsWatchers implements ILocator {
50+
export class Locators<I = PythonEnvInfo> extends PythonEnvsWatchers implements ILocator<I> {
5051
constructor(
5152
// The locators will be watched as well as iterated.
52-
private readonly locators: ReadonlyArray<ILocator>,
53+
private readonly locators: ReadonlyArray<ILocator<I>>,
5354
) {
5455
super(locators);
5556
}
5657

57-
public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator {
58+
public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator<I> {
5859
const iterators = this.locators.map((loc) => loc.iterEnvs(query));
5960
return combineIterators(iterators);
6061
}

src/client/pythonEnvironments/base/locators/common/resourceBasedLocator.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import { createDeferred, Deferred } from '../../../../common/utils/async';
55
import { Disposables, IDisposable } from '../../../../common/utils/resourceLifecycle';
6+
import { PythonEnvInfo } from '../../info';
67
import { IPythonEnvsIterator, Locator, PythonLocatorQuery } from '../../locator';
78

89
/**
@@ -17,7 +18,7 @@ import { IPythonEnvsIterator, Locator, PythonLocatorQuery } from '../../locator'
1718
*
1819
* Otherwise it will leak (and we have no leak detection).
1920
*/
20-
export abstract class LazyResourceBasedLocator extends Locator implements IDisposable {
21+
export abstract class LazyResourceBasedLocator<I = PythonEnvInfo> extends Locator<I> implements IDisposable {
2122
protected readonly disposables = new Disposables();
2223

2324
// This will be set only once we have to create necessary resources
@@ -30,7 +31,7 @@ export abstract class LazyResourceBasedLocator extends Locator implements IDispo
3031
await this.disposables.dispose();
3132
}
3233

33-
public async *iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator {
34+
public async *iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator<I> {
3435
await this.ensureResourcesReady();
3536
yield* this.doIterEnvs(query);
3637
// There is not need to wait for the watchers to get started.
@@ -40,7 +41,7 @@ export abstract class LazyResourceBasedLocator extends Locator implements IDispo
4041
/**
4142
* The subclass implementation of iterEnvs().
4243
*/
43-
protected abstract doIterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator;
44+
protected abstract doIterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator<I>;
4445

4546
/**
4647
* This is where subclasses get their resources ready.

src/client/pythonEnvironments/base/locators/common/wrappingLocator.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,31 @@ import { IPythonEnvsIterator, IResolvingLocator, PythonLocatorQuery } from '../.
88
import { PythonEnvsChangedEvent, PythonEnvsWatcher } from '../../watcher';
99
import { LazyResourceBasedLocator } from './resourceBasedLocator';
1010

11-
export type GetLocatorFunc = () => Promise<IResolvingLocator & Partial<IDisposable>>;
11+
export type GetLocatorFunc<I = PythonEnvInfo> = () => Promise<IResolvingLocator<I> & Partial<IDisposable>>;
1212

1313
/**
1414
* A locator that wraps another.
1515
*
1616
* This facilitates isolating the wrapped locator.
1717
*/
18-
export class LazyWrappingLocator extends LazyResourceBasedLocator {
18+
export class LazyWrappingLocator<I = PythonEnvInfo> extends LazyResourceBasedLocator<I> {
1919
public readonly onChanged: Event<PythonEnvsChangedEvent>;
2020

2121
private readonly watcher = new PythonEnvsWatcher();
2222

23-
private wrapped?: IResolvingLocator;
23+
private wrapped?: IResolvingLocator<I>;
2424

25-
constructor(private readonly getLocator: GetLocatorFunc) {
25+
constructor(private readonly getLocator: GetLocatorFunc<I>) {
2626
super();
2727
this.onChanged = this.watcher.onChanged;
2828
}
2929

30-
protected async *doIterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator {
30+
protected async *doIterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator<I> {
3131
yield* this.wrapped!.iterEnvs(query);
3232
}
3333

3434
public async resolveEnv(env: string): Promise<PythonEnvInfo | undefined> {
35+
await this.ensureResourcesReady();
3536
return this.wrapped!.resolveEnv(env);
3637
}
3738

0 commit comments

Comments
 (0)