- Шаг 2. Создаём Node.js-приложение
- Связываем Electron и Python
- Создание процесса Python с помощью child_process
- Использование python-shell
- Использование связи клиент-сервер
- Резюме
- Источники
- How to run a Python script from Node.js
- Why you might need to run Python scripts from Node.js
- Сall your Python script from Node.js using Child Process module
- Run Python script, that uses specific Python libraries
Шаг 2. Создаём Node.js-приложение
Начнём с создания папки для проекта и файла package.json внутри нее с помощью следующих команд:
$ mkdir python-nodejs-example $ cd python-nodejs-example $ npm init -y
Команда init сгенерирует файл package.json со следующим содержанием:
< "name": "python-nodejs-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": < "test": "echo \"Error: no test specified\" && exit 1" >, "keywords": [], "author": "", "license": "ISC" >
Вы можете вручную настроить значения в этом файле на свое усмотрение. Далее создайте внутри рабочей директории два файла: index.html и index.js :
$ touch index.js $ touch index.html
Связываем Electron и Python
Каким-то образом нам понадобится связать между собой Electron и Python.
В компьютерных науках inter-process communication или interprocess communication (IPC) относится к механизмам, которые предоставляет ОС, чтобы позволить процессам управлять общими данными. Как правило, приложения могут использовать IPC, классифицированные как клиенты и серверы, где клиент запрашивает данные, а сервер отвечает на запросы.
IPC относится к набору механизмов, поддерживаемых ОС, которые позволяют различным локальным или удаленным процессам взаимодействовать друг с другом. Например, в нашем случае мы хотим разрешить связь между процессами Electron и Python.
Рассмотрим, как этого достичь.
Создание процесса Python с помощью child_process
Node.js предоставляет модуль child_process, позволяющий создавать дочерние процессы.
Давайте используем его, чтобы породить процесс Python и запустить простой скрипт calc.py. Мы будем использовать simplecalculator для выполнения простых вычислений в Python, поэтому, сначала запустим следующую команду для его установки:
sudo pip install simplecalculator
Внутри папки проекта создайте папку py, а внутри создайте файл calc.py:
$ mkdir py & cd py $ touch calc.py
Откройте calc.py и добавьте следующий код, который выполняет вычисление и выводит результат в стандартный вывод:
from sys import argv from calculator.simple import SimpleCalculator def calc(text): try: c = SimpleCalculator() c.run(text) return c.log[-1] except Exception as e: print(e) return 0.0 if __name__ == '__main__': print(calc(argv[1]))
Затем создайте renderer.js и добавьте туда следующий код для создания процесса Python и выполнения скрипта py/calc.py:
function sendToPython() < var python = require('child_process').spawn('python', ['./py/calc.py', input.value]); python.stdout.on('data', function (data) < console.log("Python response: ", data.toString('utf8')); result.textContent = data.toString('utf8'); >); python.stderr.on('data', (data) => < console.error(`stderr: $`); >); python.on('close', (code) => < console.log(`child process exited with code $`); >); > btn.addEventListener('click', () => < sendToPython(); >); btn.dispatchEvent(new Event('click'));
Затем откройте index.html и обновите его следующим образом:
Simple Python Calculator!
Input something like 1 + 1
.
Got
Использование python-shell
Теперь посмотрим, как использовать python-shell.
Python-shell – это npm-пакет, обеспечивающий простой способ запуска скриптов Python из Node.js с межпроцессным взаимодействием и обработкой ошибок.
Вы можете также использовать python-shell для:
- порождения скриптов Python;
- переключения между текстовым, JSON и двоичным режимами;
- выполнения передачи данных через потоки stdin и stdout;
- получения трассировок стека в случае возникновения ошибок.
Перейдите в терминал и выполните следующую команду для установки python-shell:
$ npm install --save python-shell
Затем откройте renderer.js и перепишите функцию sendToPython() следующим образом:
function sendToPython() < var < PythonShell >= require('python-shell'); let options = < mode: 'text', args: [input.value] >; PythonShell.run('./py/calc.py', options, function (err, results) < if (err) throw err; // результаты - это массив, состоящий из сообщений, собранных во время выполнения console.log('results: ', results); result.textContent = results[0]; >); >
Использование связи клиент-сервер
Теперь рассмотрим другой способ создания связи между Python и Electron с помощью сервера HTTP.
В терминале выполните следующую команду для установки Flask и Flask-Cors:
$ pip install flask $ pip install Flask-Cors
В папке py вашего проекта создайте файл server.py и добавьте следующий код для запуска сервера Flask, который выполняет вычисление и возвращает результат в виде HTTP-ответа:
import sys from flask import Flask from flask_cors import cross_origin from calculator.simple import SimpleCalculator def calcOp(text): try: c = SimpleCalculator() c.run(text) return c.log[-1] except Exception as e: print(e) return 0.0 app = Flask(__name__) @app.route("/") @cross_origin() def calc(input): return calcOp(input) if __name__ == "__main__": app.run(host='127.0.0.1', port=5001)
Затем откройте файл renderer.js и добавьте следующий код для создания Python и запуска server.py:
let input = document.querySelector('#input') let result = document.querySelector('#result') let btn = document.querySelector('#btn') function sendToPython() < var < PythonShell >= require('python-shell'); let options = < mode: 'text' >; PythonShell.run('./py/server.py', options, function (err, results) < if (err) throw err; // результаты - это массив, состоящий из сообщений, собранных во время выполнения console.log('response: ', results); >); > function onclick()< fetch(`http://127.0.0.1:5001/$`).then((data)=>< return data.text(); >).then((text)=>< console.log("data: ", text); result.textContent = text; >).catch(e=>< console.log(e); >) > sendToPython(); btn.addEventListener('click', () => < onclick(); >); btn.dispatchEvent(new Event('click'))
Резюме
В этом руководстве мы представили Electron для Python-разработчиков, который может быть отличным и простым инструментом для создания GUI с помощью современных веб-технологий, основанных на HTML, CSS и JavaScript. На понятном примере с калькулятором были рассмотрены различные способы подключения Python и Electron: child_process, python-shell и сервер HTTP (Flask).
Источники
How to run a Python script from Node.js
Quite often we hear disputes about what is better to use Node.js or Python. But what if one day, you come up with a great idea to combine those, for instance to use some Python libraries in your application, but you just do not have any idea how to integrate it with your Node.js application. Of course you can always build API on top of Python backend(Flack, etc), but in that case you need to build, host and manage one more application, when you just need to run a single Python script. That’s why I want to give you step by step instructions on how to achieve this.
To begin with, let’s briefly define what are both languages.
Node.js is a server-side platform developed on Google Chrome’s Javascript Engine. Node.js allows to develop data-intensive real-time web applications. It is written on JavaScript and allows to run applications on various operating systems such as Windows, Linux, and Mac OS.
Python is an object-oriented programming language used to create dynamic web applications. The syntax of Python and dynamic typing make it an ideal programming language for scripting.
Why you might need to run Python scripts from Node.js
- Node js can’t run heavy multitask as it works on a single thread basis, making it difficult for Node Js to execute CPU-bound tasks. Whenever any request is received, the system will complete the request once and only accept the other request. This process slows down the system and ends up in a massive delay.
- Python is much more suitable for that back-end applications, that use numerical computations, big data solutions, and machine learning.
- Python has one of the largest ecosystems out of any programming community and a lot of open-source libraries. For instance some of built-in AI libraries (Pylearn2, Tensorflow for neural networks, and Scikit-learn for data analysis).
Сall your Python script from Node.js using Child Process module
You will find several libraries that can help you with this issue. As for me, I prefer to use built in child_process. The node:child_process module provides the ability to spawn subprocesses. This capability is primarily provided by the child_process.spawn() function.
First, we need to create a server.js file as the server for our app. Where we suppose that x and y are large arrays.
const express = require('express'); const < spawnSync >= require('child_process'); const < readFile >= require('fs/promises'); const < appendFile >= require('fs/promises'); const < join >= require('path'); const app = express(); app.get("/", async (req, res, next)=>< const X = [1,2, 5] // large array const y = [[1,2], [2,3], [1,2]] // large array await appendFile( join(`scripts/args.json`), JSON.stringify(< X, y >), < encoding: 'utf-8', flag: 'w', >, ); const pythonProcess = await spawnSync('python3', [ '/usr/src/app/scripts/python-script.py', 'first_function', '/usr/src/app/scripts/args.json', '/usr/src/app/scripts/results.json' ]); const result = pythonProcess.stdout?.toString()?.trim(); const error = pythonProcess.stderr?.toString()?.trim(); const status = result === 'OK'; if (status) < const buffer = await readFile('/usr/src/app/scripts/results.json'); const resultParsed = JSON.parse(buffer?.toString()); res.send(resultParsed.toString()) >else < console.log(error) res.send(JSON.stringify(< status: 500, message: 'Server error' >)) > >); //Creates the server on default port 8000 and can be accessed through localhost:8000 const port=8000; app.listen(port, () => console.log(`Server is listening on PORT $`));
The first parameter, that was passed to the spawnSync is command <string> (the command to run, in this case is python3 — the version of python that is required to run your libraries), the second is args <string[]> (list of string arguments). You can also pass the third parameter options, the more detailed information can be found by the link.
I propose to consider in more detail the list I am passing as the second argument:
- ‘/usr/src/app/scripts/python-script.py’ — is the path of the script I need to execute
- ‘first_function’ — is the name of the function I want to run inside that file
- ‘/usr/src/app/scripts/args.json’ — is the path to the file with arguments to this function. Actually you can directly pass those arguments using JSON.stringify()function in Node.js, but as for me it’s better to write those arguments down into the file and then read that from Python script. One of the reasons is that you might need to pass as arguments large arrays of data and this will cause errors
- ‘/usr/src/app/scripts/results.json’ — is the path, where I want my Python script to write results, after it is finished, so I can read that from Node.js.
Passed arguments can be found in sys.argvarray in Python script. The sys.argv list is a list that contains command line arguments in a python program. Whenever we run a python program from a command line interface, we can pass different arguments to the program. The program stores all the arguments and the file name of the python file in the sys.argv list. This can be seen in more detail by writing a simple example script:
import json import sys def first_function(): json_obj = open(sys.argv[2]) data = json.load(json_obj) calculatedResults = [1,2,4,3] # it is just example of data to return, in fact you will calculate it bellow X = data["X"] y = data["y"] # do some your calculations based on X and y and put the result to the calculatedResults # print(make_pipeline) json_object_result = json.dumps(calculatedResults, indent=4) with open(sys.argv[3], "w") as outfile: outfile.write(json_object_result) print("OK") if sys.argv[1] == 'first_function': first_function() sys.stdout.flush()
In two words, in Node.js script we write down to the file all required arguments, run spawnSync passing list of arguments and after Python script reads passed arguments from the file, makes all calculations and writes down to the file all results. At the moments all this results can be read in Node.js from file.
Run Python script, that uses specific Python libraries
One of the main goals is to have ability to use built-in AI or other libraries. It’s always not a good idea to install dependencies and libraries globally in a system, it can lead to version conflicts, deployment difficulties, and problems with multiple developers working on a project at the same time, for example. That’s why the perfect way is to use Docker. You can build the image yourself, but the easiest solution is to use an existing one.
FROM nikolaik/python-nodejs:latest as development WORKDIR /usr/src/app RUN pip install scikit-learn numpy pandas simplejson COPY ./package.json ./yarn.lock ./ RUN yarn RUN yarn install COPY ./ ./ RUN yarn build ENV PORT 4000 EXPOSE $PORT # Production mode FROM nikolaik/python-nodejs:latest as production RUN pip install scikit-learn numpy pandas simplejson ARG NODE_ENV=production ENV NODE_ENV=$ WORKDIR /usr/src/app COPY --from=development /usr/src/app ./ ENV PORT 4000 EXPOSE $PORT CMD ["node", "dist/main"]
For now we can use scikit-learn, numpy, pandas and simplejson libraries on Python script, as they are available in the created docker container.
By following the steps above, you can create a new process and execute a Python script from your Node.js application. This can be useful in cases where you need to integrate Python code into your Node.js application or when you need to perform certain tasks that are better suited for Python. Described simple app can be downloaded from the Github, there you can also find instructions how to run it.
Happy coding and wish you to have a nice day!