Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
6 changes: 6 additions & 0 deletions src/common/localize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ export namespace VenvManagerStrings {

export const installEditable = l10n.t('Install project as editable');
export const searchingDependencies = l10n.t('Searching for dependencies');

export const selectQuickOrCustomize = l10n.t('Select Quick Create or Customize');
Comment thread
karthiknadig marked this conversation as resolved.
Outdated
export const quickCreate = l10n.t('Quick Create');
export const quickCreateDescription = l10n.t('Create a virtual environment in the workspace root');
export const customize = l10n.t('Customize');
Comment thread
karthiknadig marked this conversation as resolved.
Outdated
export const customizeDescription = l10n.t('Select python version, location, packages, etc.');
Comment thread
karthiknadig marked this conversation as resolved.
Outdated
}

export namespace SysManagerStrings {
Expand Down
154 changes: 107 additions & 47 deletions src/managers/builtin/venvUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
import { showErrorMessage } from '../../common/errors/utils';
import { Common, VenvManagerStrings } from '../../common/localize';
import { isUvInstalled, runUV, runPython } from './helpers';
import { getWorkspacePackagesToInstall } from './pipUtils';
import { getProjectInstallable, getWorkspacePackagesToInstall } from './pipUtils';

export const VENV_WORKSPACE_KEY = `${ENVS_EXTENSION_ID}:venv:WORKSPACE_SELECTED`;
export const VENV_GLOBAL_KEY = `${ENVS_EXTENSION_ID}:venv:GLOBAL_SELECTED`;
Expand Down Expand Up @@ -347,6 +347,90 @@ export async function getGlobalVenvLocation(): Promise<Uri | undefined> {
return undefined;
}

async function createWithCustomization(version: string): Promise<boolean | undefined> {
const selection: QuickPickItem | undefined = await showQuickPick(
[
{
label: VenvManagerStrings.quickCreate,
description: VenvManagerStrings.quickCreateDescription,
detail: l10n.t('Will use Python version {0} and install workspace dependencies .', version),
Comment thread
karthiknadig marked this conversation as resolved.
Outdated
},
{
label: VenvManagerStrings.customize,
description: VenvManagerStrings.customizeDescription,
},
],
{
placeHolder: VenvManagerStrings.selectQuickOrCustomize,
ignoreFocusOut: true,
},
);

if (selection === undefined) {
return undefined;
} else if (selection.label === VenvManagerStrings.quickCreate) {
return false;
}
return true;
}

async function createWithProgress(
nativeFinder: NativePythonFinder,
api: PythonEnvironmentApi,
log: LogOutputChannel,
manager: EnvironmentManager,
basePython: PythonEnvironment,
venvRoot: Uri,
envPath: string,
packages?: string[],
) {
const pythonPath =
os.platform() === 'win32' ? path.join(envPath, 'Scripts', 'python.exe') : path.join(envPath, 'bin', 'python');

return await withProgress(
{
location: ProgressLocation.Notification,
title: VenvManagerStrings.venvCreating,
},
async () => {
try {
const useUv = await isUvInstalled(log);
if (basePython.execInfo?.run.executable) {
if (useUv) {
await runUV(
['venv', '--verbose', '--seed', '--python', basePython.execInfo?.run.executable, envPath],
venvRoot.fsPath,
log,
);
} else {
await runPython(
basePython.execInfo.run.executable,
['-m', 'venv', envPath],
venvRoot.fsPath,
manager.log,
);
}
if (!(await fsapi.pathExists(pythonPath))) {
log.error('no python executable found in virtual environment');
Comment thread
karthiknadig marked this conversation as resolved.
throw new Error('no python executable found in virtual environment');
}
}

const resolved = await nativeFinder.resolve(pythonPath);
const env = api.createPythonEnvironmentItem(await getPythonInfo(resolved), manager);
if (packages && packages?.length > 0) {
await api.installPackages(env, packages, { upgrade: false });
}
return env;
} catch (e) {
log.error(`Failed to create virtual environment: ${e}`);
showErrorMessage(VenvManagerStrings.venvCreateFailed);
return;
}
},
);
}

export async function createPythonVenv(
nativeFinder: NativePythonFinder,
api: PythonEnvironmentApi,
Expand All @@ -371,7 +455,27 @@ export async function createPythonVenv(
return;
}

const basePython = await pickEnvironmentFrom(sortEnvironments(filtered));
const sortedEnvs = sortEnvironments(filtered);
const project = api.getPythonProject(venvRoot);

const customize = await createWithCustomization(sortedEnvs[0].version);
if (customize === undefined) {
return;
} else if (customize === false) {
const installables = await getProjectInstallable(api, project ? [project] : undefined);
return await createWithProgress(
nativeFinder,
api,
log,
manager,
sortedEnvs[0],
venvRoot,
path.join(venvRoot.fsPath, '.venv'),
installables?.flatMap((i) => i.args ?? []),
);
}

const basePython = await pickEnvironmentFrom(sortedEnvs);
if (!basePython || !basePython.execInfo) {
log.error('No base python selected, cannot create virtual environment.');
return;
Expand All @@ -396,58 +500,14 @@ export async function createPythonVenv(
}

const envPath = path.join(venvRoot.fsPath, name);
const pythonPath =
os.platform() === 'win32' ? path.join(envPath, 'Scripts', 'python.exe') : path.join(envPath, 'bin', 'python');

const project = api.getPythonProject(venvRoot);
const packages = await getWorkspacePackagesToInstall(
api,
{ showSkipOption: true },
project ? [project] : undefined,
);

return await withProgress(
{
location: ProgressLocation.Notification,
title: VenvManagerStrings.venvCreating,
},
async () => {
try {
const useUv = await isUvInstalled(log);
if (basePython.execInfo?.run.executable) {
if (useUv) {
await runUV(
['venv', '--verbose', '--seed', '--python', basePython.execInfo?.run.executable, envPath],
venvRoot.fsPath,
log,
);
} else {
await runPython(
basePython.execInfo.run.executable,
['-m', 'venv', envPath],
venvRoot.fsPath,
manager.log,
);
}
if (!(await fsapi.pathExists(pythonPath))) {
log.error('no python executable found in virtual environment');
throw new Error('no python executable found in virtual environment');
}
}

const resolved = await nativeFinder.resolve(pythonPath);
const env = api.createPythonEnvironmentItem(await getPythonInfo(resolved), manager);
if (packages && packages?.length > 0) {
await api.installPackages(env, packages, { upgrade: false });
}
return env;
} catch (e) {
log.error(`Failed to create virtual environment: ${e}`);
showErrorMessage(VenvManagerStrings.venvCreateFailed);
return;
}
},
);
return await createWithProgress(nativeFinder, api, log, manager, basePython, venvRoot, envPath, packages);
}

export async function removeVenv(environment: PythonEnvironment, log: LogOutputChannel): Promise<boolean> {
Expand Down