Skip to content
This repository was archived by the owner on Dec 23, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,11 @@ export const HELPER_FILES = {
DEVICE_PY: "device.py",
PROCESS_USER_CODE_PY: "process_user_code.py",
PYTHON_EXE: "python.exe",
PYTHON: "python",
};

export const GLOBAL_ENV_VARS = {
PYTHON: "python",
};

export default CONSTANTS;
16 changes: 9 additions & 7 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
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 vscode from "vscode";
import {
Expand All @@ -12,6 +13,7 @@ import {
CPX_CONFIG_FILE,
DEFAULT_DEVICE,
DialogResponses,
GLOBAL_ENV_VARS,
HELPER_FILES,
SERVER_INFO,
TelemetryEventName,
Expand All @@ -34,7 +36,7 @@ import { registerDefaultFontFaces } from "office-ui-fabric-react";
let currentFileAbsPath: string = "";
let currentTextDocument: vscode.TextDocument;
let telemetryAI: TelemetryAI;
let pythonExecutableName: string = "python";
let pythonExecutablePath: string = GLOBAL_ENV_VARS.PYTHON;
let configFileCreated: boolean = false;
let inDebugMode: boolean = false;
// Notification booleans
Expand Down Expand Up @@ -98,7 +100,7 @@ export async function activate(context: vscode.ExtensionContext) {
// doesn't trigger lint errors
updatePylintArgs(context);

pythonExecutableName = await utils.setupEnv(context);
pythonExecutablePath = await utils.setupEnv(context);

try {
utils.generateCPXConfig();
Expand All @@ -108,7 +110,7 @@ export async function activate(context: vscode.ExtensionContext) {
configFileCreated = false;
}

if (pythonExecutableName === "") {
if (pythonExecutablePath === "") {
return;
}

Expand Down Expand Up @@ -442,7 +444,7 @@ export async function activate(context: vscode.ExtensionContext) {
const installDependencies: vscode.Disposable = vscode.commands.registerCommand(
"deviceSimulatorExpress.common.installDependencies",
async () => {
pythonExecutableName = await utils.setupEnv(context, true);
pythonExecutablePath = await utils.setupEnv(context, true);
telemetryAI.trackFeatureUsage(
TelemetryEventName.COMMAND_INSTALL_EXTENSION_DEPENDENCIES
);
Expand Down Expand Up @@ -568,7 +570,7 @@ export async function activate(context: vscode.ExtensionContext) {
active_device: currentActiveDevice,
});

childProcess = cp.spawn(pythonExecutableName, [
childProcess = cp.spawn(pythonExecutablePath, [
utils.getPathToScript(
context,
CONSTANTS.FILESYSTEM.OUTPUT_DIRECTORY,
Expand Down Expand Up @@ -727,7 +729,7 @@ export async function activate(context: vscode.ExtensionContext) {
CONSTANTS.INFO.FILE_SELECTED(currentFileAbsPath)
);

const deviceProcess = cp.spawn(pythonExecutableName, [
const deviceProcess = cp.spawn(pythonExecutablePath, [
utils.getPathToScript(
context,
CONSTANTS.FILESYSTEM.OUTPUT_DIRECTORY,
Expand Down Expand Up @@ -1030,7 +1032,7 @@ export async function activate(context: vscode.ExtensionContext) {
const configsChanged = vscode.workspace.onDidChangeConfiguration(
async () => {
if (utils.checkConfig(CONFIG.CONFIG_ENV_ON_SWITCH)) {
pythonExecutableName = await utils.setupEnv(context);
pythonExecutablePath = await utils.setupEnv(context);
}
}
);
Expand Down
123 changes: 74 additions & 49 deletions src/extension_utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
CONSTANTS,
CPX_CONFIG_FILE,
DialogResponses,
GLOBAL_ENV_VARS,
HELPER_FILES,
SERVER_INFO,
USER_CODE_NAMES,
Expand Down Expand Up @@ -169,9 +170,12 @@ export function generateCPXConfig(): void {
fs.writeFileSync(cpxConfigFilePath, JSON.stringify(cpxJson, null, 4));
}

export const isPipInstalled = async (pythonExecutableName: string) => {
export const isPipInstalled = async (pythonExecutablePath: string) => {
try {
await executePythonCommand(pythonExecutableName, " -m pip");
const { stdout } = await executePythonCommand(
pythonExecutablePath,
" -m pip"
);
return true;
} catch (err) {
vscode.window
Expand Down Expand Up @@ -220,7 +224,7 @@ export const addVisibleTextEditorCallback = (

export const filterForPythonFiles = (textEditors: vscode.TextEditor[]) => {
return textEditors
.filter(editor => editor.document.languageId === "python")
.filter(editor => editor.document.languageId === GLOBAL_ENV_VARS.PYTHON)
.map(editor => editor.document.fileName);
};

Expand Down Expand Up @@ -273,30 +277,30 @@ export const getTelemetryState = () => {

export const checkIfVenv = async (
context: vscode.ExtensionContext,
pythonExecutableName: string
pythonExecutablePath: string
) => {
const venvCheckerPath: string = getPathToScript(
context,
CONSTANTS.FILESYSTEM.OUTPUT_DIRECTORY,
HELPER_FILES.CHECK_IF_VENV_PY
);
const { stdout } = await executePythonCommand(
pythonExecutableName,
pythonExecutablePath,
`"${venvCheckerPath}"`
);
return stdout.trim() === "1";
};

export const executePythonCommand = async (
pythonExecutableName: string,
pythonExecutablePath: string,
command: string
) => {
return exec(`${createEscapedPath(pythonExecutableName)} ${command}`);
return exec(`${createEscapedPath(pythonExecutablePath)} ${command}`);
};

export const validatePythonVersion = async (pythonExecutableName: string) => {
export const validatePythonVersion = async (pythonExecutablePath: string) => {
const { stdout } = await executePythonCommand(
pythonExecutableName,
pythonExecutablePath,
"--version"
);
if (stdout < VERSIONS.MIN_PY_VERSION) {
Expand Down Expand Up @@ -333,7 +337,8 @@ export const hasVenv = async (context: vscode.ExtensionContext) => {

export const promptInstallVenv = (
context: vscode.ExtensionContext,
pythonExecutable: string
pythonExecutable: string,
pythonExecutableName: string
) => {
return vscode.window
.showInformationMessage(
Expand All @@ -343,7 +348,11 @@ export const promptInstallVenv = (
)
.then((selection: vscode.MessageItem | undefined) => {
if (selection === DialogResponses.YES) {
return installPythonVenv(context, pythonExecutable);
return installPythonVenv(
context,
pythonExecutable,
pythonExecutableName
);
} else {
// return pythonExecutable, notifying the caller
// that the user was unwilling to create venv
Expand All @@ -354,19 +363,23 @@ export const promptInstallVenv = (
});
};

export const getPythonVenv = async (context: vscode.ExtensionContext) => {
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),
HELPER_FILES.PYTHON_EXE
pythonExecutableName
);
};

export const installPythonVenv = async (
context: vscode.ExtensionContext,
pythonExecutable: string
pythonExecutable: string,
pythonExecutableName: string
) => {
const pathToEnv: string = getPathToScript(
context,
Expand All @@ -375,7 +388,10 @@ export const installPythonVenv = async (

vscode.window.showInformationMessage(CONSTANTS.INFO.INSTALLING_PYTHON_VENV);

const pythonPath: string = await getPythonVenv(context);
const pythonPath: string = await getPythonVenv(
context,
pythonExecutableName
);

try {
// make venv
Expand Down Expand Up @@ -478,25 +494,26 @@ export const installDependenciesWrapper = async (
}
return pythonPath;
};
export const getCurrentPythonExecutableName = async () => {
let originalPythonExecutableName = "";
export const getCurrentpythonExecutablePath = async () => {
let originalpythonExecutablePath = "";

// try to get name from interpreter
try {
originalPythonExecutableName = getConfig(CONFIG.PYTHON_PATH);
originalpythonExecutablePath = getConfig(CONFIG.PYTHON_PATH);
} catch (err) {
originalPythonExecutableName = "python";
originalpythonExecutablePath = GLOBAL_ENV_VARS.PYTHON;
}

if (
originalPythonExecutableName === "python" ||
originalPythonExecutableName === ""
originalpythonExecutablePath === GLOBAL_ENV_VARS.PYTHON ||
originalpythonExecutablePath === ""
) {
try {
const { stdout } = await exec(
'python -c "import sys; print(sys.executable)"'
const { stdout } = await executePythonCommand(
GLOBAL_ENV_VARS.PYTHON,
`-c "import sys; print(sys.executable)"`
);
originalPythonExecutableName = stdout.trim();
originalpythonExecutablePath = stdout.trim();
} catch (err) {
vscode.window
.showErrorMessage(
Expand All @@ -520,68 +537,76 @@ export const getCurrentPythonExecutableName = async () => {
}
}
// fix path to be absolute
if (!path.isAbsolute(originalPythonExecutableName)) {
originalPythonExecutableName = path.join(
if (!path.isAbsolute(originalpythonExecutablePath)) {
originalpythonExecutablePath = path.join(
vscode.workspace.rootPath,
originalPythonExecutableName
originalpythonExecutablePath
);
}

if (!fs.existsSync(originalPythonExecutableName)) {
if (!fs.existsSync(originalpythonExecutablePath)) {
await vscode.window.showErrorMessage(CONSTANTS.ERROR.BAD_PYTHON_PATH);
return "";
}

if (!(await validatePythonVersion(originalPythonExecutableName))) {
if (!(await validatePythonVersion(originalpythonExecutablePath))) {
return "";
}

return originalPythonExecutableName;
return originalpythonExecutablePath;
};
export const setupEnv = async (
context: vscode.ExtensionContext,
needsResponse: boolean = false
) => {
const originalPythonExecutableName = await getCurrentPythonExecutableName();
let pythonExecutableName = originalPythonExecutableName;

if (!(await areDependenciesInstalled(context, pythonExecutableName))) {
const originalpythonExecutablePath = await getCurrentpythonExecutablePath();
let pythonExecutablePath = originalpythonExecutablePath;
let 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, pythonExecutableName))) {
const pythonExecutableNameVenv = await getPythonVenv(context);
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,
pythonExecutableNameVenv
pythonExecutablePathVenv
))
) {
pythonExecutableName = await installDependenciesWrapper(
pythonExecutablePath = await installDependenciesWrapper(
context,
pythonExecutableNameVenv,
pythonExecutableName
pythonExecutablePathVenv,
pythonExecutablePath
);
} else {
pythonExecutableName = pythonExecutableNameVenv;
pythonExecutablePath = pythonExecutablePathVenv;
}
} else {
pythonExecutableName = await promptInstallVenv(
pythonExecutablePath = await promptInstallVenv(
context,
originalPythonExecutableName
originalpythonExecutablePath,
pythonExecutableName
);
}

if (pythonExecutableName === pythonExecutableNameVenv) {
if (pythonExecutablePath === pythonExecutablePathVenv) {
vscode.window.showInformationMessage(
CONSTANTS.INFO.UPDATED_TO_EXTENSION_VENV
);
vscode.workspace
.getConfiguration()
.update(CONFIG.PYTHON_PATH, pythonExecutableName);
.update(CONFIG.PYTHON_PATH, pythonExecutablePath);
}
}
if (pythonExecutableName === originalPythonExecutableName) {
if (pythonExecutablePath === originalpythonExecutablePath) {
// going with original interpreter, either because
// already in venv or error in creating custom venv
if (checkConfig(CONFIG.SHOW_DEPENDENCY_INSTALL)) {
Expand All @@ -598,7 +623,7 @@ export const setupEnv = async (
if (installChoice === DialogResponses.INSTALL_NOW) {
await installDependenciesWrapper(
context,
pythonExecutableName
pythonExecutablePath
);
} else {
await vscode.window
Expand All @@ -619,7 +644,7 @@ export const setupEnv = async (
) {
await installDependenciesWrapper(
context,
pythonExecutableName
pythonExecutablePath
);
}
}
Expand All @@ -635,5 +660,5 @@ export const setupEnv = async (
);
}

return pythonExecutableName;
return pythonExecutablePath;
};