diff --git a/src/constants.ts b/src/constants.ts index 488a23b5c..3654347d9 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -111,10 +111,6 @@ export const CONSTANTS = { "error.noProgramFoundDebug", "Cannot find a program to debug." ), - NO_PIP: localize( - "error.noPip", - "We found that you don't have Pip installed on your computer, please install it and try again." - ), NO_PYTHON_PATH: localize( "error.noPythonPath", "We found that you don't have Python 3 installed on your computer, please install the latest version, add it to your PATH and try again." @@ -532,7 +528,7 @@ export const STATUS_BAR_PRIORITY = { }; export const VERSIONS = { - MIN_PY_VERSION: "3.7.0", + MIN_PY_VERSION: "Python 3.7.0", }; export const HELPER_FILES = { @@ -546,6 +542,7 @@ export const HELPER_FILES = { export const GLOBAL_ENV_VARS = { PYTHON: "python", + PYTHON3: "python3", }; export const LANGUAGE_VARS = { PYTHON: { ID: "python", FILE_ENDS: ".py" }, diff --git a/src/install_dependencies.py b/src/install_dependencies.py index afadb9dd5..00fe81d37 100644 --- a/src/install_dependencies.py +++ b/src/install_dependencies.py @@ -3,7 +3,7 @@ import pathlib import os -os.chdir(pathlib.Path(__file__).parent.parent.absolute()) +os.chdir(str(pathlib.Path(__file__).parent.parent.absolute())) subprocess.check_call( [sys.executable, "-m", "pip", "install", "-r", "./out/requirements.txt"] ) diff --git a/src/service/setupService.ts b/src/service/setupService.ts index c68f78fbd..04c757a72 100644 --- a/src/service/setupService.ts +++ b/src/service/setupService.ts @@ -32,8 +32,11 @@ export class SetupService { context: vscode.ExtensionContext, needsResponse: boolean = false ) => { - const originalpythonExecutablePath = await this.getCurrentPythonExecutablePath(); - let pythonExecutablePath = originalpythonExecutablePath; + const originalPythonExecutablePath = await this.getCurrentPythonExecutablePath(); + if (originalPythonExecutablePath === "") { + return; + } + let pythonExecutablePath = originalPythonExecutablePath; const pythonExecutableName: string = os.platform() === "win32" ? HELPER_FILES.PYTHON_EXE @@ -71,7 +74,7 @@ export class SetupService { } else { pythonExecutablePath = await this.promptInstallVenv( context, - originalpythonExecutablePath, + originalPythonExecutablePath, pythonExecutableName ); this.telemetryAI.trackFeatureUsage( @@ -92,7 +95,7 @@ export class SetupService { TelemetryEventName.SETUP_HAS_VENV ); } - if (pythonExecutablePath === originalpythonExecutablePath) { + if (pythonExecutablePath === originalPythonExecutablePath) { // going with original interpreter, either because // already in venv or error in creating custom venv if (checkConfig(CONFIG.SHOW_DEPENDENCY_INSTALL)) { @@ -159,40 +162,151 @@ export class SetupService { return pythonExecutablePath; }; - public getCurrentPythonExecutablePath = async () => { - let originalpythonExecutablePath = ""; - + public getCurrentPythonExecutablePath = async ( + isTryingPython3: boolean = false + ) => { + let originalPythonExecutablePath = ""; + const systemPythonVar = isTryingPython3 + ? GLOBAL_ENV_VARS.PYTHON3 + : GLOBAL_ENV_VARS.PYTHON; // try to get name from interpreter try { - originalpythonExecutablePath = getConfig(CONFIG.PYTHON_PATH); + originalPythonExecutablePath = getConfig(CONFIG.PYTHON_PATH); } catch (err) { - originalpythonExecutablePath = GLOBAL_ENV_VARS.PYTHON; + originalPythonExecutablePath = systemPythonVar; } if ( - originalpythonExecutablePath === GLOBAL_ENV_VARS.PYTHON || - originalpythonExecutablePath === "" + originalPythonExecutablePath === GLOBAL_ENV_VARS.PYTHON3 || + originalPythonExecutablePath === GLOBAL_ENV_VARS.PYTHON || + originalPythonExecutablePath === "" ) { + // catching any instance where the python path needs to be resolved + // from an system variable this.telemetryAI.trackFeatureUsage( TelemetryEventName.SETUP_AUTO_RESOLVE_PYTHON_PATH ); try { const { stdout } = await this.executePythonCommand( - GLOBAL_ENV_VARS.PYTHON, + systemPythonVar, `-c "import sys; print(sys.executable)"` ); - originalpythonExecutablePath = stdout.trim(); + originalPythonExecutablePath = stdout.trim(); } catch (err) { this.telemetryAI.trackFeatureUsage( TelemetryEventName.SETUP_NO_PYTHON_PATH ); + if (isTryingPython3) { + // if trying python3 failed, that means that BOTH + // python and python3 failed as system variables + // so that means that there is no python + vscode.window + .showErrorMessage( + CONSTANTS.ERROR.NO_PYTHON_PATH, + DialogResponses.INSTALL_PYTHON + ) + .then((selection: vscode.MessageItem | undefined) => { + if (selection === DialogResponses.INSTALL_PYTHON) { + const okAction = () => { + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_DOWNLOAD_PYTHON + ); + open(CONSTANTS.LINKS.DOWNLOAD_PYTHON); + }; + showPrivacyModal( + okAction, + CONSTANTS.INFO.THIRD_PARTY_WEBSITE_PYTHON + ); + } + }); + // no python installed, cannot get path + return ""; + } else { + // "python" didn't resolve to anything, trying "python3" + return this.getCurrentPythonExecutablePath(true); + } + } + if ( + !(await this.validatePythonVersion( + originalPythonExecutablePath + )) + ) { + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_INVALID_PYTHON_VER + ); + if (isTryingPython3) { + // if we're trying python3, it means we already tried python and it + // all doesn't seem to work, but it got this far, so it means that + // their system python3 version is still not above 3.7, but they + // don't have a path selected. + vscode.window + .showInformationMessage( + CONSTANTS.ERROR.INVALID_PYTHON_PATH, + DialogResponses.INSTALL_PYTHON + ) + .then( + (installChoice: vscode.MessageItem | undefined) => { + if ( + installChoice === + DialogResponses.INSTALL_PYTHON + ) { + const okAction = () => { + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_DOWNLOAD_PYTHON + ); + open(CONSTANTS.LINKS.DOWNLOAD_PYTHON); + }; + showPrivacyModal( + okAction, + CONSTANTS.INFO + .THIRD_PARTY_WEBSITE_PYTHON + ); + } + } + ); + return ""; + } else { + // otherwise, we ran the "python" system variable + // and we can try python3 + return this.getCurrentPythonExecutablePath(true); + } + } + } else { + // should only be applicable if the user defined their own path + + // fix path to be absolute + if (!path.isAbsolute(originalPythonExecutablePath)) { + originalPythonExecutablePath = path.join( + vscode.workspace.rootPath, + originalPythonExecutablePath + ); + } + + if (!fs.existsSync(originalPythonExecutablePath)) { + await vscode.window.showErrorMessage( + CONSTANTS.ERROR.BAD_PYTHON_PATH + ); + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_INVALID_PYTHON_INTERPRETER_PATH + ); + return ""; + } + + if ( + !(await this.validatePythonVersion( + originalPythonExecutablePath + )) + ) { + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_INVALID_PYTHON_VER + ); vscode.window - .showErrorMessage( - CONSTANTS.ERROR.NO_PYTHON_PATH, + .showInformationMessage( + CONSTANTS.ERROR.INVALID_PYTHON_PATH, DialogResponses.INSTALL_PYTHON ) - .then((selection: vscode.MessageItem | undefined) => { - if (selection === DialogResponses.INSTALL_PYTHON) { + .then((installChoice: vscode.MessageItem | undefined) => { + if (installChoice === DialogResponses.INSTALL_PYTHON) { const okAction = () => { this.telemetryAI.trackFeatureUsage( TelemetryEventName.SETUP_DOWNLOAD_PYTHON @@ -205,49 +319,21 @@ export class SetupService { ); } }); - // no python installed, cannot get path return ""; } } - // fix path to be absolute - if (!path.isAbsolute(originalpythonExecutablePath)) { - originalpythonExecutablePath = path.join( - vscode.workspace.rootPath, - originalpythonExecutablePath - ); - } - - if (!fs.existsSync(originalpythonExecutablePath)) { - await vscode.window.showErrorMessage( - CONSTANTS.ERROR.BAD_PYTHON_PATH - ); - this.telemetryAI.trackFeatureUsage( - TelemetryEventName.SETUP_INVALID_PYTHON_INTERPRETER_PATH - ); - return ""; - } - if (!(await this.validatePythonVersion(originalpythonExecutablePath))) { - this.telemetryAI.trackFeatureUsage( - TelemetryEventName.SETUP_INVALID_PYTHON_VER - ); - return ""; - } - - return originalpythonExecutablePath; + return originalPythonExecutablePath; }; public isPipInstalled = async (pythonExecutablePath: string) => { try { - const { stdout } = await this.executePythonCommand( - pythonExecutablePath, - " -m pip" - ); + await this.executePythonCommand(pythonExecutablePath, " -m pip"); return true; } catch (err) { vscode.window .showErrorMessage( - CONSTANTS.ERROR.NO_PIP, + `We found that you may not have Pip installed on your interpreter at ${pythonExecutablePath}, please install it and try again.`, DialogResponses.INSTALL_PIP ) .then((selection: vscode.MessageItem | undefined) => { @@ -294,22 +380,6 @@ export class SetupService { "--version" ); if (stdout < VERSIONS.MIN_PY_VERSION) { - vscode.window - .showInformationMessage( - CONSTANTS.ERROR.INVALID_PYTHON_PATH, - DialogResponses.INSTALL_PYTHON - ) - .then((installChoice: vscode.MessageItem | undefined) => { - if (installChoice === DialogResponses.INSTALL_PYTHON) { - const okAction = () => { - open(CONSTANTS.LINKS.DOWNLOAD_PYTHON); - }; - showPrivacyModal( - okAction, - CONSTANTS.INFO.THIRD_PARTY_WEBSITE_PYTHON - ); - } - }); return false; } else { return true; @@ -398,7 +468,7 @@ export class SetupService { ); vscode.window .showErrorMessage( - `Virtual environment for download could not be completed. Using original interpreter at: ${pythonExecutable}.`, + `Virtual environment for download could not be completed. Using original interpreter at: ${pythonExecutable}. If you're on Linux, try running "sudo apt-get install python3-venv".`, DialogResponses.READ_INSTALL_MD ) .then((selection: vscode.MessageItem | undefined) => {