Skip to content

Commit 2e56a21

Browse files
committed
Ensure resolvers accept data from native locators (#23444)
1 parent 0f80b59 commit 2e56a21

File tree

9 files changed

+141
-12
lines changed

9 files changed

+141
-12
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ export function buildEnvInfo(init?: {
4242
sysPrefix?: string;
4343
searchLocation?: Uri;
4444
type?: PythonEnvType;
45+
/**
46+
* Command used to run Python in this environment.
47+
* E.g. `conda run -n envName python` or `python.exe`
48+
*/
49+
pythonRunCommand?: string[];
50+
identifiedUsingNativeLocator?: boolean;
4551
}): PythonEnvInfo {
4652
const env: PythonEnvInfo = {
4753
name: init?.name ?? '',
@@ -69,6 +75,8 @@ export function buildEnvInfo(init?: {
6975
org: init?.org ?? '',
7076
},
7177
source: init?.source ?? [],
78+
pythonRunCommand: init?.pythonRunCommand,
79+
identifiedUsingNativeLocator: init?.identifiedUsingNativeLocator,
7280
};
7381
if (init !== undefined) {
7482
updateEnv(env, init);

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ export type PythonEnvInfo = _PythonEnvInfo & {
203203
display?: string;
204204
detailedDisplayName?: string;
205205
searchLocation?: Uri;
206+
/**
207+
* Command used to run Python in this environment.
208+
* E.g. `conda run -n envName python` or `python.exe`
209+
*/
210+
pythonRunCommand?: string[];
211+
identifiedUsingNativeLocator?: boolean;
206212
};
207213

208214
/**

src/client/pythonEnvironments/base/locator.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,24 @@ export type BasicEnvInfo = {
144144
executablePath: string;
145145
source?: PythonEnvSource[];
146146
envPath?: string;
147+
/**
148+
* The project to which this env is related to, if any
149+
* E.g. the project directory when dealing with pipenv virtual environments.
150+
*/
147151
searchLocation?: Uri;
148152
version?: PythonVersion;
149153
name?: string;
154+
/**
155+
* Display name provided by locators, not generated by us.
156+
* E.g. display name as provided by Windows Registry or Windows Store, etc
157+
*/
158+
displayName?: string;
159+
/**
160+
* Command used to run Python in this environment.
161+
* E.g. `conda run -n envName python` or `python.exe`
162+
*/
163+
pythonRunCommand?: string[];
164+
identifiedUsingNativeLocator?: boolean;
150165
};
151166

152167
/**

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export interface NativeEnvInfo {
2323
version?: string;
2424
pythonRunCommand?: string[];
2525
envPath?: string;
26+
envManager?: NativeEnvManagerInfo;
2627
/**
2728
* Path to the project directory when dealing with pipenv virtual environments.
2829
*/

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,12 @@ export class PythonEnvsResolver implements IResolvingLocator {
154154

155155
async function setKind(env: BasicEnvInfo, environmentKinds: Map<string, PythonEnvKind>) {
156156
const { path } = getEnvPath(env.executablePath, env.envPath);
157+
// For native locators, do not try to identify the environment kind.
158+
// its already set by the native locator & thats accurate.
159+
if (env.identifiedUsingNativeLocator) {
160+
environmentKinds.set(path, env.kind);
161+
return;
162+
}
157163
let kind = environmentKinds.get(path);
158164
if (!kind) {
159165
if (!isIdentifierRegistered(env.kind)) {

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

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -139,14 +139,21 @@ async function resolveGloballyInstalledEnv(env: BasicEnvInfo): Promise<PythonEnv
139139
const { executablePath } = env;
140140
let version;
141141
try {
142-
version = parseVersionFromExecutable(executablePath);
142+
version = env.version ?? parseVersionFromExecutable(executablePath);
143143
} catch {
144144
version = UNKNOWN_PYTHON_VERSION;
145145
}
146146
const envInfo = buildEnvInfo({
147147
kind: env.kind,
148+
name: env.name,
149+
display: env.displayName,
150+
sysPrefix: env.envPath,
151+
location: env.envPath,
152+
searchLocation: env.searchLocation,
148153
version,
149154
executable: executablePath,
155+
pythonRunCommand: env.pythonRunCommand,
156+
identifiedUsingNativeLocator: env.identifiedUsingNativeLocator,
150157
});
151158
return envInfo;
152159
}
@@ -155,17 +162,59 @@ async function resolveSimpleEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
155162
const { executablePath, kind } = env;
156163
const envInfo = buildEnvInfo({
157164
kind,
158-
version: await getPythonVersionFromPath(executablePath),
165+
version: env.version ?? (await getPythonVersionFromPath(executablePath)),
159166
executable: executablePath,
167+
sysPrefix: env.envPath,
168+
location: env.envPath,
169+
display: env.displayName,
170+
searchLocation: env.searchLocation,
171+
pythonRunCommand: env.pythonRunCommand,
172+
identifiedUsingNativeLocator: env.identifiedUsingNativeLocator,
173+
name: env.name,
160174
type: PythonEnvType.Virtual,
161175
});
162-
const location = getEnvironmentDirFromPath(executablePath);
176+
const location = env.envPath ?? getEnvironmentDirFromPath(executablePath);
163177
envInfo.location = location;
164178
envInfo.name = path.basename(location);
165179
return envInfo;
166180
}
167181

168182
async function resolveCondaEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
183+
if (env.identifiedUsingNativeLocator) {
184+
// New approach using native locator.
185+
const executable = env.executablePath;
186+
const envPath = env.envPath ?? getEnvironmentDirFromPath(executable);
187+
// TODO: Hacky, `executable` is never undefined in the typedef,
188+
// However, in reality with native locator this can be undefined.
189+
const version = env.version ?? (executable ? await getPythonVersionFromPath(executable) : undefined);
190+
const info = buildEnvInfo({
191+
executable,
192+
kind: PythonEnvKind.Conda,
193+
org: AnacondaCompanyName,
194+
location: envPath,
195+
sysPrefix: envPath,
196+
display: env.displayName,
197+
pythonRunCommand: env.pythonRunCommand,
198+
identifiedUsingNativeLocator: env.identifiedUsingNativeLocator,
199+
searchLocation: env.searchLocation,
200+
source: [],
201+
version,
202+
type: PythonEnvType.Conda,
203+
name: env.name,
204+
});
205+
206+
if (env.envPath && executable && path.basename(executable) === executable) {
207+
// For environments without python, set ID using the predicted executable path after python is installed.
208+
// Another alternative could've been to set ID of all conda environments to the environment path, as that
209+
// remains constant even after python installation.
210+
const predictedExecutable = getCondaInterpreterPath(env.envPath);
211+
info.id = getEnvID(predictedExecutable, env.envPath);
212+
}
213+
return info;
214+
}
215+
216+
// Old approach (without native locator).
217+
// In this approach we need to find conda.
169218
const { executablePath } = env;
170219
const conda = await Conda.getConda();
171220
if (conda === undefined) {
@@ -210,18 +259,26 @@ async function resolveCondaEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
210259

211260
async function resolvePyenvEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
212261
const { executablePath } = env;
213-
const location = getEnvironmentDirFromPath(executablePath);
262+
const location = env.envPath ?? getEnvironmentDirFromPath(executablePath);
214263
const name = path.basename(location);
215264

216265
// The sub-directory name sometimes can contain distro and python versions.
217266
// here we attempt to extract the texts out of the name.
218267
const versionStrings = parsePyenvVersion(name);
219268

220269
const envInfo = buildEnvInfo({
221-
kind: PythonEnvKind.Pyenv,
270+
// If using native resolver, then we can get the kind from the native resolver.
271+
// E.g. pyenv can have conda environments as well.
272+
kind: env.identifiedUsingNativeLocator && env.kind ? env.kind : PythonEnvKind.Pyenv,
222273
executable: executablePath,
223274
source: [],
224275
location,
276+
searchLocation: env.searchLocation,
277+
sysPrefix: env.envPath,
278+
display: env.displayName,
279+
name: env.name,
280+
pythonRunCommand: env.pythonRunCommand,
281+
identifiedUsingNativeLocator: env.identifiedUsingNativeLocator,
225282
// Pyenv environments can fall in to these three categories:
226283
// 1. Global Installs : These are environments that are created when you install
227284
// a supported python distribution using `pyenv install <distro>` command.
@@ -240,14 +297,17 @@ async function resolvePyenvEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
240297
//
241298
// Here we look for near by files, or config files to see if we can get python version info
242299
// without running python itself.
243-
version: await getPythonVersionFromPath(executablePath, versionStrings?.pythonVer),
300+
version: env.version ?? (await getPythonVersionFromPath(executablePath, versionStrings?.pythonVer)),
244301
org: versionStrings && versionStrings.distro ? versionStrings.distro : '',
245302
});
246303

247-
if (await isBaseCondaPyenvEnvironment(executablePath)) {
248-
envInfo.name = 'base';
249-
} else {
250-
envInfo.name = name;
304+
// Do this only for the old approach, when not using native locators.
305+
if (!env.identifiedUsingNativeLocator) {
306+
if (await isBaseCondaPyenvEnvironment(executablePath)) {
307+
envInfo.name = 'base';
308+
} else {
309+
envInfo.name = name;
310+
}
251311
}
252312
return envInfo;
253313
}
@@ -256,6 +316,14 @@ async function resolveActiveStateEnv(env: BasicEnvInfo): Promise<PythonEnvInfo>
256316
const info = buildEnvInfo({
257317
kind: env.kind,
258318
executable: env.executablePath,
319+
display: env.displayName,
320+
version: env.version,
321+
identifiedUsingNativeLocator: env.identifiedUsingNativeLocator,
322+
location: env.envPath,
323+
name: env.name,
324+
pythonRunCommand: env.pythonRunCommand,
325+
searchLocation: env.searchLocation,
326+
sysPrefix: env.envPath,
259327
});
260328
const projects = await ActiveState.getState().then((v) => v?.getProjects());
261329
if (projects) {
@@ -285,8 +353,15 @@ async function resolveMicrosoftStoreEnv(env: BasicEnvInfo): Promise<PythonEnvInf
285353
return buildEnvInfo({
286354
kind: PythonEnvKind.MicrosoftStore,
287355
executable: executablePath,
288-
version: parsePythonVersionFromPath(executablePath),
356+
version: env.version ?? parsePythonVersionFromPath(executablePath),
289357
org: 'Microsoft',
358+
display: env.displayName,
359+
location: env.envPath,
360+
sysPrefix: env.envPath,
361+
searchLocation: env.searchLocation,
362+
name: env.name,
363+
pythonRunCommand: env.pythonRunCommand,
364+
identifiedUsingNativeLocator: env.identifiedUsingNativeLocator,
290365
arch: Architecture.x64,
291366
source: [PythonEnvSource.PathEnvVar],
292367
});

src/client/pythonEnvironments/base/locators/lowLevel/nativeLocator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
import { Disposable, Event, EventEmitter } from 'vscode';
4+
import { Disposable, Event, EventEmitter, Uri } from 'vscode';
55
import { IDisposable } from '../../../../common/types';
66
import { ILocator, BasicEnvInfo, IPythonEnvsIterator } from '../../locator';
77
import { PythonEnvsChangedEvent } from '../../watcher';
@@ -116,6 +116,10 @@ export class NativeLocator implements ILocator<BasicEnvInfo>, IDisposable {
116116
envPath: data.envPath,
117117
version: parseVersion(data.version),
118118
name: data.name === '' ? undefined : data.name,
119+
displayName: data.displayName,
120+
pythonRunCommand: data.pythonRunCommand,
121+
searchLocation: data.projectPath ? Uri.file(data.projectPath) : undefined,
122+
identifiedUsingNativeLocator: true,
119123
});
120124
}
121125
}),

src/test/pythonEnvironments/base/locators/composite/envsResolver.unit.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ suite('Python envs locator - Environments Resolver', () => {
7272
updatedEnv.arch = Architecture.x64;
7373
updatedEnv.display = expectedDisplay;
7474
updatedEnv.detailedDisplayName = expectedDetailedDisplay;
75+
updatedEnv.identifiedUsingNativeLocator = updatedEnv.identifiedUsingNativeLocator ?? undefined;
76+
updatedEnv.pythonRunCommand = updatedEnv.pythonRunCommand ?? undefined;
7577
if (env.kind === PythonEnvKind.Conda) {
7678
env.type = PythonEnvType.Conda;
7779
}
@@ -106,6 +108,8 @@ suite('Python envs locator - Environments Resolver', () => {
106108
searchLocation: Uri.file(location),
107109
source: [],
108110
type,
111+
identifiedUsingNativeLocator: undefined,
112+
pythonRunCommand: undefined,
109113
};
110114
}
111115
suite('iterEnvs()', () => {

src/test/pythonEnvironments/base/locators/composite/resolverUtils.unit.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ suite('Resolver Utils', () => {
153153
kind: PythonEnvKind.MicrosoftStore,
154154
distro: { org: 'Microsoft' },
155155
source: [PythonEnvSource.PathEnvVar],
156+
identifiedUsingNativeLocator: undefined,
157+
pythonRunCommand: undefined,
156158
...createExpectedInterpreterInfo(python38path),
157159
};
158160
setEnvDisplayString(expected);
@@ -175,6 +177,8 @@ suite('Resolver Utils', () => {
175177
kind: PythonEnvKind.MicrosoftStore,
176178
distro: { org: 'Microsoft' },
177179
source: [PythonEnvSource.PathEnvVar],
180+
identifiedUsingNativeLocator: undefined,
181+
pythonRunCommand: undefined,
178182
...createExpectedInterpreterInfo(python38path),
179183
};
180184
setEnvDisplayString(expected);
@@ -239,6 +243,8 @@ suite('Resolver Utils', () => {
239243
distro: { org: '' },
240244
searchLocation: undefined,
241245
source: [],
246+
identifiedUsingNativeLocator: undefined,
247+
pythonRunCommand: undefined,
242248
};
243249
info.type = PythonEnvType.Conda;
244250
setEnvDisplayString(info);
@@ -351,6 +357,8 @@ suite('Resolver Utils', () => {
351357
searchLocation: Uri.file(location),
352358
source: [],
353359
type: PythonEnvType.Virtual,
360+
identifiedUsingNativeLocator: undefined,
361+
pythonRunCommand: undefined,
354362
};
355363
setEnvDisplayString(info);
356364
return info;
@@ -406,6 +414,8 @@ suite('Resolver Utils', () => {
406414
distro: { org: '' },
407415
searchLocation: undefined,
408416
source: [],
417+
identifiedUsingNativeLocator: undefined,
418+
pythonRunCommand: undefined,
409419
};
410420
setEnvDisplayString(info);
411421
return info;

0 commit comments

Comments
 (0)