Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
/* eslint-disable class-methods-use-this */

import { inject, injectable } from 'inversify';
import * as path from 'path';
import { ModuleInstallerType } from '../../pythonEnvironments/info';
import { ExecutionInfo, IConfigurationService } from '../types';
import { ModuleInstaller } from './moduleInstaller';
import { ExecutionInfo, IConfigurationService, Product } from '../types';
import { ModuleInstaller, translateProductToModule } from './moduleInstaller';
import { InterpreterUri, ModuleInstallFlags } from './types';
import { isUvInstalled } from '../../pythonEnvironments/common/environmentManagers/uv';
import { IServiceContainer } from '../../ioc/types';
import { isResource } from '../utils/misc';
import { IWorkspaceService } from '../application/types';
import { IInterpreterService } from '../../interpreter/contracts';
import { IFileSystem } from '../platform/types';

@injectable()
export class UVInstaller extends ModuleInstaller {
Expand Down Expand Up @@ -54,8 +56,24 @@ export class UVInstaller extends ModuleInstaller {
): Promise<ExecutionInfo> {
// If the resource isSupported, then the uv binary exists
const execPath = 'uv';
// TODO: should we use uv add if a pyproject.toml exists?
const args = ['pip', 'install', '--upgrade'];

// Don't use 'uv add' for ipykernel since it's only being used to enable the Console
const isIpykernel = moduleName === translateProductToModule(Product.ipykernel);

// ...or if we're trying to break system packages
const isBreakingSystemPackages = (flags & ModuleInstallFlags.breakSystemPackages) !== 0;

// ...or if pyproject.toml doesn't exist at the workspace root
const workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
const fileSystem = this.serviceContainer.get<IFileSystem>(IFileSystem);
let workspaceFolder = isResource(resource) ? workspaceService.getWorkspaceFolder(resource) : undefined;
if (!workspaceFolder && workspaceService.workspaceFolders && workspaceService.workspaceFolders.length > 0) {
workspaceFolder = workspaceService.workspaceFolders[0];
}
const pyprojectPath = workspaceFolder ? path.join(workspaceFolder.uri.fsPath, 'pyproject.toml') : undefined;
const pyprojectExists = pyprojectPath ? await fileSystem.fileExists(pyprojectPath) : false;

const usePyprojectWorkflow = !isIpykernel && !isBreakingSystemPackages && pyprojectExists;

// Get the path to the python interpreter (similar to a part in ModuleInstaller.installModule())
const configService = this.serviceContainer.get<IConfigurationService>(IConfigurationService);
Expand All @@ -64,22 +82,22 @@ export class UVInstaller extends ModuleInstaller {
const interpreter = isResource(resource) ? await interpreterService.getActiveInterpreter(resource) : resource;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ugh, I think getActiveInterpreter() still sometimes returns the wrong one in certain workspaceless scenarios. That's a bigger problem than this PR so I'll make an issue later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

related to #6936

const interpreterPath = interpreter?.path ?? settings.pythonPath;
const pythonPath = isResource(resource) ? interpreterPath : resource.path;
args.push('--python', pythonPath);

const workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService);
const proxy = workspaceService.getConfiguration('http').get('proxy', '');
if (proxy.length > 0) {
args.push('--proxy', proxy);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized that uv doesn't support this --proxy argument, only pip does. I probably should make an issue and tackle that later.

const args: string[] = [];

if (flags & ModuleInstallFlags.reInstall) {
args.push('--force-reinstall');
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here - uv doesn't support this argument.

if (usePyprojectWorkflow) {
// Use 'uv add' for project-based workflow
args.push('add');
} else {
// Use 'uv pip install' for environment-based workflow
args.push('pip', 'install');

// Support the --break-system-packages flag to temporarily work around PEP 668.
if (flags & ModuleInstallFlags.breakSystemPackages) {
args.push('--break-system-packages');
// Support the --break-system-packages flag to temporarily work around PEP 668.
if (isBreakingSystemPackages) {
args.push('--break-system-packages');
}
}
args.push('--upgrade', '--python', pythonPath);

return {
args: [...args, moduleName],
Expand Down
Loading