Run Python scripts from Node.js with efficient inter-process communication through stdio
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Interactive Python shell class for persistent processes with real-time data exchange through events and streams. This enables bidirectional communication with long-running Python processes.
Creates an instance of PythonShell and starts the Python process.
/**
* Creates an interactive Python shell and starts the Python process
* @param scriptPath - The path of the script to execute (cannot be empty)
* @param options - The execution options
* @param stdoutSplitter - Optional custom stdout splitter (defaults to NewlineTransformer)
* @param stderrSplitter - Optional custom stderr splitter (defaults to NewlineTransformer)
*/
constructor(scriptPath: string, options?: Options, stdoutSplitter: Transform = null, stderrSplitter: Transform = null);Usage Examples:
import { PythonShell } from "python-shell";
import { Transform } from "stream"; // For custom splitters
// Basic interactive shell
const pyshell = new PythonShell('interactive_script.py');
// With options
const pyshell = new PythonShell('data_processor.py', {
mode: 'json',
pythonPath: 'python3',
scriptPath: './scripts',
args: ['--interactive'],
pythonOptions: ['-u']
});
// With custom stream splitters
import { Transform } from 'stream';
const customSplitter = new Transform({
transform(chunk, encoding, callback) {
// Custom chunk processing
callback(null, chunk);
}
});
const pyshell = new PythonShell('script.py', {}, customSplitter);Sends a message to the Python script via stdin.
/**
* Sends a message to the Python shell through stdin
* Data is formatted according to the selected mode or custom formatter
* @param message - The message to send (string or object)
* @returns The same instance for chaining calls
*/
send(message: string | Object): PythonShell;Usage Examples:
// Send text messages
pyshell.send('process this data');
pyshell.send('another message');
// Send structured data in JSON mode
const pyshell = new PythonShell('json_processor.py', { mode: 'json' });
pyshell.send({ command: 'process', data: [1, 2, 3] });
pyshell.send({ command: 'save', filename: 'output.json' });
// Method chaining
pyshell
.send('first message')
.send('second message')
.send('third message');Closes the stdin stream and allows the Python process to exit.
/**
* Closes the stdin stream, allowing the Python script to finish and exit
* The callback is invoked when the process terminates
* @param callback - Function called when process terminates
* @returns The same instance for chaining calls
*/
end(callback: (err: PythonShellError, exitCode: number, exitSignal: string) => any): PythonShell;Usage Examples:
// Basic process termination
pyshell.end((err, code, signal) => {
if (err) {
console.error('Process failed:', err.message);
console.error('Exit code:', err.exitCode);
console.error('Traceback:', err.traceback);
} else {
console.log('Process completed successfully');
console.log('Exit code:', code);
}
});
// With promise wrapper
function endShell(shell) {
return new Promise((resolve, reject) => {
shell.end((err, code, signal) => {
if (err) reject(err);
else resolve({ code, signal });
});
});
}
try {
const result = await endShell(pyshell);
console.log('Finished with code:', result.code);
} catch (error) {
console.error('Error:', error.message);
}Terminates the Python process with a signal.
/**
* Sends a kill signal to the process
* @param signal - Kill signal (defaults to SIGTERM)
* @returns The same instance for chaining calls
*/
kill(signal?: NodeJS.Signals): PythonShell;
/**
* Alias for kill method (deprecated)
* @deprecated Use kill() instead
*/
terminate(signal?: NodeJS.Signals): PythonShell;Usage Examples:
// Graceful termination
pyshell.kill(); // Uses SIGTERM by default
// Force kill
pyshell.kill('SIGKILL');
// With timeout fallback
const killTimeout = setTimeout(() => {
console.log('Force killing unresponsive process');
pyshell.kill('SIGKILL');
}, 5000);
pyshell.kill('SIGTERM');
pyshell.on('close', () => {
clearTimeout(killTimeout);
});PythonShell extends EventEmitter and emits several events:
Emitted for each parsed chunk from stdout (not in binary mode).
on(event: 'message', listener: (parsedChunk: any) => void): this;Usage Examples:
// Text mode messages
const pyshell = new PythonShell('script.py', { mode: 'text' });
pyshell.on('message', (message) => {
console.log('Received text:', message); // string
});
// JSON mode messages
const jsonShell = new PythonShell('json_script.py', { mode: 'json' });
jsonShell.on('message', (data) => {
console.log('Received object:', data); // parsed JSON object
});Emitted for each parsed chunk from stderr (not in binary mode).
on(event: 'stderr', listener: (parsedChunk: any) => void): this;Usage Examples:
pyshell.on('stderr', (stderr) => {
console.log('Python stderr:', stderr);
// Handle warnings, debug info, or errors
});Emitted when the process terminates.
on(event: 'close', listener: () => void): this;Emitted when the process could not be spawned, killed, or communication failed.
on(event: 'error', listener: (error: NodeJS.ErrnoException) => void): this;Emitted when the process terminates with a non-zero exit code.
on(event: 'pythonError', listener: (error: PythonShellError) => void): this;Complete Event Handling Example:
import { PythonShell } from "python-shell";
const pyshell = new PythonShell('interactive_app.py', {
mode: 'json',
pythonOptions: ['-u']
});
// Handle successful messages
pyshell.on('message', (data) => {
console.log('Received:', data);
if (data.type === 'request_input') {
pyshell.send({ type: 'user_input', value: 'Hello Python!' });
}
});
// Handle stderr (warnings, debug info)
pyshell.on('stderr', (stderr) => {
console.warn('Python stderr:', stderr);
});
// Handle Python-specific errors
pyshell.on('pythonError', (error) => {
console.error('Python error:', error.message);
console.error('Traceback:', error.traceback);
console.error('Exit code:', error.exitCode);
});
// Handle system errors
pyshell.on('error', (error) => {
console.error('System error:', error.message);
console.error('Error code:', error.code);
});
// Handle process completion
pyshell.on('close', () => {
console.log('Python process closed');
});
// Start communication
pyshell.send({ type: 'init', config: { debug: true } });The PythonShell instance exposes several properties for monitoring and control:
readonly scriptPath: string; // Path of the script being executed
readonly command: string[]; // Full command arguments passed to Python
readonly mode: string; // Data exchange mode (text/json/binary)
readonly terminated: boolean; // Whether the process has exited
readonly childProcess: ChildProcess; // Underlying child process
readonly stdin: Writable; // Python stdin stream
readonly stdout: Readable; // Python stdout stream
readonly stderr: Readable; // Python stderr stream
readonly exitCode: number; // Process exit code (after termination)
readonly exitSignal: string; // Process exit signal (after termination)Install with Tessl CLI
npx tessl i tessl/npm-python-shell