Stream child process output in flowing mode - javascript

I have custom command line written using Python which prints its output using "print" statement. I am using it from Node.js by spawning a child process and sending commands to it using child.stdin.write method. Here's source:
var childProcess = require('child_process'),
spawn = childProcess.spawn;
var child = spawn('./custom_cli', ['argument_1', 'argument_2']);
child.stdout.on('data', function (d) {
console.log('out: ' + d);
});
child.stderr.on('data', function (d) {
console.log('err: ' + d);
});
//execute first command after 1sec
setTimeout(function () {
child.stdin.write('some_command' + '\n');
}, 1000);
//execute "quit" command after 2sec
//to terminate the command line
setTimeout(function () {
child.stdin.write('quit' + '\n');
}, 2000);
Now the issue is I am not receiving the output in flowing mode. I want get the output from child process as soon as it's printed but I am receiving the output of all the commands only when child process is terminated (using custom cli's quit command).

You need to flush the output in the child process.
Probably you think this isn't necessary because when testing and letting the output happen on a terminal, then the library flushes itself (e. g. when a line is complete). This is not done when printing goes to a pipe (due to performance reasons).
Flush yourself:
#!/usr/bin/env python
import sys, time
while True:
print "foo"
sys.stdout.flush()
time.sleep(2)

The best way is to use unbuffered mode of python standard output. It will force python to write output to output streams without need to flush yourself.
For example:
var spawn = require('child_process').spawn,
child = spawn('python',['-u', 'myscript.py']); // Or in custom_cli add python -u myscript.py
child.stdout.on('data', function (data) {
console.log('stdout: ' + data);
});
child.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});

In my case in Python I'm using sys.stdin.readline and yielding last line:
def read_stdin():
'''
read standard input
yeld next line
'''
try:
readline = sys.stdin.readline()
while readline:
yield readline
readline = sys.stdin.readline()
except:
# LP: avoid to exit(1) at stdin end
pass
for line in read_stdin():
out = process(line)
ofp.write(out)
sys.stdout.flush()
and when in Node.js
var child = spawn(binpath, args);
// register child process signals
child.stdout.on('data', function (_data) {
var data = Buffer.from(_data, 'utf-8').toString().trim();
console.log(data);
});
child.stderr.on('data', function (data) {
console.warn('pid:%s stderr:%s', child.pid, data);
});
child.stdout.on('exit', function (_) {
console.warn('pid:%s exit', child.pid);
});
child.stdout.on('end', function (_) {
console.warn('pid:%s ended', child.pid);
});
child.on('error', function (error) {
console.error(error);
});
child.on('close', (code, signal) => { // called after `end`
console.warn('pid:%s terminated with code:%d due to receipt of signal:%s with ', child.pid, code, signal);
});
child.on('uncaughtException', function (error) {
console.warn('pid:%s terminated due to receipt of error:%s', child.pid, error);
});

Related

Python to NodeJS communication not working through spawn

I'm trying to replicate this process to establish communication be nodejs and python through stdin and stdout : https://healeycodes.com/javascript/python/beginners/webdev/2019/04/11/talking-between-languages.html
Context:
Sender - which is giving output to stdout
Listener - one who's reading it
Now, when Python is sender and NodeJS is Listener isn't working. NodeJS gives no output.
On further digging my issue is very similar to Not receiving stdout from nodejs spawned process except I don't need unbuffered output as such. Tried sol from last ques.. didn't work.
Here are files:
sensor.py
import random, time
import sys
time.sleep(random.random() * 5) # wait 0 to 5 seconds
temperature = (random.random() * 20) - 5 # -5 to 15
print(temperature, flush=True, end='')
sys.stdout.flush()
listener.js
const { spawn } = require('child_process');
//spawn()
const child = spawn('python', ['path-to-sensor.py']);
console.log('Here');
child.stdout.on('data', function(data) {
console.log('Got the data')
console.log(data)
});
child.on('error', function () {
console.log("Failed to start child.");
});
child.on('close', function (code) {
console.log('Child process exited with code ' + code);
});
child.stdout.on('end', function () {
console.log('Finished collecting data chunks.');
});
Reason: Event listener - child.stdout.on('data', callback) is never called
What I want the output to be:
Store the stdout from sensor.py in a variable
Can't figure out what else I can do to fix this
In sensor.py you are not using the right syntax in your code:
print(temperature, flush=True, end='')
The right syntax is:
print(object(s), sep=separator, end=end, file=file, flush=flush)
Source
If you left the code in sensor.py as:
print(temperature)
And in listener.js as:
child.stdout.on('data', function(data) {
console.log('Got the data')
//console.log(data)
console.log(data.toString('utf8'))
});
It works ok

Azure Function - js - not running correctly, but no error in the logs

Am trying to build a Function to get data from my IoTHub and send the data to my web service via GET.
This is what I have in my Function:
var http = require('https');
module.exports = function (context, IoTHubMessages) {
IoTHubMessages.forEach(message => {
// context.log(`Processing message9: ${JSON.stringify(message)}`);
console.log(`what the what???`);
let url = `<my site in Azure>.azurewebsites.net`;
console.log(url);
let path = "/sensor/" + message.d1 + "/" + message.d2 + "/" + message.d3 + "/";
console.log(path);
var req = http.request({
host: url,
path: path,
method: 'GET'
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
req.on('end', function(e) {
console.log('finished with request');
});
req.end();
});
context.done();
};
The logs look like this:
2019-02-10T06:06:22.503 [Information] Executing 'Functions.IoTHub_EventHub1' (Reason='', Id=ea6109b0-5037-4f15-9efc-845222c6f404)
2019-02-10T06:06:22.512 [Information] Executed 'Functions.IoTHub_EventHub1' (Succeeded, Id=ea6109b0-5037-4f15-9efc-845222c6f404)
2019-02-10T06:06:22.786 [Information] Executing 'Functions.IoTHub_EventHub1' (Reason='', Id=f344c44f-a6ff-49b3-badb-58429b3476dc)
2019-02-10T06:06:22.796 [Information] Executed 'Functions.IoTHub_EventHub1' (Succeeded, Id=f344c44f-a6ff-49b3-badb-58429b3476dc)
If I uncomment this line :
context.log(`Processing message9: ${JSON.stringify(message)}`);
then the JSON data is displayed in the log output. In between the Executing and Executed pairs I see:
2019-02-10T05:59:28.906 [Information] Processing message9: {"topic":"iot","d1":"200","d2":"200","d3":"200"}
I am not getting my GET request
I don't see the console.log messages after the initial stringify line
I don't see any errors.
I've tried different quotation marks to see if Node preferred one or the other.
Occasionally when restarting the Function I see a message like this in the log, but ignored it as the log had my JSON string
2019-02-10T06:00:10.600 [Error] Executed 'Functions.IoTHub_EventHub1' (Failed, Id=2b3959cd-5014-4c50-89a3-77e37f2a890e)
Binding parameters to complex objects (such as 'Object') uses Json.NET serialization.
1. Bind the parameter type as 'string' instead of 'Object' to get the raw values and avoid JSON deserialization, or
2. Change the queue payload to be valid json. The JSON parser failed:
Unexpected character encountered while parsing value: T. Path '', line 0, position 0.
The problem here is that the forEach loop is not a loop that is waiting for the result before calling context.done
When this happens as #nelak points out in his comment, the azure function stops and nothing else happens.
Observe the following. I decided to replace the http library for a simple setTimeout function, but this is more or less the same. What is happening with your code, is ilustrated in the next snippet, notice the order in which the console.log are called.
const myFn = function (context, IoTHubMessages) {
IoTHubMessages.forEach(message => {
console.log('inside foreach!')
setTimeout(() => {
console.log('inside settimeout, this is when your request is answered!')
}, 1)
});
console.log('outside all!')
};
myFn(null, [0, 1])
If you waned a different behaviour you could rewrite this with the async-await pattern and then it seems syncronous but it's actually asynchronous.
var callIt = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log('inside settimeout!')
return resolve('ok')
}, 1)
})
}
var myFnAwait = async (context, IoTHubMessages) => {
for (i of IoTHubMessages){
console.log('before settimeout')
await callIt()
console.log('after timeout')
}
console.log('outside all!')
};
myFnAwait(null, [0, 1])

How to obtain a more specific error message when using node.js spawn?

Right now I have the following code to spawn a python script in node.js:
logger.info('Spawning python process');
let proc1 = spawn('python3',
['experimental/paraphrase/paraphrase_detect.py',
results[0].answer, request.query.response]);
proc1.stdout.on('data', (data) => {
logger.info('Python script returned data');
let retVal;
if (data) {
logger.info(data.toString());
retVal = JSON.parse(data.toString());
}
return response.json({data: retVal});
});
proc1.on('close', (code, test) => {
if (code === 1) {
const msg = 'Python exited with exit code 1 meaning there was a ' +
'problem executing the script.';
logger.error(msg);
logger.error(test);
}
});
proc1.on('error', (data) => {
logger.error('Error with calling python script');
if (data) {
logger.error(data.toString());
}
});
The issue I'm dealing with is that specific error messages aren't being returned to node, such as permissions issues, a misspelling of the script name or a missing python package. The best I've been able to do so far is catch an exit code of 1 which just indicates generically that there was a problem running the script.
Try proc1.stderr.on("data")
More details.
https://nodejs.org/api/child_process.html#child_process_child_process

node.js listen to process error event

There is a way somehow to listen to event for node process
like process.stderr, I want event that when this line is invoked to do some logic inside, any idea if it possible ?
If you mean to listen to the child process spawned from your nodejs server, its simple, i have given the example below.
Note: its very unclear to guess what you mean
var spawn = require('child_process').spawn;
var child = spawn('myshell.sh', []);
child.stdout.on('data', function(data) {
console.log('stdout- ' + data);
});
child.stderr.on('data', function (data) {
console.log('stderr- ' + data);
});
child.on('exit', function (code) {
console.log('exit - ' + code);
});

Node.js can't get output of spawned process

This code get output of spawned process. What is wrong? node version is v0.10.22
var spawn = require('child_process').spawn;
var what = 'java';
var spawned = spawn(what, ['-version']);
console.log('starting `'+what+' -version`');
spawned.stdout.setEncoding('utf8');
spawned.stdout.on('data', function (data) {
console.log(data);
});
spawned.on('close', function (code) {
console.log('process exit code ' + code);
});
var whendone = function() {
console.log('done');
};
setTimeout(whendone,5000);
As you can see I even added some timeout to wait for a launched process to finish.
java -version writes to stderr.

Categories