Skip to content

Commit d6d5cf9

Browse files
author
Kartik Raj
committed
Allow low level locators to emit a single kind
1 parent 7d469e3 commit d6d5cf9

File tree

10 files changed

+94
-73
lines changed

10 files changed

+94
-73
lines changed

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ import {
2020
PythonVersion,
2121
virtualEnvKinds,
2222
} from '.';
23-
import { BasicEnvInfo } from '../locator';
23+
import { CompositeEnvInfo, convertKindIntoArray } from '../locator';
2424

2525
/**
2626
* Create a new info object with all values empty.
2727
*
2828
* @param init - if provided, these values are applied to the new object
2929
*/
3030
export function buildEnvInfo(init?: {
31-
kind?: PythonEnvKind[];
31+
kind?: PythonEnvKind[] | PythonEnvKind;
3232
executable?: string;
3333
name?: string;
3434
location?: string;
@@ -83,7 +83,7 @@ export function buildEnvInfo(init?: {
8383
export function copyEnvInfo(
8484
env: PythonEnvInfo,
8585
updates?: {
86-
kind?: PythonEnvKind[];
86+
kind?: PythonEnvKind[] | PythonEnvKind;
8787
},
8888
): PythonEnvInfo {
8989
// We don't care whether or not extra/hidden properties
@@ -98,15 +98,15 @@ export function copyEnvInfo(
9898
function updateEnv(
9999
env: PythonEnvInfo,
100100
updates: {
101-
kind?: PythonEnvKind[];
101+
kind?: PythonEnvKind[] | PythonEnvKind;
102102
executable?: string;
103103
location?: string;
104104
version?: PythonVersion;
105105
searchLocation?: Uri;
106106
},
107107
): void {
108108
if (updates.kind !== undefined) {
109-
env.kind = updates.kind;
109+
env.kind = convertKindIntoArray(updates.kind);
110110
}
111111
if (updates.executable !== undefined) {
112112
env.executable.filename = updates.executable;
@@ -173,7 +173,7 @@ function buildEnvDisplayString(env: PythonEnvInfo, getAllDetails = false): strin
173173
* If insufficient data is provided to generate a minimal object, such
174174
* that it is not identifiable, then `undefined` is returned.
175175
*/
176-
function getMinimalPartialInfo(env: string | PythonEnvInfo | BasicEnvInfo): Partial<PythonEnvInfo> | undefined {
176+
function getMinimalPartialInfo(env: string | PythonEnvInfo | CompositeEnvInfo): Partial<PythonEnvInfo> | undefined {
177177
if (typeof env === 'string') {
178178
if (env === '') {
179179
return undefined;
@@ -235,8 +235,8 @@ export function getEnvID(interpreterPath: string, envFolderPath?: string): strin
235235
* where multiple versions of python executables are all put in the same directory.
236236
*/
237237
export function areSameEnv(
238-
left: string | PythonEnvInfo | BasicEnvInfo,
239-
right: string | PythonEnvInfo | BasicEnvInfo,
238+
left: string | PythonEnvInfo | CompositeEnvInfo,
239+
right: string | PythonEnvInfo | CompositeEnvInfo,
240240
allowPartialMatch = true,
241241
): boolean | undefined {
242242
const leftInfo = getMinimalPartialInfo(left);

src/client/pythonEnvironments/base/locator.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,14 +296,28 @@ export type PythonLocatorQuery = BasicPythonLocatorQuery & {
296296

297297
type QueryForEvent<E> = E extends PythonEnvsChangedEvent ? PythonLocatorQuery : BasicPythonLocatorQuery;
298298

299-
export type BasicEnvInfo = {
300-
kind: PythonEnvKind[];
299+
export type BasicEnvInfo<T = PythonEnvKind[] | PythonEnvKind> = {
300+
kind: T;
301301
executablePath: string;
302302
source?: PythonEnvSource[];
303303
envPath?: string;
304304
extensionId?: ExtensionID;
305305
};
306306

307+
export type CompositeEnvInfo = BasicEnvInfo<PythonEnvKind[]>;
308+
309+
export function convertBasicToComposite(env: BasicEnvInfo): CompositeEnvInfo {
310+
env.kind = convertKindIntoArray(env.kind);
311+
return env as CompositeEnvInfo;
312+
}
313+
314+
export function convertKindIntoArray(kind: PythonEnvKind | PythonEnvKind[]): PythonEnvKind[] {
315+
if (!Array.isArray(kind)) {
316+
kind = [kind];
317+
}
318+
return kind;
319+
}
320+
307321
/**
308322
* A single Python environment locator.
309323
*

src/client/pythonEnvironments/base/locators/composite/envsReducer.ts

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { areSameEnv } from '../../info/env';
99
import { sortExtensionSource, sortKindFunction } from '../../info/envKind';
1010
import {
1111
BasicEnvInfo,
12+
CompositeEnvInfo,
13+
convertBasicToComposite,
1214
ILocator,
1315
IPythonEnvsIterator,
1416
isProgressEvent,
@@ -22,7 +24,7 @@ import { PythonEnvsChangedEvent } from '../../watcher';
2224
/**
2325
* Combines duplicate environments received from the incoming locator into one and passes on unique environments
2426
*/
25-
export class PythonEnvsReducer implements ILocator<BasicEnvInfo> {
27+
export class PythonEnvsReducer implements ILocator<CompositeEnvInfo> {
2628
public get onChanged(): Event<PythonEnvsChangedEvent> {
2729
return this.parentLocator.onChanged;
2830
}
@@ -31,8 +33,8 @@ export class PythonEnvsReducer implements ILocator<BasicEnvInfo> {
3133

3234
constructor(private readonly parentLocator: ILocator<BasicEnvInfo>) {}
3335

34-
public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator<BasicEnvInfo> {
35-
const didUpdate = new EventEmitter<PythonEnvUpdatedEvent<BasicEnvInfo> | ProgressNotificationEvent>();
36+
public iterEnvs(query?: PythonLocatorQuery): IPythonEnvsIterator<CompositeEnvInfo> {
37+
const didUpdate = new EventEmitter<PythonEnvUpdatedEvent<CompositeEnvInfo> | ProgressNotificationEvent>();
3638
const incomingIterator = this.parentLocator.iterEnvs(query);
3739
const iterator = iterEnvsIterator(incomingIterator, didUpdate);
3840
iterator.onUpdated = didUpdate.event;
@@ -42,13 +44,13 @@ export class PythonEnvsReducer implements ILocator<BasicEnvInfo> {
4244

4345
async function* iterEnvsIterator(
4446
iterator: IPythonEnvsIterator<BasicEnvInfo>,
45-
didUpdate: EventEmitter<PythonEnvUpdatedEvent<BasicEnvInfo> | ProgressNotificationEvent>,
46-
): IPythonEnvsIterator<BasicEnvInfo> {
47+
didUpdate: EventEmitter<PythonEnvUpdatedEvent<CompositeEnvInfo> | ProgressNotificationEvent>,
48+
): IPythonEnvsIterator<CompositeEnvInfo> {
4749
const state = {
4850
done: false,
4951
pending: 0,
5052
};
51-
const seen: BasicEnvInfo[] = [];
53+
const seen: CompositeEnvInfo[] = [];
5254

5355
if (iterator.onUpdated !== undefined) {
5456
const listener = iterator.onUpdated((event) => {
@@ -66,8 +68,8 @@ async function* iterEnvsIterator(
6668
);
6769
} else if (seen[event.index] !== undefined) {
6870
const oldEnv = seen[event.index];
69-
seen[event.index] = event.update;
70-
didUpdate.fire({ index: event.index, old: oldEnv, update: event.update });
71+
seen[event.index] = convertBasicToComposite(event.update);
72+
didUpdate.fire({ index: event.index, old: oldEnv, update: seen[event.index] });
7173
} else {
7274
// This implies a problem in a downstream locator
7375
traceVerbose(`Expected already iterated env, got ${event.old} (#${event.index})`);
@@ -81,7 +83,7 @@ async function* iterEnvsIterator(
8183

8284
let result = await iterator.next();
8385
while (!result.done) {
84-
const currEnv = result.value;
86+
const currEnv = convertBasicToComposite(result.value);
8587
const oldIndex = seen.findIndex((s) => areSameEnv(s, currEnv));
8688
if (oldIndex !== -1) {
8789
resolveDifferencesInBackground(oldIndex, currEnv, state, didUpdate, seen).ignoreErrors();
@@ -100,10 +102,10 @@ async function* iterEnvsIterator(
100102

101103
async function resolveDifferencesInBackground(
102104
oldIndex: number,
103-
newEnv: BasicEnvInfo,
105+
newEnv: CompositeEnvInfo,
104106
state: { done: boolean; pending: number },
105-
didUpdate: EventEmitter<PythonEnvUpdatedEvent<BasicEnvInfo> | ProgressNotificationEvent>,
106-
seen: BasicEnvInfo[],
107+
didUpdate: EventEmitter<PythonEnvUpdatedEvent<CompositeEnvInfo> | ProgressNotificationEvent>,
108+
seen: CompositeEnvInfo[],
107109
) {
108110
state.pending += 1;
109111
// It's essential we increment the pending call count before any asynchronus calls in this method.
@@ -125,15 +127,15 @@ async function resolveDifferencesInBackground(
125127
*/
126128
function checkIfFinishedAndNotify(
127129
state: { done: boolean; pending: number },
128-
didUpdate: EventEmitter<PythonEnvUpdatedEvent<BasicEnvInfo> | ProgressNotificationEvent>,
130+
didUpdate: EventEmitter<PythonEnvUpdatedEvent<CompositeEnvInfo> | ProgressNotificationEvent>,
129131
) {
130132
if (state.done && state.pending === 0) {
131133
didUpdate.fire({ stage: ProgressReportStage.discoveryFinished });
132134
didUpdate.dispose();
133135
}
134136
}
135137

136-
function resolveEnvCollision(oldEnv: BasicEnvInfo, newEnv: BasicEnvInfo): BasicEnvInfo {
138+
function resolveEnvCollision(oldEnv: CompositeEnvInfo, newEnv: CompositeEnvInfo): CompositeEnvInfo {
137139
const [env] = sortEnvInfoByPriority(oldEnv, newEnv);
138140
const merged = cloneDeep(env);
139141
merged.source = union(oldEnv.source ?? [], newEnv.source ?? []);
@@ -145,8 +147,8 @@ function resolveEnvCollision(oldEnv: BasicEnvInfo, newEnv: BasicEnvInfo): BasicE
145147
* Selects an environment based on the environment selection priority. This should
146148
* match the priority in the environment identifier.
147149
*/
148-
function sortEnvInfoByPriority(...envs: BasicEnvInfo[]): BasicEnvInfo[] {
149-
return envs.sort((a: BasicEnvInfo, b: BasicEnvInfo) => {
150+
function sortEnvInfoByPriority(...envs: CompositeEnvInfo[]): CompositeEnvInfo[] {
151+
return envs.sort((a: CompositeEnvInfo, b: CompositeEnvInfo) => {
150152
const kindDiff = sortKindFunction(getTopKind(a.kind), getTopKind(b.kind));
151153
if (kindDiff !== 0) {
152154
return kindDiff;

src/client/pythonEnvironments/base/locators/composite/envsResolver.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { PythonEnvInfo, PythonEnvKind } from '../../info';
99
import { getEnvPath, setEnvDisplayString } from '../../info/env';
1010
import { InterpreterInformation } from '../../info/interpreter';
1111
import {
12-
BasicEnvInfo,
12+
CompositeEnvInfo,
1313
IInternalEnvironmentProvider,
1414
ILocator,
1515
InternalEnvironmentProviderMetadata,
@@ -58,7 +58,7 @@ export class PythonEnvsResolver implements IResolvingLocator {
5858
}
5959

6060
constructor(
61-
private readonly parentLocator: ILocator<BasicEnvInfo>,
61+
private readonly parentLocator: ILocator<CompositeEnvInfo>,
6262
private readonly environmentInfoService: IEnvironmentInfoService,
6363
) {}
6464

@@ -86,7 +86,7 @@ export class PythonEnvsResolver implements IResolvingLocator {
8686
}
8787

8888
private async *iterEnvsIterator(
89-
iterator: IPythonEnvsIterator<BasicEnvInfo>,
89+
iterator: IPythonEnvsIterator<CompositeEnvInfo>,
9090
didUpdate: EventEmitter<PythonEnvUpdatedEvent | ProgressNotificationEvent>,
9191
): IPythonEnvsIterator {
9292
const state = {

src/client/pythonEnvironments/base/locators/composite/resolverUtils.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ import { getPyenvVersionsDir, parsePyenvVersion } from '../../../common/environm
2323
import { Architecture, getOSType, OSType } from '../../../../common/utils/platform';
2424
import { getPythonVersionFromPath as parsePythonVersionFromPath, parseVersion } from '../../info/pythonVersion';
2525
import { getRegistryInterpreters, getRegistryInterpretersSync } from '../../../common/windowsUtils';
26-
import { BasicEnvInfo } from '../../locator';
26+
import { CompositeEnvInfo } from '../../locator';
2727
import { parseVersionFromExecutable } from '../../info/executable';
2828
import { traceError, traceWarn } from '../../../../logging';
2929
import { sortExtensionSource } from '../../info/envKind';
3030

31-
type ResolverType = (_: BasicEnvInfo, useCache?: boolean) => Promise<PythonEnvInfo | undefined>;
31+
type ResolverType = (_: CompositeEnvInfo, useCache?: boolean) => Promise<PythonEnvInfo | undefined>;
3232
type ResolversType = { resolver: ResolverType; extensionId?: string }[];
3333
const resolvers = new Map<PythonEnvKind, ResolverType | ResolversType>();
3434
Object.values(PythonEnvKind).forEach((k) => {
@@ -43,7 +43,7 @@ resolvers.set(PythonEnvKind.Pyenv, resolvePyenvEnv);
4343

4444
export function registerResolver(
4545
kind: PythonEnvKind,
46-
resolver: (_: BasicEnvInfo, useCache?: boolean) => Promise<PythonEnvInfo | undefined>,
46+
resolver: (_: CompositeEnvInfo, useCache?: boolean) => Promise<PythonEnvInfo | undefined>,
4747
extensionId: string,
4848
): void {
4949
const resolversForKind = resolvers.get(kind);
@@ -63,7 +63,7 @@ export function registerResolver(
6363
* executable and returns it. Notice `undefined` is never returned, so environment
6464
* returned could still be invalid.
6565
*/
66-
export async function resolveBasicEnv(env: BasicEnvInfo, useCache = false): Promise<PythonEnvInfo | undefined> {
66+
export async function resolveBasicEnv(env: CompositeEnvInfo, useCache = false): Promise<PythonEnvInfo | undefined> {
6767
const { kind, source } = env;
6868
const value = resolvers.get(kind[0]);
6969
if (!value) {
@@ -146,7 +146,7 @@ async function updateEnvUsingRegistry(env: PythonEnvInfo): Promise<void> {
146146
}
147147
}
148148

149-
async function resolveGloballyInstalledEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
149+
async function resolveGloballyInstalledEnv(env: CompositeEnvInfo): Promise<PythonEnvInfo> {
150150
const { executablePath } = env;
151151
let version;
152152
try {
@@ -162,7 +162,7 @@ async function resolveGloballyInstalledEnv(env: BasicEnvInfo): Promise<PythonEnv
162162
return envInfo;
163163
}
164164

165-
async function resolveSimpleEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
165+
async function resolveSimpleEnv(env: CompositeEnvInfo): Promise<PythonEnvInfo> {
166166
const { executablePath, kind } = env;
167167
const envInfo = buildEnvInfo({
168168
kind,
@@ -175,7 +175,7 @@ async function resolveSimpleEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
175175
return envInfo;
176176
}
177177

178-
async function resolveCondaEnv(env: BasicEnvInfo, useCache?: boolean): Promise<PythonEnvInfo> {
178+
async function resolveCondaEnv(env: CompositeEnvInfo, useCache?: boolean): Promise<PythonEnvInfo> {
179179
const { executablePath } = env;
180180
const conda = await Conda.getConda();
181181
if (conda === undefined) {
@@ -184,7 +184,7 @@ async function resolveCondaEnv(env: BasicEnvInfo, useCache?: boolean): Promise<P
184184
const envs = (await conda?.getEnvList(useCache)) ?? [];
185185
for (const { name, prefix } of envs) {
186186
let executable = await getInterpreterPathFromDir(prefix);
187-
const currEnv: BasicEnvInfo = { executablePath: executable ?? '', kind: env.kind, envPath: prefix };
187+
const currEnv: CompositeEnvInfo = { executablePath: executable ?? '', kind: env.kind, envPath: prefix };
188188
if (areSameEnv(env, currEnv)) {
189189
if (env.executablePath.length > 0) {
190190
executable = env.executablePath;
@@ -215,7 +215,7 @@ async function resolveCondaEnv(env: BasicEnvInfo, useCache?: boolean): Promise<P
215215
return resolveSimpleEnv(env);
216216
}
217217

218-
async function resolvePyenvEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
218+
async function resolvePyenvEnv(env: CompositeEnvInfo): Promise<PythonEnvInfo> {
219219
const { executablePath } = env;
220220
const location = getEnvironmentDirFromPath(executablePath);
221221
const name = path.basename(location);
@@ -268,7 +268,7 @@ async function isBaseCondaPyenvEnvironment(executablePath: string) {
268268
return arePathsSame(path.dirname(location), pyenvVersionDir);
269269
}
270270

271-
async function resolveWindowsStoreEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
271+
async function resolveWindowsStoreEnv(env: CompositeEnvInfo): Promise<PythonEnvInfo> {
272272
const { executablePath } = env;
273273
return buildEnvInfo({
274274
kind: env.kind,

src/client/pythonEnvironments/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ async function createLocator(
106106
// This is shared.
107107
): Promise<IDiscoveryAPI> {
108108
// Create the low-level locators.
109-
let locators: ILocator<BasicEnvInfo> = new ExtensionLocators(
109+
const locators: ILocator<BasicEnvInfo> = new ExtensionLocators(
110110
// Here we pull the locators together.
111111
createNonWorkspaceLocators(ext),
112112
createWorkspaceLocator(ext),
@@ -116,9 +116,9 @@ async function createLocator(
116116
const envInfoService = getEnvironmentInfoService(ext.disposables);
117117

118118
// Build the stack of composite locators.
119-
locators = new PythonEnvsReducer(locators);
119+
const reducer = new PythonEnvsReducer(locators);
120120
const resolvingLocator = new PythonEnvsResolver(
121-
locators,
121+
reducer,
122122
// These are shared.
123123
envInfoService,
124124
);

src/client/pythonEnvironments/legacyIOC.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ function convertEnvInfo(info: PythonEnvInfo): PythonEnvironment {
5151
architecture: arch,
5252
};
5353

54-
const envType = convertedKinds.get(kind);
54+
const envType = convertedKinds.get(kind[0]);
5555
if (envType !== undefined) {
5656
env.envType = envType;
5757
}

src/test/pythonEnvironments/base/common.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
import { buildEnvInfo } from '../../../client/pythonEnvironments/base/info/env';
1818
import { getEmptyVersion, parseVersion } from '../../../client/pythonEnvironments/base/info/pythonVersion';
1919
import {
20-
BasicEnvInfo,
20+
CompositeEnvInfo,
2121
IPythonEnvsIterator,
2222
isProgressEvent,
2323
Locator,
@@ -53,7 +53,7 @@ export function createLocatedEnv(
5353
? getEmptyVersion() // an empty version
5454
: parseVersion(versionStr);
5555
const env = buildEnvInfo({
56-
kind,
56+
kind: [kind],
5757
executable,
5858
location,
5959
version,
@@ -71,8 +71,8 @@ export function createBasicEnv(
7171
executablePath: string,
7272
source?: PythonEnvSource[],
7373
envPath?: string,
74-
): BasicEnvInfo {
75-
const basicEnv = { executablePath, kind, source, envPath };
74+
): CompositeEnvInfo {
75+
const basicEnv = { executablePath, kind: [kind], source, envPath };
7676
if (!source) {
7777
delete basicEnv.source;
7878
}

0 commit comments

Comments
 (0)