Search code examples
pythonnode.jsnw.js

PythonShell in node (nwjs)


I am trying to create a nw.js application that communicates with Python using the node module PythonShell.

The issue I am having is that nothing is written to the console unless I close stdin. However I would like to keep the stream open so that I can send multiple commands to the Python script, and have Python save its state.

Here are my scripts:

script.py

import sys

def main():
    command = sys.stdin.readlines()  # unused for now
    sys.stdout.write("HELLO WORLD")
    sys.stdout.flush()

if __name__ == '__main__':
    main()

main.js

var PythonShell = require('python-shell');
var pyshell = new PythonShell('script.py');

pyshell.on('message', function (message) {
  console.log(message);
});

pyshell.send('hello');

At this point, nothing happens.

If I do pyshell.end(), then HELLO WORLD is output to the console. But then I am unable issue further pyshell.send commands.

How can I keep the Python child process running and waiting for input, yet pipe all output back to JS?


Solution

  • A couple of problems:

    • Use sys.stdin.readline() instead of sys.stdin.readlines(). Otherwise, Python will continue to wait for you to finish the input stream. You should be able to send a ^D signal to terminate the end of the input, but that didn't work for me.

    • To keep the stream open, wrap the command line input in a loop (see Python code below)

    Also important:

    • Input automatically appends \n, but output does not. For whatever reason, output needs both \n and sys.stdout.flush() to work; one or the other won't cut it.

    • Python-shell seems to cache your Python code. So if you make any changes to your Python file, you must restart the nwjs application for it to take effect.

    Here is the full sample code that works:

    script.py

    import sys
    
    def main():
        while True:
            command = sys.stdin.readline()
            command = command.split('\n')[0]
            if command == "hello":
                sys.stdout.write("You said hello!\n")
            elif command == "goodbye":
                sys.stdout.write("You said goodbye!\n")
            else:
                sys.stdout.write("Sorry, I didn't understand that.\n")
            sys.stdout.flush()
    
    if __name__ == '__main__':
        main()
    

    main.js

    var PythonShell = require('python-shell');
    var pyshell = new PythonShell('script.py');
    
    pyshell.on('message', function (message) {
      console.log(message);
    });
    
    pyshell.send('hello');
    

    Now use pyshell.send("hello"), pyshell.send("goodbye"), or pyshell.send("garbage") and receive an immediate response in the JS console!