Skip to content

Commit 2c47063

Browse files
author
Kartik Raj
committed
Document resolver works for both env path and executable
1 parent ee4daca commit 2c47063

File tree

8 files changed

+40
-36
lines changed

8 files changed

+40
-36
lines changed

src/client/interpreter/interpreterService.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export class InterpreterService implements Disposable, IInterpreterService {
6262
return this.didChangeInterpreterConfigurationEmitter.event;
6363
}
6464

65-
public _pythonPathSetting: string | undefined = '';
65+
public _currentInterpreterDisplay: string | undefined = '';
6666

6767
private readonly didChangeInterpreterConfigurationEmitter = new EventEmitter<Uri | undefined>();
6868

@@ -181,8 +181,11 @@ export class InterpreterService implements Disposable, IInterpreterService {
181181
this.didChangeInterpreterConfigurationEmitter.fire(resource);
182182
// Check if we actually changed our python path
183183
const interpreter = await this.getActiveInterpreter(resource);
184-
if (this._pythonPathSetting === '' || this._pythonPathSetting !== interpreter?.detailedDisplayName) {
185-
this._pythonPathSetting = interpreter?.detailedDisplayName;
184+
if (
185+
this._currentInterpreterDisplay === '' ||
186+
this._currentInterpreterDisplay !== interpreter?.detailedDisplayName
187+
) {
188+
this._currentInterpreterDisplay = interpreter?.detailedDisplayName;
186189
this.didChangeInterpreterEmitter.fire();
187190
const pySettings = this.configService.getSettings(resource);
188191
reportActiveInterpreterChanged({

src/client/pythonEnvironments/base/locator.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,11 @@ export interface ILocator<I = PythonEnvInfo, E extends BasicPythonEnvsChangedEve
161161
interface IResolver {
162162
/**
163163
* Find as much info about the given Python environment as possible.
164-
* If executable passed is invalid, then `undefined` is returned.
164+
* If path passed is invalid, then `undefined` is returned.
165165
*
166-
* @param env - the Python executable path to resolve more information about
166+
* @param path - Python executable path or environment path to resolve more information about
167167
*/
168-
resolveEnv(env: string): Promise<PythonEnvInfo | undefined>;
168+
resolveEnv(path: string): Promise<PythonEnvInfo | undefined>;
169169
}
170170

171171
export interface IResolvingLocator<I = PythonEnvInfo> extends IResolver, ILocator<I> {}
@@ -194,9 +194,11 @@ export interface IDiscoveryAPI {
194194
getEnvs(query?: PythonLocatorQuery): PythonEnvInfo[];
195195
/**
196196
* Find as much info about the given Python environment as possible.
197-
* If executable passed is invalid, then `undefined` is returned.
197+
* If path passed is invalid, then `undefined` is returned.
198+
*
199+
* @param path - Python executable path or environment path to resolve more information about
198200
*/
199-
resolveEnv(executablePath: string): Promise<PythonEnvInfo | undefined>;
201+
resolveEnv(path: string): Promise<PythonEnvInfo | undefined>;
200202
}
201203

202204
interface IEmitter<E extends PythonEnvsChangedEvent> {

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@ export interface IEnvsCollectionCache {
3636
addEnv(env: PythonEnvInfo, hasCompleteInfo?: boolean): void;
3737

3838
/**
39-
* Return cached environment information for a given interpreter path if it exists and
39+
* Return cached environment information for a given path if it exists and
4040
* has complete info, otherwise return `undefined`.
41+
*
42+
* @param path - Python executable path or path to environment
4143
*/
4244
getCompleteInfo(path: string): PythonEnvInfo | undefined;
4345

@@ -125,6 +127,7 @@ export class PythonEnvInfoCache extends PythonEnvsWatcher<PythonEnvCollectionCha
125127
}
126128

127129
public getCompleteInfo(path: string): PythonEnvInfo | undefined {
130+
// `path` can either be path to environment or executable path
128131
let env = this.envs.find((e) => arePathsSame(e.location, path));
129132
if (env?.hasCompleteInfo) {
130133
return env;

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,15 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
5555
});
5656
}
5757

58-
public async resolveEnv(executablePath: string): Promise<PythonEnvInfo | undefined> {
58+
public async resolveEnv(path: string): Promise<PythonEnvInfo | undefined> {
5959
// Note cache may have incomplete info when a refresh is happening.
6060
// This API is supposed to return complete info by definition, so
6161
// only use cache if it has complete info on an environment.
62-
const cachedEnv = this.cache.getCompleteInfo(executablePath);
62+
const cachedEnv = this.cache.getCompleteInfo(path);
6363
if (cachedEnv) {
6464
return cachedEnv;
6565
}
66-
const resolved = await this.locator.resolveEnv(executablePath);
66+
const resolved = await this.locator.resolveEnv(path);
6767
if (resolved) {
6868
this.cache.addEnv(resolved, true);
6969
}

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

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,7 @@ import {
1919
import { PythonEnvsChangedEvent } from '../../watcher';
2020
import { resolveBasicEnv } from './resolverUtils';
2121
import { traceVerbose } from '../../../../logging';
22-
import {
23-
getEnvironmentDirFromPath,
24-
getInterpreterPathFromDir,
25-
isExecutableOrEnvPath,
26-
} from '../../../common/commonUtils';
22+
import { getEnvironmentDirFromPath, getInterpreterPathFromDir, isPythonExecutable } from '../../../common/commonUtils';
2723

2824
/**
2925
* Calls environment info service which runs `interpreterInfo.py` script on environments received
@@ -161,13 +157,13 @@ function getResolvedEnv(interpreterInfo: InterpreterInformation, environment: Py
161157
async function getExecutablePathAndEnvPath(path: string) {
162158
let executablePath: string;
163159
let envPath: string;
164-
const isPathAnExecutable = await isExecutableOrEnvPath(path);
160+
const isPathAnExecutable = await isPythonExecutable(path);
165161
if (isPathAnExecutable) {
166162
executablePath = path;
167-
envPath = getEnvironmentDirFromPath(path);
163+
envPath = getEnvironmentDirFromPath(executablePath);
168164
} else {
169-
executablePath = (await getInterpreterPathFromDir(path)) ?? '';
170165
envPath = path;
166+
executablePath = (await getInterpreterPathFromDir(envPath)) ?? '';
171167
}
172168
return [executablePath, envPath];
173169
}

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,11 @@ import { traceError, traceWarn } from '../../../../logging';
2929

3030
function getResolvers(): Map<PythonEnvKind, (env: BasicEnvInfo) => Promise<PythonEnvInfo>> {
3131
const resolvers = new Map<PythonEnvKind, (_: BasicEnvInfo) => Promise<PythonEnvInfo>>();
32-
const defaultResolver = (k: PythonEnvKind) => (e: BasicEnvInfo) => resolveGloballyInstalledEnv(e, k);
33-
const defaultVirtualEnvResolver = (k: PythonEnvKind) => (e: BasicEnvInfo) => resolveSimpleEnv(e, k);
3432
Object.values(PythonEnvKind).forEach((k) => {
35-
resolvers.set(k, defaultResolver(k));
33+
resolvers.set(k, resolveGloballyInstalledEnv);
3634
});
3735
virtualEnvKinds.forEach((k) => {
38-
resolvers.set(k, defaultVirtualEnvResolver(k));
36+
resolvers.set(k, resolveSimpleEnv);
3937
});
4038
resolvers.set(PythonEnvKind.Conda, resolveCondaEnv);
4139
resolvers.set(PythonEnvKind.WindowsStore, resolveWindowsStoreEnv);
@@ -110,7 +108,7 @@ async function updateEnvUsingRegistry(env: PythonEnvInfo): Promise<void> {
110108
}
111109
}
112110

113-
async function resolveGloballyInstalledEnv(env: BasicEnvInfo, kind: PythonEnvKind): Promise<PythonEnvInfo> {
111+
async function resolveGloballyInstalledEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
114112
const { executablePath } = env;
115113
let version;
116114
try {
@@ -119,17 +117,17 @@ async function resolveGloballyInstalledEnv(env: BasicEnvInfo, kind: PythonEnvKin
119117
version = UNKNOWN_PYTHON_VERSION;
120118
}
121119
const envInfo = buildEnvInfo({
122-
kind,
120+
kind: env.kind,
123121
version,
124122
executable: executablePath,
125123
});
126124
return envInfo;
127125
}
128126

129-
async function resolveSimpleEnv(env: BasicEnvInfo, kind: PythonEnvKind): Promise<PythonEnvInfo> {
127+
async function resolveSimpleEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
130128
const { executablePath } = env;
131129
const envInfo = buildEnvInfo({
132-
kind,
130+
kind: env.kind,
133131
version: await getPythonVersionFromPath(executablePath),
134132
executable: executablePath,
135133
});
@@ -173,7 +171,7 @@ async function resolveCondaEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {
173171
`${executablePath} identified as a Conda environment but is not returned via '${conda?.command} info' command`,
174172
);
175173
// Environment could still be valid, resolve as a simple env.
176-
return resolveSimpleEnv(env, PythonEnvKind.Conda);
174+
return resolveSimpleEnv(env);
177175
}
178176

179177
async function resolvePyenvEnv(env: BasicEnvInfo): Promise<PythonEnvInfo> {

src/client/pythonEnvironments/common/commonUtils.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,18 @@ const matchPythonBinFilename =
2020
type FileFilterFunc = (filename: string) => boolean;
2121

2222
/**
23-
* Returns `true` if path provides is likely an executable and not path to environment folder
23+
* Returns `true` if path provided is likely a python executable.
2424
*/
25-
export async function isExecutableOrEnvPath(filePath: string): Promise<boolean> {
26-
const matchesExecutableRegex = matchPythonBinFilename(filePath);
27-
if (!matchesExecutableRegex) {
25+
export async function isPythonExecutable(filePath: string): Promise<boolean> {
26+
const isMatch = matchPythonBinFilename(filePath);
27+
if (!isMatch) {
2828
return false;
2929
}
30+
// On Windows it's fair to assume a path ending with `.exe` denotes a file.
3031
if (getOSType() === OSType.Windows) {
3132
return true;
3233
}
34+
// For other operating systems verify if it's a file.
3335
if (await isFile(filePath)) {
3436
return true;
3537
}

src/test/interpreters/interpreterService.unit.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ suite('Interpreters service', () => {
248248
test('If stored setting is an empty string, refresh the interpreter display', async () => {
249249
const service = new InterpreterService(serviceContainer, pyenvs.object);
250250
const resource = Uri.parse('a');
251-
service._pythonPathSetting = '';
251+
service._currentInterpreterDisplay = '';
252252
configService.reset();
253253
configService.setup((c) => c.getSettings(resource)).returns(() => ({ pythonPath: 'current path' } as any));
254254
interpreterDisplay
@@ -266,7 +266,7 @@ suite('Interpreters service', () => {
266266
test('If stored setting is not equal to current interpreter path setting, refresh the interpreter display', async () => {
267267
const service = new InterpreterService(serviceContainer, pyenvs.object);
268268
const resource = Uri.parse('a');
269-
service._pythonPathSetting = 'stored setting';
269+
service._currentInterpreterDisplay = 'stored setting';
270270
configService.reset();
271271
configService.setup((c) => c.getSettings(resource)).returns(() => ({ pythonPath: 'current path' } as any));
272272
interpreterDisplay
@@ -284,7 +284,7 @@ suite('Interpreters service', () => {
284284
test('If stored setting is equal to current interpreter path setting, do not refresh the interpreter display', async () => {
285285
const service = new InterpreterService(serviceContainer, pyenvs.object);
286286
const resource = Uri.parse('a');
287-
service._pythonPathSetting = 'setting';
287+
service._currentInterpreterDisplay = 'setting';
288288
configService.reset();
289289
configService.setup((c) => c.getSettings(resource)).returns(() => ({ pythonPath: 'setting' } as any));
290290
interpreterDisplay

0 commit comments

Comments
 (0)