Skip to content

Commit 509bac3

Browse files
committed
Address pr comments
1 parent 375d775 commit 509bac3

10 files changed

Lines changed: 38 additions & 31 deletions

File tree

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@
738738
"@vscode/test-cli": "^0.0.10",
739739
"dotenv": "^16.4.5",
740740
"fs-extra": "^11.2.0",
741-
"semver": "^7.7.4",
741+
"semver": "^7.8.0",
742742
"stack-trace": "0.0.10",
743743
"vscode-jsonrpc": "^9.0.0-next.5",
744744
"which": "^4.0.0"

src/api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ export interface PackageManager {
700700
* @returns A promise that resolves to an array of version strings (newest first),
701701
* or `undefined` if this manager does not support version listing.
702702
*/
703-
getAvailableVersions?(packageName: string, environment: PythonEnvironment): Promise<string[] | undefined>;
703+
getAvailableVersions?(packageName: string, environment: PythonEnvironment): Promise<semver.SemVer[] | undefined>;
704704
}
705705

706706
/**
@@ -1086,7 +1086,7 @@ export interface PythonPackageVersionApi {
10861086
* @param environment The Python Environment context for the lookup.
10871087
* @returns An array of version strings (newest first), or `undefined` if not supported.
10881088
*/
1089-
getAvailableVersions(packageName: string, environment: PythonEnvironment): Promise<string[] | undefined>;
1089+
getAvailableVersions(packageName: string, environment: PythonEnvironment): Promise<semver.SemVer[] | undefined>;
10901090
}
10911091

10921092
export interface PythonPackageManagerApi

src/features/envCommands.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,8 @@ export async function handlePackageVersionManagement(context: unknown, em: Envir
337337

338338
if (availableVersions && availableVersions.length > 0) {
339339
const items = availableVersions.map((v) => ({
340-
label: v,
341-
description: v === pkg.version ? `$(check) ${l10n.t('Installed')}` : undefined,
340+
label: v.version,
341+
description: v.version === pkg.version ? `$(check) ${l10n.t('Installed')}` : undefined,
342342
}));
343343

344344
const selected = await showQuickPick(items, {

src/features/pythonApi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ class PythonEnvironmentApiImpl implements PythonEnvironmentApi {
277277
return manager.getVersion(environment);
278278
}
279279

280-
async getAvailableVersions(packageName: string, environment: PythonEnvironment): Promise<string[] | undefined> {
280+
async getAvailableVersions(packageName: string, environment: PythonEnvironment): Promise<semver.SemVer[] | undefined> {
281281
await waitForEnvManagerId([environment.envId.managerId]);
282282
const manager = this.envManagers.getPackageManager(environment);
283283
if (!manager) {

src/internal.api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ export class InternalPackageManager implements PackageManager {
384384
return this.manager.getVersion ? this.manager.getVersion(environment) : Promise.resolve(undefined);
385385
}
386386

387-
getAvailableVersions(packageName: string, environment: PythonEnvironment): Promise<string[] | undefined> {
387+
getAvailableVersions(packageName: string, environment: PythonEnvironment): Promise<semver.SemVer[] | undefined> {
388388
return this.manager.getAvailableVersions
389389
? this.manager.getAvailableVersions(packageName, environment)
390390
: Promise.resolve(undefined);

src/managers/builtin/pipManager.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export class PipPackageManager implements PackageManager, Disposable {
156156
}
157157
}
158158

159-
async getAvailableVersions(packageName: string, environment: PythonEnvironment): Promise<string[] | undefined> {
159+
async getAvailableVersions(packageName: string, environment: PythonEnvironment): Promise<semver.SemVer[] | undefined> {
160160
try {
161161
const python = environment.execInfo?.run?.executable;
162162
if (!python) {
@@ -215,21 +215,22 @@ export class PipPackageManager implements PackageManager, Disposable {
215215
* No matching distribution found for <package>==__invalid__
216216
* ```
217217
*/
218-
export function parsePipInstallVersions(output: string): string[] | undefined {
218+
export function parsePipInstallVersions(output: string): semver.SemVer[] | undefined {
219219
const match = output.match(/from versions:\s*([^\)]+)\)/);
220220
if (match && match[1]) {
221221
return match[1]
222222
.split(',')
223-
.map((v) => v.trim())
224-
.filter((v) => v.length > 0);
223+
.filter((v) => !!v.trim())
224+
.map((v) => semver.coerce(v.trim()) as semver.SemVer)
225+
.sort((a, b) => semver.rcompare(a, b));
225226
}
226227
}
227228

228229
/**
229230
* Parses JSON output from `pip index versions <package> --json`.
230231
* Expected format: { "name": "...", "versions": ["1.2.3", "1.2.2", ...] }
231232
*/
232-
export function parsePipIndexVersionsJson(output: string): string[] | undefined {
233+
export function parsePipIndexVersionsJson(output: string): semver.SemVer[] | undefined {
233234
// Only capture output between braces
234235
const match = output.match(/{[\s\S]*}/);
235236
if (!match) {
@@ -238,7 +239,10 @@ export function parsePipIndexVersionsJson(output: string): string[] | undefined
238239
try {
239240
const parsed = JSON.parse(match[0]);
240241
if (parsed && Array.isArray(parsed.versions) && parsed.versions.length > 0) {
241-
return parsed.versions;
242+
return (parsed.versions as string[])
243+
.filter((v) => !!v.trim())
244+
.map((v) => semver.coerce(v) as semver.SemVer)
245+
.sort((a, b) => semver.rcompare(a, b));
242246
}
243247
return undefined;
244248
} catch {

src/managers/conda/condaPackageManager.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ export class CondaPackageManager implements PackageManager, Disposable {
4040

4141
private packages: Map<string, Package[]> = new Map();
4242

43-
constructor(public readonly api: PythonEnvironmentApi, public readonly log: LogOutputChannel) {
43+
constructor(
44+
public readonly api: PythonEnvironmentApi,
45+
public readonly log: LogOutputChannel,
46+
) {
4447
this.name = 'conda';
4548
this.displayName = 'Conda';
4649
this.description = CondaStrings.condaPackageMgr;
@@ -128,22 +131,21 @@ export class CondaPackageManager implements PackageManager, Disposable {
128131
const output = await runCondaExecutable(['--version'], this.log);
129132
// "conda X.Y.Z"
130133
const match = output.match(/conda\s+(\d+\.\d+(?:\.\d+)*)/i);
131-
return match ? semver.coerce(match[1]) ?? undefined : undefined;
134+
return match ? (semver.coerce(match[1]) ?? undefined) : undefined;
132135
} catch {
133136
return undefined;
134137
}
135138
}
136139

137-
async getAvailableVersions(packageName: string, _environment: PythonEnvironment): Promise<string[] | undefined> {
140+
async getAvailableVersions(packageName: string, _environment: PythonEnvironment): Promise<semver.SemVer[] | undefined> {
138141
try {
139142
const output = await runCondaExecutable(['search', packageName, '--json'], this.log);
140143
const parsed = JSON.parse(output);
141144
if (parsed && typeof parsed === 'object' && Array.isArray(parsed[packageName])) {
142-
const versions: string[] = parsed[packageName]
143-
.map((entry: { version?: string }) => entry.version)
144-
.filter((v: unknown): v is string => typeof v === 'string');
145-
// Deduplicate and return newest first
146-
return [...new Set(versions)].reverse();
145+
return parsed[packageName]
146+
.filter((entry: { version?: string }) => !!entry.version?.trim())
147+
.map((entry: { version?: string }) => semver.coerce(entry.version) as semver.SemVer)
148+
.sort((a: semver.SemVer, b: semver.SemVer) => semver.rcompare(a, b));
147149
}
148150
return undefined;
149151
} catch {

src/managers/poetry/poetryPackageManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export class PoetryPackageManager implements PackageManager, Disposable {
164164
return versionStr ? semver.coerce(versionStr) ?? undefined : undefined;
165165
}
166166

167-
async getAvailableVersions(_packageName: string, _environment: PythonEnvironment): Promise<string[] | undefined> {
167+
async getAvailableVersions(_packageName: string, _environment: PythonEnvironment): Promise<semver.SemVer[] | undefined> {
168168
// Poetry doesn't have a native "list available versions" command.
169169
// Poetry 2.x supports `poetry search` but it was disabled on PyPI.
170170
// Return undefined to indicate this manager doesn't support version listing.

src/test/managers/builtin/pipVersions.unit.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import assert from 'assert';
2+
import * as semver from 'semver';
23
import { parsePipIndexVersionsJson } from '../../../managers/builtin/pipManager';
34

45
suite('Pip Version Parsing', () => {
56
suite('parsePipIndexVersionsJson', () => {
67
test('parses valid JSON with versions array', () => {
78
const output = JSON.stringify({ name: 'requests', versions: ['2.31.0', '2.30.0', '2.29.0'] });
89
const versions = parsePipIndexVersionsJson(output);
9-
assert.deepStrictEqual(versions, ['2.31.0', '2.30.0', '2.29.0']);
10+
assert.deepStrictEqual(versions, ['2.31.0', '2.30.0', '2.29.0'].map((v) => semver.coerce(v)));
1011
});
1112

1213
test('parses output with a single version', () => {
1314
const output = JSON.stringify({ name: 'my-package', versions: ['1.0.0'] });
1415
const versions = parsePipIndexVersionsJson(output);
15-
assert.deepStrictEqual(versions, ['1.0.0']);
16+
assert.deepStrictEqual(versions, [semver.coerce('1.0.0')]);
1617
});
1718

1819
test('returns undefined for empty versions array', () => {

0 commit comments

Comments
 (0)