diff --git a/extensions/positron-python/src/client/common/configSettings.ts b/extensions/positron-python/src/client/common/configSettings.ts index 6f43bab6a1e..80dd9e61aa5 100644 --- a/extensions/positron-python/src/client/common/configSettings.ts +++ b/extensions/positron-python/src/client/common/configSettings.ts @@ -2,6 +2,7 @@ // eslint-disable-next-line camelcase import * as path from 'path'; +import * as fs from 'fs'; import { ConfigurationChangeEvent, ConfigurationTarget, @@ -35,6 +36,8 @@ import { } from './types'; import { debounceSync } from './utils/decorators'; import { SystemVariables } from './variables/systemVariables'; +import { getOSType, OSType } from './utils/platform'; +import { isWindows } from './platform/platformService'; const untildify = require('untildify'); @@ -412,7 +415,7 @@ export class PythonSettings implements IPythonSettings { // eslint-disable-next-line class-methods-use-this protected getPythonExecutable(pythonPath: string): string { - return untildify(pythonPath); + return getPythonExecutable(pythonPath); } protected onWorkspaceFoldersChanged(): void { @@ -511,3 +514,63 @@ function getAbsolutePath(pathToCheck: string, rootDir: string | undefined): stri } return path.isAbsolute(pathToCheck) ? pathToCheck : path.resolve(rootDir, pathToCheck); } + +function getPythonExecutable(pythonPath: string): string { + pythonPath = untildify(pythonPath) as string; + + // If only 'python'. + if ( + pythonPath === 'python' || + pythonPath.indexOf(path.sep) === -1 || + path.basename(pythonPath) === path.dirname(pythonPath) + ) { + return pythonPath; + } + + if (isValidPythonPath(pythonPath)) { + return pythonPath; + } + // Keep python right on top, for backwards compatibility. + + const KnownPythonExecutables = [ + 'python', + 'python4', + 'python3.6', + 'python3.5', + 'python3', + 'python2.7', + 'python2', + 'python3.7', + 'python3.8', + 'python3.9', + ]; + + for (let executableName of KnownPythonExecutables) { + // Suffix with 'python' for linux and 'osx', and 'python.exe' for 'windows'. + if (isWindows()) { + executableName = `${executableName}.exe`; + if (isValidPythonPath(path.join(pythonPath, executableName))) { + return path.join(pythonPath, executableName); + } + if (isValidPythonPath(path.join(pythonPath, 'Scripts', executableName))) { + return path.join(pythonPath, 'Scripts', executableName); + } + } else { + if (isValidPythonPath(path.join(pythonPath, executableName))) { + return path.join(pythonPath, executableName); + } + if (isValidPythonPath(path.join(pythonPath, 'bin', executableName))) { + return path.join(pythonPath, 'bin', executableName); + } + } + } + + return pythonPath; +} + +function isValidPythonPath(pythonPath: string): boolean { + return ( + fs.existsSync(pythonPath) && + path.basename(getOSType() === OSType.Windows ? pythonPath.toLowerCase() : pythonPath).startsWith('python') + ); +} diff --git a/extensions/positron-python/src/client/common/utils/localize.ts b/extensions/positron-python/src/client/common/utils/localize.ts index ae4d7efa38a..f0957361410 100644 --- a/extensions/positron-python/src/client/common/utils/localize.ts +++ b/extensions/positron-python/src/client/common/utils/localize.ts @@ -43,9 +43,12 @@ export namespace Diagnostics { export const pylanceDefaultMessage = l10n.t( "The Python extension now includes Pylance to improve completions, code navigation, overall performance and much more! You can learn more about the update and learn how to change your language server [here](https://aka.ms/new-python-bundle).\n\nRead Pylance's license [here](https://marketplace.visualstudio.com/items/ms-python.vscode-pylance/license).", ); - export const invalidSmartSendMessage = l10n.t(`Python is unable to parse the code provided. Please + export const invalidSmartSendMessage = l10n.t( + `Python is unable to parse the code provided. Please turn off Smart Send if you wish to always run line by line or explicitly select code - to force run. See [logs](command:${Commands.ViewOutput}) for more details`); + to force run. See [logs](command:{0}) for more details`, + Commands.ViewOutput, + ); } export namespace Common { diff --git a/extensions/positron-python/src/client/logging/settingLogs.ts b/extensions/positron-python/src/client/logging/settingLogs.ts index 257e204e151..1243e544cae 100644 --- a/extensions/positron-python/src/client/logging/settingLogs.ts +++ b/extensions/positron-python/src/client/logging/settingLogs.ts @@ -99,7 +99,8 @@ async function notifyLegacySettings(): Promise { _isShown = true; const response = await showWarningMessage( l10n.t( - `You have deprecated linting or formatting settings for Python. Please see the [logs](command:${Commands.ViewOutput}) for more details.`, + 'You have deprecated linting or formatting settings for Python. Please see the [logs](command:{0}) for more details.', + Commands.ViewOutput, ), Common.learnMore, ); diff --git a/extensions/positron-python/src/client/pythonEnvironments/creation/common/installCheckUtils.ts b/extensions/positron-python/src/client/pythonEnvironments/creation/common/installCheckUtils.ts index 5d51b6186b1..caace793cc7 100644 --- a/extensions/positron-python/src/client/pythonEnvironments/creation/common/installCheckUtils.ts +++ b/extensions/positron-python/src/client/pythonEnvironments/creation/common/installCheckUtils.ts @@ -27,7 +27,7 @@ function parseDiagnostics(data: string): Diagnostic[] { diagnostics = raw.map((item) => { const d = new Diagnostic( new Range(item.line, item.character, item.endLine, item.endCharacter), - l10n.t(`Package \`${item.package}\` is not installed in the selected environment.`), + l10n.t('Package `{0}` is not installed in the selected environment.', item.package), item.severity, ); d.code = { value: item.code, target: Uri.parse(`https://pypi.org/p/${item.package}`) }; diff --git a/extensions/positron-python/src/test/terminals/codeExecution/smartSend.test.ts b/extensions/positron-python/src/test/terminals/codeExecution/smartSend.test.ts index 01f490e2b25..4963629d0fd 100644 --- a/extensions/positron-python/src/test/terminals/codeExecution/smartSend.test.ts +++ b/extensions/positron-python/src/test/terminals/codeExecution/smartSend.test.ts @@ -300,9 +300,8 @@ suite('REPL - Smart Send', () => { .setup((a) => a.showWarningMessage( l10n.t( - `Python is unable to parse the code provided. Please - turn off Smart Send if you wish to always run line by line or explicitly select code - to force run. [logs](command:${Commands.ViewOutput}) for more details.`, + 'Python is unable to parse the code provided. Please turn off Smart Send if you wish to always run line by line or explicitly select code to force run. [logs](command:{0}) for more details.', + Commands.ViewOutput, ), 'Switch to line-by-line', ),