Skip to content

Commit 4a0982d

Browse files
authored
Do not process system Python 2 on macOS Monterey (#17909)
* Do not process system Python 2 on macOS Monterey. * Missing word * Update isMacDefaultPythonPath * Remove code in posix locator * Add isMacDefaultPythonPath tests
1 parent 970bed9 commit 4a0982d

File tree

5 files changed

+109
-3
lines changed

5 files changed

+109
-3
lines changed

news/2 Fixes/17870.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Do not process system Python 2 installs on macOS Monterey.

src/client/pythonEnvironments/base/locators/lowLevel/macDefaultLocator.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,8 @@ export function isMacDefaultPythonPath(pythonPath: string): boolean {
1212
if (getOSType() !== OSType.OSX) {
1313
return false;
1414
}
15-
return pythonPath === 'python' || pythonPath === '/usr/bin/python';
15+
16+
const defaultPaths = ['python', '/usr/bin/python'];
17+
18+
return defaultPaths.includes(pythonPath) || pythonPath.startsWith('/usr/bin/python2');
1619
}

src/client/pythonEnvironments/base/locators/lowLevel/posixKnownPathsLocator.ts

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

4+
import * as os from 'os';
5+
import { gte } from 'semver';
46
import { traceError } from '../../../../common/logger';
57
import { PythonEnvKind, PythonEnvSource } from '../../info';
68
import { BasicEnvInfo, IPythonEnvsIterator, Locator } from '../../locator';
79
import { commonPosixBinPaths, getPythonBinFromPosixPaths } from '../../../common/posixUtils';
810
import { isPyenvShimDir } from '../../../common/environmentManagers/pyenv';
11+
import { getOSType, OSType } from '../../../../common/utils/platform';
12+
import { isMacDefaultPythonPath } from './macDefaultLocator';
913

1014
export class PosixKnownPathsLocator extends Locator<BasicEnvInfo> {
1115
private kind: PythonEnvKind = PythonEnvKind.OtherGlobal;
1216

1317
public iterEnvs(): IPythonEnvsIterator<BasicEnvInfo> {
18+
// Flag to remove system installs of Python 2 from the list of discovered interpreters
19+
// If on macOS Monterey or later.
20+
// See https://github.com/microsoft/vscode-python/issues/17870.
21+
let isMacPython2Deprecated = false;
22+
if (getOSType() === OSType.OSX && gte(os.release(), '21.0.0')) {
23+
isMacPython2Deprecated = true;
24+
}
25+
1426
const iterator = async function* (kind: PythonEnvKind) {
1527
// Filter out pyenv shims. They are not actual python binaries, they are used to launch
1628
// the binaries specified in .python-version file in the cwd. We should not be reporting
1729
// those binaries as environments.
1830
const knownDirs = (await commonPosixBinPaths()).filter((dirname) => !isPyenvShimDir(dirname));
19-
const pythonBinaries = await getPythonBinFromPosixPaths(knownDirs);
31+
let pythonBinaries = await getPythonBinFromPosixPaths(knownDirs);
32+
33+
// Filter out MacOS system installs of Python 2 if necessary.
34+
if (isMacPython2Deprecated) {
35+
pythonBinaries = pythonBinaries.filter((binary) => !isMacDefaultPythonPath(binary));
36+
}
37+
2038
for (const bin of pythonBinaries) {
2139
try {
2240
yield { executablePath: bin, kind, source: [PythonEnvSource.PathEnvVar] };
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import * as assert from 'assert';
5+
import * as sinon from 'sinon';
6+
import * as osUtils from '../../../../../client/common/utils/platform';
7+
import { isMacDefaultPythonPath } from '../../../../../client/pythonEnvironments/base/locators/lowLevel/macDefaultLocator';
8+
9+
suite('isMacDefaultPythonPath', () => {
10+
let getOSTypeStub: sinon.SinonStub;
11+
12+
setup(() => {
13+
getOSTypeStub = sinon.stub(osUtils, 'getOSType');
14+
});
15+
16+
teardown(() => {
17+
sinon.restore();
18+
});
19+
20+
const testCases: { path: string; os: osUtils.OSType; expected: boolean }[] = [
21+
{ path: 'python', os: osUtils.OSType.OSX, expected: true },
22+
{ path: 'python', os: osUtils.OSType.Windows, expected: false },
23+
{ path: '/usr/bin/python', os: osUtils.OSType.OSX, expected: true },
24+
{ path: '/usr/bin/python', os: osUtils.OSType.Linux, expected: false },
25+
{ path: '/usr/bin/python2', os: osUtils.OSType.OSX, expected: true },
26+
{ path: '/usr/local/bin/python2', os: osUtils.OSType.OSX, expected: false },
27+
{ path: '/usr/bin/python3', os: osUtils.OSType.OSX, expected: false },
28+
{ path: '/usr/bin/python3', os: osUtils.OSType.Linux, expected: false },
29+
];
30+
31+
testCases.forEach(({ path, os, expected }) => {
32+
const testName = `If the Python path is ${path} on ${os}, it is${
33+
expected ? '' : ' not'
34+
} a macOS default Python path`;
35+
36+
test(testName, () => {
37+
getOSTypeStub.returns(os);
38+
39+
const result = isMacDefaultPythonPath(path);
40+
41+
assert.strictEqual(result, expected);
42+
});
43+
});
44+
});

src/test/pythonEnvironments/base/locators/lowLevel/posixKnownPathsLocator.unit.test.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
import * as assert from 'assert';
45
import * as path from 'path';
56
import * as sinon from 'sinon';
7+
import * as semver from 'semver';
68
import * as executablesAPI from '../../../../../client/common/utils/exec';
9+
import * as osUtils from '../../../../../client/common/utils/platform';
710
import { PythonEnvKind, PythonEnvSource } from '../../../../../client/pythonEnvironments/base/info';
811
import { BasicEnvInfo } from '../../../../../client/pythonEnvironments/base/locator';
912
import { getEnvs } from '../../../../../client/pythonEnvironments/base/locatorUtils';
1013
import { PosixKnownPathsLocator } from '../../../../../client/pythonEnvironments/base/locators/lowLevel/posixKnownPathsLocator';
1114
import { createBasicEnv } from '../../common';
1215
import { TEST_LAYOUT_ROOT } from '../../../common/commonTestConstants';
1316
import { assertBasicEnvsEqual } from '../envTestUtils';
17+
import { isMacDefaultPythonPath } from '../../../../../client/pythonEnvironments/base/locators/lowLevel/macDefaultLocator';
1418

1519
suite('Posix Known Path Locator', () => {
1620
let getPathEnvVar: sinon.SinonStub;
@@ -33,7 +37,7 @@ suite('Posix Known Path Locator', () => {
3337
locator = new PosixKnownPathsLocator();
3438
});
3539
teardown(() => {
36-
getPathEnvVar.restore();
40+
sinon.restore();
3741
});
3842

3943
test('iterEnvs(): get python bin from known test roots', async () => {
@@ -56,4 +60,40 @@ suite('Posix Known Path Locator', () => {
5660
const actualEnvs = (await getEnvs(locator.iterEnvs())).filter((e) => e.executablePath.indexOf('posixroot') > 0);
5761
assertBasicEnvsEqual(actualEnvs, expectedEnvs);
5862
});
63+
64+
test('iterEnvs(): Do not return Python 2 installs when on macOS Monterey', async function () {
65+
if (osUtils.getOSType() !== osUtils.OSType.OSX) {
66+
this.skip();
67+
}
68+
69+
const getOSTypeStub = sinon.stub(osUtils, 'getOSType');
70+
const gteStub = sinon.stub(semver, 'gte');
71+
72+
getOSTypeStub.returns(osUtils.OSType.OSX);
73+
gteStub.returns(true);
74+
75+
const actualEnvs = await getEnvs(locator.iterEnvs());
76+
77+
const globalPython2Envs = actualEnvs.filter((env) => isMacDefaultPythonPath(env.executablePath));
78+
79+
assert.strictEqual(globalPython2Envs.length, 0);
80+
});
81+
82+
test('iterEnvs(): Return Python 2 installs when not on macOS Monterey', async function () {
83+
if (osUtils.getOSType() !== osUtils.OSType.OSX) {
84+
this.skip();
85+
}
86+
87+
const getOSTypeStub = sinon.stub(osUtils, 'getOSType');
88+
const gteStub = sinon.stub(semver, 'gte');
89+
90+
getOSTypeStub.returns(osUtils.OSType.OSX);
91+
gteStub.returns(false);
92+
93+
const actualEnvs = await getEnvs(locator.iterEnvs());
94+
95+
const globalPython2Envs = actualEnvs.filter((env) => isMacDefaultPythonPath(env.executablePath));
96+
97+
assert.notStrictEqual(globalPython2Envs.length, 0);
98+
});
5999
});

0 commit comments

Comments
 (0)