Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/managers/base/commands/packageManagerCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface BaseExecuteArgs {
cancellationToken?: CancellationToken;
strict?: boolean;
toolExecution?: boolean;
env?: NodeJS.ProcessEnv;
}

/**
Expand Down
26 changes: 24 additions & 2 deletions src/managers/common/packageChanges.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { CancellationError } from 'vscode';
import { Package, PackageChangeKind, PackageManager, PythonEnvironment } from '../../api';
import { normalizePackageName } from './packageUtils';

Expand All @@ -10,6 +11,8 @@ import { normalizePackageName } from './packageUtils';
export type PackageChangesCallback = (changes: { kind: PackageChangeKind; pkg: Package }[]) => void;

type PackageFetcher = () => Promise<Package[] | undefined>;
type DirectPackageNamesFetcher = () => Promise<Set<string> | undefined>;
type PackagesResolvedCallback = (packages: Package[]) => void;

/**
* Computes the list of package changes between a before and after snapshot.
Expand Down Expand Up @@ -49,6 +52,10 @@ export function getPackageChanges(before: Package[], after: Package[]): { kind:
* @param before The package snapshot from before the operation.
* @param onChanges Callback invoked when package changes are detected.
* @param fetchPackages Optional internal fetcher for operation-specific refresh behavior.
* @param skipDirectDependencies Whether to skip direct-dependency enrichment.
* @param fetchDirectPackageNames Optional internal fetcher for operation-specific direct dependencies.
* @param onPackagesResolved Optional callback invoked with enriched packages before change events.
* @returns The refreshed package list, or undefined when packages could not be fetched.
*/
export async function updatePackagesAndNotify(
packageManager: PackageManager,
Expand All @@ -57,11 +64,24 @@ export async function updatePackagesAndNotify(
onChanges: PackageChangesCallback,
fetchPackages?: PackageFetcher,
skipDirectDependencies = false,
fetchDirectPackageNames?: DirectPackageNamesFetcher,
onPackagesResolved?: PackagesResolvedCallback,
): Promise<Package[] | undefined> {
const directPackageNames = skipDirectDependencies
? undefined
: (
fetchDirectPackageNames
? fetchDirectPackageNames()
: packageManager.getDirectPackageNames?.(environment)
)?.catch((error) => {
if (error instanceof CancellationError) {
throw error;
}
return undefined;
});
const [after, afterDirectDependenciesNames] = await Promise.all([
fetchPackages?.() ?? packageManager.getPackages(environment, { skipCache: true }),
// Handle transitive dependencies (best-effort, don't break package refresh on failure)
skipDirectDependencies ? undefined : packageManager.getDirectPackageNames?.(environment).catch(() => undefined),
directPackageNames,
]);

if (after === undefined) {
Expand All @@ -76,6 +96,8 @@ export async function updatePackagesAndNotify(
}))
: after;

onPackagesResolved?.(enriched);

// Fire change event
const changes = getPackageChanges(before ?? [], enriched);
if (changes.length > 0) {
Expand Down
1 change: 1 addition & 0 deletions src/managers/poetry/commands/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export class PoetryAddCommand extends InstallCommand {
this.log,
executeArgs.cancellationToken,
executeArgs.toolExecution,
executeArgs.env,
);
}
}
1 change: 1 addition & 0 deletions src/managers/poetry/commands/remove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class PoetryRemoveCommand extends UninstallCommand {
this.log,
executeArgs.cancellationToken,
executeArgs.toolExecution,
executeArgs.env,
);
}
}
39 changes: 26 additions & 13 deletions src/managers/poetry/commands/runPoetry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CancellationError, CancellationToken, LogOutputChannel } from 'vscode';
import { CancellationError, CancellationToken, Disposable, l10n, LogOutputChannel } from 'vscode';
import { spawnProcess } from '../../../common/childProcess.apis';
import { runLoggedProcess } from '../../../common/utils/processRunner';
import { throwIfCancelled } from '../../../internal/pythonToolSupport';
Expand All @@ -10,25 +10,39 @@ export async function runPoetry(
log?: LogOutputChannel,
token?: CancellationToken,
toolExecution = false,
env?: NodeJS.ProcessEnv,
): Promise<string> {
if (toolExecution) {
throwIfCancelled(token);
}
throwIfCancelled(token);
const poetry = await getPoetry();
if (!poetry) {
throw new Error('Poetry executable not found');
throw new Error(l10n.t('Poetry executable not found.'));
}
throwIfCancelled(token);

if (toolExecution) {
return runLoggedProcess(poetry, ['--no-interaction', ...args], { cwd }, log, token, 300_000, true);
return runLoggedProcess(poetry, ['--no-interaction', ...args], { cwd, env }, log, token, 300_000);
}
log?.info(`Running: ${poetry} ${args.join(' ')}`);

return new Promise<string>((resolve, reject) => {
const proc = spawnProcess(poetry, args, { cwd });
token?.onCancellationRequested(() => {
const proc = spawnProcess(poetry, args, { cwd, env });
let settled = false;
let subscription: Disposable | undefined;
const finish = (error?: Error, output?: string) => {
if (settled) {
return;
}
settled = true;
subscription?.dispose();
if (error) {
reject(error);
} else {
resolve(output ?? '');
}
};
subscription = token?.onCancellationRequested(() => {
proc.kill();
reject(new CancellationError());
finish(new CancellationError());
});
let builder = '';
proc.stdout?.on('data', (data) => {
Expand All @@ -38,19 +52,18 @@ export async function runPoetry(
});
proc.stderr?.on('data', (data) => {
const output = data.toString('utf-8');
builder += output;
log?.append(`poetry: ${output}`);
});
proc.on('close', (code) => {
if (code !== 0) {
reject(new Error(`Failed to run poetry ${args.join(' ')}`));
finish(new Error(l10n.t('Failed to run Poetry command: {0}', args.join(' '))));
return;
}
resolve(builder);
finish(undefined, builder);
});
proc.on('error', (error) => {
log?.error(`Error executing poetry command: ${error}`);
reject(error);
finish(error);
});
});
}
9 changes: 8 additions & 1 deletion src/managers/poetry/commands/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ export class PoetryShowCommand extends ListCommand {
}

async execute(executeArgs?: BaseExecuteArgs): Promise<PackageInfo[]> {
const output = await runPoetry(this.buildCommand(), this.cwd, this.log, executeArgs?.cancellationToken);
const output = await runPoetry(
this.buildCommand(),
this.cwd,
this.log,
executeArgs?.cancellationToken,
executeArgs?.toolExecution,
executeArgs?.env,
);
const packages: PackageInfo[] = [];
try {
// Parse poetry show output
Expand Down
9 changes: 8 additions & 1 deletion src/managers/poetry/commands/showTopLevel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ export class PoetryShowTopLevelCommand extends ListDirectNamesCommand {
}

async execute(executeArgs?: BaseExecuteArgs): Promise<Set<string>> {
const output = await runPoetry(this.buildCommand(), this.cwd, this.log, executeArgs?.cancellationToken);
const output = await runPoetry(
this.buildCommand(),
this.cwd,
this.log,
executeArgs?.cancellationToken,
executeArgs?.toolExecution,
executeArgs?.env,
);

try {
const names = output
Expand Down
Loading
Loading