Skip to content

Commit 69be6a4

Browse files
Fix wrong Python executable path on Linux/macOS after creating conda environments (microsoft#1459)
Part of microsoft#1454 ## Bug In `createNamedCondaEnvironment` and `createPrefixCondaEnvironment`, the Python executable path is computed as: ```ts const bin = os.platform() === 'win32' ? 'python.exe' : 'python'; // Then: path.join(envPath, bin) ``` On Linux/macOS, this produces `/path/to/env/python`, but conda installs the Python executable at `/path/to/env/bin/python`. ## Why it's a bug The environment appears in the VS Code picker and can be selected. However, the internal `execInfo.run.executable` points to a non-existent path. Any operation that invokes Python — running scripts, linting, getting version info — fails silently. Users see a valid-looking conda environment that they can select, but nothing works after selecting it. The `quickCreateConda` function (line 1225) already had the correct path: ```ts os.platform() === 'win32' ? path.join(prefix, 'python.exe') : path.join(prefix, 'bin', 'python') ``` ## Repro steps 1. On Linux or macOS, create a conda environment through the VS Code UI (either named or prefix type). 2. The environment appears in the picker — select it. 3. Try running a Python script or checking the Python version. 4. Operations fail because the executable path `/path/to/env/python` does not exist. ## Fix Changed both `createNamedCondaEnvironment` (line 1085) and `createPrefixCondaEnvironment` (line 1169) to use: ```ts const bin = os.platform() === 'win32' ? 'python.exe' : path.join('bin', 'python'); ``` This matches the correct pattern already used in `quickCreateConda`. ## Tests added - `condaUtils.pythonExePath.unit.test.ts`: 3 test cases verifying correct executable paths for both platform types and both environment types.
1 parent ee40206 commit 69be6a4

2 files changed

Lines changed: 69 additions & 2 deletions

File tree

src/managers/conda/condaUtils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,7 @@ export async function createNamedCondaEnvironment(
10821082
},
10831083
async () => {
10841084
try {
1085-
const bin = os.platform() === 'win32' ? 'python.exe' : 'python';
1085+
const bin = os.platform() === 'win32' ? 'python.exe' : path.join('bin', 'python');
10861086
const output = await runCondaExecutable(runArgs);
10871087
log.info(output);
10881088

@@ -1166,7 +1166,7 @@ export async function createPrefixCondaEnvironment(
11661166
},
11671167
async () => {
11681168
try {
1169-
const bin = os.platform() === 'win32' ? 'python.exe' : 'python';
1169+
const bin = os.platform() === 'win32' ? 'python.exe' : path.join('bin', 'python');
11701170
const output = await runCondaExecutable(runArgs);
11711171
log.info(output);
11721172
const version = await getVersion(prefix);
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import assert from 'assert';
3+
import * as path from 'path';
4+
import * as sinon from 'sinon';
5+
import { EnvironmentManager } from '../../../api';
6+
import { CondaEnvManager } from '../../../managers/conda/condaEnvManager';
7+
import { getNamedCondaPythonInfo, getPrefixesCondaPythonInfo } from '../../../managers/conda/condaUtils';
8+
import { NativePythonFinder } from '../../../managers/common/nativePythonFinder';
9+
import * as platformUtils from '../../../common/utils/platformUtils';
10+
11+
suite('Conda Python executable path construction', () => {
12+
let isWindowsStub: sinon.SinonStub;
13+
let mockManager: EnvironmentManager;
14+
15+
setup(() => {
16+
mockManager = new CondaEnvManager(
17+
{} as NativePythonFinder,
18+
{} as any,
19+
{ info: sinon.stub(), error: sinon.stub(), warn: sinon.stub() } as any,
20+
);
21+
});
22+
23+
teardown(() => {
24+
sinon.restore();
25+
});
26+
27+
test('getNamedCondaPythonInfo: executable path uses bin/python on non-Windows', async () => {
28+
isWindowsStub = sinon.stub(platformUtils, 'isWindows').returns(false);
29+
const prefix = '/home/user/miniconda3/envs/myenv';
30+
const executable = path.posix.join(prefix, 'bin', 'python');
31+
const info = await getNamedCondaPythonInfo('myenv', prefix, executable, '3.12.0', '/usr/bin/conda', mockManager);
32+
33+
assert.ok(
34+
info.execInfo.run.executable.includes(path.join('bin', 'python')) ||
35+
info.execInfo.run.executable.endsWith('python'),
36+
`executable should contain bin/python, got: ${info.execInfo.run.executable}`,
37+
);
38+
isWindowsStub.restore();
39+
});
40+
41+
test('getPrefixesCondaPythonInfo: executable path uses bin/python on non-Windows', async () => {
42+
isWindowsStub = sinon.stub(platformUtils, 'isWindows').returns(false);
43+
const prefix = '/home/user/projects/.conda';
44+
const executable = path.posix.join(prefix, 'bin', 'python');
45+
const info = await getPrefixesCondaPythonInfo(prefix, executable, '3.12.0', '/usr/bin/conda', mockManager);
46+
47+
assert.ok(
48+
info.execInfo.run.executable.includes(path.join('bin', 'python')) ||
49+
info.execInfo.run.executable.endsWith('python'),
50+
`executable should contain bin/python, got: ${info.execInfo.run.executable}`,
51+
);
52+
isWindowsStub.restore();
53+
});
54+
55+
test('getNamedCondaPythonInfo: executable path uses python.exe on Windows', async () => {
56+
isWindowsStub = sinon.stub(platformUtils, 'isWindows').returns(true);
57+
const prefix = 'C:\\Users\\user\\miniconda3\\envs\\myenv';
58+
const executable = path.win32.join(prefix, 'python.exe');
59+
const info = await getNamedCondaPythonInfo('myenv', prefix, executable, '3.12.0', 'C:\\conda\\conda.exe', mockManager);
60+
61+
assert.ok(
62+
info.execInfo.run.executable.endsWith('python.exe'),
63+
`executable should end with python.exe, got: ${info.execInfo.run.executable}`,
64+
);
65+
isWindowsStub.restore();
66+
});
67+
});

0 commit comments

Comments
 (0)