From d076b2e48f1faa00ccb2acd04d539a1075906431 Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 3 Mar 2020 20:48:24 -0800 Subject: [PATCH 1/3] venv telemetry --- src/constants.ts | 14 +++++++ src/extension.ts | 13 ++++-- src/extension_utils/utils.ts | 78 ++++++++++++++++++++++++++++++------ 3 files changed, 90 insertions(+), 15 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index 1be7ca4c8..0e554361f 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -374,6 +374,20 @@ export enum TelemetryEventName { MICROBIT_PERFORMANCE_DEPLOY_DEVICE = "MICROBIT.PERFORMANCE.DEPLOY.DEVICE", MICROBIT_PERFORMANCE_NEW_FILE = "MICROBIT.PERFORMANCE.NEW.FILE", MICROBIT_PERFORMANCE_OPEN_SIMULATOR = "MICROBIT.PERFORMANCE.OPEN.SIMULATOR", + + // Venv options + SETUP_VENV_CREATION_ERR = "SETUP.VENV.CREATION.ERR", + SETUP_NO_PIP = "SETUP.NO.PIP", + SETUP_DEP_INSTALL_FAIL = "SETUP.DEP.INSTALL.FAIL", + SETUP_AUTO_RESOLVE_PYTHON_PATH = "SETUP.AUTO.RESOLVE.PYTHON.PATH", + SETUP_NO_PYTHON_PATH = "SETUP.NO.PYTHON.PATH", + SETUP_DOWNLOAD_PYTHON = "SETUP.DOWNLOAD.PYTHON", + SETUP_BAD_PYTHON_PATH = "SETUP.BAD.PYTHON.PATH", + SETUP_INVALID_PYTHON_VER = "SETUP.INVALID.PYTHON.VER", + SETUP_INSTALL_VENV = "SETUP.INSTALL.VENV", + SETUP_ORIGINAL_INTERPRETER_DEP_INSTALL = "SETUP.ORIGINAL.INTERPRETER.DEP.INSTALL", + SETUP_HAS_VENV = "SETUP.HAS.VENV", + SETUP_NO_DEPS_INSTALLED = "SETUP.NO.DEPS.INSTALLED", } export const DEFAULT_DEVICE = CONSTANTS.DEVICE_NAME.CPX; diff --git a/src/extension.ts b/src/extension.ts index e3c544ca3..b0ade24bd 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -99,7 +99,7 @@ export async function activate(context: vscode.ExtensionContext) { // doesn't trigger lint errors updatePylintArgs(context); - pythonExecutablePath = await utils.setupEnv(context); + pythonExecutablePath = await utils.setupEnv(context, telemetryAI); try { utils.generateCPXConfig(); @@ -443,7 +443,11 @@ export async function activate(context: vscode.ExtensionContext) { const installDependencies: vscode.Disposable = vscode.commands.registerCommand( "deviceSimulatorExpress.common.installDependencies", async () => { - pythonExecutablePath = await utils.setupEnv(context, true); + pythonExecutablePath = await utils.setupEnv( + context, + telemetryAI, + true + ); telemetryAI.trackFeatureUsage( TelemetryEventName.COMMAND_INSTALL_EXTENSION_DEPENDENCIES ); @@ -1028,7 +1032,10 @@ export async function activate(context: vscode.ExtensionContext) { const configsChanged = vscode.workspace.onDidChangeConfiguration( async () => { if (utils.checkConfig(CONFIG.CONFIG_ENV_ON_SWITCH)) { - pythonExecutablePath = await utils.setupEnv(context); + pythonExecutablePath = await utils.setupEnv( + context, + telemetryAI + ); } } ); diff --git a/src/extension_utils/utils.ts b/src/extension_utils/utils.ts index 398b9de31..01c926791 100644 --- a/src/extension_utils/utils.ts +++ b/src/extension_utils/utils.ts @@ -16,10 +16,12 @@ import { GLOBAL_ENV_VARS, HELPER_FILES, SERVER_INFO, + TelemetryEventName, VERSIONS, } from "../constants"; import { CPXWorkspace } from "../cpxWorkspace"; import { DeviceContext } from "../deviceContext"; +import TelemetryAI from "../telemetry/telemetryAI"; const exec = util.promisify(cp.exec); @@ -330,7 +332,8 @@ export const hasVenv = async (context: vscode.ExtensionContext) => { export const promptInstallVenv = ( context: vscode.ExtensionContext, pythonExecutable: string, - pythonExecutableName: string + pythonExecutableName: string, + telemetryAI: TelemetryAI ) => { return vscode.window .showInformationMessage( @@ -343,7 +346,8 @@ export const promptInstallVenv = ( return installPythonVenv( context, pythonExecutable, - pythonExecutableName + pythonExecutableName, + telemetryAI ); } else { // return pythonExecutable, notifying the caller @@ -371,7 +375,8 @@ export const getPythonVenv = async ( export const installPythonVenv = async ( context: vscode.ExtensionContext, pythonExecutable: string, - pythonExecutableName: string + pythonExecutableName: string, + telemetryAI: TelemetryAI ) => { const pathToEnv: string = getPathToScript( context, @@ -390,6 +395,9 @@ export const installPythonVenv = async ( // run command to download dependencies to out/python_libs await executePythonCommand(pythonExecutable, `-m venv "${pathToEnv}"`); } catch (err) { + telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_VENV_CREATION_ERR + ); vscode.window .showErrorMessage( `Virtual environment for download could not be completed. Using original interpreter at: ${pythonExecutable}.`, @@ -406,7 +414,12 @@ export const installPythonVenv = async ( return pythonExecutable; } - return installDependenciesWrapper(context, pythonPath, pythonExecutable); + return installDependenciesWrapper( + context, + pythonPath, + telemetryAI, + pythonExecutable + ); }; export const areDependenciesInstalled = async ( @@ -436,7 +449,8 @@ export const areDependenciesInstalled = async ( export const installDependencies = async ( context: vscode.ExtensionContext, - pythonPath: string + pythonPath: string, + telemetryAI: TelemetryAI ) => { const requirementsPath: string = getPathToScript( context, @@ -445,6 +459,7 @@ export const installDependencies = async ( ); if (!isPipInstalled(pythonPath)) { + telemetryAI.trackFeatureUsage(TelemetryEventName.SETUP_NO_PIP); return false; } @@ -465,13 +480,14 @@ export const installDependencies = async ( export const installDependenciesWrapper = async ( context: vscode.ExtensionContext, pythonPath: string, + telemetryAI: TelemetryAI, backupPythonPath: string = "" ) => { let errMessage = CONSTANTS.ERROR.DEPENDENCY_DOWNLOAD_ERROR; if (backupPythonPath !== "") { errMessage = `${errMessage} Using original interpreter at: ${backupPythonPath}.`; } - if (!(await installDependencies(context, pythonPath))) { + if (!(await installDependencies(context, pythonPath, telemetryAI))) { vscode.window .showErrorMessage( CONSTANTS.ERROR.DEPENDENCY_DOWNLOAD_ERROR, @@ -482,11 +498,17 @@ export const installDependenciesWrapper = async ( open(CONSTANTS.LINKS.INSTALL); } }); + + telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_DEP_INSTALL_FAIL + ); return backupPythonPath; } return pythonPath; }; -export const getCurrentpythonExecutablePath = async () => { +export const getCurrentpythonExecutablePath = async ( + telemetryAI: TelemetryAI +) => { let originalpythonExecutablePath = ""; // try to get name from interpreter @@ -500,6 +522,9 @@ export const getCurrentpythonExecutablePath = async () => { originalpythonExecutablePath === GLOBAL_ENV_VARS.PYTHON || originalpythonExecutablePath === "" ) { + telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_AUTO_RESOLVE_PYTHON_PATH + ); try { const { stdout } = await executePythonCommand( GLOBAL_ENV_VARS.PYTHON, @@ -507,6 +532,9 @@ export const getCurrentpythonExecutablePath = async () => { ); originalpythonExecutablePath = stdout.trim(); } catch (err) { + telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_NO_PYTHON_PATH + ); vscode.window .showErrorMessage( CONSTANTS.ERROR.NO_PYTHON_PATH, @@ -515,6 +543,9 @@ export const getCurrentpythonExecutablePath = async () => { .then((selection: vscode.MessageItem | undefined) => { if (selection === DialogResponses.INSTALL_PYTHON) { const okAction = () => { + telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_DOWNLOAD_PYTHON + ); open(CONSTANTS.LINKS.DOWNLOAD_PYTHON); }; showPrivacyModal( @@ -523,7 +554,6 @@ export const getCurrentpythonExecutablePath = async () => { ); } }); - // no python installed, cannot get path return ""; } @@ -538,10 +568,14 @@ export const getCurrentpythonExecutablePath = async () => { if (!fs.existsSync(originalpythonExecutablePath)) { await vscode.window.showErrorMessage(CONSTANTS.ERROR.BAD_PYTHON_PATH); + telemetryAI.trackFeatureUsage(TelemetryEventName.SETUP_BAD_PYTHON_PATH); return ""; } if (!(await validatePythonVersion(originalpythonExecutablePath))) { + telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_INVALID_PYTHON_VER + ); return ""; } @@ -549,9 +583,12 @@ export const getCurrentpythonExecutablePath = async () => { }; export const setupEnv = async ( context: vscode.ExtensionContext, + telemetryAI: TelemetryAI, needsResponse: boolean = false ) => { - const originalpythonExecutablePath = await getCurrentpythonExecutablePath(); + const originalpythonExecutablePath = await getCurrentpythonExecutablePath( + telemetryAI + ); let pythonExecutablePath = originalpythonExecutablePath; let pythonExecutableName: string = os.platform() === "win32" @@ -567,6 +604,7 @@ export const setupEnv = async ( ); if (await hasVenv(context)) { // venv in extention exists with wrong dependencies + if ( !(await areDependenciesInstalled( context, @@ -576,6 +614,7 @@ export const setupEnv = async ( pythonExecutablePath = await installDependenciesWrapper( context, pythonExecutablePathVenv, + telemetryAI, pythonExecutablePath ); } else { @@ -585,7 +624,11 @@ export const setupEnv = async ( pythonExecutablePath = await promptInstallVenv( context, originalpythonExecutablePath, - pythonExecutableName + pythonExecutableName, + telemetryAI + ); + telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_INSTALL_VENV ); } @@ -597,6 +640,8 @@ export const setupEnv = async ( .getConfiguration() .update(CONFIG.PYTHON_PATH, pythonExecutablePath); } + } else { + telemetryAI.trackFeatureUsage(TelemetryEventName.SETUP_HAS_VENV); } if (pythonExecutablePath === originalpythonExecutablePath) { // going with original interpreter, either because @@ -613,9 +658,13 @@ export const setupEnv = async ( installChoice: vscode.MessageItem | undefined ) => { if (installChoice === DialogResponses.INSTALL_NOW) { + telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_ORIGINAL_INTERPRETER_DEP_INSTALL + ); await installDependenciesWrapper( context, - pythonExecutablePath + pythonExecutablePath, + telemetryAI ); } else { await vscode.window @@ -636,7 +685,12 @@ export const setupEnv = async ( ) { await installDependenciesWrapper( context, - pythonExecutablePath + pythonExecutablePath, + telemetryAI + ); + } else { + telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_NO_DEPS_INSTALLED ); } } From 434f8bf16d9bceddb977ffa50e86cec7c155a030 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 4 Mar 2020 17:07:02 -0800 Subject: [PATCH 2/3] abstracted setup into its own class --- src/constants.ts | 2 +- src/extension.ts | 12 +- src/extension_utils/utils.ts | 481 +-------------------------------- src/service/setupService.ts | 511 +++++++++++++++++++++++++++++++++++ 4 files changed, 522 insertions(+), 484 deletions(-) create mode 100644 src/service/setupService.ts diff --git a/src/constants.ts b/src/constants.ts index 0e554361f..7bcb56329 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -382,7 +382,7 @@ export enum TelemetryEventName { SETUP_AUTO_RESOLVE_PYTHON_PATH = "SETUP.AUTO.RESOLVE.PYTHON.PATH", SETUP_NO_PYTHON_PATH = "SETUP.NO.PYTHON.PATH", SETUP_DOWNLOAD_PYTHON = "SETUP.DOWNLOAD.PYTHON", - SETUP_BAD_PYTHON_PATH = "SETUP.BAD.PYTHON.PATH", + SETUP_INVALID_PYTHON_INTERPRETER_PATH = "SETUP.INVALID.PYTHON.INTERPRETER.PATH", SETUP_INVALID_PYTHON_VER = "SETUP.INVALID.PYTHON.VER", SETUP_INSTALL_VENV = "SETUP.INSTALL.VENV", SETUP_ORIGINAL_INTERPRETER_DEP_INSTALL = "SETUP.ORIGINAL.INTERPRETER.DEP.INSTALL", diff --git a/src/extension.ts b/src/extension.ts index 568f918e2..de1a9b937 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -27,6 +27,7 @@ import { SerialMonitor } from "./serialMonitor"; import { DebuggerCommunicationService } from "./service/debuggerCommunicationService"; import { MessagingService } from "./service/messagingService"; import { PopupService } from "./service/PopupService"; +import { SetupService } from "./service/SetupService"; import { SimulatorDebugConfigurationProvider } from "./simulatorDebugConfigurationProvider"; import getPackageInfo from "./telemetry/getPackageInfo"; import TelemetryAI from "./telemetry/telemetryAI"; @@ -43,8 +44,10 @@ let inDebugMode: boolean = false; let firstTimeClosed: boolean = true; let shouldShowRunCodePopup: boolean = true; const messagingService = new MessagingService(); +let setupService: SetupService; const debuggerCommunicationService = new DebuggerCommunicationService(); + let currentActiveDevice: string = DEFAULT_DEVICE; export let outChannel: vscode.OutputChannel | undefined; @@ -87,6 +90,7 @@ export async function activate(context: vscode.ExtensionContext) { console.info(CONSTANTS.INFO.EXTENSION_ACTIVATED); telemetryAI = new TelemetryAI(context); + setupService = new SetupService(telemetryAI); let currentPanel: vscode.WebviewPanel | undefined; let childProcess: cp.ChildProcess | undefined; let messageListener: vscode.Disposable; @@ -99,7 +103,7 @@ export async function activate(context: vscode.ExtensionContext) { // doesn't trigger lint errors updatePylintArgs(context); - pythonExecutablePath = await utils.setupEnv(context, telemetryAI); + pythonExecutablePath = await setupService.setupEnv(context, telemetryAI); try { utils.generateCPXConfig(); @@ -443,7 +447,7 @@ export async function activate(context: vscode.ExtensionContext) { const installDependencies: vscode.Disposable = vscode.commands.registerCommand( "deviceSimulatorExpress.common.installDependencies", async () => { - pythonExecutablePath = await utils.setupEnv( + pythonExecutablePath = await setupService.setupEnv( context, telemetryAI, true @@ -1032,7 +1036,7 @@ export async function activate(context: vscode.ExtensionContext) { const configsChanged = vscode.workspace.onDidChangeConfiguration( async () => { if (utils.checkConfig(CONFIG.CONFIG_ENV_ON_SWITCH)) { - pythonExecutablePath = await utils.setupEnv( + pythonExecutablePath = await setupService.setupEnv( context, telemetryAI ); @@ -1087,7 +1091,7 @@ const updateCurrentFileIfPython = async ( if ( currentTextDocument && utils.getActiveEditorFromPath(currentTextDocument.fileName) === - undefined + undefined ) { await vscode.window.showTextDocument( currentTextDocument, diff --git a/src/extension_utils/utils.ts b/src/extension_utils/utils.ts index 2904b88a0..4cc915e1d 100644 --- a/src/extension_utils/utils.ts +++ b/src/extension_utils/utils.ts @@ -3,27 +3,20 @@ import * as cp from "child_process"; import * as fs from "fs"; -import * as open from "open"; -import * as os from "os"; import * as path from "path"; import * as util from "util"; import * as vscode from "vscode"; import { - CONFIG, CONSTANTS, CPX_CONFIG_FILE, DialogResponses, GLOBAL_ENV_VARS, - HELPER_FILES, SERVER_INFO, - TelemetryEventName, - VERSIONS, } from "../constants"; import { CPXWorkspace } from "../cpxWorkspace"; import { DeviceContext } from "../deviceContext"; -import TelemetryAI from "../telemetry/telemetryAI"; -const exec = util.promisify(cp.exec); +export const exec = util.promisify(cp.exec); const errorChannel = vscode.window.createOutputChannel( CONSTANTS.ERROR.INSTALLATION_ERROR @@ -80,7 +73,7 @@ export function tryParseJSON(jsonString: string): any | boolean { if (jsonObj && typeof jsonObj === "object") { return jsonObj; } - } catch (exception) {} + } catch (exception) { } return false; } @@ -164,34 +157,6 @@ export function generateCPXConfig(): void { fs.writeFileSync(cpxConfigFilePath, JSON.stringify(cpxJson, null, 4)); } -export const isPipInstalled = async (pythonExecutablePath: string) => { - try { - const { stdout } = await executePythonCommand( - pythonExecutablePath, - " -m pip" - ); - return true; - } catch (err) { - vscode.window - .showErrorMessage( - CONSTANTS.ERROR.NO_PIP, - DialogResponses.INSTALL_PIP - ) - .then((selection: vscode.MessageItem | undefined) => { - if (selection === DialogResponses.INSTALL_PIP) { - const okAction = () => { - open(CONSTANTS.LINKS.DOWNLOAD_PIP); - }; - showPrivacyModal( - okAction, - CONSTANTS.INFO.THIRD_PARTY_WEBSITE_PIP - ); - } - }); - return false; - } -}; - export const addVisibleTextEditorCallback = ( currentPanel: vscode.WebviewPanel, context: vscode.ExtensionContext @@ -266,445 +231,3 @@ export const getTelemetryState = () => { .getConfiguration() .get("telemetry.enableTelemetry", true); }; - -// Setup code starts - -export const checkIfVenv = async ( - context: vscode.ExtensionContext, - pythonExecutablePath: string -) => { - const venvCheckerPath: string = getPathToScript( - context, - CONSTANTS.FILESYSTEM.OUTPUT_DIRECTORY, - HELPER_FILES.CHECK_IF_VENV_PY - ); - const { stdout } = await executePythonCommand( - pythonExecutablePath, - `"${venvCheckerPath}"` - ); - return stdout.trim() === "1"; -}; - -export const executePythonCommand = async ( - pythonExecutablePath: string, - command: string -) => { - return exec(`${createEscapedPath(pythonExecutablePath)} ${command}`); -}; - -export const validatePythonVersion = async (pythonExecutablePath: string) => { - const { stdout } = await executePythonCommand( - pythonExecutablePath, - "--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; - } -}; - -export const hasVenv = async (context: vscode.ExtensionContext) => { - const pathToEnv: string = getPathToScript( - context, - CONSTANTS.FILESYSTEM.PYTHON_VENV_DIR - ); - - return fs.existsSync(pathToEnv); -}; - -export const promptInstallVenv = ( - context: vscode.ExtensionContext, - pythonExecutable: string, - pythonExecutableName: string, - telemetryAI: TelemetryAI -) => { - return vscode.window - .showInformationMessage( - CONSTANTS.INFO.INSTALL_PYTHON_VENV, - DialogResponses.YES, - DialogResponses.NO - ) - .then((selection: vscode.MessageItem | undefined) => { - if (selection === DialogResponses.YES) { - return installPythonVenv( - context, - pythonExecutable, - pythonExecutableName, - telemetryAI - ); - } else { - // return pythonExecutable, notifying the caller - // that the user was unwilling to create venv - // and by default, this will trigger the extension to - // try using pythonExecutable - return pythonExecutable; - } - }); -}; - -export const getPythonVenv = async ( - context: vscode.ExtensionContext, - pythonExecutableName: string -) => { - const subFolder = os.platform() === "win32" ? "Scripts" : "bin"; - - return getPathToScript( - context, - path.join(CONSTANTS.FILESYSTEM.PYTHON_VENV_DIR, subFolder), - pythonExecutableName - ); -}; - -export const installPythonVenv = async ( - context: vscode.ExtensionContext, - pythonExecutable: string, - pythonExecutableName: string, - telemetryAI: TelemetryAI -) => { - const pathToEnv: string = getPathToScript( - context, - CONSTANTS.FILESYSTEM.PYTHON_VENV_DIR - ); - - vscode.window.showInformationMessage(CONSTANTS.INFO.INSTALLING_PYTHON_VENV); - - const pythonPath: string = await getPythonVenv( - context, - pythonExecutableName - ); - - try { - // make venv - // run command to download dependencies to out/python_libs - await executePythonCommand(pythonExecutable, `-m venv "${pathToEnv}"`); - } catch (err) { - telemetryAI.trackFeatureUsage( - TelemetryEventName.SETUP_VENV_CREATION_ERR - ); - vscode.window - .showErrorMessage( - `Virtual environment for download could not be completed. Using original interpreter at: ${pythonExecutable}.`, - DialogResponses.READ_INSTALL_MD - ) - .then((selection: vscode.MessageItem | undefined) => { - if (selection === DialogResponses.READ_INSTALL_MD) { - open(CONSTANTS.LINKS.INSTALL); - } - }); - - console.error(err); - - return pythonExecutable; - } - - return installDependenciesWrapper( - context, - pythonPath, - telemetryAI, - pythonExecutable - ); -}; - -export const areDependenciesInstalled = async ( - context: vscode.ExtensionContext, - pythonPath: string -) => { - const dependencyCheckerPath: string = getPathToScript( - context, - CONSTANTS.FILESYSTEM.OUTPUT_DIRECTORY, - HELPER_FILES.CHECK_PYTHON_DEPENDENCIES - ); - try { - // python script will throw exception - // if not all dependencies are downloaded - const { stdout } = await executePythonCommand( - pythonPath, - `"${dependencyCheckerPath}"` - ); - - // output for debugging purposes - console.info(stdout); - return true; - } catch (err) { - return false; - } -}; - -export const installDependencies = async ( - context: vscode.ExtensionContext, - pythonPath: string, - telemetryAI: TelemetryAI -) => { - const requirementsPath: string = getPathToScript( - context, - CONSTANTS.FILESYSTEM.OUTPUT_DIRECTORY, - "requirements.txt" - ); - - if (!isPipInstalled(pythonPath)) { - telemetryAI.trackFeatureUsage(TelemetryEventName.SETUP_NO_PIP); - return false; - } - - try { - const { stdout } = await executePythonCommand( - pythonPath, - `-m pip install -r "${requirementsPath}"` - ); - - console.info(stdout); - vscode.window.showInformationMessage(CONSTANTS.INFO.SUCCESSFUL_INSTALL); - return true; - } catch (err) { - return false; - } -}; - -export const installDependenciesWrapper = async ( - context: vscode.ExtensionContext, - pythonPath: string, - telemetryAI: TelemetryAI, - backupPythonPath: string = "" -) => { - let errMessage = CONSTANTS.ERROR.DEPENDENCY_DOWNLOAD_ERROR; - if (backupPythonPath !== "") { - errMessage = `${errMessage} Using original interpreter at: ${backupPythonPath}.`; - } - if (!(await installDependencies(context, pythonPath, telemetryAI))) { - vscode.window - .showErrorMessage( - CONSTANTS.ERROR.DEPENDENCY_DOWNLOAD_ERROR, - DialogResponses.READ_INSTALL_MD - ) - .then((selection: vscode.MessageItem | undefined) => { - if (selection === DialogResponses.READ_INSTALL_MD) { - open(CONSTANTS.LINKS.INSTALL); - } - }); - - telemetryAI.trackFeatureUsage( - TelemetryEventName.SETUP_DEP_INSTALL_FAIL - ); - return backupPythonPath; - } - return pythonPath; -}; -export const getCurrentpythonExecutablePath = async ( - telemetryAI: TelemetryAI -) => { - let originalpythonExecutablePath = ""; - - // try to get name from interpreter - try { - originalpythonExecutablePath = getConfig(CONFIG.PYTHON_PATH); - } catch (err) { - originalpythonExecutablePath = GLOBAL_ENV_VARS.PYTHON; - } - - if ( - originalpythonExecutablePath === GLOBAL_ENV_VARS.PYTHON || - originalpythonExecutablePath === "" - ) { - telemetryAI.trackFeatureUsage( - TelemetryEventName.SETUP_AUTO_RESOLVE_PYTHON_PATH - ); - try { - const { stdout } = await executePythonCommand( - GLOBAL_ENV_VARS.PYTHON, - `-c "import sys; print(sys.executable)"` - ); - originalpythonExecutablePath = stdout.trim(); - } catch (err) { - telemetryAI.trackFeatureUsage( - TelemetryEventName.SETUP_NO_PYTHON_PATH - ); - vscode.window - .showErrorMessage( - CONSTANTS.ERROR.NO_PYTHON_PATH, - DialogResponses.INSTALL_PYTHON - ) - .then((selection: vscode.MessageItem | undefined) => { - if (selection === DialogResponses.INSTALL_PYTHON) { - const okAction = () => { - 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 ""; - } - } - // 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); - telemetryAI.trackFeatureUsage(TelemetryEventName.SETUP_BAD_PYTHON_PATH); - return ""; - } - - if (!(await validatePythonVersion(originalpythonExecutablePath))) { - telemetryAI.trackFeatureUsage( - TelemetryEventName.SETUP_INVALID_PYTHON_VER - ); - return ""; - } - - return originalpythonExecutablePath; -}; -export const setupEnv = async ( - context: vscode.ExtensionContext, - telemetryAI: TelemetryAI, - needsResponse: boolean = false -) => { - const originalpythonExecutablePath = await getCurrentpythonExecutablePath( - telemetryAI - ); - let pythonExecutablePath = originalpythonExecutablePath; - const pythonExecutableName: string = - os.platform() === "win32" - ? HELPER_FILES.PYTHON_EXE - : HELPER_FILES.PYTHON; - - if (!(await areDependenciesInstalled(context, pythonExecutablePath))) { - // environment needs to install dependencies - if (!(await checkIfVenv(context, pythonExecutablePath))) { - const pythonExecutablePathVenv = await getPythonVenv( - context, - pythonExecutableName - ); - if (await hasVenv(context)) { - // venv in extention exists with wrong dependencies - - if ( - !(await areDependenciesInstalled( - context, - pythonExecutablePathVenv - )) - ) { - pythonExecutablePath = await installDependenciesWrapper( - context, - pythonExecutablePathVenv, - telemetryAI, - pythonExecutablePath - ); - } else { - pythonExecutablePath = pythonExecutablePathVenv; - } - } else { - pythonExecutablePath = await promptInstallVenv( - context, - originalpythonExecutablePath, - pythonExecutableName, - telemetryAI - ); - telemetryAI.trackFeatureUsage( - TelemetryEventName.SETUP_INSTALL_VENV - ); - } - - if (pythonExecutablePath === pythonExecutablePathVenv) { - vscode.window.showInformationMessage( - CONSTANTS.INFO.UPDATED_TO_EXTENSION_VENV - ); - vscode.workspace - .getConfiguration() - .update(CONFIG.PYTHON_PATH, pythonExecutablePath); - } - } else { - telemetryAI.trackFeatureUsage(TelemetryEventName.SETUP_HAS_VENV); - } - if (pythonExecutablePath === originalpythonExecutablePath) { - // going with original interpreter, either because - // already in venv or error in creating custom venv - if (checkConfig(CONFIG.SHOW_DEPENDENCY_INSTALL)) { - await vscode.window - .showInformationMessage( - CONSTANTS.INFO.INSTALL_PYTHON_DEPS, - DialogResponses.INSTALL_NOW, - DialogResponses.DONT_INSTALL - ) - .then( - async ( - installChoice: vscode.MessageItem | undefined - ) => { - if (installChoice === DialogResponses.INSTALL_NOW) { - telemetryAI.trackFeatureUsage( - TelemetryEventName.SETUP_ORIGINAL_INTERPRETER_DEP_INSTALL - ); - await installDependenciesWrapper( - context, - pythonExecutablePath, - telemetryAI - ); - } else { - await vscode.window - .showInformationMessage( - CONSTANTS.INFO.ARE_YOU_SURE, - DialogResponses.INSTALL_NOW, - DialogResponses.DONT_INSTALL - ) - .then( - async ( - installChoice2: - | vscode.MessageItem - | undefined - ) => { - if ( - installChoice2 === - DialogResponses.INSTALL_NOW - ) { - await installDependenciesWrapper( - context, - pythonExecutablePath, - telemetryAI - ); - } else { - telemetryAI.trackFeatureUsage( - TelemetryEventName.SETUP_NO_DEPS_INSTALLED - ); - } - } - ); - } - } - ); - } - } - } else if (needsResponse) { - vscode.window.showInformationMessage( - CONSTANTS.INFO.ALREADY_SUCCESSFUL_INSTALL - ); - } - - return pythonExecutablePath; -}; diff --git a/src/service/setupService.ts b/src/service/setupService.ts new file mode 100644 index 000000000..e1943ad8d --- /dev/null +++ b/src/service/setupService.ts @@ -0,0 +1,511 @@ +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; +import * as vscode from "vscode"; +import { + CONFIG, + CONSTANTS, + DialogResponses, + GLOBAL_ENV_VARS, + HELPER_FILES, + TelemetryEventName, + VERSIONS, +} from "../constants"; +import { + checkConfig, + createEscapedPath, + exec, + getConfig, + getPathToScript, + showPrivacyModal, +} from "../extension_utils/utils"; +import TelemetryAI from "../telemetry/telemetryAI"; + +export class SetupService { + private telemetryAI: TelemetryAI; + + constructor(telemetryAI: TelemetryAI) { + this.telemetryAI = telemetryAI + } + + public setupEnv = async ( + context: vscode.ExtensionContext, + telemetryAI: TelemetryAI, + needsResponse: boolean = false + ) => { + this.telemetryAI = telemetryAI + + const originalpythonExecutablePath = await this.getCurrentPythonExecutablePath(); + let pythonExecutablePath = originalpythonExecutablePath; + const pythonExecutableName: string = + os.platform() === "win32" + ? HELPER_FILES.PYTHON_EXE + : HELPER_FILES.PYTHON; + + if ( + !(await this.areDependenciesInstalled( + context, + pythonExecutablePath + )) + ) { + // environment needs to install dependencies + if (!(await this.checkIfVenv(context, pythonExecutablePath))) { + const pythonExecutablePathVenv = await this.getPythonVenv( + context, + pythonExecutableName + ); + if (await this.hasVenv(context)) { + // venv in extention exists with wrong dependencies + + if ( + !(await this.areDependenciesInstalled( + context, + pythonExecutablePathVenv + )) + ) { + pythonExecutablePath = await this.installDependenciesWrapper( + context, + pythonExecutablePathVenv, + pythonExecutablePath + ); + } else { + pythonExecutablePath = pythonExecutablePathVenv; + } + } else { + pythonExecutablePath = await this.promptInstallVenv( + context, + originalpythonExecutablePath, + pythonExecutableName + ); + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_INSTALL_VENV + ); + } + + if (pythonExecutablePath === pythonExecutablePathVenv) { + vscode.window.showInformationMessage( + CONSTANTS.INFO.UPDATED_TO_EXTENSION_VENV + ); + vscode.workspace + .getConfiguration() + .update(CONFIG.PYTHON_PATH, pythonExecutablePath); + } + } else { + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_HAS_VENV + ); + } + if (pythonExecutablePath === originalpythonExecutablePath) { + // going with original interpreter, either because + // already in venv or error in creating custom venv + if (checkConfig(CONFIG.SHOW_DEPENDENCY_INSTALL)) { + await vscode.window + .showInformationMessage( + CONSTANTS.INFO.INSTALL_PYTHON_DEPS, + DialogResponses.INSTALL_NOW, + DialogResponses.DONT_INSTALL + ) + .then( + async ( + installChoice: vscode.MessageItem | undefined + ) => { + if ( + installChoice === + DialogResponses.INSTALL_NOW + ) { + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_ORIGINAL_INTERPRETER_DEP_INSTALL + ); + await this.installDependenciesWrapper( + context, + pythonExecutablePath + ); + } else { + await vscode.window + .showInformationMessage( + CONSTANTS.INFO.ARE_YOU_SURE, + DialogResponses.INSTALL_NOW, + DialogResponses.DONT_INSTALL + ) + .then( + async ( + installChoice2: + | vscode.MessageItem + | undefined + ) => { + if ( + installChoice2 === + DialogResponses.INSTALL_NOW + ) { + await this.installDependenciesWrapper( + context, + pythonExecutablePath + ); + } else { + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_NO_DEPS_INSTALLED + ); + } + } + ); + } + } + ); + } + } + } else if (needsResponse) { + vscode.window.showInformationMessage( + CONSTANTS.INFO.ALREADY_SUCCESSFUL_INSTALL + ); + } + + return pythonExecutablePath; + }; + + public isPipInstalled = async (pythonExecutablePath: string) => { + try { + const { stdout } = await this.executePythonCommand( + pythonExecutablePath, + " -m pip" + ); + return true; + } catch (err) { + vscode.window + .showErrorMessage( + CONSTANTS.ERROR.NO_PIP, + DialogResponses.INSTALL_PIP + ) + .then((selection: vscode.MessageItem | undefined) => { + if (selection === DialogResponses.INSTALL_PIP) { + const okAction = () => { + open(CONSTANTS.LINKS.DOWNLOAD_PIP); + }; + showPrivacyModal( + okAction, + CONSTANTS.INFO.THIRD_PARTY_WEBSITE_PIP + ); + } + }); + return false; + } + }; + + public checkIfVenv = async ( + context: vscode.ExtensionContext, + pythonExecutablePath: string + ) => { + const venvCheckerPath: string = getPathToScript( + context, + CONSTANTS.FILESYSTEM.OUTPUT_DIRECTORY, + HELPER_FILES.CHECK_IF_VENV_PY + ); + const { stdout } = await this.executePythonCommand( + pythonExecutablePath, + `"${venvCheckerPath}"` + ); + return stdout.trim() === "1"; + }; + + public executePythonCommand = async ( + pythonExecutablePath: string, + command: string + ) => { + return exec(`${createEscapedPath(pythonExecutablePath)} ${command}`); + }; + + public validatePythonVersion = async (pythonExecutablePath: string) => { + const { stdout } = await this.executePythonCommand( + pythonExecutablePath, + "--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; + } + }; + + public hasVenv = async (context: vscode.ExtensionContext) => { + const pathToEnv: string = getPathToScript( + context, + CONSTANTS.FILESYSTEM.PYTHON_VENV_DIR + ); + + return fs.existsSync(pathToEnv); + }; + + public promptInstallVenv = ( + context: vscode.ExtensionContext, + pythonExecutable: string, + pythonExecutableName: string + ) => { + return vscode.window + .showInformationMessage( + CONSTANTS.INFO.INSTALL_PYTHON_VENV, + DialogResponses.YES, + DialogResponses.NO + ) + .then((selection: vscode.MessageItem | undefined) => { + if (selection === DialogResponses.YES) { + return this.installPythonVenv( + context, + pythonExecutable, + pythonExecutableName + ); + } else { + // return pythonExecutable, notifying the caller + // that the user was unwilling to create venv + // and by default, this will trigger the extension to + // try using pythonExecutable + return pythonExecutable; + } + }); + }; + + public getPythonVenv = async ( + context: vscode.ExtensionContext, + pythonExecutableName: string + ) => { + const subFolder = os.platform() === "win32" ? "Scripts" : "bin"; + + return getPathToScript( + context, + path.join(CONSTANTS.FILESYSTEM.PYTHON_VENV_DIR, subFolder), + pythonExecutableName + ); + }; + + public installPythonVenv = async ( + context: vscode.ExtensionContext, + pythonExecutable: string, + pythonExecutableName: string + ) => { + const pathToEnv: string = getPathToScript( + context, + CONSTANTS.FILESYSTEM.PYTHON_VENV_DIR + ); + + vscode.window.showInformationMessage( + CONSTANTS.INFO.INSTALLING_PYTHON_VENV + ); + + const pythonPath: string = await this.getPythonVenv( + context, + pythonExecutableName + ); + + try { + // make venv + // run command to download dependencies to out/python_libs + await this.executePythonCommand( + pythonExecutable, + `-m venv "${pathToEnv}"` + ); + } catch (err) { + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_VENV_CREATION_ERR + ); + vscode.window + .showErrorMessage( + `Virtual environment for download could not be completed. Using original interpreter at: ${pythonExecutable}.`, + DialogResponses.READ_INSTALL_MD + ) + .then((selection: vscode.MessageItem | undefined) => { + if (selection === DialogResponses.READ_INSTALL_MD) { + open(CONSTANTS.LINKS.INSTALL); + } + }); + + console.error(err); + + return pythonExecutable; + } + + return this.installDependenciesWrapper( + context, + pythonPath, + pythonExecutable + ); + }; + + public areDependenciesInstalled = async ( + context: vscode.ExtensionContext, + pythonPath: string + ) => { + const dependencyCheckerPath: string = getPathToScript( + context, + CONSTANTS.FILESYSTEM.OUTPUT_DIRECTORY, + HELPER_FILES.CHECK_PYTHON_DEPENDENCIES + ); + try { + // python script will throw exception + // if not all dependencies are downloaded + const { stdout } = await this.executePythonCommand( + pythonPath, + `"${dependencyCheckerPath}"` + ); + + // output for debugging purposes + console.info(stdout); + return true; + } catch (err) { + return false; + } + }; + + public installDependencies = async ( + context: vscode.ExtensionContext, + pythonPath: string + ) => { + const requirementsPath: string = getPathToScript( + context, + CONSTANTS.FILESYSTEM.OUTPUT_DIRECTORY, + "requirements.txt" + ); + + if (!this.isPipInstalled(pythonPath)) { + this.telemetryAI.trackFeatureUsage(TelemetryEventName.SETUP_NO_PIP); + return false; + } + + try { + const { stdout } = await this.executePythonCommand( + pythonPath, + `-m pip install -r "${requirementsPath}"` + ); + + console.info(stdout); + vscode.window.showInformationMessage( + CONSTANTS.INFO.SUCCESSFUL_INSTALL + ); + return true; + } catch (err) { + return false; + } + }; + + public installDependenciesWrapper = async ( + context: vscode.ExtensionContext, + pythonPath: string, + backupPythonPath: string = "" + ) => { + let errMessage = CONSTANTS.ERROR.DEPENDENCY_DOWNLOAD_ERROR; + if (backupPythonPath !== "") { + errMessage = `${errMessage} Using original interpreter at: ${backupPythonPath}.`; + } + if ( + !(await this.installDependencies(context, pythonPath)) + ) { + vscode.window + .showErrorMessage( + CONSTANTS.ERROR.DEPENDENCY_DOWNLOAD_ERROR, + DialogResponses.READ_INSTALL_MD + ) + .then((selection: vscode.MessageItem | undefined) => { + if (selection === DialogResponses.READ_INSTALL_MD) { + open(CONSTANTS.LINKS.INSTALL); + } + }); + + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_DEP_INSTALL_FAIL + ); + return backupPythonPath; + } + return pythonPath; + }; + public getCurrentPythonExecutablePath = async () => { + let originalpythonExecutablePath = ""; + + // try to get name from interpreter + try { + originalpythonExecutablePath = getConfig(CONFIG.PYTHON_PATH); + } catch (err) { + originalpythonExecutablePath = GLOBAL_ENV_VARS.PYTHON; + } + + if ( + originalpythonExecutablePath === GLOBAL_ENV_VARS.PYTHON || + originalpythonExecutablePath === "" + ) { + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_AUTO_RESOLVE_PYTHON_PATH + ); + try { + const { stdout } = await this.executePythonCommand( + GLOBAL_ENV_VARS.PYTHON, + `-c "import sys; print(sys.executable)"` + ); + originalpythonExecutablePath = stdout.trim(); + } catch (err) { + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_NO_PYTHON_PATH + ); + 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 ""; + } + } + // 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; + }; + +} From 6113b16a416b980f6fcb4ce848cb91bc69ddb393 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 4 Mar 2020 22:41:46 -0800 Subject: [PATCH 3/3] telemtry handle removed from setupEnv --- src/extension.ts | 16 +--- src/extension_utils/utils.ts | 2 +- src/service/setupService.ts | 165 +++++++++++++++++------------------ 3 files changed, 85 insertions(+), 98 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index de1a9b937..1ac00cfa6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -47,7 +47,6 @@ const messagingService = new MessagingService(); let setupService: SetupService; const debuggerCommunicationService = new DebuggerCommunicationService(); - let currentActiveDevice: string = DEFAULT_DEVICE; export let outChannel: vscode.OutputChannel | undefined; @@ -103,7 +102,7 @@ export async function activate(context: vscode.ExtensionContext) { // doesn't trigger lint errors updatePylintArgs(context); - pythonExecutablePath = await setupService.setupEnv(context, telemetryAI); + pythonExecutablePath = await setupService.setupEnv(context); try { utils.generateCPXConfig(); @@ -447,11 +446,7 @@ export async function activate(context: vscode.ExtensionContext) { const installDependencies: vscode.Disposable = vscode.commands.registerCommand( "deviceSimulatorExpress.common.installDependencies", async () => { - pythonExecutablePath = await setupService.setupEnv( - context, - telemetryAI, - true - ); + pythonExecutablePath = await setupService.setupEnv(context, true); telemetryAI.trackFeatureUsage( TelemetryEventName.COMMAND_INSTALL_EXTENSION_DEPENDENCIES ); @@ -1036,10 +1031,7 @@ export async function activate(context: vscode.ExtensionContext) { const configsChanged = vscode.workspace.onDidChangeConfiguration( async () => { if (utils.checkConfig(CONFIG.CONFIG_ENV_ON_SWITCH)) { - pythonExecutablePath = await setupService.setupEnv( - context, - telemetryAI - ); + pythonExecutablePath = await setupService.setupEnv(context); } } ); @@ -1091,7 +1083,7 @@ const updateCurrentFileIfPython = async ( if ( currentTextDocument && utils.getActiveEditorFromPath(currentTextDocument.fileName) === - undefined + undefined ) { await vscode.window.showTextDocument( currentTextDocument, diff --git a/src/extension_utils/utils.ts b/src/extension_utils/utils.ts index 4cc915e1d..b7be3b71c 100644 --- a/src/extension_utils/utils.ts +++ b/src/extension_utils/utils.ts @@ -73,7 +73,7 @@ export function tryParseJSON(jsonString: string): any | boolean { if (jsonObj && typeof jsonObj === "object") { return jsonObj; } - } catch (exception) { } + } catch (exception) {} return false; } diff --git a/src/service/setupService.ts b/src/service/setupService.ts index e1943ad8d..569dbc943 100644 --- a/src/service/setupService.ts +++ b/src/service/setupService.ts @@ -25,16 +25,13 @@ export class SetupService { private telemetryAI: TelemetryAI; constructor(telemetryAI: TelemetryAI) { - this.telemetryAI = telemetryAI + this.telemetryAI = telemetryAI; } public setupEnv = async ( context: vscode.ExtensionContext, - telemetryAI: TelemetryAI, needsResponse: boolean = false ) => { - this.telemetryAI = telemetryAI - const originalpythonExecutablePath = await this.getCurrentPythonExecutablePath(); let pythonExecutablePath = originalpythonExecutablePath; const pythonExecutableName: string = @@ -162,6 +159,84 @@ export class SetupService { return pythonExecutablePath; }; + public getCurrentPythonExecutablePath = async () => { + let originalpythonExecutablePath = ""; + + // try to get name from interpreter + try { + originalpythonExecutablePath = getConfig(CONFIG.PYTHON_PATH); + } catch (err) { + originalpythonExecutablePath = GLOBAL_ENV_VARS.PYTHON; + } + + if ( + originalpythonExecutablePath === GLOBAL_ENV_VARS.PYTHON || + originalpythonExecutablePath === "" + ) { + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_AUTO_RESOLVE_PYTHON_PATH + ); + try { + const { stdout } = await this.executePythonCommand( + GLOBAL_ENV_VARS.PYTHON, + `-c "import sys; print(sys.executable)"` + ); + originalpythonExecutablePath = stdout.trim(); + } catch (err) { + this.telemetryAI.trackFeatureUsage( + TelemetryEventName.SETUP_NO_PYTHON_PATH + ); + 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 ""; + } + } + // 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; + }; + public isPipInstalled = async (pythonExecutablePath: string) => { try { const { stdout } = await this.executePythonCommand( @@ -409,9 +484,7 @@ export class SetupService { if (backupPythonPath !== "") { errMessage = `${errMessage} Using original interpreter at: ${backupPythonPath}.`; } - if ( - !(await this.installDependencies(context, pythonPath)) - ) { + if (!(await this.installDependencies(context, pythonPath))) { vscode.window .showErrorMessage( CONSTANTS.ERROR.DEPENDENCY_DOWNLOAD_ERROR, @@ -430,82 +503,4 @@ export class SetupService { } return pythonPath; }; - public getCurrentPythonExecutablePath = async () => { - let originalpythonExecutablePath = ""; - - // try to get name from interpreter - try { - originalpythonExecutablePath = getConfig(CONFIG.PYTHON_PATH); - } catch (err) { - originalpythonExecutablePath = GLOBAL_ENV_VARS.PYTHON; - } - - if ( - originalpythonExecutablePath === GLOBAL_ENV_VARS.PYTHON || - originalpythonExecutablePath === "" - ) { - this.telemetryAI.trackFeatureUsage( - TelemetryEventName.SETUP_AUTO_RESOLVE_PYTHON_PATH - ); - try { - const { stdout } = await this.executePythonCommand( - GLOBAL_ENV_VARS.PYTHON, - `-c "import sys; print(sys.executable)"` - ); - originalpythonExecutablePath = stdout.trim(); - } catch (err) { - this.telemetryAI.trackFeatureUsage( - TelemetryEventName.SETUP_NO_PYTHON_PATH - ); - 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 ""; - } - } - // 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; - }; - }