Python distribution for the browser and Node.js based on WebAssembly that enables running Python code with full JavaScript interoperability
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Customize input/output handling for Python code execution in different environments and use cases.
Set custom standard input handler for Python input operations.
function setStdin(options?: {
stdin?: () => string;
read?: (buffer: Uint8Array) => number;
isatty?: boolean;
}): void;Parameters:
options.stdin - Function called when Python requests input, should return a stringoptions.read - Low-level read function for binary inputoptions.isatty - Whether input is from a terminalSet custom standard output handler for Python print statements and output.
function setStdout(options?: {
batched?: (output: string) => void;
raw?: (charCode: number) => void;
write?: (buffer: Uint8Array) => number;
isatty?: boolean;
}): void;Parameters:
options.batched - Function called with each line of Python outputoptions.raw - Function called with individual character codesoptions.write - Low-level write function for binary outputoptions.isatty - Whether output is to a terminalSet custom standard error handler for Python error messages and warnings.
function setStderr(options?: {
batched?: (output: string) => void;
raw?: (charCode: number) => void;
write?: (buffer: Uint8Array) => number;
isatty?: boolean;
}): void;Parameters:
options.batched - Function called with each line of Python error outputoptions.raw - Function called with individual character codesoptions.write - Low-level write function for binary error outputoptions.isatty - Whether error output is to a terminal// Redirect Python output to custom handlers
pyodide.setStdout({
batched: (message) => {
console.log(`[Python Output] ${message}`);
}
});
pyodide.setStderr({
batched: (message) => {
console.error(`[Python Error] ${message}`);
}
});
// Test output redirection
pyodide.runPython(`
print("This goes to stdout")
import sys
sys.stderr.write("This goes to stderr\\n")
`);// Simple prompt-based input
pyodide.setStdin({
stdin: () => {
return prompt("Python input requested:");
}
});
pyodide.runPython(`
name = input("What's your name? ")
print(f"Hello, {name}!")
`);class InputQueue {
constructor() {
this.queue = [];
this.waitingResolvers = [];
}
addInput(input) {
if (this.waitingResolvers.length > 0) {
const resolver = this.waitingResolvers.shift();
resolver(input);
} else {
this.queue.push(input);
}
}
async getInput() {
if (this.queue.length > 0) {
return this.queue.shift();
}
return new Promise((resolve) => {
this.waitingResolvers.push(resolve);
});
}
}
const inputQueue = new InputQueue();
// Set up async input handler
pyodide.setStdin(() => {
// This is a blocking call in the Python context
// but we can use a synchronous approach with queued inputs
if (inputQueue.queue.length > 0) {
return inputQueue.queue.shift();
}
return ""; // Return empty if no input available
});
// Add inputs programmatically
inputQueue.addInput("Alice");
inputQueue.addInput("25");
inputQueue.addInput("Engineer");
pyodide.runPython(`
name = input("Name: ")
age = input("Age: ")
job = input("Job: ")
print(f"Hello {name}, you are {age} years old and work as an {job}")
`);class StreamLogger {
constructor() {
this.outputLog = [];
this.errorLog = [];
this.inputLog = [];
}
logOutput(message) {
const entry = {
type: 'stdout',
message,
timestamp: new Date().toISOString()
};
this.outputLog.push(entry);
console.log(`[OUT] ${message}`);
}
logError(message) {
const entry = {
type: 'stderr',
message,
timestamp: new Date().toISOString()
};
this.errorLog.push(entry);
console.error(`[ERR] ${message}`);
}
logInput(input) {
const entry = {
type: 'stdin',
input,
timestamp: new Date().toISOString()
};
this.inputLog.push(entry);
return input;
}
getFullLog() {
return [...this.outputLog, ...this.errorLog, ...this.inputLog]
.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
}
exportLog() {
return JSON.stringify(this.getFullLog(), null, 2);
}
}
const logger = new StreamLogger();
pyodide.setStdout((msg) => logger.logOutput(msg));
pyodide.setStderr((msg) => logger.logError(msg));
pyodide.setStdin(() => logger.logInput("user_input"));
// Run Python code with full logging
pyodide.runPython(`
print("Starting calculation...")
try:
result = 10 / 2
print(f"Result: {result}")
except Exception as e:
print(f"Error: {e}")
user_data = input("Enter data: ")
print(f"You entered: {user_data}")
`);
// Export execution log
console.log("Execution log:", logger.exportLog());// HTML elements for I/O
const outputDiv = document.getElementById('python-output');
const errorDiv = document.getElementById('python-errors');
const inputField = document.getElementById('python-input');
const inputButton = document.getElementById('input-submit');
let pendingInputResolver = null;
// Set up output handlers
pyodide.setStdout((message) => {
const line = document.createElement('div');
line.className = 'output-line';
line.textContent = message;
outputDiv.appendChild(line);
outputDiv.scrollTop = outputDiv.scrollHeight;
});
pyodide.setStderr((message) => {
const line = document.createElement('div');
line.className = 'error-line';
line.textContent = message;
errorDiv.appendChild(line);
errorDiv.scrollTop = errorDiv.scrollHeight;
});
// Set up input handler
pyodide.setStdin(() => {
// Show input UI
inputField.style.display = 'block';
inputButton.style.display = 'block';
inputField.focus();
// Wait for user input
return new Promise((resolve) => {
pendingInputResolver = resolve;
});
});
inputButton.addEventListener('click', () => {
if (pendingInputResolver) {
const input = inputField.value;
inputField.value = '';
inputField.style.display = 'none';
inputButton.style.display = 'none';
pendingInputResolver(input);
pendingInputResolver = null;
}
});
// Handle Enter key
inputField.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
inputButton.click();
}
});class BufferedOutputStream {
constructor(flushCallback, bufferSize = 1024) {
this.buffer = '';
this.flushCallback = flushCallback;
this.bufferSize = bufferSize;
this.timer = null;
}
write(message) {
this.buffer += message;
// Flush if buffer is full
if (this.buffer.length >= this.bufferSize) {
this.flush();
} else {
// Schedule flush after short delay
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(() => this.flush(), 10);
}
}
flush() {
if (this.buffer.length > 0) {
this.flushCallback(this.buffer);
this.buffer = '';
}
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
}
}
const bufferedOutput = new BufferedOutputStream((content) => {
console.log('Flushed output:', content);
});
const bufferedError = new BufferedOutputStream((content) => {
console.error('Flushed errors:', content);
});
pyodide.setStdout((msg) => bufferedOutput.write(msg + '\n'));
pyodide.setStderr((msg) => bufferedError.write(msg + '\n'));
// Test with rapid output
pyodide.runPython(`
for i in range(100):
print(f"Line {i}")
`);
// Ensure final flush
setTimeout(() => {
bufferedOutput.flush();
bufferedError.flush();
}, 100);// Redirect output to virtual files
pyodide.FS.writeFile('/tmp/output.log', '');
pyodide.FS.writeFile('/tmp/error.log', '');
pyodide.setStdout((message) => {
const current = pyodide.FS.readFile('/tmp/output.log', { encoding: 'utf8' });
pyodide.FS.writeFile('/tmp/output.log', current + message + '\n');
});
pyodide.setStderr((message) => {
const current = pyodide.FS.readFile('/tmp/error.log', { encoding: 'utf8' });
pyodide.FS.writeFile('/tmp/error.log', current + message + '\n');
});
// Run Python code
pyodide.runPython(`
print("This goes to output.log")
import sys
sys.stderr.write("This goes to error.log\\n")
import warnings
warnings.warn("This is a warning")
`);
// Read logged output
const outputLog = pyodide.FS.readFile('/tmp/output.log', { encoding: 'utf8' });
const errorLog = pyodide.FS.readFile('/tmp/error.log', { encoding: 'utf8' });
console.log('Output log:', outputLog);
console.log('Error log:', errorLog);class StreamMultiplexer {
constructor() {
this.handlers = {
stdout: [],
stderr: [],
stdin: []
};
}
addOutputHandler(handler) {
this.handlers.stdout.push(handler);
}
addErrorHandler(handler) {
this.handlers.stderr.push(handler);
}
addInputHandler(handler) {
this.handlers.stdin.push(handler);
}
handleOutput(message) {
this.handlers.stdout.forEach(handler => handler(message));
}
handleError(message) {
this.handlers.stderr.forEach(handler => handler(message));
}
handleInput() {
// Use first available input handler
if (this.handlers.stdin.length > 0) {
return this.handlers.stdin[0]();
}
return '';
}
}
const multiplexer = new StreamMultiplexer();
// Add multiple output handlers
multiplexer.addOutputHandler((msg) => console.log(`[Console] ${msg}`));
multiplexer.addOutputHandler((msg) => {
// Send to websocket, log file, etc.
// websocket.send(JSON.stringify({type: 'stdout', message: msg}));
});
multiplexer.addErrorHandler((msg) => console.error(`[Console Error] ${msg}`));
multiplexer.addErrorHandler((msg) => {
// Send errors to monitoring service
// monitoringService.reportError(msg);
});
// Set up Pyodide with multiplexer
pyodide.setStdout((msg) => multiplexer.handleOutput(msg));
pyodide.setStderr((msg) => multiplexer.handleError(msg));
pyodide.setStdin(() => multiplexer.handleInput());// Save original stream handlers for restoration
const originalHandlers = {
stdout: null,
stderr: null,
stdin: null
};
function saveStreamHandlers() {
// Note: Pyodide doesn't expose current handlers directly
// This is conceptual - you'd need to track them in your app
originalHandlers.stdout = console.log;
originalHandlers.stderr = console.error;
originalHandlers.stdin = () => prompt("Input:");
}
function restoreStreamHandlers() {
pyodide.setStdout(originalHandlers.stdout);
pyodide.setStderr(originalHandlers.stderr);
pyodide.setStdin(originalHandlers.stdin);
}
// Custom temporary handlers
function withCustomStreams(customHandlers, callback) {
saveStreamHandlers();
if (customHandlers.stdout) pyodide.setStdout(customHandlers.stdout);
if (customHandlers.stderr) pyodide.setStderr(customHandlers.stderr);
if (customHandlers.stdin) pyodide.setStdin(customHandlers.stdin);
try {
return callback();
} finally {
restoreStreamHandlers();
}
}
// Usage
withCustomStreams({
stdout: (msg) => console.log(`[CUSTOM] ${msg}`),
stderr: (msg) => console.error(`[CUSTOM ERROR] ${msg}`)
}, () => {
pyodide.runPython(`
print("This uses custom handlers")
import sys
sys.stderr.write("Custom error handler\\n")
`);
});
// Original handlers are restored automatically
pyodide.runPython(`print("Back to original handlers")`);Install with Tessl CLI
npx tessl i tessl/npm-pyodide