Skip to content

Commit 492fc99

Browse files
committed
Ensure pyenv virtual envs are not skipped when in discovery experiment (#15451)
* Ensure pyenv virtual envs are not skipped * Add news * Clean up * Address comments
1 parent 0eeaab4 commit 492fc99

File tree

3 files changed

+31
-18
lines changed

3 files changed

+31
-18
lines changed

news/2 Fixes/15439.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix for missing pyenv virtual environments from selectable environments.

src/client/pythonEnvironments/common/externalDependencies.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -97,23 +97,35 @@ export async function getFileInfo(filePath: string): Promise<{ ctime: number; mt
9797
}
9898
}
9999

100-
export async function resolveSymbolicLink(filepath: string): Promise<string> {
101-
const stats = await fsapi.lstat(filepath);
100+
export async function resolveSymbolicLink(absPath: string): Promise<string> {
101+
const stats = await fsapi.lstat(absPath);
102102
if (stats.isSymbolicLink()) {
103-
const link = await fsapi.readlink(filepath);
103+
const link = await fsapi.readlink(absPath);
104104
return resolveSymbolicLink(link);
105105
}
106-
return filepath;
106+
return absPath;
107107
}
108108

109-
export async function* getSubDirs(root: string): AsyncIterableIterator<string> {
110-
const dirContents = await fsapi.readdir(root);
109+
/**
110+
* Returns full path to sub directories of a given directory.
111+
* @param root
112+
* @param resolveSymlinks
113+
*/
114+
export async function* getSubDirs(root: string, resolveSymlinks: boolean): AsyncIterableIterator<string> {
115+
const dirContents = await fsapi.promises.readdir(root, { withFileTypes: true });
111116
const generators = dirContents.map((item) => {
112117
async function* generator() {
113-
const stat = await fsapi.lstat(path.join(root, item));
114-
115-
if (stat.isDirectory()) {
116-
yield item;
118+
const fullPath = path.join(root, item.name);
119+
if (item.isDirectory()) {
120+
yield fullPath;
121+
} else if (resolveSymlinks && item.isSymbolicLink()) {
122+
// The current FS item is a symlink. It can potentially be a file
123+
// or a directory. Resolve it first and then check if it is a directory.
124+
const resolvedPath = await resolveSymbolicLink(fullPath);
125+
const resolvedPathStat = await fsapi.lstat(resolvedPath);
126+
if (resolvedPathStat.isDirectory()) {
127+
yield resolvedPath;
128+
}
117129
}
118130
}
119131

src/client/pythonEnvironments/discovery/locators/services/pyenvLocator.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -259,15 +259,15 @@ export function parsePyenvVersion(str: string): Promise<IPyenvVersionStrings | u
259259
async function* getPyenvEnvironments(): AsyncIterableIterator<PythonEnvInfo> {
260260
const pyenvVersionDir = getPyenvVersionsDir();
261261

262-
const subDirs = getSubDirs(pyenvVersionDir);
263-
for await (const subDir of subDirs) {
264-
const envDir = path.join(pyenvVersionDir, subDir);
265-
const interpreterPath = await getInterpreterPathFromDir(envDir);
262+
const subDirs = getSubDirs(pyenvVersionDir, true);
263+
for await (const subDirPath of subDirs) {
264+
const envDirName = path.basename(subDirPath);
265+
const interpreterPath = await getInterpreterPathFromDir(subDirPath);
266266

267267
if (interpreterPath) {
268268
// The sub-directory name sometimes can contain distro and python versions.
269269
// here we attempt to extract the texts out of the name.
270-
const versionStrings = await parsePyenvVersion(subDir);
270+
const versionStrings = await parsePyenvVersion(envDirName);
271271

272272
// Here we look for near by files, or config files to see if we can get python version info
273273
// without running python itself.
@@ -290,7 +290,7 @@ async function* getPyenvEnvironments(): AsyncIterableIterator<PythonEnvInfo> {
290290
// `pyenv local|global <env-name>` or `pyenv shell <env-name>`
291291
//
292292
// For the display name we are going to treat these as `pyenv` environments.
293-
const display = `${subDir}:pyenv`;
293+
const display = `${envDirName}:pyenv`;
294294

295295
const org = versionStrings && versionStrings.distro ? versionStrings.distro : '';
296296

@@ -299,14 +299,14 @@ async function* getPyenvEnvironments(): AsyncIterableIterator<PythonEnvInfo> {
299299
const envInfo = buildEnvInfo({
300300
kind: PythonEnvKind.Pyenv,
301301
executable: interpreterPath,
302-
location: envDir,
302+
location: subDirPath,
303303
version: pythonVersion,
304304
source: [PythonEnvSource.Pyenv],
305305
display,
306306
org,
307307
fileInfo,
308308
});
309-
envInfo.name = subDir;
309+
envInfo.name = envDirName;
310310

311311
yield envInfo;
312312
}

0 commit comments

Comments
 (0)