Skip to content

Commit ee8e80e

Browse files
author
Kartik Raj
authored
Improve getting started experience when starting on a fresh macOS (#20789)
Closes #20635 - Suggest to install from `python.org` if brew is not available - Do not suggest irrelevant prompts
1 parent 16c0437 commit ee8e80e

File tree

5 files changed

+57
-22
lines changed

5 files changed

+57
-22
lines changed

src/client/common/utils/localize.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,13 @@ export namespace Interpreters {
204204
export const selectInterpreterTip = l10n.t(
205205
'Tip: you can change the Python interpreter used by the Python extension by clicking on the Python version in the status bar',
206206
);
207-
export const installPythonTerminalMessage = l10n.t(
207+
export const installPythonTerminalMessageLinux = l10n.t(
208208
'💡 Please try installing the python package using your package manager. Alternatively you can also download it from https://www.python.org/downloads',
209209
);
210+
211+
export const installPythonTerminalMacMessage = l10n.t(
212+
'💡 Brew does not seem to be available. Please try to download Python from https://www.python.org/downloads. Alternatively, you can install the python package using some other package manager which is available.',
213+
);
210214
export const changePythonInterpreter = l10n.t('Change Python Interpreter');
211215
export const selectedPythonInterpreter = l10n.t('Selected Python Interpreter');
212216
}

src/client/interpreter/configuration/interpreterSelector/commands/installPython/installPythonViaTerminal.ts

+25-16
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,13 @@ export class InstallPythonViaTerminal implements IExtensionSingleActivationServi
5555

5656
public async _installPythonOnUnix(os: OSType.Linux | OSType.OSX): Promise<void> {
5757
const commands = await this.getCommands(os);
58+
const installMessage =
59+
os === OSType.OSX
60+
? Interpreters.installPythonTerminalMacMessage
61+
: Interpreters.installPythonTerminalMessageLinux;
5862
const terminal = this.terminalManager.createTerminal({
5963
name: 'Python',
60-
message: commands.length ? undefined : Interpreters.installPythonTerminalMessage,
64+
message: commands.length ? undefined : installMessage,
6165
});
6266
terminal.show(true);
6367
await waitForTerminalToStartup();
@@ -69,31 +73,36 @@ export class InstallPythonViaTerminal implements IExtensionSingleActivationServi
6973

7074
private async getCommands(os: OSType.Linux | OSType.OSX) {
7175
if (os === OSType.OSX) {
72-
return this.packageManagerCommands[PackageManagers.brew];
76+
return this.getCommandsForPackageManagers([PackageManagers.brew]);
7377
}
74-
return this.getCommandsForLinux();
78+
if (os === OSType.Linux) {
79+
return this.getCommandsForPackageManagers([PackageManagers.apt, PackageManagers.dnf]);
80+
}
81+
throw new Error('OS not supported');
7582
}
7683

77-
private async getCommandsForLinux() {
78-
for (const packageManager of [PackageManagers.apt, PackageManagers.dnf]) {
79-
let isPackageAvailable = false;
80-
try {
81-
const which = require('which') as typeof whichTypes;
82-
const resolvedPath = await which(packageManager);
83-
traceVerbose(`Resolved path to ${packageManager} module:`, resolvedPath);
84-
isPackageAvailable = resolvedPath.trim().length > 0;
85-
} catch (ex) {
86-
traceVerbose(`${packageManager} not found`, ex);
87-
isPackageAvailable = false;
88-
}
89-
if (isPackageAvailable) {
84+
private async getCommandsForPackageManagers(packageManagers: PackageManagers[]) {
85+
for (const packageManager of packageManagers) {
86+
if (await isPackageAvailable(packageManager)) {
9087
return this.packageManagerCommands[packageManager];
9188
}
9289
}
9390
return [];
9491
}
9592
}
9693

94+
async function isPackageAvailable(packageManager: PackageManagers) {
95+
try {
96+
const which = require('which') as typeof whichTypes;
97+
const resolvedPath = await which(packageManager);
98+
traceVerbose(`Resolved path to ${packageManager} module:`, resolvedPath);
99+
return resolvedPath.trim().length > 0;
100+
} catch (ex) {
101+
traceVerbose(`${packageManager} not found`, ex);
102+
return false;
103+
}
104+
}
105+
97106
async function waitForTerminalToStartup() {
98107
// Sometimes the terminal takes some time to start up before it can start accepting input.
99108
await sleep(100);

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export function isMacDefaultPythonPath(pythonPath: string): boolean {
1313
return false;
1414
}
1515

16-
const defaultPaths = ['python', '/usr/bin/python'];
16+
const defaultPaths = ['/usr/bin/python'];
1717

1818
return defaultPaths.includes(pythonPath) || pythonPath.startsWith('/usr/bin/python2');
1919
}

src/test/configuration/interpreterSelector/commands/installPythonViaTerminal.unit.test.ts

+26-2
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,22 @@ suite('Install Python via Terminal', () => {
9696
await installPythonCommand.activate();
9797
await installCommandHandler!();
9898

99-
expect(message).to.be.equal(Interpreters.installPythonTerminalMessage);
99+
expect(message).to.be.equal(Interpreters.installPythonTerminalMessageLinux);
100100
});
101101

102-
test('Sends expected commands on Mac when InstallPythonOnMac command is executed if no dnf is available', async () => {
102+
test('Sends expected commands on Mac when InstallPythonOnMac command is executed if brew is available', async () => {
103103
let installCommandHandler: () => Promise<void>;
104104
when(cmdManager.registerCommand(Commands.InstallPythonOnMac, anything())).thenCall((_, cb) => {
105105
installCommandHandler = cb;
106106
return TypeMoq.Mock.ofType<IDisposable>().object;
107107
});
108+
rewiremock('which').with((cmd: string) => {
109+
if (cmd === 'brew') {
110+
return 'path/to/brew';
111+
}
112+
throw new Error('Command not found');
113+
});
114+
108115
await installPythonCommand.activate();
109116
when(terminalService.sendText('brew install python3')).thenResolve();
110117

@@ -113,4 +120,21 @@ suite('Install Python via Terminal', () => {
113120
verify(terminalService.sendText('brew install python3')).once();
114121
expect(message).to.be.equal(undefined);
115122
});
123+
124+
test('Creates terminal with appropriate message when InstallPythonOnMac command is executed if brew is not available', async () => {
125+
let installCommandHandler: () => Promise<void>;
126+
when(cmdManager.registerCommand(Commands.InstallPythonOnMac, anything())).thenCall((_, cb) => {
127+
installCommandHandler = cb;
128+
return TypeMoq.Mock.ofType<IDisposable>().object;
129+
});
130+
rewiremock('which').with((_cmd: string) => {
131+
throw new Error('Command not found');
132+
});
133+
134+
await installPythonCommand.activate();
135+
136+
await installCommandHandler!();
137+
138+
expect(message).to.be.equal(Interpreters.installPythonTerminalMacMessage);
139+
});
116140
});

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

-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ suite('isMacDefaultPythonPath', () => {
1818
});
1919

2020
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 },
2321
{ path: '/usr/bin/python', os: osUtils.OSType.OSX, expected: true },
2422
{ path: '/usr/bin/python', os: osUtils.OSType.Linux, expected: false },
2523
{ path: '/usr/bin/python2', os: osUtils.OSType.OSX, expected: true },

0 commit comments

Comments
 (0)