JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 5
  • Score
    100M100P100Q56908F
  • License MIT

Self-contained Python 3 + pip runtime embedded in Node.js

Package Exports

  • python-bundle

Readme

python-bundle

python-bundle logo

python-bundle

Embed Portable Python 3 + pip into Node.js projects

npm version npm downloads license


✨ Features

  • Portable Python 3.12 via Miniforge (auto-download on install)
  • Built-in pip for Python package management
  • Supports CommonJS & ESM (require & import)
  • No system Python dependency (ideal for Pterodactyl, Docker, serverless)
  • Stream & Interactive modes for real-time output & user input
  • Four main methods: execPython, runScript, runCode, pipInstall
  • Stdin support for passing input to Python scripts

🚀 Installation

npm install python-bundle

Note: Runs postinstall hook to download Miniforge into vendor/python.


🛠️ API Reference

Import the module:

CommonJS

const { pipInstall, runScript, runCode, execPython } = require('python-bundle');

ESM

import { pipInstall, runScript, runCode, execPython } from 'python-bundle';

execPython(args, options)

Execute any Python command with optimizations enabled (-O flag).

Parameters:

  • args (string[]): CLI arguments for Python (e.g. ['-c', 'print("Hi")'])
  • options (object):
    • cwd (string): Working directory (default: process.cwd())
    • stream (boolean): Pipe output to console in real-time (default: false)
    • stdin (string): Input string to send to stdin
    • interactive (boolean): Note: Not supported in execPython (use runScript instead)

Returns: Promise<string | void>

  • Resolves with output string when stream: false
  • Resolves with undefined when stream: true
  • Rejects with error on non-zero exit code

Example:

// Execute inline code with stdin input
await execPython(
  ['-c', 'import sys; print(sys.stdin.read().upper())'], 
  { 
    stdin: 'hello world', 
    stream: true 
  }
);

runScript(scriptPath, args, options)

Run a Python .py file with two execution modes.

Parameters:

  • scriptPath (string): Path to Python script
  • args (string[]): Arguments passed to script (default: [])
  • options (object):
    • cwd (string): Working directory (default: process.cwd())
    • stream (boolean): Pipe output to console in real-time
    • stdin (string): Input string for non-interactive mode
    • interactive (boolean): Enable interactive mode (returns ChildProcess)

Returns:

  • Promise<string | void> when non-interactive
  • ChildProcess when interactive: true

Non-interactive Example:

// Run script with arguments and capture output
const output = await runScript('script.py', ['args1'], {
  cwd: './scripts'
});

Interactive Example:

// Run interactively and manage process directly
const child = runScript('script.py', [], { interactive: true });

child.stdout.on('data', data => process.stdout.write(data));
child.stderr.on('data', data => process.stderr.write(data));
child.stdin.write('user input\n'); 

child.on('exit', code => {
  console.log(`Process exited with code ${code}`);
});

runCode(codeString, options)

Execute inline Python code block.

Parameters:

  • codeString (string): Python source code
  • options (object): Same as execPython

Returns: Promise<string | void>
Same behavior as execPython

Example:

// Execute code with stdin and stream output
await runCode(
  `import sys, json; 
   data = json.load(sys.stdin); 
   print(data['message'])`,
  {
    stdin: JSON.stringify({ message: "Hello from stdin!" }),
    stream: true
  }
);

pipInstall(packages, options)

Install Python packages via pip.

Parameters:

  • packages (string[]): Package names to install
  • options (object):
    • cwd (string): Working directory
    • stream (boolean): Show live installation output

Returns: Promise<void>

Example:

// Install multiple packages with live output
await pipInstall(['numpy', 'pandas', 'scikit-learn'], { 
  stream: true 
});

📁 Project Structure

After installation:

your-project/
├── node_modules/
│   └── python-bundle/
│       ├── vendor/
│       │   └── python/      # Portable Python
│       │       ├── bin/
│       │       │   └── python  # Python binary
│       │       └── ...       # Python environment
│       └── index.js         # Module entry point
├── scripts/
│   └── your_script.py      # Your Python scripts
└── main.js                 # Your Node.js code

🔧 Usage Examples

1. Basic Usage (CommonJS)

// JavaScript
const { runScript, pipInstall } = require('python-bundle');

(async () => {
  // Install Flask
  await pipInstall(['flask'], { stream: true });
  
  // Run Python web server
  await runScript('web_server.py', ['3000'], { stream: true });
})();
# web_server.py (Python)
from flask import Flask
import sys

app = Flask(__name__)
port = 3000 if len(sys.argv) < 2 else int(sys.argv[1])

@app.route('/')
def home():
    return "Hello from Python bundle!"

if __name__ == '__main__':
    print(f"Starting server on port {port}")
    app.run(port=port)

2. Advanced Usage (ESM)

// JavaScript
import { runCode, pipInstall } from 'python-bundle';

// Install NumPy
await pipInstall(['numpy'], { stream: true });

// Run data processing code
await runCode(`
import numpy as np

 arr = np.array([1, 2, 3, 4])
 print(f"Mean: {np.mean(arr)}")
 print(f"Sum: {np.sum(arr)}")
`, { stream: true });

3. Interactive Script Handling

// JavaScript
const { runScript } = require('python-bundle');

// Start calculator process
const calc = runScript('calculator.py', [], { interactive: true });

calc.stdout.on('data', data => {
  process.stdout.write(data);
  if (data.includes('Enter operation')) {
    calc.stdin.write('add\n');
  }
  if (data.includes('Enter first number')) {
    calc.stdin.write('15\n');
  }
  if (data.includes('Enter second number')) {
    calc.stdin.write('25\n');
  }
});

calc.on('exit', () => console.log('Calculation complete!'));
# calculator.py (Python)
print("Simple Calculator")
while True:
    try:
        op = input("Enter operation (add/sub/mul/div): ")
        num1 = float(input("Enter first number: "))
        num2 = float(input("Enter second number: "))
        
        if op == 'add': result = num1 + num2
        elif op == 'sub': result = num1 - num2
        elif op == 'mul': result = num1 * num2
        elif op == 'div': result = num1 / num2
        else: 
            print("Invalid operation")
            continue
            
        print(f"Result: {result}")
        break
    except ValueError:
        print("Please enter valid numbers!")
    except ZeroDivisionError:
        print("Cannot divide by zero!")

4. File Processing Example

// JavaScript
const { runScript } = require('python-bundle');

// Process CSV file
await runScript('csv_processor.py', ['input.csv', 'output.json'], {
  stream: true,
  cwd: './data'
});
# csv_processor.py (Python)
import csv
import json
import sys

if len(sys.argv) < 3:
    print("Usage: python csv_processor.py <input.csv> <output.json>")
    exit(1)

input_file = sys.argv[1]
output_file = sys.argv[2]

data = []
with open(input_file, 'r') as f:
    reader = csv.DictReader(f)
    for row in reader:
        data.append(row)

print(f"Processed {len(data)} records")
with open(output_file, 'w') as f:
    json.dump(data, f, indent=2)

⚠️ Troubleshooting

  • Interactive Mode Requirements
    Use { interactive: true } only with runScript(), not with execPython() or runCode()

  • Stdin Handling
    Non-interactive mode: use stdin option
    Interactive mode: write to child.stdin directly

  • Python Optimizations
    All executions run with -O flag. Disable optimizations by modifying source if needed

  • Path Handling
    Use absolute paths or relative to options.cwd for scripts

  • Permission Issues
    On Linux systems:

    chmod +x node_modules/python-bundle/vendor/python/bin/python
  • Environment Conflicts
    Module sets PYTHONHOME automatically. Unset this variable if you encounter conflicts


📄 License

MIT © 2025 RelixOfficial


Maintained with ❤️ by RelixTeam. PRs and contributions are welcome!