Skip to content

Commit 37db846

Browse files
author
Kartik Raj
committed
Added environments reducer
1 parent bc9cd8b commit 37db846

File tree

3 files changed

+106
-5
lines changed

3 files changed

+106
-5
lines changed

src/client/pythonEnvironments/base/locator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export type PythonLocatorQuery = BasicPythonLocatorQuery & {
9292
searchLocations?: Uri[];
9393
};
9494

95-
type QueryForEvent<E> = E extends PythonEnvsChangedEvent ? PythonLocatorQuery : BasicPythonLocatorQuery;
95+
export type QueryForEvent<E> = E extends PythonEnvsChangedEvent ? PythonLocatorQuery : BasicPythonLocatorQuery;
9696

9797
/**
9898
* A single Python environment locator.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { Event, EventEmitter } from 'vscode';
5+
import { createDeferredFrom, Deferred } from '../common/utils/async';
6+
import { PythonEnvInfo } from './base/info';
7+
import {
8+
ILocator, IPythonEnvsIterator, PythonEnvUpdatedEvent, QueryForEvent,
9+
} from './base/locator';
10+
import { BasicPythonEnvsChangedEvent, PythonEnvsChangedEvent } from './base/watcher';
11+
import { mergeEnvironments } from './info';
12+
13+
export class PythonEnvsReducer<E extends BasicPythonEnvsChangedEvent = PythonEnvsChangedEvent> implements ILocator {
14+
public get onChanged(): Event<E> {
15+
return this.didChange.event;
16+
}
17+
18+
private readonly environmentsReceived: Map<string, PythonEnvInfo> = new Map<string, PythonEnvInfo>();
19+
20+
private readonly didChange = new EventEmitter<E>();
21+
22+
constructor(private readonly pythonEnvsManager: ILocator) {}
23+
24+
public iterEnvs(
25+
query?: QueryForEvent<E>,
26+
): IPythonEnvsIterator {
27+
const didUpdate = new EventEmitter<PythonEnvUpdatedEvent | null>();
28+
return { ...this.iterEnvsIterator(didUpdate, query), onUpdated: didUpdate.event };
29+
}
30+
31+
public resolveEnv(env: string | PythonEnvInfo): Promise<PythonEnvInfo | undefined> {
32+
return this.pythonEnvsManager.resolveEnv(env);
33+
}
34+
35+
private async* iterEnvsIterator(
36+
didUpdate: EventEmitter<PythonEnvUpdatedEvent | null>,
37+
query?: QueryForEvent<E>,
38+
): AsyncIterator<PythonEnvInfo, void> {
39+
const deferreds: Deferred<void>[] = [];
40+
const iterator = this.pythonEnvsManager.iterEnvs(query);
41+
let result = await iterator.next();
42+
while (!result.done) {
43+
const currEnv = result.value;
44+
if (this.environmentsReceived.has(currEnv.id)) {
45+
// Resolve differences in the background
46+
const resolveDifferences = async () => {
47+
const storedEnv = this.environmentsReceived.get(currEnv.id)!;
48+
const resolvedEnv = mergeEnvironments([currEnv, storedEnv])[0];
49+
didUpdate.fire({ old: currEnv, new: resolvedEnv });
50+
};
51+
const promise = resolveDifferences();
52+
// Carries all the promises ongoing in background
53+
deferreds.push(createDeferredFrom(promise));
54+
this.checkIfFinishedAndNotify({ allItemsIterated: !!result.done, deferreds }, didUpdate);
55+
} else {
56+
yield currEnv;
57+
}
58+
this.environmentsReceived.set(currEnv.id, currEnv);
59+
// eslint-disable-next-line no-await-in-loop
60+
result = await iterator.next();
61+
}
62+
this.checkIfFinishedAndNotify({ allItemsIterated: result.done, deferreds }, didUpdate);
63+
}
64+
65+
/**
66+
* When all items are iterated and all background calls finishes, notify that we're done
67+
* @param state Carries the current state of progress
68+
* @param didUpdate Used to notify when finished
69+
*/
70+
private checkIfFinishedAndNotify(
71+
state: {
72+
allItemsIterated: boolean,
73+
// Carries all the promises ongoing in background
74+
deferreds: Deferred<void>[]
75+
},
76+
didUpdate: EventEmitter<PythonEnvUpdatedEvent | null>,
77+
) {
78+
if (state.deferreds.length === 0) {
79+
return;
80+
}
81+
state.deferreds = state.deferreds.filter((item) => !item.completed);
82+
if (state.deferreds.length === 0 && state.allItemsIterated) {
83+
didUpdate.fire(null);
84+
didUpdate.dispose();
85+
}
86+
Promise.all(state.deferreds.map((item) => item.promise))
87+
.then(() => this.checkIfFinishedAndNotify(state, didUpdate))
88+
.ignoreErrors();
89+
}
90+
}

src/client/pythonEnvironments/info/index.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as path from 'path';
77
import * as semver from 'semver';
88
import { IFileSystem } from '../../common/platform/types';
99
import { Architecture } from '../../common/utils/platform';
10+
import { PythonEnvInfo } from '../base/info';
1011
import { areSameVersion, PythonVersion } from './pythonVersion';
1112

1213
/**
@@ -124,7 +125,7 @@ export function getEnvironmentTypeName(environmentType: EnvironmentType) {
124125
export function areSameEnvironment(
125126
environment1: PartialPythonEnvironment | undefined,
126127
environment2: PartialPythonEnvironment | undefined,
127-
fs: IFileSystem,
128+
fs: IFileSystem
128129
): boolean {
129130
if (!environment1 || !environment2) {
130131
return false;
@@ -163,7 +164,7 @@ export function updateEnvironment(environment: PartialPythonEnvironment, other:
163164
'architecture',
164165
'sysVersion',
165166
'version',
166-
'pipEnvWorkspaceFolder',
167+
'pipEnvWorkspaceFolder'
167168
];
168169
props.forEach((prop) => {
169170
if (!environment[prop] && other[prop]) {
@@ -172,6 +173,16 @@ export function updateEnvironment(environment: PartialPythonEnvironment, other:
172173
}
173174
});
174175
}
176+
/**
177+
* Combine env info for matching environments.
178+
*
179+
* Environments are matched by path and version.
180+
*
181+
* @param environments - the env infos to merge
182+
*/
183+
export function mergeEnvironments(environments: PythonEnvInfo[]): PythonEnvInfo[] {
184+
return environments;
185+
}
175186

176187
/**
177188
* Combine env info for matching environments.
@@ -180,9 +191,9 @@ export function updateEnvironment(environment: PartialPythonEnvironment, other:
180191
*
181192
* @param environments - the env infos to merge
182193
*/
183-
export function mergeEnvironments(
194+
export function mergeEnvironmentsOld(
184195
environments: PartialPythonEnvironment[],
185-
fs: IFileSystem,
196+
fs: IFileSystem
186197
): PartialPythonEnvironment[] {
187198
return environments.reduce<PartialPythonEnvironment[]>((accumulator, current) => {
188199
const existingItem = accumulator.find((item) => areSameEnvironment(current, item, fs));

0 commit comments

Comments
 (0)