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
Utility functions for Python environment management, syntax checking, version detection, and stream processing. These functions provide helpful tools for working with Python environments and validating code before execution.
Validate Python code syntax without executing it, useful for code validation and error prevention.
Validates Python code syntax from a string without execution.
/**
* Checks syntax of Python code string without executing
* Creates temporary file for syntax validation
* @param code - Python code string to validate
* @returns Promise that resolves if syntax is valid, rejects with error details if invalid
*/
static async checkSyntax(code: string): Promise<void>;Usage Examples:
import { PythonShell } from "python-shell";
// Valid syntax
try {
await PythonShell.checkSyntax('print("Hello, World!")');
console.log('Syntax is valid');
} catch (error) {
console.error('Syntax error:', error.message);
}
// Invalid syntax
try {
await PythonShell.checkSyntax('print("Missing closing quote');
} catch (error) {
console.error('Syntax error detected:', error.stderr);
// Error contains Python compiler error message
}
// Complex code validation
const pythonCode = `
import json
import sys
def process_data(data):
if not isinstance(data, list):
raise ValueError("Data must be a list")
return [x * 2 for x in data if x > 0]
if __name__ == "__main__":
data = json.loads(sys.argv[1])
result = process_data(data)
print(json.dumps(result))
`;
try {
await PythonShell.checkSyntax(pythonCode);
console.log('Complex code syntax is valid');
} catch (error) {
console.error('Syntax issues found:', error.stderr);
}Validates Python file syntax without execution.
/**
* Checks syntax of Python file without executing
* Uses py_compile module for validation
* @param filePath - Path to Python file to validate
* @returns Promise that resolves if syntax is valid, rejects with error details if invalid
*/
static async checkSyntaxFile(filePath: string): Promise<void>;Usage Examples:
import { PythonShell } from "python-shell";
import { promises as fs } from 'fs';
// Check individual file
try {
await PythonShell.checkSyntaxFile('./scripts/data_processor.py');
console.log('File syntax is valid');
} catch (error) {
console.error('File has syntax errors:', error.stderr);
}
// Batch validate multiple files
const scriptFiles = [
'./scripts/processor.py',
'./scripts/validator.py',
'./scripts/exporter.py'
];
const results = await Promise.allSettled(
scriptFiles.map(file => PythonShell.checkSyntaxFile(file))
);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`${scriptFiles[index]}: Valid syntax`);
} else {
console.error(`${scriptFiles[index]}: Syntax error -`, result.reason.stderr);
}
});
// Validate before execution
async function safeExecute(scriptPath: string, options?: Options) {
try {
await PythonShell.checkSyntaxFile(scriptPath);
return await PythonShell.run(scriptPath, options);
} catch (syntaxError) {
throw new Error(`Cannot execute ${scriptPath}: ${syntaxError.stderr}`);
}
}Get information about Python installations and versions.
Asynchronously gets Python version information.
/**
* Gets Python version information asynchronously
* @param pythonPath - Optional path to specific Python executable
* @returns Promise resolving to object with stdout and stderr from version command
*/
static getVersion(pythonPath?: string): Promise<{stdout: string, stderr: string}>;Usage Examples:
import { PythonShell } from "python-shell";
// Get default Python version
try {
const version = await PythonShell.getVersion();
console.log('Python version:', version.stdout.trim());
// Output: "Python 3.9.7"
} catch (error) {
console.error('Python not found or error getting version:', error.message);
}
// Check specific Python installation
try {
const version = await PythonShell.getVersion('/usr/bin/python3.10');
console.log('Python 3.10 version:', version.stdout.trim());
} catch (error) {
console.error('Python 3.10 not found:', error.message);
}
// Check multiple Python versions
const pythonPaths = ['python', 'python3', 'python3.9', 'python3.10'];
const versionChecks = pythonPaths.map(async (path) => {
try {
const version = await PythonShell.getVersion(path);
return { path, version: version.stdout.trim(), available: true };
} catch (error) {
return { path, error: error.message, available: false };
}
});
const results = await Promise.all(versionChecks);
results.forEach(result => {
if (result.available) {
console.log(`${result.path}: ${result.version}`);
} else {
console.log(`${result.path}: Not available - ${result.error}`);
}
});Synchronously gets Python version information.
/**
* Gets Python version information synchronously
* @param pythonPath - Optional path to specific Python executable
* @returns Python version string
*/
static getVersionSync(pythonPath?: string): string;Usage Examples:
import { PythonShell } from "python-shell";
// Get default Python version synchronously
try {
const version = PythonShell.getVersionSync();
console.log('Python version:', version.trim());
} catch (error) {
console.error('Error getting Python version:', error.message);
}
// Check specific Python executable
try {
const version = PythonShell.getVersionSync('./venv/bin/python');
console.log('Virtual environment Python:', version.trim());
} catch (error) {
console.error('Virtual environment Python not found');
}
// Version comparison utility
function comparePythonVersions() {
try {
const systemVersion = PythonShell.getVersionSync('python');
const python3Version = PythonShell.getVersionSync('python3');
console.log('System Python:', systemVersion.trim());
console.log('Python3:', python3Version.trim());
// Extract version numbers for comparison
const systemMatch = systemVersion.match(/Python (\d+\.\d+\.\d+)/);
const python3Match = python3Version.match(/Python (\d+\.\d+\.\d+)/);
if (systemMatch && python3Match) {
console.log('System version number:', systemMatch[1]);
console.log('Python3 version number:', python3Match[1]);
}
} catch (error) {
console.error('Error comparing versions:', error.message);
}
}Gets the currently configured Python executable path.
/**
* Gets the current Python path configuration
* Returns either defaultOptions.pythonPath or defaultPythonPath
* @returns Current Python executable path
*/
static getPythonPath(): string;Usage Examples:
import { PythonShell } from "python-shell";
// Get current Python path
const currentPath = PythonShell.getPythonPath();
console.log('Current Python path:', currentPath);
// Default: "python3" on Unix, "python" on Windows
// After setting custom default
PythonShell.defaultOptions = { pythonPath: '/usr/bin/python3.9' };
const customPath = PythonShell.getPythonPath();
console.log('Custom Python path:', customPath); // "/usr/bin/python3.9"
// Utility to validate current Python setup
async function validatePythonSetup() {
const pythonPath = PythonShell.getPythonPath();
console.log('Using Python executable:', pythonPath);
try {
const version = await PythonShell.getVersion(pythonPath);
console.log('Version:', version.stdout.trim());
// Test basic functionality
await PythonShell.checkSyntax('print("Hello")');
console.log('Python setup is working correctly');
} catch (error) {
console.error('Python setup has issues:', error.message);
}
}Utility class for processing streams with custom delimiters.
Stream transformer that splits data into chunks separated by newlines.
/**
* Stream transformer for splitting data by newlines
* Used internally as default stdout/stderr splitter
*/
class NewlineTransformer extends Transform {
/**
* Transform stream chunks, splitting by newlines
* @param chunk - Input data chunk
* @param encoding - Character encoding
* @param callback - Completion callback
*/
_transform(chunk: any, encoding: string, callback: TransformCallback): void;
/**
* Flush remaining data when stream ends
* @param done - Completion callback
*/
_flush(done: TransformCallback): void;
}Usage Examples:
import { PythonShell, NewlineTransformer } from "python-shell";
import { Transform } from 'stream';
// Using NewlineTransformer for custom streams
const customPipe = someReadableStream;
const newlineTransformer = new NewlineTransformer();
customPipe
.pipe(newlineTransformer)
.on('data', (line) => {
console.log('Received line:', line.toString());
});
// Custom splitter for special delimiters
class CustomSplitter extends Transform {
private buffer = '';
_transform(chunk: any, encoding: string, callback: TransformCallback) {
this.buffer += chunk.toString();
const parts = this.buffer.split('||'); // Custom delimiter
this.buffer = parts.pop() || '';
parts.forEach(part => this.push(part));
callback();
}
_flush(callback: TransformCallback) {
if (this.buffer) this.push(this.buffer);
callback();
}
}
// Use custom splitter with PythonShell
const customSplitter = new CustomSplitter();
const pyshell = new PythonShell('script.py', {}, customSplitter);
// Advanced stream processing with multiple custom pipes
const options = {
stdio: ['pipe', 'pipe', 'pipe', 'pipe'] // stdin, stdout, stderr, custom
};
const pyshell = new PythonShell('advanced_script.py', options);
const customStream = pyshell.childProcess.stdio[3];
if (customStream) {
customStream
.pipe(new NewlineTransformer())
.on('data', (customData) => {
console.log('Custom stream data:', customData.toString());
});
}Combined utility function for comprehensive Python environment validation:
// Example comprehensive validation function
async function validatePythonEnvironment(options?: Options) {
const pythonPath = options?.pythonPath || PythonShell.getPythonPath();
console.log('Validating Python environment...');
console.log('Python path:', pythonPath);
try {
// Check if Python is accessible
const version = await PythonShell.getVersion(pythonPath);
console.log('✓ Python version:', version.stdout.trim());
// Test syntax checking
await PythonShell.checkSyntax('import sys; print("Hello")');
console.log('✓ Syntax checking works');
// Test basic execution
const result = await PythonShell.runString('print("test")', { pythonPath });
console.log('✓ Basic execution works:', result);
// Test with provided options
if (options?.pythonOptions) {
const testResult = await PythonShell.runString('print("options test")', options);
console.log('✓ Custom options work:', testResult);
}
console.log('✓ Python environment validation complete');
return true;
} catch (error) {
console.error('✗ Python environment validation failed:', error.message);
return false;
}
}Install with Tessl CLI
npx tessl i tessl/npm-python-shell