Skip to content

Commit f701500

Browse files
edvilmeCopilotCopilot
authored
feature: Manage package versions from sidebar (microsoft#1511)
<img width="809" height="308" alt="image" src="https://github.com/user-attachments/assets/70d99115-4825-4d83-946f-e61f45b63e21" /> # Feature: Manage package versions from sidebar This pull request adds a new "Manage Package Version" command to the Python Envs extension, allowing users to update the version of an installed package via the UI. ## 1. Select "Manage Package Version" in packages view. - Click on the new gear button next to each package to manage versions. <img width="659" height="763" alt="image" src="https://github.com/user-attachments/assets/04a14f4b-93f4-43af-b806-7ff434ce5eec" /> - Note that this feature is disabled for transitive packages (microsoft#1530) <img width="639" height="538" alt="image" src="https://github.com/user-attachments/assets/f9e8bf02-3ea3-42fd-8795-1793cd7c4b56" /> ## 2a. Select the desired version For package managers that support it (conda, uv, pip >= 21.2.0), the extension fetches all available versions from the index and displays them in a picker. <img width="1339" height="804" alt="image" src="https://github.com/user-attachments/assets/505a3cd6-e7c4-4ba2-a69f-7c9912c4b232" /> ## 2b. Type the desired version If the package versions cannot be fetched, the extension prompts the user to type in the desired version <img width="1205" height="198" alt="image" src="https://github.com/user-attachments/assets/94fd322b-e1e0-498a-8c7e-486426924041" /> __User is warned if input is not a correct version number.__ <img width="1207" height="206" alt="image" src="https://github.com/user-attachments/assets/0d7df6e5-55e4-4747-8736-884fdcb835c6" /> ------------------------- Fixes microsoft#1368 Fixes microsoft#512 --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent ff74c27 commit f701500

16 files changed

Lines changed: 443 additions & 10 deletions

api/package-lock.json

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,8 @@
4848
"@types/vscode": "^1.99.0",
4949
"mve": "^0.1.2",
5050
"typescript": "^5.1.3"
51+
},
52+
"dependencies": {
53+
"@renovatebot/pep440": "^3.1.0"
5154
}
5255
}

api/src/main.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
import type { Pep440Version } from '@renovatebot/pep440';
45
import {
56
Disposable,
67
Event,
@@ -20,6 +21,7 @@ import {
2021
* This is the public API for other extensions to interact with the Python Environments extension.
2122
*/
2223

24+
export type { Pep440Version } from '@renovatebot/pep440';
2325
/**
2426
* The path to an icon, or a theme-specific configuration of icons.
2527
*/
@@ -711,6 +713,38 @@ export interface PackageManager {
711713
* @returns A promise that resolves when the cache is cleared.
712714
*/
713715
clearCache?(): Promise<void>;
716+
717+
/**
718+
* Returns the version of the underlying package management tool (e.g., pip, uv, conda).
719+
* @param environment - The Python environment context.
720+
* @returns A promise that resolves to a {@link Pep440Version} object, or `undefined` if not available.
721+
*/
722+
getVersion?(environment: PythonEnvironment): Promise<Pep440Version | undefined>;
723+
724+
/**
725+
* Retrieves the list of available versions for a given package.
726+
* @param environment - The Python environment context for the lookup.
727+
* @param packageName - The name of the package to look up.
728+
* @returns A promise that resolves to an array of {@link Pep440Version} objects (newest first),
729+
* or `undefined` if this manager does not support version listing.
730+
*/
731+
getPackageAvailableVersions?(
732+
environment: PythonEnvironment,
733+
packageName: string,
734+
): Promise<Pep440Version[] | undefined>;
735+
736+
/**
737+
* Formats a versioned install specification for this package manager.
738+
*
739+
* Different package managers use different syntax (e.g. pip uses `name==version`,
740+
* conda uses `name=version`). Implement this method to return the correct format.
741+
* When absent, callers should default to `name==version`.
742+
*
743+
* @param packageName - The name of the package.
744+
* @param version - The version string.
745+
* @returns The install specification string (e.g. `"requests==2.31.0"` or `"requests=2.31.0"`).
746+
*/
747+
formatInstallSpec?(packageName: string, version: string): string;
714748
}
715749

716750
/**

package-lock.json

Lines changed: 25 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,12 @@
282282
"category": "Python Envs",
283283
"icon": "$(trash)"
284284
},
285+
{
286+
"command": "python-envs.managePackageVersion",
287+
"title": "%python-envs.managePackageVersion.title%",
288+
"category": "Python Envs",
289+
"icon": "$(gear)"
290+
},
285291
{
286292
"command": "python-envs.copyEnvPath",
287293
"title": "%python-envs.copyEnvPath.title%",
@@ -514,6 +520,11 @@
514520
"group": "inline",
515521
"when": "view == env-managers && viewItem =~ /.*pythonBrokenEnvironment.*/ && viewItem =~ /.*copied.*/"
516522
},
523+
{
524+
"command": "python-envs.managePackageVersion",
525+
"group": "inline",
526+
"when": "view == env-managers && viewItem == python-package"
527+
},
517528
{
518529
"command": "python-envs.uninstallPackage",
519530
"group": "inline",
@@ -556,6 +567,11 @@
556567
"command": "python-envs.revealProjectInExplorer",
557568
"when": "view == python-projects && viewItem =~ /.*python-workspace.*/"
558569
},
570+
{
571+
"command": "python-envs.managePackageVersion",
572+
"group": "inline",
573+
"when": "view == python-projects && viewItem == python-package"
574+
},
559575
{
560576
"command": "python-envs.uninstallPackage",
561577
"group": "inline",

package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,6 @@
4545
"python-envs.revealProjectInExplorer.title": "Reveal Project in Explorer",
4646
"python-envs.revealEnvInManagerView.title": "Reveal in Environment Managers View",
4747
"python-envs.runPetInTerminal.title": "Run Python Environment Tool (PET) in Terminal...",
48+
"python-envs.managePackageVersion.title": "Manage Package Version",
4849
"python-envs.alwaysUseUv.description": "When set to true, uv will be used to manage all virtual environments if available. When set to false, uv will only manage virtual environments explicitly created by uv."
4950
}

src/api.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
import type { Pep440Version } from '@renovatebot/pep440';
45
import type {
56
Disposable,
67
Event,
@@ -14,6 +15,8 @@ import type {
1415
Uri,
1516
} from 'vscode';
1617

18+
export type { Pep440Version } from '@renovatebot/pep440';
19+
1720
/**
1821
* The path to an icon, or a theme-specific configuration of icons.
1922
*/
@@ -705,6 +708,37 @@ export interface PackageManager {
705708
* @returns A promise that resolves when the cache is cleared.
706709
*/
707710
clearCache?(): Promise<void>;
711+
712+
/**
713+
* Returns the version of the underlying package management tool (e.g., pip, conda).
714+
* @returns A promise that resolves to a {@link Pep440Version} object, or `undefined` if not available.
715+
*/
716+
getVersion?(environment: PythonEnvironment): Promise<Pep440Version | undefined>;
717+
718+
/**
719+
* Retrieves the list of available versions for a given package.
720+
* @param environment - The Python environment context for the lookup.
721+
* @param packageName - The name of the package to look up.
722+
* @returns A promise that resolves to an array of {@link Pep440Version} objects (newest first),
723+
* or `undefined` if this manager does not support version listing.
724+
*/
725+
getPackageAvailableVersions?(
726+
environment: PythonEnvironment,
727+
packageName: string,
728+
): Promise<Pep440Version[] | undefined>;
729+
730+
/**
731+
* Formats a versioned install specification for this package manager.
732+
*
733+
* Different package managers use different syntax (e.g. pip uses `name==version`,
734+
* conda uses `name=version`). Implement this method to return the correct format.
735+
* When absent, callers should default to `name==version`.
736+
*
737+
* @param packageName - The name of the package.
738+
* @param version - The version string.
739+
* @returns The install specification string (e.g. `"requests==2.31.0"` or `"requests=2.31.0"`).
740+
*/
741+
formatInstallSpec?(packageName: string, version: string): string;
708742
}
709743

710744
/**

src/extension.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import {
5050
createTerminalCommand,
5151
getPackageCommandOptions,
5252
handlePackageUninstall,
53+
managePackageVersion,
5354
refreshPackagesCommand,
5455
removeEnvironmentCommand,
5556
removePythonProject,
@@ -320,6 +321,9 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
320321
commands.registerCommand('python-envs.uninstallPackage', async (context: unknown) => {
321322
await handlePackageUninstall(context, envManagers);
322323
}),
324+
commands.registerCommand('python-envs.managePackageVersion', async (context: unknown) => {
325+
await managePackageVersion(context, envManagers);
326+
}),
323327
commands.registerCommand('python-envs.set', async (item) => {
324328
await setEnvironmentCommand(item, envManagers, projectManager);
325329
}),

0 commit comments

Comments
 (0)