Skip to content

Commit e414ed7

Browse files
committed
Refactor utils to use command classes directly instead of helper functions
Replace direct helper calls (runPython, runUV) with command class usage: - refreshPipPackages() now uses PipListCommand/UvListCommand - refreshPipDirectPackageNames() now uses PipListDirectNamesCommand/UvListDirectNamesCommand Remove: - execPipList() function - no longer needed - PIP_LIST_TIMEOUT_MS constant - no longer used - parsePipListJson import - parsing now handled by command classes This eliminates intermediate abstraction layers and uses the consistent command class pattern throughout the codebase.
1 parent 3abf21d commit e414ed7

2 files changed

Lines changed: 21 additions & 95 deletions

File tree

src/managers/builtin/pipUtils.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import { findFiles } from '../../common/workspace.apis';
1212
import { selectFromCommonPackagesToInstall, selectFromInstallableToInstall } from '../common/pickers';
1313
import { Installable } from '../common/types';
1414
import { mergePackages } from '../common/utils';
15-
import { refreshPipPackages } from './utils';
15+
import { PipListCommand, UvListCommand } from './commands/index';
16+
import { shouldUseUv } from './helpers';
1617

1718
export interface PyprojectToml {
1819
project?: {
@@ -250,7 +251,23 @@ export async function getWorkspacePackagesToInstall(
250251
let common = await getCommonPackages();
251252
let installed: string[] | undefined;
252253
if (environment) {
253-
installed = (await refreshPipPackages(environment, log, { showProgress: true }))?.map((pkg) => pkg.name);
254+
const pythonExecutable = environment.execInfo?.run?.executable;
255+
if (pythonExecutable) {
256+
const useUv = await shouldUseUv(log, environment.environmentPath.fsPath);
257+
const ListCmd = useUv ? UvListCommand : PipListCommand;
258+
const listCmd = new ListCmd({
259+
pythonExecutable,
260+
log,
261+
cancellationToken: undefined,
262+
});
263+
const data = await withProgress(
264+
{
265+
location: ProgressLocation.Notification,
266+
},
267+
async () => await listCmd.execute(),
268+
);
269+
installed = data?.map((pkg) => pkg.name);
270+
}
254271
common = mergePackages(common, installed ?? []);
255272
}
256273
return selectWorkspaceOrCommon(installableResult, common, !!options.showSkipOption, installed ?? []);

src/managers/builtin/utils.ts

Lines changed: 2 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { LogOutputChannel, ProgressLocation, QuickPickItem, Uri, window } from 'vscode';
1+
import { LogOutputChannel, QuickPickItem, Uri, window } from 'vscode';
22
import { EnvironmentManager, Package, PythonEnvironment, PythonEnvironmentApi, PythonEnvironmentInfo } from '../../api';
3-
import { showErrorMessageWithLogs } from '../../common/errors/utils';
43
import { getExtension } from '../../common/extension.apis';
54
import { Common, PixiStrings, SysManagerStrings } from '../../common/localize';
65
import { traceInfo, traceVerbose } from '../../common/logging';
76
import { getGlobalPersistentState } from '../../common/persistentState';
8-
import { showInformationMessage, withProgress } from '../../common/window.apis';
7+
import { showInformationMessage } from '../../common/window.apis';
98
import { openExtension } from '../../common/workbenchCommands';
109
import {
1110
isNativeEnvInfo,
@@ -14,8 +13,6 @@ import {
1413
NativePythonFinder,
1514
} from '../common/nativePythonFinder';
1615
import { shortenVersionString, sortEnvironments } from '../common/utils';
17-
import { runPython, runUV, shouldUseUv } from './helpers';
18-
import { parsePipListJson, parseUvTree, PipPackage } from './pipListUtils';
1916

2017
const PIXI_EXTENSION_ID = 'renan-r-santos.pixi-code';
2118
const PIXI_RECOMMEND_DONT_ASK_KEY = 'pixi-extension-recommend-dont-ask';
@@ -186,94 +183,6 @@ export async function refreshPythons(
186183
return sortEnvironments(collection);
187184
}
188185

189-
const PIP_LIST_TIMEOUT_MS = 30_000;
190-
191-
async function execPipList(environment: PythonEnvironment, log?: LogOutputChannel, args?: string[]): Promise<string> {
192-
// Use environmentPath directly for consistency with UV environment tracking
193-
const useUv = await shouldUseUv(log, environment.environmentPath.fsPath);
194-
if (useUv) {
195-
return await runUV(
196-
['pip', 'list', '--python', environment.execInfo.run.executable, '--format=json', ...(args ?? [])],
197-
undefined,
198-
log,
199-
undefined,
200-
PIP_LIST_TIMEOUT_MS,
201-
);
202-
}
203-
try {
204-
return await runPython(
205-
environment.execInfo.run.executable,
206-
['-m', 'pip', 'list', '--format=json', ...(args ?? [])],
207-
undefined,
208-
log,
209-
undefined,
210-
PIP_LIST_TIMEOUT_MS,
211-
);
212-
} catch (ex) {
213-
log?.error('Error running pip list', ex);
214-
log?.info(
215-
'Package list retrieval attempted using pip, action can be done with uv if installed and setting `alwaysUseUv` is enabled.',
216-
);
217-
throw ex;
218-
}
219-
}
220-
221-
export async function refreshPipPackages(
222-
environment: PythonEnvironment,
223-
log?: LogOutputChannel,
224-
options?: { showProgress: boolean },
225-
): Promise<PipPackage[] | undefined> {
226-
let data: string;
227-
try {
228-
if (options?.showProgress) {
229-
data = await withProgress(
230-
{
231-
location: ProgressLocation.Notification,
232-
},
233-
async () => {
234-
return await execPipList(environment, log);
235-
},
236-
);
237-
} else {
238-
data = await execPipList(environment, log);
239-
}
240-
241-
return parsePipListJson(data, log);
242-
} catch (e) {
243-
log?.error('Error refreshing packages', e);
244-
showErrorMessageWithLogs(SysManagerStrings.packageRefreshError, log);
245-
return undefined;
246-
}
247-
}
248-
249-
/**
250-
* Returns names of packages with no installed dependents (leaf packages).
251-
*
252-
* Uses `pip list --not-required` (pip) or `uv pip tree --depth=0` (uv). These report
253-
* packages that nothing else depends on, which is a proxy for "directly installed" but
254-
* not equivalent — e.g., `pip install flask werkzeug` will report werkzeug as having
255-
* dependents (flask) even though the user installed it explicitly.
256-
*/
257-
export async function refreshPipDirectPackageNames(
258-
environment: PythonEnvironment,
259-
log?: LogOutputChannel,
260-
): Promise<string[] | undefined> {
261-
const useUv = await shouldUseUv(log, environment.environmentPath.fsPath);
262-
if (useUv) {
263-
const treeOutput = await runUV(
264-
['pip', 'tree', '--python', environment.execInfo.run.executable, '--depth=0'],
265-
undefined,
266-
log,
267-
undefined,
268-
PIP_LIST_TIMEOUT_MS,
269-
);
270-
return parseUvTree(treeOutput);
271-
}
272-
const data = await execPipList(environment, log, ['--not-required']);
273-
const packages = parsePipListJson(data);
274-
return packages.map((pkg) => pkg.name);
275-
}
276-
277186
/**
278187
* Process pip install arguments to correctly handle editable installs with extras
279188
* This function will combine consecutive -e arguments that represent the same package with extras

0 commit comments

Comments
 (0)