My Node.js parent program executes a child Python process and send to it text data - see here for the implementation. This works ok, the node parent process writes data as:
child.stdin.setEncoding('utf-8');
child.stdin.write(data + '\r\n');
child.stdin.end();
the Python child process echoes it back:
for line in sys.stdin:
ofp.write(line)
and it gets back the data to the parent:
child.stdout.on('data', function (_data) {
var data = Buffer.from(_data, 'utf-8').toString().trim();
res += data;
});
I want my parent process to fork once the child process, and make subsequent calls the child.stdin.write();
without closing the stream. If in the parent process I do like
setInterval(() => {
child.stdin.write(data + '\r\n');
child.stdin.end();
}, 1000);
this will cause, at the second very execution a Error [ERR_STREAM_WRITE_AFTER_END]: write after end
error, because of the end()
call. While if I omits the child.stdin.end();
I will get any output. According to here sys.stdin.readline
will read line-by-line until Ctrl + D
def read_stdin():
readline = sys.stdin.readline()
while readline:
yield readline
readline = sys.stdin.readline()
for line in read_stdin():
ofp.write(line)
the other approach was
for line in iter(sys.stdin.readline, ''):
print line
In both cases I get no data back to the parent Node process.
Answer
The issue you're encountering is related to how the stdin
stream is being handled in the Node.js parent process. Specifically, the error occurs because you're calling child.stdin.end()
every time you write to the stream, which closes the stdin
stream after the first write. Once the stream is closed, you cannot write to it again, causing the error you observed (Error [ERR_STREAM_WRITE_AFTER_END]: write after end
).
Solution
To keep the stdin
stream open for subsequent writes, you should not call end()
every time you write to stdin
. Instead, simply omit the child.stdin.end()
call and keep writing to stdin
as needed. The Python process will continue to read from stdin
line by line.
Here’s how you can modify both your Node.js and Python code:
Node.js (Parent Process)
Instead of calling end()
after each write, you should only call end()
when you are done with all interactions. This means in your setInterval
, remove the end()
call:
setInterval(() => {
child.stdin.write(data + '\r\n'); // Just write to stdin
// child.stdin.end(); // Don't call end() here
}, 1000);
Now, the Node.js process can continue sending data to the Python child process without closing the stdin
stream after each write.
Python (Child Process)
In the Python code, you can use the iter()
function to read continuously from stdin
line-by-line, without needing to close stdin
. Here's how you can modify the Python code:
import sys
# Using iter to keep reading from stdin until it's closed
for line in iter(sys.stdin.readline, ''): # This reads line by line
sys.stdout.write(line) # Echo the line back to stdout
In this Python snippet:
iter(sys.stdin.readline, '')
will continuously read fromstdin
until it reaches the end (i.e., whenstdin
is closed). Since we're not closingstdin
in the Node.js parent process (because we're omittingend()
), Python will keep reading and processing the input.- The
sys.stdout.write(line)
sends the data back to the Node.js parent process.
Handling Multiple Writes to stdin
in Node.js
If you need to write multiple pieces of data to the child process in an interval, simply keep the stdin
open without calling end()
. The Python process will continue to receive data until the Node.js parent process explicitly closes the stdin
stream.
For example, if you want to send data continuously every second:
const child = require('child_process').spawn('python', ['your_python_script.py']);
child.stdin.setEncoding('utf-8');
child.stdout.on('data', function (_data) {
var data = Buffer.from(_data, 'utf-8').toString().trim();
console.log(data); // Do something with the data from Python
});
setInterval(() => {
let data = "Some data to send";
child.stdin.write(data + '\r\n'); // Keep writing without closing stdin
}, 1000);
// You can close the stdin stream when you're done with the process
// child.stdin.end(); // Call this only when you're done sending all data
Additional Considerations:
- Child Process Lifecycle: Make sure that you're not inadvertently closing or terminating the child process unless you're done with all communication. If you do need to terminate the child process, call
child.stdin.end()
andchild.kill()
appropriately. - Handling Errors: Ensure you handle potential errors in the child process properly, especially in the
stdout
event handler of the parent process.
With this setup, your Node.js parent process can send multiple pieces of data to the Python child process, and the Python process will continue to read and process the input as it comes in.