Passing parameter from node-red to python

Hello,

I would like to pass parameter from Node-red to python script.
1.Is it possible to do that using: https://flows.nodered.org/node/node-red-contrib-pythonshell

Python script would be:

a={Passed parameter}
b=2

print(a*b)
  1. Have also tried to understand: Passing (msg.payload) in exec node to a python script function without success

how to define script path in windows? In my case: C:\Users\admin\PycharmProjects\TEST\script.py

You can pass the extra parameter's as a command arg or from standard-in (what ever works better for your flow). The test files for that node give you an example of each:

From command arg: https://github.com/namgk/node-red-contrib-pythonshell/blob/master/test/sample-with-arg.py

From Standard in: https://github.com/namgk/node-red-contrib-pythonshell/blob/master/test/stdin-data.py

Thank you,

so dumb of me to miss github for examples... Anyway

import sys

while True:
	line = sys.stdin.readline()
	print line

I get error

File "C:/Users/admin/PycharmProjects/TEST/debug.py", line 5
    print line
             
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(line)

when I try:

print(line)

I get no output

[{"id":"5420ad3a.8a1b44","type":"pythonshell in","z":"8397e515.d35b18","name":"","pyfile":"C:\\Users\\admin\\PycharmProjects\\TEST\\debug.py","virtualenv":"","continuous":false,"stdInData":false,"x":341.5,"y":154,"wires":[["23eadfc5.92c34"]]},{"id":"95914516.3e1af8","type":"inject","z":"8397e515.d35b18","name":"","topic":"","payload":"20","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":"","x":130.5,"y":76,"wires":[["5420ad3a.8a1b44"]]},{"id":"23eadfc5.92c34","type":"debug","z":"8397e515.d35b18","name":"","active":true,"console":"false","complete":"false","x":537.5,"y":233,"wires":[]}]

And code is in infinite loop.

Any ideas?

print line verse print(line) is python 2 verse python 3. With python3 the print statement has been replaced with a print function.

As for the code being in an infinite loop: yes ... that's what that example does. It's an infinite loop that accepts a line of input from standard-in and prints it out to standard-out.

Your flow seems to be working perfect. There's a script that starts running ... everything that get's injected goes into the script via standard-in and gets replayed back out via standard-out. If you want the script to run once for each payload you would need to edit the python node and turn off "Continuous" and pass the data as an argument instead.

Here's an example of the script running once for each payload and reading the payload of the argument:

Python 2 code:

import sys
print 'Hello ' + sys.argv[1]

Flow:

[{"id":"2c4b9956.371766","type":"inject","z":"f549c99a.345c38","name":"","topic":"","payload":"KarolisL","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":"","x":170,"y":520,"wires":[["1919c9a5.8929a6"]]},{"id":"e70314d0.69e5e8","type":"debug","z":"f549c99a.345c38","name":"","active":true,"console":"false","complete":"false","x":590,"y":520,"wires":[]},{"id":"1919c9a5.8929a6","type":"pythonshell in","z":"f549c99a.345c38","name":"","pyfile":"sample-with-arg.py","virtualenv":"","continuous":false,"stdInData":false,"x":380,"y":520,"wires":[["e70314d0.69e5e8"]]}]

This ones once rather than a continues loop

1 Like

Sorry for dumb question, but I only managed to solve it by running this code:

import sys

a = int(sys.argv[1])

print (2*a)

I receive 40 in debug node.

When I run:

import sys

line = sys.stdin.readline()
print (line)

I get still don't get output from pythonshell.

Bonus question.. Is it possible to pass object

a ={};
a.val1=1;
a.val2=2;

msg.payload=a;
return msg; 

as msg.payload to python script and how to extract those values in the script?

Question was answered by googling...

It is best to use json node

[{"id":"45771f1f.8f26e","type":"function","z":"8397e515.d35b18","name":"","func":"a ={};\na.val1=1;\na.val2=2;\n\nmsg.payload=a;\nreturn msg;","outputs":1,"noerr":0,"x":233.16669464111328,"y":432.66666984558105,"wires":[["572f1be0.01ef04"]]},{"id":"c0f867b2.7f5698","type":"inject","z":"8397e515.d35b18","name":"","topic":"","payload":"KarolisL","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":"","x":136.00000762939453,"y":345.00000953674316,"wires":[["45771f1f.8f26e"]]},{"id":"d7e850ac.ed588","type":"debug","z":"8397e515.d35b18","name":"","active":true,"console":"false","complete":"false","x":614,"y":345,"wires":[]},{"id":"d74a5980.dd7fe8","type":"pythonshell in","z":"8397e515.d35b18","name":"","pyfile":"C:\\Users\\KL\\PycharmProjects\\TEST\\debug.py","virtualenv":"","continuous":false,"stdInData":false,"x":404,"y":345,"wires":[["d7e850ac.ed588"]]},{"id":"572f1be0.01ef04","type":"json","z":"8397e515.d35b18","name":"","property":"payload","action":"str","pretty":true,"x":392.16668701171875,"y":446.66668701171875,"wires":[["d74a5980.dd7fe8"]]}]

Answered in Passing JSON object (as string) to python script (exec node)

Hi, can you advise on a way of passing two arguments? If I try:

import sys
print('Hello ' + sys.argv[1])
print('Hello ' + sys.argv[2])

I get:

exit code: 1, Traceback (most recent call last):
File "helloarg.py", line 6, in
print('Hello ' + sys.argv[2])
IndexError: list index out of range

I'm trying to get a script a bit more complex than that, but solving that baseline would be very helpful. I'd appreciate it, thank you!

The IndexError is raised when attempting to retrieve an index from a sequence (e.g. list, tuple), and the index isn’t found in the sequence. The Python documentation defines when this exception is raised:

Raised when a sequence subscript is out of range.

Here’s an Python Split() example that raises the IndexError:

data = "one%two%three%four%five"
numbers = data.split('%')

The list numbers has 5 elements, and the indexing starts with 0, so, the last element will have index 4. If you try to subscript with an index higher than 4, the Python Interpreter will raise an IndexError since there is no element at such index.

A solution to pass multiples parameters to your python script is to use a unique json which contains all parameters.

From Nodered to Python : Build your JSON with multiples elements

We build a JSON and pass it in one argument : inject an msg.payload to {} : {"myfirst":"hello","mysecond":"world"}

  • Each element is divided in key and value -> "key":"value"
  • Every elements are separated by ,
  • JSON is always writed between {}

To receive this message from nodered into your python script :

noderedmsg = json.loads(sys.argv[1]) # read the argument with json library

noderedmsg will be a dictionnarie

value = mydictionnarie["key"]

In our case :

first_parameter = noderedmsg["myfirst"] # in first_parameter you'll find the value. myfirst is the key.
second_parameter = noderedmsg["mysecond"]
myfile= (sys.argv[0]) # argv[0] give the name of python file executed

From Python to Nodered : Build your return as a payload with multiples lines
In your python script :

print("Where does it come from : {}".format(myfile))
print("The first is {}, and the second is {}".format(noderedmsg["myfirst"], noderedmsg["mysecond"]))

Example for Nodered :

[{"id":"d8a446b8227bbacb","type":"inject","z":"f827b63b46b85c49","name":"Now","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"myfirst\":\"hello\",\"mysecond\":\"world\"}","payloadType":"json","x":170,"y":280,"wires":[["c1f609ed5b7e2b1e"]]},{"id":"c1194e81047211de","type":"debug","z":"f827b63b46b85c49","name":"Python return","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":620,"y":280,"wires":[]},{"id":"c1f609ed5b7e2b1e","type":"pythonshell in","z":"f827b63b46b85c49","name":"nodered <-> python","pyfile":"/home/pi/BirdRec/program/Python/my_pythonfile_fornodered.py","virtualenv":"","continuous":false,"stdInData":false,"x":400,"y":280,"wires":[["c1194e81047211de"]]}]

Example for Python [my_pythonfile_fornodered.py] :

# You need to use the librairy to read a json format message
import json

def main(args):
	
	# Read differents elements of execution 
	myfile= (sys.argv[0]) # argv[0] give the name of python file executed
	noderedmsg = json.loads(sys.argv[1]) # read the argument with json library
	
	print("Where does it come from : {}".format(myfile))
	print("The first is {}, and the second is {}".format(noderedmsg["myfirst"], noderedmsg["mysecond"]))
	
	return 0

if __name__ == '__main__':
	import sys
	sys.exit(main(sys.argv)) # Return the prints from main()

Yes, you can pass an object.
To extract in your python script the values, use dictionnaries data structures of python

In your pyhton script

import json
a = json.loads(sys.argv[1])
myval1 =a["val1"]
myval2=a["val2"]
print(myval1)
print(myval2)

or

import json
a = json.loads(sys.argv[1]) # read the argument with json library
for i in a :
		print(a[i])

Also, you can use Nodered execnode
image
with these parameters (very important to write python and not py)
image

Inject a msg.payload to send the file of python script and every parameters (arguments) as :
msg.payload = "/home/pi/myscript.py argument1 argument2"

I your python script :

#!/usr/bin/env python

def main(args):
	thisfile=sys.argv[0] # argv[0] give the name of python file executed
	arg1= sys.argv[1] # first argument 
	arg2= sys.argv[2] # second argument 
	print("{} is executed with arguments 1 : {} and : {}".format(thisfile, arg1, arg2))  # This sentence will appear in debug node. 

if __name__ == '__main__':
	import sys
	sys.exit(main(sys.argv))