Node.js IPC between a Python parent and a Node.js child
Asked Answered
R

1

2

Node.js has integrated JSON-based IPC:

// main.js

const cp = require('node:child_process');
const n = cp.fork(`${__dirname}/sub.js`);

n.on('message', (m) => {
  console.log('PARENT got message:', m);
});

// Causes the child to print: CHILD got message: { hello: 'world' }
n.send({ hello: 'world' });
// sub.js

process.on('message', (m) => {
  console.log('CHILD got message:', m);
});

// Causes the parent to print: PARENT got message: { foo: 'bar', baz: null }
process.send({ foo: 'bar', baz: NaN });

How can I write main.js in Python? I understand that this isn't supported:

... using the IPC channel with a child process that is not a Node.js instance is not supported

But can it be done?

Rout answered 8/8, 2022 at 12:38 Comment(0)
R
1

The IPC channel in Node.js is just a simple bidirectional pipe. Channel's initialization starts here. The serialization happens here, each message is just a newline-trailing string of JSON.

main.js can be rewritten as follows:

# main.py

import json
import multiprocessing
import os
import subprocess

parent_conn, child_conn = multiprocessing.Pipe()
env = os.environ.copy()
env['NODE_CHANNEL_FD'] = str(child_conn.fileno())

subprocess.Popen(
    ['node', 'sub.js'],
    pass_fds=(child_conn.fileno(),),
    env=env,
)

parent_conn._write(parent_conn.fileno(), json.dumps({ 'hello': 'world' }).encode("ascii") + b'\n')
print('PARENT got message: ' + str(json.loads(parent_conn._read(parent_conn.fileno(), 1000))))

This will work with sub.js as-is! Using multiprocessing.Pipe because it's a duplex and multi-platform. parent_conn._read and parent_conn._write because they use custom code on Windows.

This is of course just a proof of concept, more code is needed to match Node.js' IPC ergonomics, but it works!

This will likely be used in the LSP plugin for Sublime Text.

Rout answered 8/8, 2022 at 12:38 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.