Skip to content

Commit bcbe7e2

Browse files
Use environment from defaultInterpreterPath if not set (#10)
1 parent 6d62d5f commit bcbe7e2

File tree

8 files changed

+256
-133
lines changed

8 files changed

+256
-133
lines changed

package-lock.json

Lines changed: 116 additions & 116 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Pixi support for the Python Environments extension",
55
"icon": "assets/icon.png",
66
"license": "MIT",
7-
"version": "0.1.2",
7+
"version": "0.1.3",
88
"homepage": "https://github.com/renan-r-santos/pixi-code",
99
"repository": {
1010
"type": "git",
@@ -20,7 +20,7 @@
2020
"conda"
2121
],
2222
"engines": {
23-
"vscode": "^1.102.0"
23+
"vscode": "^1.103.0"
2424
},
2525
"categories": [
2626
"Data Science",
@@ -71,18 +71,18 @@
7171
"format:check": "prettier --check ."
7272
},
7373
"devDependencies": {
74-
"@types/node": "20.x",
75-
"@types/vscode": "^1.102.0",
74+
"@types/node": "24.x",
75+
"@types/vscode": "^1.103.0",
7676
"@types/which": "^3.0.4",
77-
"@typescript-eslint/eslint-plugin": "^8.31.1",
78-
"@typescript-eslint/parser": "^8.31.1",
79-
"eslint": "^9.25.1",
77+
"@typescript-eslint/eslint-plugin": "^8.40.0",
78+
"@typescript-eslint/parser": "^8.40.0",
79+
"eslint": "^9.34.0",
8080
"eslint-plugin-simple-import-sort": "^12.1.1",
81-
"eslint-plugin-unused-imports": "^4.1.4",
82-
"prettier": "^3.4.2",
83-
"ts-loader": "^9.5.2",
84-
"typescript": "^5.8.3",
85-
"webpack": "^5.99.7",
81+
"eslint-plugin-unused-imports": "^4.2.0",
82+
"prettier": "^3.6.2",
83+
"ts-loader": "^9.5.4",
84+
"typescript": "^5.9.2",
85+
"webpack": "^5.101.3",
8686
"webpack-cli": "^6.0.1"
8787
},
8888
"dependencies": {

src/common/defaultInterpreter.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as path from 'path';
2+
import { workspace } from 'vscode';
3+
4+
import { PythonProject } from '../api';
5+
import { expandVariables } from './variableExpansion';
6+
7+
export function getDefaultInterpreterPath(project: PythonProject): string | undefined {
8+
const defaultInterpreterPath = workspace
9+
.getConfiguration('python', project.uri)
10+
.get<string>('defaultInterpreterPath');
11+
12+
if (!defaultInterpreterPath) {
13+
return undefined;
14+
}
15+
16+
const expandedPath = expandVariables(defaultInterpreterPath, project.uri);
17+
return path.resolve(project.uri.fsPath, expandedPath);
18+
}

src/common/variableExpansion.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Uri, workspace } from 'vscode';
2+
3+
export function expandVariables(value: string, project?: Uri, env?: { [key: string]: string }): string {
4+
const substitutions = new Map<string, string>();
5+
6+
const home = process.env.HOME || process.env.USERPROFILE;
7+
if (home) {
8+
substitutions.set('${userHome}', home);
9+
}
10+
11+
if (project) {
12+
substitutions.set('${pythonProject}', project.fsPath);
13+
}
14+
15+
const ws = project ? workspace.getWorkspaceFolder(project) : undefined;
16+
if (ws) {
17+
substitutions.set('${workspaceFolder}', ws.uri.fsPath);
18+
}
19+
20+
substitutions.set('${cwd}', process.cwd());
21+
22+
(workspace.workspaceFolders ?? []).forEach((w) => {
23+
substitutions.set('${workspaceFolder:' + w.name + '}', w.uri.fsPath);
24+
});
25+
26+
const substEnv = env || process.env;
27+
if (substEnv) {
28+
for (const [key, value] of Object.entries(substEnv)) {
29+
if (value && key.length > 0) {
30+
substitutions.set('${env:' + key + '}', value);
31+
}
32+
}
33+
}
34+
35+
let result = value;
36+
substitutions.forEach((v, k) => {
37+
while (k.length > 0 && result.indexOf(k) >= 0) {
38+
result = result.replace(k, v);
39+
}
40+
});
41+
42+
return result;
43+
}

src/pixi/envManager.ts

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as path from 'path';
12
import { EventEmitter, LogOutputChannel, MarkdownString, ProgressLocation, ThemeIcon, Uri, window } from 'vscode';
23

34
import {
@@ -12,11 +13,13 @@ import {
1213
IconPath,
1314
PythonEnvironment,
1415
PythonEnvironmentApi,
16+
PythonProject,
1517
QuickCreateConfig,
1618
RefreshEnvironmentsScope,
1719
ResolveEnvironmentContext,
1820
SetEnvironmentScope,
1921
} from '../api';
22+
import { getDefaultInterpreterPath } from '../common/defaultInterpreter';
2023
import { createDeferred, Deferred } from '../common/deferred';
2124
import { traceVerbose } from '../common/logging';
2225
import { EXTENSION_ID } from '../common/utils';
@@ -140,7 +143,7 @@ export class PixiEnvManager implements EnvironmentManager {
140143
return this.projectToEnvs.get(project.uri.fsPath) || [];
141144
}
142145

143-
// Skip 'global'. Pixi does not have the notion of global or base environments like Conda
146+
// Skip 'global'. Pixi does not have global or base environments like Conda
144147
return [];
145148
}
146149

@@ -227,6 +230,9 @@ export class PixiEnvManager implements EnvironmentManager {
227230
const projectPath = project.uri.fsPath;
228231

229232
const newEnvs = await refreshPixi(projectPath);
233+
const defaultEnvs = await this.getDefaultPathEnvironments(project);
234+
newEnvs.push(...defaultEnvs);
235+
230236
const oldEnvs = oldProjectToEnvs.get(projectPath) || [];
231237

232238
const oldEnvIds = new Set(oldEnvs.map((env) => env.envId.id));
@@ -264,9 +270,12 @@ export class PixiEnvManager implements EnvironmentManager {
264270
const projectPath = project.uri.fsPath;
265271

266272
const envId = await getProjectEnvId(projectPath);
267-
const env = envId ? envIdToEnv.get(envId) : undefined;
273+
let env = envId ? envIdToEnv.get(envId) : undefined;
274+
268275
if (env) {
269276
this.activeEnv.set(projectPath, env);
277+
} else {
278+
env = await this.trySetActiveFromDefault(project, projectPath);
270279
}
271280

272281
this.triggerDidChangeEnvironment(project.uri, oldActiveEnv.get(projectPath), env);
@@ -285,6 +294,8 @@ export class PixiEnvManager implements EnvironmentManager {
285294

286295
const oldEnvs = this.projectToEnvs.get(projectPath) || [];
287296
const newEnvs = await refreshPixi(projectPath);
297+
const defaultEnvs = await this.getDefaultPathEnvironments(project);
298+
newEnvs.push(...defaultEnvs);
288299

289300
const oldEnvIds = new Set(oldEnvs.map((env) => env.envId.id));
290301
const newEnvIds = new Set(newEnvs.map((env) => env.envId.id));
@@ -309,11 +320,51 @@ export class PixiEnvManager implements EnvironmentManager {
309320
);
310321

311322
const envId = await getProjectEnvId(projectPath);
312-
const env = envId ? envIdToEnv.get(envId) : undefined;
323+
let env = envId ? envIdToEnv.get(envId) : undefined;
313324
this.triggerDidChangeEnvironment(project.uri, this.activeEnv.get(projectPath), env);
325+
314326
if (env) {
315327
this.activeEnv.set(projectPath, env);
328+
} else {
329+
env = await this.trySetActiveFromDefault(project, projectPath);
330+
}
331+
}
332+
333+
private async getDefaultPathEnvironments(project: PythonProject): Promise<PixiEnvironment[]> {
334+
const defaultInterpreterPath = getDefaultInterpreterPath(project);
335+
336+
if (!defaultInterpreterPath) {
337+
return [];
316338
}
339+
340+
const binPath = path.dirname(defaultInterpreterPath);
341+
traceVerbose(`Also refreshing Pixi environments using defaultInterpreterPath: ${binPath}`);
342+
return await refreshPixi(binPath);
343+
}
344+
345+
private async trySetActiveFromDefault(
346+
project: PythonProject,
347+
projectPath: string,
348+
): Promise<PixiEnvironment | undefined> {
349+
const defaultInterpreterPath = getDefaultInterpreterPath(project);
350+
351+
if (defaultInterpreterPath) {
352+
const projectEnvs = this.projectToEnvs.get(projectPath) || [];
353+
const matchingEnv = projectEnvs.find((env) =>
354+
defaultInterpreterPath.startsWith(env.environmentPath.fsPath),
355+
);
356+
357+
if (matchingEnv) {
358+
traceVerbose(
359+
`Setting active environment for project ${projectPath} based on default interpreter path ${defaultInterpreterPath}`,
360+
);
361+
this.activeEnv.set(projectPath, matchingEnv);
362+
await setProjectEnvId(projectPath, matchingEnv.envId.id);
363+
return matchingEnv;
364+
}
365+
}
366+
367+
return undefined;
317368
}
318369

319370
private triggerDidChangeEnvironment(

src/pixi/projectManager.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ export class PixiPackageManager implements PackageManager, Disposable {
6666
title: 'Refreshing Pixi packages',
6767
},
6868
async () => {
69+
if (!environment.pixiInfo.project_info) {
70+
traceVerbose('No project info found in pixiInfo; skipping package refresh.');
71+
return;
72+
}
73+
6974
const manifest_path = environment.pixiInfo.project_info.manifest_path;
7075
const project_path = path.dirname(manifest_path);
7176

src/pixi/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Package, PythonEnvironment } from '../api';
22

33
export interface PixiInfo {
4-
project_info: {
4+
project_info?: {
55
name: string;
66
manifest_path: string;
77
};

src/pixi/utils.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Package } from '../api';
66
import { createDeferred } from '../common/deferred';
77
import { quoteArgs } from '../common/execUtils';
88
import { findPythonExecutable } from '../common/findPython';
9-
import { traceError, traceInfo } from '../common/logging';
9+
import { traceError, traceInfo, traceVerbose } from '../common/logging';
1010
import { getWorkspacePersistentState } from '../common/persistentState';
1111
import { EXTENSION_ID, untildify } from '../common/utils';
1212
import { PixiEnvironment, PixiInfo, PixiPackage } from './types';
@@ -92,6 +92,12 @@ export async function refreshPixi(project_path: string): Promise<PixiEnvironment
9292

9393
const stdout = await runPixi(['info', '--json'], { cwd: project_path });
9494
const pixiInfo: PixiInfo = JSON.parse(stdout);
95+
96+
if (!pixiInfo.project_info) {
97+
traceVerbose(`No project info found for Pixi project at ${project_path}`);
98+
return [];
99+
}
100+
95101
const projectName = pixiInfo.project_info.name;
96102
const manifestPath = pixiInfo.project_info.manifest_path;
97103

0 commit comments

Comments
 (0)