Skip to content
This repository has been archived by the owner on Dec 23, 2021. It is now read-only.

Commit

Permalink
Install dependencies for users (#135)
Browse files Browse the repository at this point in the history
PBI: 32822 
Task: 32598

* install dependencies for users

* Only show the dialog once if you click yes

* Update docs

* update develope docs

* Add success  notifications
  • Loading branch information
LukeSlev authored Aug 22, 2019
1 parent e4e73f2 commit 418bcd9
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 44 deletions.
43 changes: 16 additions & 27 deletions docs/developers-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,22 @@
(for example it could be found at : `c:\users\<...>\appdata\local\programs\python\python37\lib\site-packages\pip`)
- Run in a console `python -m pip install --upgrade pip`

- Playsound

- Run the command in a console : `pip install playsound`

- pytest

- Run the command in a console : `pip install pytest`

- Pywin32

- Run the command in a console : `pip install pywin32`

- Python-Socketio

- Run the command in a console : `pip install python-socketio`

- Application Insights

- Run the command in a console : `pip install applicationinsights`

- Requests

- Run the command in a console : `pip install requests`

- Application Insights

- Run the command in a console : `pip install applicationinsights`
- Python Modules

- **Note:** On extension activation you will be prompted asking if you want the modules to be automatically installed for you
- Playsound
- Run the command in a console : `pip install playsound`
- pytest
- Run the command in a console : `pip install pytest`
- Pywin32
- **Note:** This is only needed for Windows computers
- Run the command in a console : `pip install pywin32`
- Python-Socketio
- Run the command in a console : `pip install python-socketio`
- Requests
- Run the command in a console : `pip install requests`
- Application Insights
- Run the command in a console : `pip install applicationinsights`

- VS Code

Expand Down
16 changes: 9 additions & 7 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ _Note: You need to install all the dependencies in order to use the extension._
_(Note: the easiest way to do it might be when you install Python, you can select the "Add to PATH" option directly. Otherwise you can search how to insert it manually, but make sure that when you type `python` in a terminal, the command is recognized.)_
- Python VS Code extension (downloaded from VS Code Marketplace)
- **Note:** This extension is installed automatically from the marketplace when you install our extension
- Playsound :
- `python -m pip install --upgrade pip`
- `pip install playsound`
- Pywin32 : `pip install pywin32`
- Python-Socketio : `pip install python-socketio`
- Requests : `pip install requests`
- Application Insights: `pip install applicationinsights`
- Python Modules
- **Note:** On extension activation you will be prompted asking if you want the modules to be automatically installed for you
- Playsound : `pip install playsound`
- Pywin32 :
- **Note:** This is only needed for Windows computers
- `pip install pywin32`
- Python-Socketio : `pip install python-socketio`
- Requests : `pip install requests`
- Application Insights: `pip install applicationinsights`

## How to use the Extension

Expand Down
6 changes: 5 additions & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ gulp.task("clean", () => {
);
});

const pythonToMove = ["./src/adafruit_circuitplayground/*.*", "./src/*.py"];
const pythonToMove = [
"./src/adafruit_circuitplayground/*.*",
"./src/*.py",
"./src/requirements.txt"
];

gulp.task("python-compile", () => {
// the base option sets the relative root for the set of files,
Expand Down
6 changes: 5 additions & 1 deletion locales/en/out/constants.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"dialogResponses.exampleCode": "Example Code on GitHub",
"dialogResponses.help": "I need help",
"dialogResponses.installPython": "Install from python.org",
"dialogResponses.installNow": "Install Now",
"dialogResponses.dontInstall": "Don't Install",
"dialogResponses.tutorials": "Tutorials on Adafruit",
"error.debuggerServerInitFailed": "Warning : The Debugger Server cannot be opened. Please try to free the port {0} if it's already in use or select another one in your Settings 'Pacifica: Debugger Server Port' and start another debug session.\n You can still debug your code but you won't be able to use the Simulator.",
"error.debuggingSessionInProgress": "[ERROR] A debugging session is currently in progress, please stop it before running your code. \n",
Expand All @@ -24,14 +26,16 @@
"info.deploySuccess": "\n[INFO] Code successfully deployed\n",
"info.extensionActivated": "Congratulations, your extension Adafruit_Simulator is now active!",
"info.firstTimeWebview": "To reopen the simulator click on the \"Open Simulator\" button on the upper right corner of the text editor, or select the command \"Open Simulator\" from command palette.",
"info.installPythonDependencies": "Do you want us to try and install this extensions dependencies for you?",
"error.invalidFileExtensionDebug": "The file you tried to run isn\\'t a Python file.",
"info.newFile": "New to Python or the Circuit Playground Express? We are here to help!",
"info.redirect": "You are being redirected.",
"info.runningCode": "Running user code",
"info.privacyStatement": "Privacy Statement",
"info.successfulInstall": "Successfully installed Python dependencies.",
"info.thirdPartyWebsite": "By clicking \"Agree and Proceed\" you will be redirected to adafruit.com, a third party website not managed by Microsoft. Please note that your activity on adafruit.com is subject to Adafruit's privacy policy",
"info.welcomeOutputTab": "Welcome to the Adafruit Simulator output tab !\n\n",
"label.webviewPanel": "Adafruit CPX",
"name": "Pacifica Simulator",
"warning.agreeAndRun": "By selecting ‘Agree and Run’, you understand the extension executes Python code on your local computer, which may be a potential security risk."
}
}
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@
"description": "%pacificaExtension.configuration.properties.device%",
"scope": "resource"
},
"pacifica.showDependencyInstall": {
"type": "boolean",
"default": true,
"scope": "resource"
},
"pacifica.debuggerServerPort": {
"type": "number",
"default": 5577,
Expand Down Expand Up @@ -330,4 +335,4 @@
"extensionDependencies": [
"ms-python.python"
]
}
}
20 changes: 20 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ const localize: nls.LocalizeFunc = nls.config({
messageFormat: nls.MessageFormat.file
})();

export const CONFIG = {
SHOW_DEPENDENCY_INSTALL: "pacifica.showDependencyInstall"
}

export const CONSTANTS = {
DEBUG_CONFIGURATION_TYPE: "pacifica",
DEPENDENCY_CHECKER: {
PIP3: "pip3",
PYTHON: "python",
PYTHON3: "python3",
PYTHON_LAUNCHER: "py -3"
Expand Down Expand Up @@ -89,6 +94,10 @@ export const CONSTANTS = {
)
},
INFO: {
ARE_YOU_SURE: localize(
"info.areYouSure",
"Are you sure you don't want to install the dependencies? The extension can't run without installing it"
),
CLOSED_SERIAL_PORT: (port: string) => {
return localize(
"info.closedSerialPort",
Expand Down Expand Up @@ -130,6 +139,10 @@ export const CONSTANTS = {
"info.incorrectFileNameForSimulatorPopup",
'We want your code to work on your actual board as well. Make sure you name your file "code.py" or "main.py" to be able to run your code on an actual physical device'
),
INSTALL_PYTHON_DEPENDENCIES: localize(
"info.installPythonDependencies",
"Do you want us to try and install this extensions dependencies for you?"
),
INVALID_FILE_NAME_DEBUG: localize(
"info.invalidFileNameDebug",
'The file you tried to debug isn\'t named "code.py" or "main.py". Rename your file if you want your code to work on your actual device.'
Expand All @@ -156,6 +169,7 @@ export const CONSTANTS = {
),
REDIRECT: localize("info.redirect", "You are being redirected."),
RUNNING_CODE: localize("info.runningCode", "Running user code"),
SUCCESSFUL_INSTALL: localize("info.successfulInstall", "Successfully installed Python dependencies."),
THIRD_PARTY_WEBSITE: localize(
"info.thirdPartyWebsite",
'By clicking "Agree and Proceed" you will be redirected to adafruit.com, a third party website not managed by Microsoft. Please note that your activity on adafruit.com is subject to Adafruit\'s privacy policy'
Expand Down Expand Up @@ -298,6 +312,12 @@ export namespace DialogResponses {
export const NO: MessageItem = {
title: localize("dialogResponses.No", "No")
};
export const INSTALL_NOW: MessageItem = {
title: localize("dialogResponses.installNow", "Install Now")
};
export const DONT_INSTALL: MessageItem = {
title: localize("dialogResponses.dontInstall", "Don't Install")
};
export const PRIVACY_STATEMENT: MessageItem = {
title: localize("info.privacyStatement", "Privacy Statement")
};
Expand Down
2 changes: 2 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export async function activate(context: vscode.ExtensionContext) {
// Add our library path to settings.json for autocomplete functionality
updatePythonExtraPaths();

await utils.checkPythonDependencies(context)

// Generate cpx.json
utils.generateCPXConfig();
pythonExecutableName = await utils.setPythonExectuableName();
Expand Down
29 changes: 22 additions & 7 deletions src/extension_utils/dependencyChecker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface IDependency {
}

const PYTHON3_REGEX = RegExp("^(Python )(3\\.[0-9]+\\.[0-9]+)");
const MINIMUM_PYTHON_VERSION = "3.5.0"

export class DependencyChecker {
constructor() { }
Expand All @@ -27,26 +28,36 @@ export class DependencyChecker {
const userOnWin: boolean = userOS.indexOf("win") === 0;

if (
await this.runPythonVersionCommand(CONSTANTS.DEPENDENCY_CHECKER.PYTHON3)
await this.runCommandVersion(CONSTANTS.DEPENDENCY_CHECKER.PYTHON3, MINIMUM_PYTHON_VERSION)
) {
state = true;
dependencyName = CONSTANTS.DEPENDENCY_CHECKER.PYTHON3;
} else if (
await this.runPythonVersionCommand(CONSTANTS.DEPENDENCY_CHECKER.PYTHON)
await this.runCommandVersion(CONSTANTS.DEPENDENCY_CHECKER.PYTHON, MINIMUM_PYTHON_VERSION)
) {
state = true;
dependencyName = CONSTANTS.DEPENDENCY_CHECKER.PYTHON;
} else if (
userOnWin &&
(await this.runPythonVersionCommand(
CONSTANTS.DEPENDENCY_CHECKER.PYTHON_LAUNCHER
(await this.runCommandVersion(
CONSTANTS.DEPENDENCY_CHECKER.PYTHON_LAUNCHER,
MINIMUM_PYTHON_VERSION
))
) {
state = true;
dependencyName = CONSTANTS.DEPENDENCY_CHECKER.PYTHON;
} else {
state = false;
}
} else if (dependencyName === CONSTANTS.DEPENDENCY_CHECKER.PIP3) {
if (
await this.runCommandVersion(CONSTANTS.DEPENDENCY_CHECKER.PIP3)
) {
state = true;
dependencyName = CONSTANTS.DEPENDENCY_CHECKER.PYTHON3;
} else {
state = false;
}
}
return {
payload: {
Expand All @@ -56,12 +67,16 @@ export class DependencyChecker {
};
}

private async runPythonVersionCommand(command: string) {
let installed: boolean;
private async runCommandVersion(command: string, versionDependency?: string) {
let installed: boolean = false;
try {
const { stdout } = await exec(command + " --version");
const matches = PYTHON3_REGEX.exec(stdout);
installed = matches ? compareVersions(matches[2], "3.5.0") >= 0 : false;
if (versionDependency) {
installed = matches ? compareVersions(matches[2], versionDependency) >= 0 : false;
} else {
installed = true
}
} catch (err) {
installed = false;
}
Expand Down
70 changes: 70 additions & 0 deletions src/extension_utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ import { DependencyChecker } from "./dependencyChecker";
import { DeviceContext } from "../deviceContext";
import * as vscode from "vscode";
import {
CONFIG,
CONSTANTS,
CPX_CONFIG_FILE,
DialogResponses,
USER_CODE_NAMES,
SERVER_INFO
} from "../constants";
import { CPXWorkspace } from "../cpxWorkspace";
import * as cp from "child_process";
import * as util from "util";
const exec = util.promisify(cp.exec);

// tslint:disable-next-line: export-name
export const getPathToScript = (
Expand Down Expand Up @@ -160,6 +164,14 @@ export const checkPythonDependency = async () => {
return result.payload;
};

export const checkPipDependency = async () => {
const dependencyChecker: DependencyChecker = new DependencyChecker();
const result = await dependencyChecker.checkDependency(
CONSTANTS.DEPENDENCY_CHECKER.PIP3
);
return result.payload;
}

export const setPythonExectuableName = async () => {
// Find our what command is the PATH for python
let executableName: string = "";
Expand Down Expand Up @@ -217,3 +229,61 @@ export const getServerPortConfig = (): number => {
}
return SERVER_INFO.DEFAULT_SERVER_PORT;
};

export const checkConfig = (configName: string): boolean => {
return vscode.workspace.getConfiguration().get(configName) === true;
}

export const checkPythonDependencies = async (context: vscode.ExtensionContext) => {
let hasInstalledDependencies: boolean = false;
if (checkPipDependency() && checkPythonDependency()) {
if (checkConfig(CONFIG.SHOW_DEPENDENCY_INSTALL)) {
hasInstalledDependencies = await promptInstallPythonDependencies(context);
if (hasInstalledDependencies) {
await vscode.workspace.getConfiguration().update(CONFIG.SHOW_DEPENDENCY_INSTALL, false);
}
}
} else {
hasInstalledDependencies = false;
}
return hasInstalledDependencies;
}


export const promptInstallPythonDependencies = (context: vscode.ExtensionContext) => {
return vscode.window.showInformationMessage(
CONSTANTS.INFO.INSTALL_PYTHON_DEPENDENCIES,
DialogResponses.YES,
DialogResponses.NO)
.then((selection: vscode.MessageItem | undefined) => {
if (selection === DialogResponses.YES) {
return installPythonDependencies(context);
} else if (selection === DialogResponses.NO) {
return vscode.window.showInformationMessage(
CONSTANTS.INFO.ARE_YOU_SURE,
DialogResponses.INSTALL_NOW,
DialogResponses.DONT_INSTALL
).then((installChoice: vscode.MessageItem | undefined) => {
if (installChoice === DialogResponses.INSTALL_NOW) {
return installPythonDependencies(context);
} else {
return false;
}
})
}
});
}

export const installPythonDependencies = async (context: vscode.ExtensionContext) => {
let installed: boolean = false;
try {
const requirementsPath: string = getPathToScript(context, "out", "requirements.txt");
const { stdout } = await exec(`pip3 install -r ${requirementsPath}`);
installed = true;
vscode.window.showInformationMessage(CONSTANTS.INFO.SUCCESSFUL_INSTALL)
} catch (err) {
console.error(err);
installed = false;
}
return installed
}

0 comments on commit 418bcd9

Please sign in to comment.