Files
Python-bindings-for-nodejs/README.md
2025-11-18 11:02:39 +01:00

3.6 KiB

Python Binding for Node.js

Description

This project enables seamless integration of a Python backend with a Node.js application by maintaining a persistent Python subprocess. It allows calling Python methods asynchronously from Node.js using a dynamic JavaScript class interface, supports setting and getting Python-side properties, and enables receiving streamed partial results from long-running Python operations.

How to Use

JavaScript Usage Example

import ChatModel from "./python_bindings.js";

async function runExample() {

	const chat = new ChatModel({ model: "something" });

	// Set properties dynamically
	await chat.setProperty("model", "something");
	await chat.setProperty("tokenizer", 123);

	// Get properties from Python backend
	const modelResponse = await chat.getModelPath();
	console.log(modelResponse);     // { Model: "something" }

	const tokenizerResponse = await chat.getTokenizer();
	console.log(tokenizerResponse); // { Tokenizer: 123 }

	// Call Python methods asynchronously
	let response = await chat.increment({ by: 5 });
	console.log("Incremented counter:", response.counter);

	response = await chat.increment({ by: 2 });
	console.log("Incremented counter:", response.counter);

	response = await chat.increment({ by: 2 });
	console.log("Incremented counter:", response.counter);

	// Listen for streamed partial results
	chat.onMessage(function(data) {
		console.log("Streamed data:", data);
	});

	// Call method that streams partial results
	await chat.testStream();

	// Cleanly terminate Python subprocess
	chat.end();
}

runExample();

How to Write the Python Controller

The Python controller defines the backend logic and exposes methods callable from Node.js. It should extend the provided BaseController class and implement any methods you want to call from Node.js.

Controller Structure Example

# python/controller.py

import time
from baseController import BaseController

class Controller(BaseController):

	model     = None
	tokenizer = None
	counter   = 0

	def getModelPath(self, params):
		return {"Model": self.model}

	def getTokenizer(self, params):
		return {"Tokenizer": self.tokenizer}

	def increment(self, params):
		self.counter += params.get("by", 1)
		return {"counter": self.counter}

	def reset(self, params):
		self.counter = 0
		return {"counter": self.counter}

	def testStream(self, params):
		for i in range(5):
			time.sleep(0.5)
			self.send({"partial": f"step {i+1} complete"})
		return {"counter": self.counter}

Important Details

  • Inheritance: Your controller must inherit from BaseController.
  • Methods: Each method takes a single params dictionary argument containing parameters passed from Node.js.
  • Return Value: Methods return a JSON-serializable dictionary as a response.
  • Streaming: Use self.send(data) within methods to send partial streaming data back to Node.js. The JavaScript side will receive these via the registered stream callback.
  • Properties: Define class properties to maintain state accessible from both Python and Node.js via dynamic setProperty and getProperty calls.

Summary

  • Extend the Python Controller class to implement your backend logic.
  • Methods receive parameters and return JSON-serializable results.
  • Use self.send() to stream intermediate results when needed.
  • From Node.js, call these methods via the binding class, passing parameters as objects and receiving results asynchronously.
  • The binding handles JSON serialization, communication, and a persistent Python process lifecycle.

This design allows flexible and efficient integration between Node.js and Python for complex applications.