Skip to content

python: add uv to New Project Wizard #7723

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 22, 2025
Merged
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
9 changes: 7 additions & 2 deletions extensions/positron-python/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -536,12 +536,14 @@
"python.environmentProviders.enable": {
"default": {
"Venv": true,
"Conda": true
"Conda": true,
"uv": true
},
"examples": [
{
"Venv": true,
"Conda": true
"Conda": true,
"uv": true
}
],
"markdownDescription": "%python.environmentProviders.enable.markdownDescription%",
Expand All @@ -553,6 +555,9 @@
},
"Conda": {
"type": "boolean"
},
"uv": {
"type": "boolean"
}
},
"additionalProperties": false
Expand Down
2 changes: 2 additions & 0 deletions extensions/positron-python/src/client/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ export namespace Commands {
export const Get_Create_Environment_Providers = 'python.getCreateEnvironmentProviders';
export const Is_Conda_Installed = 'python.isCondaInstalled';
export const Get_Conda_Python_Versions = 'python.getCondaPythonVersions';
export const Is_Uv_Installed = 'python.isUvInstalled';
export const Get_Uv_Python_Versions = 'python.getUvPythonVersions';
export const Is_Global_Python = 'python.isGlobalPython';
export const Show_Interpreter_Debug_Info = 'python.interpreters.debugInfo';
// --- End Positron ---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,12 @@ export namespace CreateEnv {
export const errorDeletingEnvironment = l10n.t('Error while deleting existing ".conda" environment.');
}

// --- Start Positron ---
export namespace Uv {
export const providerDescription = l10n.t('Creates a `uv` virtual environment in the current workspace');
}
// --- End Positron ---

export namespace Trigger {
export const workspaceTriggerMessage = l10n.t(
'A virtual environment is not currently selected for your Python interpreter. Would you like to create a virtual environment?',
Expand Down
4 changes: 3 additions & 1 deletion extensions/positron-python/src/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ async function activateUnsafe(

// Then we finish activating.
const componentsActivated = await activateComponents(ext, components, activationStopWatch);
activateFeatures(ext, components);
// --- Start Positron ---
await activateFeatures(ext, components);
// --- End Positron ---

const nonBlocking = componentsActivated.map((r) => r.fullyReady);
const activationPromise = (async () => {
Expand Down
9 changes: 7 additions & 2 deletions extensions/positron-python/src/client/extensionActivation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ export async function activateComponents(
return Promise.all([legacyActivationResult, ...promises]);
}

export function activateFeatures(ext: ExtensionState, _components: Components): void {
// --- Start Positron ---
// Changed this function to be async
export async function activateFeatures(ext: ExtensionState, _components: Components): Promise<void> {
// --- End Positron ---
const interpreterQuickPick: IInterpreterQuickPick = ext.legacyIOC.serviceContainer.get<IInterpreterQuickPick>(
IInterpreterQuickPick,
);
Expand All @@ -118,7 +121,9 @@ export function activateFeatures(ext: ExtensionState, _components: Components):
// --- End Positron ---

registerPixiFeatures(ext.disposables);
registerAllCreateEnvironmentFeatures(
// --- Start Positron ---
await registerAllCreateEnvironmentFeatures(
// --- End Positron ---
ext.disposables,
interpreterQuickPick,
interpreterPathService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { PVSC_EXTENSION_ID } from '../common/constants';
import { getConfiguration } from '../common/vscodeApis/workspaceApis';
import { CONDA_PROVIDER_ID } from '../pythonEnvironments/creation/provider/condaCreationProvider';
import { VENV_PROVIDER_ID } from '../pythonEnvironments/creation/provider/venvCreationProvider';
import { UV_PROVIDER_ID } from '../pythonEnvironments/creation/provider/uvCreationProvider';
import { traceInfo, traceVerbose } from '../logging';

/**
Expand Down Expand Up @@ -63,10 +64,10 @@ export async function createEnvironmentAndRegister(
pythonRuntimeManager: IPythonRuntimeManager,
options: CreateEnvironmentOptions & CreateEnvironmentOptionsInternal,
): Promise<CreateEnvironmentAndRegisterResult | undefined> {
if (!options.providerId || (!options.interpreterPath && !options.condaPythonVersion)) {
if (!options.providerId || (!options.interpreterPath && !options.condaPythonVersion && !options.uvPythonVersion)) {
return {
error: new Error(
'Missing required options for creating an environment. Please specify a provider ID and a Python interpreter path or a Conda Python version.',
'Missing required options for creating an environment. Please specify a provider ID and a Python interpreter path or a Conda or uv Python version.',
),
};
}
Expand Down Expand Up @@ -118,6 +119,7 @@ export async function isGlobalPython(interpreterPath: string): Promise<boolean |
enum EnvProviderToProviderId {
'Venv' = VENV_PROVIDER_ID,
'Conda' = CONDA_PROVIDER_ID,
'uv' = UV_PROVIDER_ID,
}

/**
Expand All @@ -129,7 +131,7 @@ function getEnabledEnvProviderIds(): string[] {
if (!envProviderConfig) {
// If the config hasn't been set, return the default providers
traceInfo('[getEnabledEnvProviderIds] No environment provider settings configured. Using default providers.');
return [VENV_PROVIDER_ID, CONDA_PROVIDER_ID];
return [VENV_PROVIDER_ID, CONDA_PROVIDER_ID, UV_PROVIDER_ID];
}
const enabledProviderIds = Object.entries(envProviderConfig)
// filter to include only enabled providers that are supported
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,12 @@ export async function isUvEnvironment(interpreterPath: string): Promise<boolean>

return false;
}

/**
* Checks if uv is installed.
* @returns {boolean} Returns true if uv is installed.
*/
export async function isUvInstalled(): Promise<boolean> {
const uvUtils = await UvUtils.getUvUtils();
return uvUtils !== undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ export async function showErrorMessageWithLogs(message: string): Promise<void> {
}
}

// --- Start Positron ---
// Same as above but opens the Positron session picker instead
export async function showPositronErrorMessageWithLogs(message: string): Promise<void> {
const result = await showErrorMessage(message, Common.openOutputPanel, Common.selectNewSession);
if (result === Common.openOutputPanel) {
await executeCommand(Commands.ViewOutput);
} else if (result === Common.selectNewSession) {
await executeCommand('workbench.action.language.runtime.openActivePicker');
}
}
// --- End Positron ---

export function getVenvPath(workspaceFolder: WorkspaceFolder): string {
return path.join(workspaceFolder.uri.fsPath, '.venv');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ import { PythonEnvironment } from '../../envExt/types';
import { getCondaPythonVersions } from './provider/condaUtils';
import { IPythonRuntimeManager } from '../../positron/manager';
import { Conda } from '../common/environmentManagers/conda';
import { getUvPythonVersions } from './provider/uvUtils';
import { isUvInstalled } from '../common/environmentManagers/uv';
import { UvCreationProvider } from './provider/uvCreationProvider';
import {
createEnvironmentAndRegister,
getCreateEnvironmentProviders,
Expand All @@ -48,8 +51,9 @@ class CreateEnvironmentProviders {
this._createEnvProviders = [];
}

public add(provider: CreateEnvironmentProvider) {
// --- Start Positron ---
// --- Start Positron ---
// Added toTopOfList param
public add(provider: CreateEnvironmentProvider, toTopOfList: boolean) {
if (!isEnvProviderEnabled(provider.id)) {
traceLog(`${provider.name} environment provider ${provider.id} is not enabled...skipping registration`);
return;
Expand All @@ -59,6 +63,12 @@ class CreateEnvironmentProviders {
if (this._createEnvProviders.filter((p) => p.id === provider.id).length > 0) {
throw new Error(`Create Environment provider with id ${provider.id} already registered`);
}
// --- Start Positron ---
if (toTopOfList) {
this._createEnvProviders.unshift(provider);
return;
}
// --- End Positron ---
this._createEnvProviders.push(provider);
}

Expand All @@ -73,24 +83,33 @@ class CreateEnvironmentProviders {

const _createEnvironmentProviders: CreateEnvironmentProviders = new CreateEnvironmentProviders();

export function registerCreateEnvironmentProvider(provider: CreateEnvironmentProvider): Disposable {
_createEnvironmentProviders.add(provider);
// --- Start Positron ---
// Added toTopOfList param
export function registerCreateEnvironmentProvider(
provider: CreateEnvironmentProvider,
toTopOfList: boolean = false,
): Disposable {
_createEnvironmentProviders.add(provider, toTopOfList);
// --- End Positron ---
return new Disposable(() => {
_createEnvironmentProviders.remove(provider);
});
}

export const { onCreateEnvironmentStarted, onCreateEnvironmentExited, isCreatingEnvironment } = getCreationEvents();

export function registerCreateEnvironmentFeatures(
// --- Start Positron ---
// Changed this function to be async
export async function registerCreateEnvironmentFeatures(
// --- End Positron ---
disposables: IDisposableRegistry,
interpreterQuickPick: IInterpreterQuickPick,
interpreterPathService: IInterpreterPathService,
pathUtils: IPathUtils,
// --- Start Positron ---
pythonRuntimeManager: IPythonRuntimeManager,
): Promise<void> {
// --- End Positron ---
): void {
disposables.push(
registerCommand(
Commands.Create_Environment,
Expand Down Expand Up @@ -150,10 +169,15 @@ export function registerCreateEnvironmentFeatures(
},
),
registerCommand(Commands.Get_Conda_Python_Versions, () => getCondaPythonVersions()),
registerCommand(Commands.Is_Uv_Installed, async () => await isUvInstalled()),
registerCommand(Commands.Get_Uv_Python_Versions, () => getUvPythonVersions()),
registerCommand(Commands.Is_Global_Python, (interpreterPath: string) => isGlobalPython(interpreterPath)),
// --- End Positron ---
registerCreateEnvironmentProvider(new VenvCreationProvider(interpreterQuickPick)),
registerCreateEnvironmentProvider(condaCreationProvider()),
// --- Start Positron ---
registerCreateEnvironmentProvider(new UvCreationProvider(), await isUvInstalled()),
// --- End Positron ---
onCreateEnvironmentExited(async (e: EnvironmentDidCreateEvent) => {
if (e.path && e.options?.selectEnvironment) {
await interpreterPathService.update(
Expand Down
Loading
Loading