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()
Related
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)}")
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}`);
});
My python script has two main steps:
Open two webbrowser tabs (default browser). And let a LED blink.
When i am executing the python script via shell with "python netflix.py" everything works fine.
But when i am trying to start it via my (see below) NodeJS script. Only the LED will be blink. The webbrowser tabs won't come up.
Does anyone know where the issue is?
#!/usr/bin/env node
process.env.UV_THREADPOOL_SIZE = 1208;
var http = require('http');
const server = http.createServer(function(request, response) {
try{
console.log('Request incoming...');
var spawn = require('child_process').spawn;
if(request.url == '/abort'){
console.log('Calling abort.py ...');
var process = spawn('python',["./abort.py"]);
}else{
console.log('Calling netflix.py');
var process = spawn('python',["./netflix.py"]);
}
}catch(e){
console.log('Error');
console.log(e);
}
});
server.on('error', function(){
console.log('error');
});
const port = 8000;
server.listen(port);
server.timeout = 10000;
console.log(`Listening at http://leitner-pi:${port}`);
import sys
import webbrowser
from gpiozero import LED
from time import sleep
try:
webbrowser.open_new_tab("http://netflix.com");
finally:
print("")
print("Lasse PIN 7 blinken..")
led = LED(17)
while True:
led.on()
sleep(0.3)
led.off()
sleep(0.3)
Fixed: Well, I made a workaround.
First, I split the python script into two scripts. Webbrowser and LED script, to avoid any kind of interruptions or something else. Second, I changed the webbrowser to:
os.system(‘sudo -upi chromium-browser URL1 URL2’).
At last, I am calling both new
scripts from my node webserver, like I used before.
Now it works fine.
I'm relatively new in python, but I'm trying to learn hard. For the last couple of days tried to solve an error on this script :
import requests
import subprocess
import json
import sys
import multiprocessing
import time
import random
channel_url = "gaming.youtube.com/game/"
processes = [5]
def get_channel():
# Reading the channel name - passed as an argument to this script
if len(sys.argv) >= 2:
global channel_url
channel_url += sys.argv[1]
else:
print "An error has occurred while trying to read arguments. Did you specify the channel?"
sys.exit(1)
def get_proxies():
# Reading the list of proxies
try:
lines = [line.rstrip("\n") for line in open("proxylist.txt")]
except IOError as e:
print "An error has occurred while trying to read the list of proxies: %s" % e.strerror
sys.exit(1)
return lines
def get_url():
# Getting the json with all data regarding the stream
try:
response = subprocess.Popen(
["livestreamer.exe", "--http-header", "Client-ID=ewvlchtxgqq88ru9gmfp1gmyt6h2b93",
channel_url, "-j"], stdout=subprocess.PIPE).communicate()[0]
except subprocess.CalledProcessError:
print "An error has occurred while trying to get the stream data. Is the channel online? Is the channel name correct?"
sys.exit(1)
except OSError:
print "An error has occurred while trying to use livestreamer package. Is it installed? Do you have Python in your PATH variable?"
# Decoding the url to the worst quality of the stream
try:
url = json.loads(response)['streams']['audio_only']['url']
except:
try:
url = json.loads(response)['streams']['worst']['url']
except (ValueError, KeyError):
print "An error has occurred while trying to get the stream data. Is the channel online? Is the channel name correct?"
sys.exit(1)
return url
def open_url(url, proxy):
# Sending HEAD requests
while True:
try:
with requests.Session() as s:
response = s.head(url, proxies=proxy)
print "Sent HEAD request with %s" % proxy["http"]
time.sleep(20)
except requests.exceptions.Timeout:
print " Timeout error for %s" % proxy["http"]
except requests.exceptions.ConnectionError:
print " Connection error for %s" % proxy["http"]
def prepare_processes():
global processes
proxies = get_proxies()
n = 0
if len(proxies) < 1:
print "An error has occurred while preparing the process: Not enough proxy servers. Need at least 1 to function."
sys.exit(1)
for proxy in proxies:
# Preparing the process and giving it its own proxy
processes.append(
multiprocessing.Process(
target=open_url, kwargs={
"url": get_url(), "proxy": {
"http": proxy}}))
print '.',
print ''
if __name__ == "__main__":
print "Obtaining the channel..."
get_channel()
print "Obtained the channel"
print "Preparing the processes..."
prepare_processes()
print "Prepared the processes"
print "Booting up the processes..."
# Timer multiplier
n = 8
# Starting up the processes
for process in processes:
time.sleep(random.randint(1, 5) * n)
process.daemon = True
process.start()
if n > 1:
n -= 1
# Running infinitely
while True:
time.sleep(1)
ERROR :
python test999.py UCbadKBJT1bE14AtfBnsw27g
Obtaining the channel...
Obtained the channel
Preparing the processes...
An error has occurred while trying to use livestreamer package. Is it installed? Do you have Python in your PATH variable?
Traceback (most recent call last):
File "test999.py", line 115, in <module>
prepare_processes()
File "test999.py", line 103, in prepare_processes
"url": get_url(), "proxy": {
File "test999.py", line 67, in get_url
url = json.loads(response)['streams']['worst']['url']
UnboundLocalError: local variable 'response' referenced before assignment
I had tried on windows, installed all modules (and updated them ) livestreamer, rtmdump, dlls and other needed binary files.
On linux : installed livestreamer, requests, json and all neede modules. Still can't resolve it. Please help !
response = subprocess.Popen() in try clause. When it fails, no response lkocal variable. You should assign response before try,and process None that subporcess.Popen() may return.
I'm studying vCenter 6.5 and community samples help a lot, but in this particular situation I can't figure out, what's going on. The script:
from __future__ import with_statement
import atexit
from tools import cli
from pyVim import connect
from pyVmomi import vim, vmodl
def get_args():
*Boring args parsing works*
return args
def main():
args = get_args()
try:
service_instance = connect.SmartConnectNoSSL(host=args.host,
user=args.user,
pwd=args.password,
port=int(args.port))
atexit.register(connect.Disconnect, service_instance)
content = service_instance.RetrieveContent()
vm = content.searchIndex.FindByUuid(None, args.vm_uuid, True)
creds = vim.vm.guest.NamePasswordAuthentication(
username=args.vm_user, password=args.vm_pwd
)
try:
pm = content.guestOperationsManager.processManager
ps = vim.vm.guest.ProcessManager.ProgramSpec(
programPath=args.path_to_program,
arguments=args.program_arguments
)
res = pm.StartProgramInGuest(vm, creds, ps)
if res > 0:
print "Program executed, PID is %d" % res
except IOError, e:
print e
except vmodl.MethodFault as error:
print "Caught vmodl fault : " + error.msg
return -1
return 0
# Start program
if __name__ == "__main__":
main()
When I execute it in console, it successfully connects to the target virtual machine and prints
Program executed, PID is 2036
In task manager I see process with mentioned PID, it was created by the correct user, but there is no GUI of the process (calc.exe). RMB click does not allow to "Expand" the process.
I suppose, that this process was created with special parameters, maybe in different session.
In addition, I tried to run batch file to check if it actually executes, but the answer is no, batch file does not execute.
Any help, advices, clues would be awesome.
P.S. I tried other scripts and successfully transferred a file to the VM.
P.P.S. Sorry for my English.
Update: All such processes start in session 0.
Have you tried interactiveSession ?
https://github.com/vmware/pyvmomi/blob/master/docs/vim/vm/guest/GuestAuthentication.rst
This boolean argument passed to NamePasswordAuthentication and means the following:
This is set to true if the client wants an interactive session in the guest.