exchanging data between node and Python with python-shell - python

I am trying to learn how to exchange data between Node and Python with python-shell, on the git repo they have some example code:
Borrowing some of this code, this is app.js:
import {PythonShell} from 'python-shell';
let options = {
mode: 'text',
args: ['get_time()', 'get_weekday()']
};
PythonShell.run('my_script.py', options, function (err, results) {
if (err) throw err;
// results is an array consisting of messages collected during execution
console.log('results: %j', results);
});
And this is my_script.py below that will just print the weekday and current time:
from datetime import datetime
# Create datetime object
date = datetime.now()
# Get the weekday value, as an integer
date.weekday()
# Define a list of weekday names
days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
def get_weekday():
return days[date.weekday()]
def get_time():
return datetime.now().time()
#print(get_time())
#print(get_weekday())
When run app.js this throws an error:
(node:15380) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
Uncaught c:\Users\bbartling\Desktop\javascript\stout\test_SO\app.js:1
import {PythonShell} from 'python-shell';
^^^^^^
SyntaxError: Cannot use import statement outside a module
No debugger available, can not send 'variables'
Process exited with code 1
Any ideas to try? Thanks for any tips not a lot of wisdom here. Can I call these functions on the Python script through python-shell? Just curious if I could use Javascript with this python-shell package to retrieve either the current weekday or current time from Python.

Replace import {PythonShell} from 'python-shell'; with let {PythonShell} = require('python-shell');
Edit: In .py file, import sys and then the arguments passed from nodejs will be available in sys.argv as array. I suppose by putting if checks you can call specific functions and at the end whatever you print in python would be available in results in js
Sample:
.py file:
import sys
def log():
print('hello from python')
if sys.argv[1][0]=="1":
log()
.js file:
let { PythonShell } = require("python-shell");
let options = {
mode: "text",
args: "1",
};
PythonShell.run("my_script.py", options, function (err, results) {
if (err) throw err;
// results is an array consisting of messages collected during execution
console.log("results: %j", results);
});

Thanks to #sandeep.kgp getting me started here's a complete answer thanks to the help of a YouTube video for python-shell. The code below will just pass some values back and forth checking current time with 2 different python pacakges.
app.js
let {PythonShell} = require("python-shell");
let options = {
mode: "text",
args: ["datetime","time"],
};
PythonShell.run("my_script.py", options, function (err, results) {
if (err){
console.log(err)
console.log("An error happened")
}else{
// results is an array consisting of messages collected during execution
console.log("results: ", results);
console.log("Python Script Finished");
}
})
my_script.py
import sys, time
from datetime import datetime
date = datetime.now()
today = date.today()
today.strftime("%d/%m/%Y")
def use_datetime():
print("Using datetime package its: ", today)
def use_time():
print("Using the time package its: ", time.ctime())
try:
for arg in sys.argv:
#print(arg)
if arg == "time":
use_time()
elif arg == "datetime":
use_datetime()
else:
print("An arg passed: ", arg)
except Exception as error:
print(f"Error: {str(error)}")

Related

python-shell node, keep a python script running

Would anyone know when using python-shell if you can keep the python script running when its called from Javascript? Maybe call a method from a Python class from Javascript using python-shell?
For example this code works but it kills the Python script every time its called.
let {PythonShell} = require("python-shell");
let options = {
mode: "text",
args: ["read", "12345:2", "analogInput", "2"],
};
PythonShell.run("bac0Worker.py", options, function (err, results) {
if (err){
console.log(err)
console.log("An error happened")
}else{
// results is an array consisting of messages collected during execution
console.log("results: ", results);
console.log("Python Script Finished");
}
})
On the Python side I am experimenting with a package called BAC0 for BACnet systems. What I am trying to do is figure out if I can keep the BAC0 script running, I think under the hood of BAC0 there's alot of process going on where it may create lots of unnecessary traffic on a BACnet network if the script is started/stopped a lot.
bac0Worker.py
import sys
import BAC0
BAC0.log_level('silence')
bacnet = BAC0.lite()
def do_things(address,object_type,object_instance):
try:
read_vals = f'{address} {object_type} {object_instance} presentValue'
read_result = bacnet.read(read_vals)
if isinstance(read_result, str):
pass
else:
read_result = round(read_result,2)
print(read_result)
except Exception as error:
print("read error")
def main():
# args from Javascript
first = sys.argv[1]
second = sys.argv[2]
third = sys.argv[3]
fourth = sys.argv[4]
# get sensor data
do_things(second, third, fourth)
# all done
bacnet.disconnect()
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("bacnet app error")
bacnet.disconnect()
Like I mentioned the code works if I run from Node I can see the sensor value from the BACnet network 71.7 degrees.
C:\Program Files\nodejs\node.exe .\start.js
results: (1) ['71.7']
Python Script Finished
Hopefully this makes sense, sorry for the odd question but curious if anyone would have any tips for how to keep the Python script running where I could then just pass values (sensor & device addressing info) to BAC0 script to request a BACnet read from the network. In the main function of the Python file if I replace this:
# all done
bacnet.disconnect()
With:
while True:
pass
This would keep the Python file alive I just don't know how to pass values to a live Python script using python-shell. Thanks for any tips not a lot of wisdom here best practices. Curious if I should change my do_things function into a Python class with a method called do_things where then this class could get called from Javascript?
.js
const { PythonShell } = require("python-shell");
let pyshell = new PythonShell("my_script.py");
pyshell.send("hello");
pyshell.on("message", function (message) {
console.log(message);
});
setTimeout(() => {
pyshell.send("Another Hello");
}, 3000);
const end = () => {
pyshell.end(function (err, code, signal) {
if (err) throw err;
console.log("finished");
});
};
// end();
.py
import sys
def log():
print('hello from python')
while True:
inp = input(" ")
if inp=="hello":
log()
this will keep the process running unless you call the end method from the nodejs or some error occurs. This may incur performance issues because python thingy will be sharing same resources as your nodejs. A better way to do this would be to use a microservice like deploy some Rest Api which handles python specific tasks.
Just for fun Ill post what I was able to learn from #sandeep (thanks a million btw), this works. I can print BACnet sensor values which are gathered from Python from running a javascript file on 60 second setInterval(pythonDo, 60000); with keeping the Python file alive:
C:\Program Files\nodejs\node.exe .\start.js
BAC0 start success
68.47
68.43
68.29
68.25
start.js
const { PythonShell } = require("python-shell");
let pyshell = new PythonShell("bac0Worker.py");
function pythonDo() {
pyshell.send("read 12345:2 analogInput 2");
}
pyshell.on("message", function (message) {
console.log(message);
});
var data = setInterval(pythonDo, 60000);
bac0Worker.py
import sys
import BAC0
BAC0.log_level('silence')
bacnet = BAC0.lite()
print("BAC0 start success")
# available BACnet actions
SUBSTRING_READ = "read"
SUBSTRING_WRITE = "write"
SUBSTRING_RELEASE = "release"
def bac0_worker(action, BACnet_request, **kwargs):
value = kwargs.get('value', None)
priority = kwargs.get('priority', None)
if action == "read":
try:
read_vals = f'{BACnet_request} presentValue'
read_result = bacnet.read(read_vals)
if isinstance(read_result, str):
pass
else:
read_result = round(read_result,2)
print(read_result)
except Exception as error:
print("read error")
elif action == "write":
try:
write_vals = f'{BACnet_request} presentValue {value} - {priority}'
bacnet.write(write_vals)
print(write_vals)
except Exception as error:
print("write error")
elif action == "release":
try:
release_vals = f'{BACnet_request} presentValue null - {priority}'
bacnet.write(release_vals)
print(release_vals)
except Exception as error:
print("release error")
else:
return "server error on BACnet opts"
def main():
while True:
# from javascript
inp = input(" ")
for word in inp.split():
if SUBSTRING_READ == word:
stripped_string = inp.replace(SUBSTRING_READ + " ", "")
# do a BACnet READ request with BAC0
bac0_worker(SUBSTRING_READ,stripped_string)
if SUBSTRING_WRITE == word:
print("WRITE Match Found")
if SUBSTRING_RELEASE == word:
print("RELEASE Match Found")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("bacnet app error")
bacnet.disconnect()

How to bundle python code with vsix (vscode extension)

I am creating a vscode extension where I need machine learning tasks to be performed. I have python files that have code that is required in vscode extension. I don't want things to be done using request-response on any python server. What I want is to perform the ML tasks on device (integrated with the vsix).
We have child-process available in js to run basic python file using spawn. It is running fine on both, extension host window and external vscode editor after packaging, with the python code that has basic imports like import sys. But if I try to import some other libraries like numpy, pygments, it is working only on extension host environment, not on other vs window after packaging it. How can I run the typical python code with the vsix?
Below are both the codes that is working fine and not working at all-
TS file (MLOps.ts)-
import { ChildProcessWithoutNullStreams, spawn } from "child_process";
import { join } from "path";
import * as vscode from 'vscode'
export async function pythonOps(): Promise<string> {
var result = "testt"
var promise = await new Promise((resolve, reject) => {
var p = __dirname.split('\\')
p.pop()
var path = p.join('\\')
var pyPath = join(path, 'src', 'py_operations.py')
var result = "blank result"
var arg1 = "arg one"
var arg2 = "arg two"
var py_process = spawn('python', [pyPath, arg1, arg2])
py_process.stdout.on('data', (data: any) => {
vscode.window.showInformationMessage(data.toString())
result = data.toString()
})
})
}
Working Python code (py_operations.py)- This code is working on both, on extension host window and after packaging the extension and installing the vsix on other system.
import sys
print("Some text with: ",sys.argv[0], sys.argv[1], sys.argv[2])
sys.stdout.flush()
Not working Python code- This code is working only on extension host window, and not working after packaging this and isntalling on other system.
import sys
from pygments.lexers.javascript import TypeScriptLexer
lexer = TypeScriptLexer()
src = "alert('text here')"
lexer_tokens = lexer.get_tokens(src)
l = []
for t in lexer_tokens:
l.append(t[1])
print("list: ",l)
sys.stdout.flush()
How can I run the second python code with packaged vsix?

Ansible Dynamic Inventory - "([Errno 2] No such file or directory:"

Situation: I am getting an issue when trying to load a basic dynamic inventory python file in Ansible.
Background: When I perform python test.py it will output the list however in ansible it fails
Assessment: I have tried lots of other example files and none have worked, this is the simplest i could make it
Recommendation: Can someone please point me in the right direction? is this to do with the ansible.cfg?
vagrant#cd98f180bc88 /vagrant/adc-cmdb-inventory/enviroments $ python test.py --list
{"all": {"hosts": ["192.168.28.71", "192.168.28.72"], "vars": {"ansible_python_interpreter": "/usr/bin/python3", "ansible_ssh_private_key_file": "~/.vagrant.d/insecure_private_key", "example_variable": "value", "ansible_user": "vagrant"}}, "_me
ta": {"hostvars": {"192.168.28.72": {"host_specific_var": "bar"}, "192.168.28.71": {"host_specific_var": "foo"}}}}
ISSUE:
Command used:
ansible -i test.py all -m ping --list-host
[WARNING]: * Failed to parse /vagrant/adc-cmdb-inventory/enviroments/test.py with script plugin: problem running /vagrant/adc-cmdb-inventory/enviroments/test.py --list ([Errno 2] No such file or directory: '/vagrant/adc-cmdb-
inventory/enviroments/test.py': '/vagrant/adc-cmdb-inventory/enviroments/test.py')
[WARNING]: * Failed to parse /vagrant/adc-cmdb-inventory/enviroments/test.py with ini plugin: /vagrant/adc-cmdb-inventory/enviroments/test.py:4: Expected key=value host variable assignment, got: os
[WARNING]: Unable to parse /vagrant/adc-cmdb-inventory/enviroments/test.py as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
hosts (0):
test.py
#!/usr/bin/python
# -*- coding:utf-8 -*-
import os
import sys
import argparse
import json
class ExampleInventory(object):
def __init__(self):
self.inventory = {}
self.read_cli_args()
# Called with `--list`.
if self.args.list:
self.inventory = self.example_inventory()
# Called with `--host [hostname]`.
elif self.args.host:
# Not implemented, since we return _meta info `--list`.
self.inventory = self.empty_inventory()
# If no groups or vars are present, return empty inventory.
else:
self.inventory = self.empty_inventory()
print(json.dumps(self.inventory))
# Example inventory for testing.
def example_inventory(self):
return {
'all': {
'hosts': ['192.168.28.71', '192.168.28.72'],
'vars': {
'ansible_user': 'vagrant',
'ansible_ssh_private_key_file':
'~/.vagrant.d/insecure_private_key',
'ansible_python_interpreter':
'/usr/bin/python3',
'example_variable': 'value'
}
},
'_meta': {
'hostvars': {
'192.168.28.71': {
'host_specific_var': 'foo'
},
'192.168.28.72': {
'host_specific_var': 'bar'
}
}
}
}
# Empty inventory for testing.
def empty_inventory(self):
return {'_meta': {'hostvars': {}}}
# Read the command line args passed to the script.
def read_cli_args(self):
parser = argparse.ArgumentParser()
parser.add_argument('--list', action = 'store_true')
parser.add_argument('--host', action = 'store')
self.args = parser.parse_args()
# Get the inventory.
ExampleInventory()
Invisible Line endings!
I ended up installing dos2unix and converting the files there were CR and LF tags at the end of each line.
Remove the CF lines with dos2unix and it working first time.
https://imgur.com/AN8ACWC

How to import python packages while using it in node.js application in VS code

I am developing a quiz application in node.js. I need some python script to keep log of user,so I want to use key logger to keep monitoring the user while attempting the quiz.
Here is the python keylogger script:
from pynput.keyboard import Key, Listener
import logging
log_directory = r"G:/Pythonin Node/Keylogger/key_logger/public/log_files/"
logging.basicConfig(filename = (log_directory+"keylog.txt"), level = logging.DEBUG)
def on_press(key):
logging.info(str(key))
with Listener(on_press = on_press) as listener:
listener.join()
Script working well when i run it in pycharm.but when I call it from node application using python-shell I found an error:
{
traceback: "Traceback (most recent call last): File "script.py", line 1, in <module> from pynput.keyboard import Key, Listener ModuleNotFoundError: No module named 'pynput' ",
executable: "py",
options: null,
script: "script.py",
args: [
"xyz",
"abc"
],
exitCode: 1
}
This is the simple json response.
Here is my node code:
app.get('/', callD_alembert);
function callD_alembert(req, res) {
var x="xyz";
var y="abc";
var options = {
args:
[
x,
y
]
}
PythonShell.run('./script.py', options, function (err, data) {
if (err) res.send(err);
res.send(data.toString())
});
}
python shell executes the simple python script in which I don't use any external package.but when I to use "pynput" package and want to import it.it gives the following error:
Here is also running a python interpreter:
please help me to solve this issue.
Thank you
It looks like you are running the python interpreter in different environments.
Try adding the code below to your python script, and run it from pycharm and using PythonShell:
import sys
print(sys.executable)
If the printed paths are different, try modifying the options you pass to PythonShell, so that the path matches the one you have while running the script via pycharm:
var options = {
// replace this with the path you got by running the script in pycharm
pythonPath: 'path/to/python',
args:
[
x,
y
]
}

Node.JS Pyshell crashes on second message to Python, [ERR_STREAM_WRITE_AFTER_END]: write after end

I have a Node.JS server that sends messages to a Python script using Pyshell.send
When Pyshell returns a message using print Node.JS recieves it using pyshell.on and then reads out the returned message.
When this is then done again with a second message it crashes with the error:
Error [ERR_STREAM_WRITE_AFTER_END]: write after end
at writeAfterEnd (_stream_writable.js:243:12)
at Socket.Writable.write (_stream_writable.js:291:5)
at PythonShell.send (/Users/maxkenney/Desktop/nodeLanguageProcessing/node_modules/python-shell/index.js:285:20)
at Namespace.<anonymous> (/Users/maxkenney/Desktop/nodeLanguageProcessing/app.js:87:10)
at Namespace.emit (events.js:189:13)
at Namespace.emit (/Users/maxkenney/Desktop/nodeLanguageProcessing/node_modules/socket.io/lib/namespace.js:213:10)
at /Users/maxkenney/Desktop/nodeLanguageProcessing/node_modules/socket.io/lib/namespace.js:181:14
at process._tickCallback (internal/process/next_tick.js:61:11)
Emitted 'error' event at:
at writeAfterEnd (_stream_writable.js:245:10)
at Socket.Writable.write (_stream_writable.js:291:5)
[... lines matching original stack trace ...]
at process._tickCallback (internal/process/next_tick.js:61:11)
I believe it could possibly be because the Python shell is closed after one message but I am unsure. Any help would be greatly appreciated!
Node.JS Script (relevant parts)
let pyshell = new PythonShell('my_script.py');
pyshell.on('message', function(message){
console.log("python said: " + message);
});
pyshell.send("This is a test entry").end(function(err){
if(err) handleError(err);
});
That last pyshell.send is repeated later on and it crashes.
Python Script:
import sys
import spacy
import nltk
nlp = spacy.load("en_core_web_sm");
def convertGeneratorObjectToListOfString(generatorObject):
list = []
for item in generatorObject:
list.append(str(item))
return list
def main():
#doc = nlp(sys.argv[1])
doc = nlp(sys.stdin.readline())
# Return the printed message to Node
jsonObjectToReturn = {}
for token in doc:
dependents = token.children
listOfChildrenTag = []
for d in dependents:
listOfChildrenTag.append(d.dep_)
jsonObjectToReturn[token.text] = {
"text": token.text,
"lemma": token.lemma_,
"tag": token.tag_,
"dep": token.dep_,
"dependencies": convertGeneratorObjectToListOfString(token.children),
"dependenciesTag": listOfChildrenTag
}
print(jsonObjectToReturn)
if __name__ == '__main__':
main()
I have included all python code here as I am unsure but I believe the issue could also be caused by the fact that the Python script closes once the main finishes. Apologies I am unfamiliar with running constant python scripts.
When you call .end() you close the stream so it cannot be used any more. So, don't call .end() until you're done with the stream. You can call .send() without calling .end().
if you also need to monitor when the python script ends, you can use the childProcess property of the pyshell object.
let pyshell = new PythonShell('my_script.py');
pyshell.childProcess.on('exit', (code) => {
console.log(`python program ended with exit code: ${code}`);
});

Categories