Skip to content

Commit 86cbc12

Browse files
committed
Use a more forgiving comparer when comparing version (#15912)
* Use a more forgiving comparer when comparing version * Update tests * Move sort interpreters function
1 parent 1c3b683 commit 86cbc12

File tree

7 files changed

+108
-23
lines changed

7 files changed

+108
-23
lines changed

src/client/interpreter/autoSelection/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
'use strict';
55

66
import { inject, injectable, named } from 'inversify';
7-
import { compare } from 'semver';
87
import { Event, EventEmitter, Uri } from 'vscode';
98
import { IWorkspaceService } from '../../common/application/types';
109
import '../../common/extensions';
1110
import { IFileSystem } from '../../common/platform/types';
1211
import { IPersistentState, IPersistentStateFactory, Resource } from '../../common/types';
1312
import { createDeferred, Deferred } from '../../common/utils/async';
13+
import { compareSemVerLikeVersions } from '../../pythonEnvironments/base/info/pythonVersion';
1414
import { PythonEnvironment } from '../../pythonEnvironments/info';
1515
import { captureTelemetry, sendTelemetryEvent } from '../../telemetry';
1616
import { EventName } from '../../telemetry/constants';
@@ -172,7 +172,7 @@ export class InterpreterAutoSelectionService implements IInterpreterAutoSelectio
172172
this.globallyPreferredInterpreter.value.version &&
173173
interpreter &&
174174
interpreter.version &&
175-
compare(this.globallyPreferredInterpreter.value.version.raw, interpreter.version.raw) > 0
175+
compareSemVerLikeVersions(this.globallyPreferredInterpreter.value.version, interpreter.version) > 0
176176
) {
177177
return;
178178
}

src/client/interpreter/autoSelection/rules/baseRule.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
'use strict';
55

66
import { inject, injectable, unmanaged } from 'inversify';
7-
import { compare } from 'semver';
87
import '../../../common/extensions';
98
import { traceDecorators, traceVerbose } from '../../../common/logger';
109
import { IFileSystem } from '../../../common/platform/types';
1110
import { IPersistentState, IPersistentStateFactory, Resource } from '../../../common/types';
1211
import { StopWatch } from '../../../common/utils/stopWatch';
12+
import { compareSemVerLikeVersions } from '../../../pythonEnvironments/base/info/pythonVersion';
1313
import { PythonEnvironment } from '../../../pythonEnvironments/info';
1414
import { sendTelemetryEvent } from '../../../telemetry';
1515
import { EventName } from '../../../telemetry/constants';
@@ -74,7 +74,7 @@ export abstract class BaseRuleService implements IInterpreterAutoSelectionRule {
7474
const preferredInterpreter = manager.getAutoSelectedInterpreter(undefined);
7575
const comparison =
7676
preferredInterpreter && preferredInterpreter.version
77-
? compare(interpreter.version.raw, preferredInterpreter.version.raw)
77+
? compareSemVerLikeVersions(interpreter.version, preferredInterpreter.version)
7878
: 1;
7979
if (comparison > 0) {
8080
await manager.setGlobalInterpreter(interpreter);

src/client/interpreter/helpers.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import { FileSystemPaths } from '../common/platform/fs-paths';
77
import { IPythonExecutionFactory } from '../common/process/types';
88
import { IExperimentService, IPersistentStateFactory, Resource } from '../common/types';
99
import { IServiceContainer } from '../ioc/types';
10+
import { compareSemVerLikeVersions } from '../pythonEnvironments/base/info/pythonVersion';
1011
import { isMacDefaultPythonPath } from '../pythonEnvironments/discovery';
1112
import { getInterpreterHash } from '../pythonEnvironments/discovery/locators/services/hashProvider';
1213
import {
1314
EnvironmentType,
1415
getEnvironmentTypeName,
1516
InterpreterInformation,
1617
PythonEnvironment,
17-
sortInterpreters,
1818
} from '../pythonEnvironments/info';
1919
import { IComponentAdapter, IInterpreterHelper, WorkspacePythonPath } from './contracts';
2020

@@ -28,6 +28,21 @@ export function isInterpreterLocatedInWorkspace(interpreter: PythonEnvironment,
2828
return interpreterPath.startsWith(resourcePath);
2929
}
3030

31+
/**
32+
* Build a version-sorted list from the given one, with lowest first.
33+
*/
34+
function sortInterpreters(interpreters: PythonEnvironment[]): PythonEnvironment[] {
35+
if (interpreters.length === 0) {
36+
return [];
37+
}
38+
if (interpreters.length === 1) {
39+
return [interpreters[0]];
40+
}
41+
const sorted = interpreters.slice();
42+
sorted.sort((a, b) => (a.version && b.version ? compareSemVerLikeVersions(a.version, b.version) : 0));
43+
return sorted;
44+
}
45+
3146
@injectable()
3247
export class InterpreterHelper implements IInterpreterHelper {
3348
private readonly persistentFactory: IPersistentStateFactory;

src/client/pythonEnvironments/base/info/pythonVersion.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,3 +262,29 @@ export function toSemverLikeVersion(
262262
prerelease: preRelease,
263263
};
264264
}
265+
266+
/**
267+
* Compares major, minor, patch for two versions of python
268+
* @param v1 : semVer like version object
269+
* @param v2 : semVer like version object
270+
* @returns {1 | 0 | -1} : 0 if v1 === v2,
271+
* 1 if v1 > v2,
272+
* -1 if v1 < v2
273+
* Remarks: primarily used compare to old type of version info.
274+
* @deprecated
275+
*/
276+
export function compareSemVerLikeVersions(
277+
v1: { major: number; minor: number; patch: number },
278+
v2: { major: number; minor: number; patch: number },
279+
): 1 | 0 | -1 {
280+
if (v1.major === v2.major) {
281+
if (v1.minor === v2.minor) {
282+
if (v1.patch === v2.patch) {
283+
return 0;
284+
}
285+
return v1.patch > v2.patch ? 1 : -1;
286+
}
287+
return v1.minor > v2.minor ? 1 : -1;
288+
}
289+
return v1.major > v2.major ? 1 : -1;
290+
}

src/client/pythonEnvironments/discovery/locators/services/condaLocatorService.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
import { inject, injectable } from 'inversify';
55
import * as path from 'path';
6-
import { compare } from 'semver';
76
import { ConfigurationChangeEvent, Uri } from 'vscode';
87

98
import { IWorkspaceService } from '../../../../common/application/types';
@@ -25,6 +24,7 @@ import {
2524
WINDOWS_REGISTRY_SERVICE,
2625
} from '../../../../interpreter/contracts';
2726
import { IServiceContainer } from '../../../../ioc/types';
27+
import { compareSemVerLikeVersions } from '../../../base/info/pythonVersion';
2828
import { EnvironmentType, PythonEnvironment } from '../../../info';
2929
import { CondaEnvironmentInfo, CondaInfo } from './conda';
3030
import { parseCondaEnvFileContents } from './condaHelper';
@@ -91,7 +91,9 @@ export class CondaLocatorService implements ICondaLocatorService {
9191
private static getLatestVersion(interpreters: PythonEnvironment[]): PythonEnvironment | undefined {
9292
const sortedInterpreters = interpreters.slice();
9393

94-
sortedInterpreters.sort((a, b) => (a.version && b.version ? compare(a.version.raw, b.version.raw) : 0));
94+
sortedInterpreters.sort((a, b) =>
95+
a.version && b.version ? compareSemVerLikeVersions(a.version, b.version) : 0,
96+
);
9597
if (sortedInterpreters.length > 0) {
9698
return sortedInterpreters[sortedInterpreters.length - 1];
9799
}

src/client/pythonEnvironments/info/index.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
'use strict';
55

6-
import * as semver from 'semver';
76
import { Architecture } from '../../common/utils/platform';
87
import { PythonVersion } from './pythonVersion';
98

@@ -98,18 +97,3 @@ export function getEnvironmentTypeName(environmentType: EnvironmentType): string
9897
}
9998
}
10099
}
101-
102-
/**
103-
* Build a version-sorted list from the given one, with lowest first.
104-
*/
105-
export function sortInterpreters(interpreters: PythonEnvironment[]): PythonEnvironment[] {
106-
if (interpreters.length === 0) {
107-
return [];
108-
}
109-
if (interpreters.length === 1) {
110-
return [interpreters[0]];
111-
}
112-
const sorted = interpreters.slice();
113-
sorted.sort((a, b) => (a.version && b.version ? semver.compare(a.version.raw, b.version.raw) : 0));
114-
return sorted;
115-
}

src/test/pythonEnvironments/base/info/pythonVersion.unit.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as assert from 'assert';
55

66
import { PythonReleaseLevel, PythonVersion } from '../../../../client/pythonEnvironments/base/info';
77
import {
8+
compareSemVerLikeVersions,
89
getEmptyVersion,
910
getShortVersionString,
1011
parseVersion,
@@ -171,3 +172,60 @@ suite('pyenvs info - parseVersion', () => {
171172
});
172173
});
173174
});
175+
176+
suite('pyenvs info - compareSemVerLikeVersions', () => {
177+
const testData = [
178+
{
179+
v1: { major: 2, minor: 7, patch: 19 },
180+
v2: { major: 3, minor: 7, patch: 4 },
181+
expected: -1,
182+
},
183+
{
184+
v1: { major: 2, minor: 7, patch: 19 },
185+
v2: { major: 2, minor: 7, patch: 19 },
186+
expected: 0,
187+
},
188+
{
189+
v1: { major: 3, minor: 7, patch: 4 },
190+
v2: { major: 2, minor: 7, patch: 19 },
191+
expected: 1,
192+
},
193+
{
194+
v1: { major: 3, minor: 8, patch: 1 },
195+
v2: { major: 3, minor: 9, patch: 1 },
196+
expected: -1,
197+
},
198+
{
199+
v1: { major: 3, minor: 9, patch: 1 },
200+
v2: { major: 3, minor: 9, patch: 1 },
201+
expected: 0,
202+
},
203+
{
204+
v1: { major: 3, minor: 9, patch: 1 },
205+
v2: { major: 3, minor: 8, patch: 1 },
206+
expected: 1,
207+
},
208+
{
209+
v1: { major: 3, minor: 9, patch: 0 },
210+
v2: { major: 3, minor: 9, patch: 1 },
211+
expected: -1,
212+
},
213+
{
214+
v1: { major: 3, minor: 9, patch: 1 },
215+
v2: { major: 3, minor: 9, patch: 1 },
216+
expected: 0,
217+
},
218+
{
219+
v1: { major: 3, minor: 9, patch: 1 },
220+
v2: { major: 3, minor: 9, patch: 0 },
221+
expected: 1,
222+
},
223+
];
224+
225+
testData.forEach((data) => {
226+
test(`Compare versions ${JSON.stringify(data.v1)} and ${JSON.stringify(data.v2)}`, () => {
227+
const actual = compareSemVerLikeVersions(data.v1, data.v2);
228+
assert.deepStrictEqual(actual, data.expected);
229+
});
230+
});
231+
});

0 commit comments

Comments
 (0)