Skip to content

Commit 3c64417

Browse files
Fix bug with pip list parsing (#84)
Fixes #83 --------- Co-authored-by: Copilot <[email protected]>
1 parent 4f1dc1e commit 3c64417

17 files changed

+374
-184
lines changed

src/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
runInDedicatedTerminalCommand,
2424
} from './features/envCommands';
2525
import { registerCondaFeatures } from './managers/conda/main';
26-
import { registerSystemPythonFeatures } from './managers/sysPython/main';
26+
import { registerSystemPythonFeatures } from './managers/builtin/main';
2727
import { PythonProjectManagerImpl } from './features/projectManager';
2828
import { EnvironmentManagers, ProjectCreators, PythonProjectManager } from './internal.api';
2929
import { getPythonApi, setPythonApi } from './features/pythonApi';

src/managers/builtin/cache.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { ENVS_EXTENSION_ID } from '../../common/constants';
2+
import { getWorkspacePersistentState } from '../../common/persistentState';
3+
4+
export const SYSTEM_WORKSPACE_KEY = `${ENVS_EXTENSION_ID}:system:WORKSPACE_SELECTED`;
5+
export const SYSTEM_GLOBAL_KEY = `${ENVS_EXTENSION_ID}:system:GLOBAL_SELECTED`;
6+
7+
export async function clearSystemEnvCache(): Promise<void> {
8+
const keys = [SYSTEM_WORKSPACE_KEY, SYSTEM_GLOBAL_KEY];
9+
const state = await getWorkspacePersistentState();
10+
await state.clear(keys);
11+
}
12+
13+
export async function getSystemEnvForWorkspace(fsPath: string): Promise<string | undefined> {
14+
const state = await getWorkspacePersistentState();
15+
const data: { [key: string]: string } | undefined = await state.get(SYSTEM_WORKSPACE_KEY);
16+
if (data) {
17+
try {
18+
return data[fsPath];
19+
} catch {
20+
return undefined;
21+
}
22+
}
23+
return undefined;
24+
}
25+
26+
export async function setSystemEnvForWorkspace(fsPath: string, envPath: string | undefined): Promise<void> {
27+
const state = await getWorkspacePersistentState();
28+
const data: { [key: string]: string } = (await state.get(SYSTEM_WORKSPACE_KEY)) ?? {};
29+
if (envPath) {
30+
data[fsPath] = envPath;
31+
} else {
32+
delete data[fsPath];
33+
}
34+
await state.set(SYSTEM_WORKSPACE_KEY, data);
35+
}
36+
37+
export async function getSystemEnvForGlobal(): Promise<string | undefined> {
38+
const state = await getWorkspacePersistentState();
39+
return await state.get(SYSTEM_GLOBAL_KEY);
40+
}
41+
42+
export async function setSystemEnvForGlobal(envPath: string | undefined): Promise<void> {
43+
const state = await getWorkspacePersistentState();
44+
await state.set(SYSTEM_GLOBAL_KEY, envPath);
45+
}

src/managers/builtin/helpers.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import * as ch from 'child_process';
2+
import { CancellationError, CancellationToken, LogOutputChannel } from 'vscode';
3+
import { createDeferred } from '../../common/utils/deferred';
4+
import { sendTelemetryEvent } from '../../common/telemetry/sender';
5+
import { EventNames } from '../../common/telemetry/constants';
6+
7+
const available = createDeferred<boolean>();
8+
export async function isUvInstalled(log?: LogOutputChannel): Promise<boolean> {
9+
if (available.completed) {
10+
return available.promise;
11+
}
12+
13+
const proc = ch.spawn('uv', ['--version']);
14+
proc.on('error', () => {
15+
available.resolve(false);
16+
});
17+
proc.stdout.on('data', (d) => log?.info(d.toString()));
18+
proc.on('exit', (code) => {
19+
if (code === 0) {
20+
sendTelemetryEvent(EventNames.VENV_USING_UV);
21+
}
22+
available.resolve(code === 0);
23+
});
24+
return available.promise;
25+
}
26+
27+
export async function runUV(
28+
args: string[],
29+
cwd?: string,
30+
log?: LogOutputChannel,
31+
token?: CancellationToken,
32+
): Promise<string> {
33+
log?.info(`Running: uv ${args.join(' ')}`);
34+
return new Promise<string>((resolve, reject) => {
35+
const proc = ch.spawn('uv', args, { cwd: cwd });
36+
token?.onCancellationRequested(() => {
37+
proc.kill();
38+
reject(new CancellationError());
39+
});
40+
41+
let builder = '';
42+
proc.stdout?.on('data', (data) => {
43+
const s = data.toString('utf-8');
44+
builder += s;
45+
log?.append(s);
46+
});
47+
proc.stderr?.on('data', (data) => {
48+
log?.append(data.toString('utf-8'));
49+
});
50+
proc.on('close', () => {
51+
resolve(builder);
52+
});
53+
proc.on('exit', (code) => {
54+
if (code !== 0) {
55+
reject(new Error(`Failed to run uv ${args.join(' ')}`));
56+
}
57+
});
58+
});
59+
}
60+
61+
export async function runPython(
62+
python: string,
63+
args: string[],
64+
cwd?: string,
65+
log?: LogOutputChannel,
66+
token?: CancellationToken,
67+
): Promise<string> {
68+
log?.info(`Running: ${python} ${args.join(' ')}`);
69+
return new Promise<string>((resolve, reject) => {
70+
const proc = ch.spawn(python, args, { cwd: cwd });
71+
token?.onCancellationRequested(() => {
72+
proc.kill();
73+
reject(new CancellationError());
74+
});
75+
let builder = '';
76+
proc.stdout?.on('data', (data) => {
77+
const s = data.toString('utf-8');
78+
builder += s;
79+
log?.append(`python: ${s}`);
80+
});
81+
proc.stderr?.on('data', (data) => {
82+
const s = data.toString('utf-8');
83+
builder += s;
84+
log?.append(`python: ${s}`);
85+
});
86+
proc.on('close', () => {
87+
resolve(builder);
88+
});
89+
proc.on('exit', (code) => {
90+
if (code !== 0) {
91+
reject(new Error(`Failed to run python ${args.join(' ')}`));
92+
}
93+
});
94+
});
95+
}

src/managers/sysPython/main.ts renamed to src/managers/builtin/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { VenvManager } from './venvManager';
66
import { getPythonApi } from '../../features/pythonApi';
77
import { NativePythonFinder } from '../common/nativePythonFinder';
88
import { UvProjectCreator } from './uvProjectCreator';
9-
import { isUvInstalled } from './utils';
9+
import { isUvInstalled } from './helpers';
1010
import { createFileSystemWatcher, onDidDeleteFiles } from '../../common/workspace.apis';
1111
import { createSimpleDebounce } from '../../common/utils/debounce';
1212

src/managers/builtin/pipListUtils.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
export interface PipPackage {
2+
name: string;
3+
version: string;
4+
displayName: string;
5+
description: string;
6+
}
7+
export function parsePipList(data: string): PipPackage[] {
8+
const collection: PipPackage[] = [];
9+
10+
const lines = data.split('\n').splice(2);
11+
for (let line of lines) {
12+
if (line.trim() === '' || line.startsWith('Package') || line.startsWith('----') || line.startsWith('[')) {
13+
continue;
14+
}
15+
const parts = line.split(' ').filter((e) => e);
16+
if (parts.length > 1) {
17+
const name = parts[0].trim();
18+
const version = parts[1].trim();
19+
const pkg = {
20+
name,
21+
version,
22+
displayName: name,
23+
description: version,
24+
};
25+
collection.push(pkg);
26+
}
27+
}
28+
return collection;
29+
}

src/managers/sysPython/sysPythonManager.ts renamed to src/managers/builtin/sysPythonManager.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,18 @@ import {
1414
ResolveEnvironmentContext,
1515
SetEnvironmentScope,
1616
} from '../../api';
17-
import {
18-
clearSystemEnvCache,
19-
getSystemEnvForGlobal,
20-
getSystemEnvForWorkspace,
21-
refreshPythons,
22-
resolveSystemPythonEnvironmentPath,
23-
setSystemEnvForGlobal,
24-
setSystemEnvForWorkspace,
25-
} from './utils';
17+
import { refreshPythons, resolveSystemPythonEnvironmentPath } from './utils';
2618
import { NativePythonFinder } from '../common/nativePythonFinder';
2719
import { createDeferred, Deferred } from '../../common/utils/deferred';
2820
import { getLatest } from '../common/utils';
2921
import { SysManagerStrings } from '../../common/localize';
22+
import {
23+
setSystemEnvForWorkspace,
24+
setSystemEnvForGlobal,
25+
clearSystemEnvCache,
26+
getSystemEnvForGlobal,
27+
getSystemEnvForWorkspace,
28+
} from './cache';
3029

3130
export class SysPythonManager implements EnvironmentManager {
3231
private collection: PythonEnvironment[] = [];

0 commit comments

Comments
 (0)