Skip to content

Commit 37f0910

Browse files
author
Kartik Raj
committed
Add support to select env folders as interpreterPaths
1 parent 5978fc5 commit 37f0910

File tree

6 files changed

+70
-40
lines changed

6 files changed

+70
-40
lines changed

src/client/common/process/pythonEnvironment.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ export function createPythonEnv(
133133

134134
export async function createCondaEnv(
135135
condaInfo: CondaEnvironmentInfo,
136-
pythonPath: string,
137136
// These are used to generate the deps.
138137
procs: IProcessService,
139138
fs: IFileSystem,
@@ -150,7 +149,14 @@ export async function createCondaEnv(
150149
(file, args, opts) => procs.exec(file, args, opts),
151150
(command, opts) => procs.shellExec(command, opts),
152151
);
153-
return new PythonEnvironment(pythonPath, deps);
152+
const interpreterPath = await conda?.getInterpreterPathForEnvironment({
153+
name: condaInfo.name,
154+
prefix: condaInfo.path,
155+
});
156+
if (!interpreterPath) {
157+
return undefined;
158+
}
159+
return new PythonEnvironment(interpreterPath, deps);
154160
}
155161

156162
export function createWindowsStoreEnv(

src/client/common/process/pythonExecutionFactory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export class PythonExecutionFactory implements IPythonExecutionFactory {
131131
if (!condaEnvironment) {
132132
return undefined;
133133
}
134-
const env = await createCondaEnv(condaEnvironment, pythonPath, processService, this.fileSystem);
134+
const env = await createCondaEnv(condaEnvironment, processService, this.fileSystem);
135135
if (!env) {
136136
return undefined;
137137
}

src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand {
155155
return this._enterOrBrowseInterpreterPath(input, state, suggestions);
156156
} else {
157157
sendTelemetryEvent(EventName.SELECT_INTERPRETER_SELECTED, undefined, { action: 'selected' });
158-
state.path = (selection as IInterpreterQuickPickItem).path;
158+
const { interpreter } = selection as IInterpreterQuickPickItem;
159+
state.path = interpreter.envType === EnvironmentType.Conda ? interpreter.envPath : interpreter.path;
159160
}
160161

161162
return undefined;

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { Event } from 'vscode';
55
import { traceInfo } from '../../../../logging';
66
import { reportInterpretersChanged } from '../../../../proposedApi';
7-
import { pathExists } from '../../../common/externalDependencies';
7+
import { arePathsSame, pathExists } from '../../../common/externalDependencies';
88
import { PythonEnvInfo } from '../../info';
99
import { areSameEnv } from '../../info/env';
1010
import {
@@ -124,8 +124,12 @@ export class PythonEnvInfoCache extends PythonEnvsWatcher<PythonEnvCollectionCha
124124
}
125125
}
126126

127-
public getCompleteInfo(executablePath: string): PythonEnvInfo | undefined {
128-
const env = this.envs.find((e) => areSameEnv(e, executablePath));
127+
public getCompleteInfo(path: string): PythonEnvInfo | undefined {
128+
let env = this.envs.find((e) => arePathsSame(e.location, path));
129+
if (env?.hasCompleteInfo) {
130+
return env;
131+
}
132+
env = this.envs.find((e) => areSameEnv(e, path));
129133
return env?.hasCompleteInfo ? env : undefined;
130134
}
131135

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

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,8 @@
33
import '../../../../common/extensions';
44
import { PythonEnvKind } from '../../info';
55
import { BasicEnvInfo, IPythonEnvsIterator, Locator } from '../../locator';
6-
import { getInterpreterPathFromDir } from '../../../common/commonUtils';
7-
import { Conda, CONDA_ACTIVATION_TIMEOUT } from '../../../common/environmentManagers/conda';
6+
import { Conda } from '../../../common/environmentManagers/conda';
87
import { traceError, traceVerbose } from '../../../../logging';
9-
import { shellExecute } from '../../../common/externalDependencies';
10-
import { getExecutable } from '../../../../common/process/internal/python';
11-
import { buildPythonExecInfo, copyPythonExecInfo } from '../../../exec';
128

139
export class CondaEnvironmentLocator extends Locator<BasicEnvInfo> {
1410
// eslint-disable-next-line class-methods-use-this
@@ -21,38 +17,15 @@ export class CondaEnvironmentLocator extends Locator<BasicEnvInfo> {
2117
traceVerbose(`Searching for conda environments using ${conda.command}`);
2218

2319
const envs = await conda.getEnvList();
24-
for (const { name, prefix } of envs) {
25-
let executablePath = await getInterpreterPathFromDir(prefix);
20+
for (const env of envs) {
21+
const executablePath = await conda.getInterpreterPathForEnvironment(env);
2622
if (executablePath !== undefined) {
2723
traceVerbose(`Found conda environment: ${executablePath}`);
2824
try {
29-
yield { kind: PythonEnvKind.Conda, executablePath, envPath: prefix };
25+
yield { kind: PythonEnvKind.Conda, executablePath, envPath: env.prefix };
3026
} catch (ex) {
3127
traceError(`Failed to process environment: ${executablePath}`, ex);
3228
}
33-
} else {
34-
const runArgs = await conda.getRunPythonArgs({ name, prefix });
35-
if (runArgs) {
36-
try {
37-
const [args, parse] = getExecutable();
38-
const python = buildPythonExecInfo(runArgs);
39-
const info = copyPythonExecInfo(python, args);
40-
const argv = [info.command, ...info.args];
41-
// Concat these together to make a set of quoted strings
42-
const quoted = argv.reduce(
43-
(p, c) => (p ? `${p} ${c.toCommandArgument()}` : `${c.toCommandArgument()}`),
44-
'',
45-
);
46-
const result = await shellExecute(quoted, { timeout: CONDA_ACTIVATION_TIMEOUT });
47-
executablePath = parse(result.stdout);
48-
if (executablePath !== '') {
49-
traceVerbose(`Found conda environment: ${JSON.stringify({ name, prefix })}`);
50-
yield { kind: PythonEnvKind.Conda, executablePath, envPath: prefix };
51-
}
52-
} catch (ex) {
53-
traceError(`Failed to process environment: ${JSON.stringify({ name, prefix })}`, ex);
54-
}
55-
}
5629
}
5730
}
5831
}

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

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@ import * as fsapi from 'fs-extra';
22
import * as path from 'path';
33
import { lt, parse, SemVer } from 'semver';
44
import { getEnvironmentVariable, getOSType, getUserHomeDir, OSType } from '../../../common/utils/platform';
5-
import { arePathsSame, exec, getPythonSetting, isParentPath, pathExists, readFile } from '../externalDependencies';
5+
import {
6+
arePathsSame,
7+
exec,
8+
getPythonSetting,
9+
isParentPath,
10+
pathExists,
11+
readFile,
12+
shellExecute,
13+
} from '../externalDependencies';
614

715
import { PythonVersion, UNKNOWN_PYTHON_VERSION } from '../../base/info';
816
import { parseVersion } from '../../base/info/pythonVersion';
@@ -13,6 +21,8 @@ import { cache } from '../../../common/utils/decorators';
1321
import { isTestExecution } from '../../../common/constants';
1422
import { traceError, traceVerbose } from '../../../logging';
1523
import { OUTPUT_MARKER_SCRIPT } from '../../../common/process/internal/scripts';
24+
import { getExecutable } from '../../../common/process/internal/python';
25+
import { buildPythonExecInfo, copyPythonExecInfo } from '../../exec';
1626

1727
export const AnacondaCompanyName = 'Anaconda, Inc.';
1828

@@ -82,7 +92,7 @@ export async function parseCondaInfo(
8292
.then((interpreters) => interpreters.map((interpreter) => interpreter!));
8393
}
8494

85-
function getCondaMetaPaths(interpreterPath: string): string[] {
95+
export function getCondaMetaPaths(interpreterPath: string): string[] {
8696
const condaMetaDir = 'conda-meta';
8797

8898
// Check if the conda-meta directory is in the same directory as the interpreter.
@@ -200,6 +210,15 @@ export async function getPythonVersionFromConda(interpreterPath: string): Promis
200210
return UNKNOWN_PYTHON_VERSION;
201211
}
202212

213+
/**
214+
* Return the interpreter's filename for the given environment.
215+
*/
216+
function getInterpreterPath(condaEnvironmentPath: string): string {
217+
// where to find the Python binary within a conda env.
218+
const relativePath = getOSType() === OSType.Windows ? 'python.exe' : path.join('bin', 'python');
219+
return path.join(condaEnvironmentPath, relativePath);
220+
}
221+
203222
// Minimum version number of conda required to be able to use 'conda run' with '--no-capture-output' flag.
204223
export const CONDA_RUN_VERSION = '4.9.0';
205224
export const CONDA_ACTIVATION_TIMEOUT = 45000;
@@ -405,6 +424,33 @@ export class Conda {
405424
return envList.find((e) => isParentPath(executable, e.prefix));
406425
}
407426

427+
@cache(-1, true)
428+
public async getInterpreterPathForEnvironment(condaEnv: CondaEnvInfo): Promise<string | undefined> {
429+
let executablePath = getInterpreterPath(condaEnv.prefix);
430+
if (executablePath) {
431+
return executablePath;
432+
}
433+
const runArgs = await this.getRunPythonArgs(condaEnv);
434+
if (runArgs) {
435+
try {
436+
const [args, parseOutput] = getExecutable();
437+
const python = buildPythonExecInfo(runArgs);
438+
const info = copyPythonExecInfo(python, args);
439+
const argv = [info.command, ...info.args];
440+
// Concat these together to make a set of quoted strings
441+
const quoted = argv.reduce(
442+
(p, c) => (p ? `${p} ${c.toCommandArgument()}` : `${c.toCommandArgument()}`),
443+
'',
444+
);
445+
const result = await shellExecute(quoted, { timeout: CONDA_ACTIVATION_TIMEOUT });
446+
executablePath = parseOutput(result.stdout);
447+
} catch (ex) {
448+
traceError(`Failed to process environment: ${JSON.stringify(condaEnv)}`, ex);
449+
}
450+
}
451+
return executablePath;
452+
}
453+
408454
public async getRunPythonArgs(env: CondaEnvInfo): Promise<string[] | undefined> {
409455
const condaVersion = await this.getCondaVersion();
410456
if (condaVersion && lt(condaVersion, CONDA_RUN_VERSION)) {

0 commit comments

Comments
 (0)