Skip to content

Commit 1925cf0

Browse files
author
Kartik Raj
committed
Only show envs related to current worksapce
1 parent e9870c0 commit 1925cf0

File tree

9 files changed

+61
-35
lines changed

9 files changed

+61
-35
lines changed

news/1 Enhancements/18263.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Only display Pipenv and Poetry envs related to the currently opened workspace in "Select Interpreter" UI.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ type _PythonEnvInfo = PythonEnvBaseInfo & PythonBuildInfo;
168168
*
169169
* @prop distro - the installed Python distro that this env is using or belongs to
170170
* @prop display - the text to use when showing the env to users
171-
* @prop searchLocation - the root under which a locator found this env, if any
171+
* @prop searchLocation - the project to which this env is related to, if any
172172
*/
173173
export type PythonEnvInfo = _PythonEnvInfo & {
174174
distro: PythonDistroInfo;

src/client/pythonEnvironments/base/locator.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,12 @@ export type PythonLocatorQuery = BasicPythonLocatorQuery & {
116116

117117
type QueryForEvent<E> = E extends PythonEnvsChangedEvent ? PythonLocatorQuery : BasicPythonLocatorQuery;
118118

119-
export type BasicEnvInfo = { kind: PythonEnvKind; executablePath: string; source?: PythonEnvSource[] };
119+
export type BasicEnvInfo = {
120+
kind: PythonEnvKind;
121+
executablePath: string;
122+
source?: PythonEnvSource[];
123+
searchLocation?: Uri;
124+
};
120125

121126
/**
122127
* A single Python environment locator.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ function resolveEnvCollision(oldEnv: BasicEnvInfo, newEnv: BasicEnvInfo): BasicE
119119
const [env] = sortEnvInfoByPriority(oldEnv, newEnv);
120120
const merged = cloneDeep(env);
121121
merged.source = uniq((oldEnv.source ?? []).concat(newEnv.source ?? []));
122+
merged.searchLocation = oldEnv.searchLocation ?? newEnv.searchLocation;
122123
return merged;
123124
}
124125

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

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the MIT License.
33

44
import * as path from 'path';
5-
import { Uri } from 'vscode';
65
import { uniq } from 'lodash';
76
import { PythonEnvInfo, PythonEnvKind, PythonEnvSource, UNKNOWN_PYTHON_VERSION, virtualEnvKinds } from '../../info';
87
import { buildEnvInfo, comparePythonVersionSpecificity, getEnvDisplayString, getEnvMatcher } from '../../info/env';
@@ -11,7 +10,7 @@ import {
1110
getInterpreterPathFromDir,
1211
getPythonVersionFromPath,
1312
} from '../../../common/commonUtils';
14-
import { arePathsSame, getWorkspaceFolders, isParentPath } from '../../../common/externalDependencies';
13+
import { arePathsSame } from '../../../common/externalDependencies';
1514
import { AnacondaCompanyName, Conda } from '../../../common/environmentManagers/conda';
1615
import { parsePyenvVersion } from '../../../common/environmentManagers/pyenv';
1716
import { Architecture, getOSType, OSType } from '../../../../common/utils/platform';
@@ -42,11 +41,16 @@ function getResolvers(): Map<PythonEnvKind, (executablePath: string) => Promise<
4241
* executable and returns it. Notice `undefined` is never returned, so environment
4342
* returned could still be invalid.
4443
*/
45-
export async function resolveBasicEnv({ kind, executablePath, source }: BasicEnvInfo): Promise<PythonEnvInfo> {
44+
export async function resolveBasicEnv({
45+
kind,
46+
executablePath,
47+
source,
48+
searchLocation,
49+
}: BasicEnvInfo): Promise<PythonEnvInfo> {
4650
const resolvers = getResolvers();
4751
const resolverForKind = resolvers.get(kind)!;
4852
const resolvedEnv = await resolverForKind(executablePath);
49-
resolvedEnv.searchLocation = getSearchLocation(resolvedEnv);
53+
resolvedEnv.searchLocation = searchLocation;
5054
resolvedEnv.source = uniq(resolvedEnv.source.concat(source ?? []));
5155
if (getOSType() === OSType.Windows && resolvedEnv.source?.includes(PythonEnvSource.WindowsRegistry)) {
5256
// We can update env further using information we can get from the Windows registry.
@@ -56,24 +60,6 @@ export async function resolveBasicEnv({ kind, executablePath, source }: BasicEnv
5660
return resolvedEnv;
5761
}
5862

59-
function getSearchLocation(env: PythonEnvInfo): Uri | undefined {
60-
const folders = getWorkspaceFolders();
61-
const isRootedEnv = folders.some((f) => isParentPath(env.executable.filename, f));
62-
if (isRootedEnv) {
63-
// For environments inside roots, we need to set search location so they can be queried accordingly.
64-
// Search location particularly for virtual environments is intended as the directory in which the
65-
// environment was found in.
66-
// For eg.the default search location for an env containing 'bin' or 'Scripts' directory is:
67-
//
68-
// searchLocation <--- Default search location directory
69-
// |__ env
70-
// |__ bin or Scripts
71-
// |__ python <--- executable
72-
return Uri.file(path.dirname(env.location));
73-
}
74-
return undefined;
75-
}
76-
7763
async function updateEnvUsingRegistry(env: PythonEnvInfo): Promise<void> {
7864
// Environment source has already been identified as windows registry, so we expect windows registry
7965
// cache to already be populated. Call sync function which relies on cache.

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

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@
33

44
import { uniq } from 'lodash';
55
import * as path from 'path';
6+
import { Uri } from 'vscode';
67
import { chain, iterable } from '../../../../common/utils/async';
78
import { getEnvironmentVariable, getOSType, getUserHomeDir, OSType } from '../../../../common/utils/platform';
89
import { PythonEnvKind } from '../../info';
910
import { BasicEnvInfo, IPythonEnvsIterator } from '../../locator';
1011
import { FSWatchingLocator } from './fsWatchingLocator';
11-
import { findInterpretersInDir, looksLikeBasicVirtualPython } from '../../../common/commonUtils';
12+
import {
13+
findInterpretersInDir,
14+
getEnvironmentDirFromPath,
15+
looksLikeBasicVirtualPython,
16+
} from '../../../common/commonUtils';
1217
import { pathExists, untildify } from '../../../common/externalDependencies';
13-
import { isPipenvEnvironment } from '../../../common/environmentManagers/pipenv';
18+
import { getProjectDir, isPipenvEnvironment } from '../../../common/environmentManagers/pipenv';
1419
import {
1520
isVenvEnvironment,
1621
isVirtualenvEnvironment,
@@ -79,6 +84,18 @@ async function getVirtualEnvKind(interpreterPath: string): Promise<PythonEnvKind
7984
return PythonEnvKind.Unknown;
8085
}
8186

87+
async function getSearchLocation(env: BasicEnvInfo): Promise<Uri | undefined> {
88+
if (env.kind === PythonEnvKind.Pipenv) {
89+
// Pipenv environments are created only for a specific project, so they must only
90+
// appear if that particular project is being queried.
91+
const project = await getProjectDir(getEnvironmentDirFromPath(env.executablePath));
92+
if (project) {
93+
return Uri.file(project);
94+
}
95+
}
96+
return undefined;
97+
}
98+
8299
/**
83100
* Finds and resolves virtual environments created in known global locations.
84101
*/
@@ -118,7 +135,8 @@ export class GlobalVirtualEnvironmentLocator extends FSWatchingLocator<BasicEnvI
118135
// we can use the kind to determine this anyway.
119136
const kind = await getVirtualEnvKind(filename);
120137
try {
121-
yield { kind, executablePath: filename };
138+
const searchLocation = await getSearchLocation({ kind, executablePath: filename });
139+
yield { kind, executablePath: filename, searchLocation };
122140
traceVerbose(`Global Virtual Environment: [added] ${filename}`);
123141
} catch (ex) {
124142
traceError(`Failed to process environment: ${filename}`, ex);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
'use strict';
55

66
import * as path from 'path';
7+
import { Uri } from 'vscode';
78
import { chain, iterable } from '../../../../common/utils/async';
89
import { PythonEnvKind } from '../../info';
910
import { BasicEnvInfo, IPythonEnvsIterator } from '../../locator';
@@ -83,7 +84,7 @@ export class PoetryLocator extends FSWatchingLocator<BasicEnvInfo> {
8384
// We should extract the kind here to avoid doing is*Environment()
8485
// check multiple times. Those checks are file system heavy and
8586
// we can use the kind to determine this anyway.
86-
yield { executablePath: filename, kind };
87+
yield { executablePath: filename, kind, searchLocation: Uri.file(root) };
8788
traceVerbose(`Poetry Virtual Environment: [added] ${filename}`);
8889
} catch (ex) {
8990
traceError(`Failed to process environment: ${filename}`, ex);

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22
// Licensed under the MIT License.
33

44
import * as path from 'path';
5+
import { Uri } from 'vscode';
56
import { chain, iterable } from '../../../../common/utils/async';
6-
import { findInterpretersInDir, looksLikeBasicVirtualPython } from '../../../common/commonUtils';
7+
import {
8+
findInterpretersInDir,
9+
getEnvironmentDirFromPath,
10+
looksLikeBasicVirtualPython,
11+
} from '../../../common/commonUtils';
712
import { pathExists } from '../../../common/externalDependencies';
813
import { isPipenvEnvironment } from '../../../common/environmentManagers/pipenv';
914
import { isVenvEnvironment, isVirtualenvEnvironment } from '../../../common/environmentManagers/simplevirtualenvs';
@@ -80,11 +85,19 @@ export class WorkspaceVirtualEnvironmentLocator extends FSWatchingLocator<BasicE
8085
// python.exe or python in the same directory in the case of virtual
8186
// environments.
8287
if (await looksLikeBasicVirtualPython(entry)) {
83-
// We should extract the kind here to avoid doing is*Environment()
84-
// check multiple times. Those checks are file system heavy and
85-
// we can use the kind to determine this anyway.
88+
const location = getEnvironmentDirFromPath(filename);
89+
// For environments inside roots, we need to set search location so they can be queried accordingly.
90+
// Search location particularly for virtual environments is intended as the directory in which the
91+
// environment was found in.
92+
// For eg.the default search location for an env containing 'bin' or 'Scripts' directory is:
93+
//
94+
// searchLocation <--- Default search location directory
95+
// |__ env
96+
// |__ bin or Scripts
97+
// |__ python <--- executable
98+
const searchLocation = Uri.file(path.dirname(location));
8699
const kind = await getVirtualEnvKind(filename);
87-
yield { kind, executablePath: filename };
100+
yield { kind, executablePath: filename, searchLocation };
88101
traceVerbose(`Workspace Virtual Environment: [added] ${filename}`);
89102
} else {
90103
traceVerbose(`Workspace Virtual Environment: [skipped] ${filename}`);

src/client/pythonEnvironments/common/environmentManagers/pipenv.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import * as path from 'path';
55
import { getEnvironmentVariable } from '../../../common/utils/platform';
66
import { traceError } from '../../../logging';
7+
import { getEnvironmentDirFromPath } from '../commonUtils';
78
import { arePathsSame, normCasePath, pathExists, readFile } from '../externalDependencies';
89

910
function getSearchHeight() {
@@ -70,7 +71,7 @@ async function getPipfileIfLocal(interpreterPath: string): Promise<string | unde
7071
* Returns the project directory for pipenv environments given the environment folder
7172
* @param envFolder Path to the environment folder
7273
*/
73-
async function getProjectDir(envFolder: string): Promise<string | undefined> {
74+
export async function getProjectDir(envFolder: string): Promise<string | undefined> {
7475
// Global pipenv environments have a .project file with the absolute path to the project
7576
// See https://github.com/pypa/pipenv/blob/v2018.6.25/CHANGELOG.rst#features--improvements
7677
// This is the layout we expect
@@ -98,7 +99,7 @@ async function getProjectDir(envFolder: string): Promise<string | undefined> {
9899
* @param interpreterPath Absolute path to any python interpreter.
99100
*/
100101
async function getPipfileIfGlobal(interpreterPath: string): Promise<string | undefined> {
101-
const envFolder = path.dirname(path.dirname(interpreterPath));
102+
const envFolder = getEnvironmentDirFromPath(interpreterPath);
102103
const projectDir = await getProjectDir(envFolder);
103104
if (projectDir === undefined) {
104105
return undefined;

0 commit comments

Comments
 (0)