python-shell

Run Python scripts from Node.js with simple, efficient stdio-based inter-process communication.

Library
npm
v5.0.0
2,162stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
48/100Fair
Development Activity4
Maintenance20
Community68
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
59/100Fair
Architecture65
Code Quality55
Innovation60
Learning Curve55

python-shell is a Node.js library for spawning Python scripts as child processes and exchanging data with them over stdin/stdout. It wraps child_process.spawn with a mode system (text, JSON, or binary) that automatically formats outgoing messages and parses incoming ones, so a Node application can send commands to a running Python process and receive structured messages back without manually managing stream buffering or line splitting.

Beyond raw process control, it adds developer conveniences that plain child_process doesn’t: a promise-based run/runString API for one-shot script execution, syntax-checking helpers (checkSyntax/checkSyntaxFile) that validate Python code before running it, and error handling that parses Python tracebacks out of stderr into a JavaScript PythonShellError with an extended stack trace showing both the Node and Python call sites.

It has zero runtime dependencies and ships as a small, TypeScript-authored package, making it a common glue layer for Node applications that need to call into Python-only libraries or scripts (data processing, ML inference, scripting) without standing up a separate service.

What You Get

  • A PythonShell class (extends EventEmitter) that spawns a Python process and exposes its stdin/stdout/stderr as manageable streams
  • Built-in text, json, and binary modes that automatically format sent messages and parse received ones per line
  • A NewlineTransformer stream that splits chunked stdout/stderr into discrete newline-delimited messages, avoiding partial-JSON parsing bugs
  • Promise-based PythonShell.run() and PythonShell.runString() statics for one-off script/code execution that collect all emitted messages
  • checkSyntax()/checkSyntaxFile() helpers that validate Python source without executing it
  • Automatic Python traceback parsing into a PythonShellError/PythonShellErrorWithLogs with an extended, combined Node+Python stack trace

Common Use Cases

  • Calling a Python data-processing or ML script from a Node.js backend and collecting its output as JSON
  • Running short one-off Python snippets via runString() for quick calculations or environment checks
  • Maintaining a long-lived interactive Python process that a Node app repeatedly sends commands to via .send() and listens to via the message event
  • Validating user- or template-generated Python code with checkSyntax() before executing it
  • Surfacing Python exceptions in a Node error-handling pipeline with full traceback context attached to the thrown error

Under The Hood

Architecture The entire library lives in a single index.ts module built around the PythonShell class, which extends EventEmitter and wraps child_process.spawn directly in its constructor. Message framing is handled by piping stdout/stderr through a NewlineTransformer (a custom Transform stream) before applying the mode-specific parser, and process termination is coordinated through a terminateIfNeeded() closure that waits for both streams to end and an exit code/signal before resolving. There is no internal layering beyond this one class plus a couple of free functions (toArray, extend) — a monolithic but narrowly scoped design where the constructor itself does spawning, stream wiring, and error-state tracking together, so changes to child_process’s stream/event contract would touch nearly the whole file at once.

Tech Stack Written in TypeScript (es6 target, commonjs module) and distributed with zero runtime dependencies — it only uses Node’s built-in child_process, events, stream, os, path, fs, and util modules. The dev toolchain is mocha with should assertions run through ts-node, tsc for compilation, prettier for formatting, and legacy AppVeyor CI with Codecov coverage reporting rather than GitHub Actions.

Code Quality The test suite (test/test-python-shell.ts) is extensive, covering constructor argument handling, all three IO modes, error and traceback parsing, syntax checking, and process lifecycle events against real Python fixture scripts in test/python/. Error handling is explicit: a dedicated PythonShellError type carries the parsed Python traceback and exit code rather than swallowing failures. That said, the TypeScript config doesn’t enable strict mode, several signatures fall back to any/Object, and there’s no linter configured — only prettier for formatting — and CI runs on a stale, non-GitHub-Actions pipeline, consistent with the repo’s own looking-for-maintainer topic tags.

API Design The public surface is small and pragmatic: a promise-based run/runString pair for fire-and-forget scripts, and an instance-based new PythonShell() API with .send()/.on('message') for interactive processes, unified by a single mode option that swaps the formatter/parser pair instead of requiring separate methods per format. Getting started requires a single import and one call. The concepts (spawn wrapper, mode-based (de)serialization, traceback bridging) aren’t novel relative to other child-process wrappers, but the ergonomics around error reporting and stream-splitting are a genuine convenience over hand-rolling child_process.spawn directly.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search