1
votes

I have a python script that takes two arguments; a directory and a file name.

The python script will create a JSON object from specific files in the directory provided and save it with the name being the second argument.

However if the second argument is equal to string "stream", the the JSON data is output to STDOUT.

I wrote a node script that spawns a child process to call the python script from terminal and it works as intended.

"use strict";

const spawn = require("child_process").spawn;

const command = "(path to python)";
const loc = "(path to .py script)";

const acct = process.argv[2];
const output = process.argv[3];

let callPy = spawn(command, ["erik.py", acct, output], {
    cwd: loc,
    stdio: "pipe"
});

callPy.stdout.on("data", (data) => {
    if (data.toString() === "success") {
        console.log(acct, "generated");
    } else {
        console.log(data.toString());
    }
});

EDIT:

I have unmarked this issue as solved: after spending a bit more time trying to implement this, I have not come to a satisfactory solution that allows me to synchronously call a child process from node, signal the python script to emit JSON data, receive the data, and then send the data to the browser. I tried using a promise chain on the child process:

let child = require("child_process").spawn; // or spawnSync

let spawn = () => {
    let spawned = child(command, args, options,(err, stdout, stderr) => {
        if (err) { console.log(err) };
    });
    return spawned
};

let listen = (child) => {
    child.stdout.on("data", (data) => {
        console.log("PID", child.pid);
        console.log("data from listen func: ", data);
        return child
    });
};

let kill = (child) => {
    child.kill( "SIGTERM" );
}

var p = new Promise((res, e) => {
    res( spawn() )
    e( console.error(e) )
});

p.then(result => {
    return listen(result);
    })
    p.then(result => {
        return kill(result);
});
    

using spawn() the child terminates before any of the data is returned using spawnSync() the promise chain tries (and fails) to listen on the child's io before the child is spawned

I have yet to try websockets to transmit the data but I doubt that will solve this, the promise is returning an empty object to my router function invocation before the promise chain retrieves the chunks from the python script.

Any further insight is welcome.

2

2 Answers

1
votes

So you need at least two things to do this

  • A way to queue commands to execute with spawn
  • A async pattern to wait execution of a command and join processes when each executable terminates

A minimalistic examples is

var cmd = new CommandLine({ 
    debug : true, 
    error : true, 
    delay : true });
// commandItemsArray is a list of commands list, command options, command arguments
commandItemsArray = [ ['ls','-l','./'], ['ls','-a','./'] ];
cmd.executeCommands( commandItemsArray
, function(results) {
    console.log( results );
}
, function(error) {
    console.log( error );
});

there are several package on npm to do both (search for node cli, command line, etc), one is this one node-commander that usese a Promise.all pattern to achieve the second task:

      function PromiseAll(items, block, done, fail) {
        var self=this;
        var promises = [], index=0;
        items.forEach(function(item) {
          promises.push( function(item,i) {
              return new Promise(function(resolve, reject) {
                return block.apply(this,[item,index,resolve,reject]);
              });
            }(item,++index))
        });
        Promise.all(promises).then(function AcceptHandler(results) {
          if(done) done( results );
        }, function ErrorHandler(error) {
          if(fail) fail( error );
        });
      } //promiseAll
0
votes

I was able to resolve this issue relatively simply using websockets:

the client submits the request, which is communicated to the server via socket.IO, the request is received and the spawn event is triggered, when the chunks are finished appending a termination event is emitted which triggers killing of the child process and returning the data to the client