0
# I/O Streams
1
2
Customize input/output handling for Python code execution in different environments and use cases.
3
4
## Stream Control Functions
5
6
### setStdin
7
8
Set custom standard input handler for Python input operations.
9
10
```javascript { .api }
11
function setStdin(options?: {
12
stdin?: () => string;
13
read?: (buffer: Uint8Array) => number;
14
isatty?: boolean;
15
}): void;
16
```
17
18
**Parameters:**
19
- `options.stdin` - Function called when Python requests input, should return a string
20
- `options.read` - Low-level read function for binary input
21
- `options.isatty` - Whether input is from a terminal
22
23
### setStdout
24
25
Set custom standard output handler for Python print statements and output.
26
27
```javascript { .api }
28
function setStdout(options?: {
29
batched?: (output: string) => void;
30
raw?: (charCode: number) => void;
31
write?: (buffer: Uint8Array) => number;
32
isatty?: boolean;
33
}): void;
34
```
35
36
**Parameters:**
37
- `options.batched` - Function called with each line of Python output
38
- `options.raw` - Function called with individual character codes
39
- `options.write` - Low-level write function for binary output
40
- `options.isatty` - Whether output is to a terminal
41
42
### setStderr
43
44
Set custom standard error handler for Python error messages and warnings.
45
46
```javascript { .api }
47
function setStderr(options?: {
48
batched?: (output: string) => void;
49
raw?: (charCode: number) => void;
50
write?: (buffer: Uint8Array) => number;
51
isatty?: boolean;
52
}): void;
53
```
54
55
**Parameters:**
56
- `options.batched` - Function called with each line of Python error output
57
- `options.raw` - Function called with individual character codes
58
- `options.write` - Low-level write function for binary error output
59
- `options.isatty` - Whether error output is to a terminal
60
61
## Usage Examples
62
63
### Basic Stream Redirection
64
65
```javascript
66
// Redirect Python output to custom handlers
67
pyodide.setStdout({
68
batched: (message) => {
69
console.log(`[Python Output] ${message}`);
70
}
71
});
72
73
pyodide.setStderr({
74
batched: (message) => {
75
console.error(`[Python Error] ${message}`);
76
}
77
});
78
79
// Test output redirection
80
pyodide.runPython(`
81
print("This goes to stdout")
82
import sys
83
sys.stderr.write("This goes to stderr\\n")
84
`);
85
```
86
87
### Interactive Input Handling
88
89
```javascript
90
// Simple prompt-based input
91
pyodide.setStdin({
92
stdin: () => {
93
return prompt("Python input requested:");
94
}
95
});
96
97
pyodide.runPython(`
98
name = input("What's your name? ")
99
print(f"Hello, {name}!")
100
`);
101
```
102
103
### Advanced Input Queue System
104
105
```javascript
106
class InputQueue {
107
constructor() {
108
this.queue = [];
109
this.waitingResolvers = [];
110
}
111
112
addInput(input) {
113
if (this.waitingResolvers.length > 0) {
114
const resolver = this.waitingResolvers.shift();
115
resolver(input);
116
} else {
117
this.queue.push(input);
118
}
119
}
120
121
async getInput() {
122
if (this.queue.length > 0) {
123
return this.queue.shift();
124
}
125
126
return new Promise((resolve) => {
127
this.waitingResolvers.push(resolve);
128
});
129
}
130
}
131
132
const inputQueue = new InputQueue();
133
134
// Set up async input handler
135
pyodide.setStdin(() => {
136
// This is a blocking call in the Python context
137
// but we can use a synchronous approach with queued inputs
138
if (inputQueue.queue.length > 0) {
139
return inputQueue.queue.shift();
140
}
141
return ""; // Return empty if no input available
142
});
143
144
// Add inputs programmatically
145
inputQueue.addInput("Alice");
146
inputQueue.addInput("25");
147
inputQueue.addInput("Engineer");
148
149
pyodide.runPython(`
150
name = input("Name: ")
151
age = input("Age: ")
152
job = input("Job: ")
153
154
print(f"Hello {name}, you are {age} years old and work as an {job}")
155
`);
156
```
157
158
### Logging and Analytics
159
160
```javascript
161
class StreamLogger {
162
constructor() {
163
this.outputLog = [];
164
this.errorLog = [];
165
this.inputLog = [];
166
}
167
168
logOutput(message) {
169
const entry = {
170
type: 'stdout',
171
message,
172
timestamp: new Date().toISOString()
173
};
174
this.outputLog.push(entry);
175
console.log(`[OUT] ${message}`);
176
}
177
178
logError(message) {
179
const entry = {
180
type: 'stderr',
181
message,
182
timestamp: new Date().toISOString()
183
};
184
this.errorLog.push(entry);
185
console.error(`[ERR] ${message}`);
186
}
187
188
logInput(input) {
189
const entry = {
190
type: 'stdin',
191
input,
192
timestamp: new Date().toISOString()
193
};
194
this.inputLog.push(entry);
195
return input;
196
}
197
198
getFullLog() {
199
return [...this.outputLog, ...this.errorLog, ...this.inputLog]
200
.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
201
}
202
203
exportLog() {
204
return JSON.stringify(this.getFullLog(), null, 2);
205
}
206
}
207
208
const logger = new StreamLogger();
209
210
pyodide.setStdout((msg) => logger.logOutput(msg));
211
pyodide.setStderr((msg) => logger.logError(msg));
212
pyodide.setStdin(() => logger.logInput("user_input"));
213
214
// Run Python code with full logging
215
pyodide.runPython(`
216
print("Starting calculation...")
217
try:
218
result = 10 / 2
219
print(f"Result: {result}")
220
except Exception as e:
221
print(f"Error: {e}")
222
223
user_data = input("Enter data: ")
224
print(f"You entered: {user_data}")
225
`);
226
227
// Export execution log
228
console.log("Execution log:", logger.exportLog());
229
```
230
231
### Web UI Integration
232
233
```javascript
234
// HTML elements for I/O
235
const outputDiv = document.getElementById('python-output');
236
const errorDiv = document.getElementById('python-errors');
237
const inputField = document.getElementById('python-input');
238
const inputButton = document.getElementById('input-submit');
239
240
let pendingInputResolver = null;
241
242
// Set up output handlers
243
pyodide.setStdout((message) => {
244
const line = document.createElement('div');
245
line.className = 'output-line';
246
line.textContent = message;
247
outputDiv.appendChild(line);
248
outputDiv.scrollTop = outputDiv.scrollHeight;
249
});
250
251
pyodide.setStderr((message) => {
252
const line = document.createElement('div');
253
line.className = 'error-line';
254
line.textContent = message;
255
errorDiv.appendChild(line);
256
errorDiv.scrollTop = errorDiv.scrollHeight;
257
});
258
259
// Set up input handler
260
pyodide.setStdin(() => {
261
// Show input UI
262
inputField.style.display = 'block';
263
inputButton.style.display = 'block';
264
inputField.focus();
265
266
// Wait for user input
267
return new Promise((resolve) => {
268
pendingInputResolver = resolve;
269
});
270
});
271
272
inputButton.addEventListener('click', () => {
273
if (pendingInputResolver) {
274
const input = inputField.value;
275
inputField.value = '';
276
inputField.style.display = 'none';
277
inputButton.style.display = 'none';
278
pendingInputResolver(input);
279
pendingInputResolver = null;
280
}
281
});
282
283
// Handle Enter key
284
inputField.addEventListener('keypress', (e) => {
285
if (e.key === 'Enter') {
286
inputButton.click();
287
}
288
});
289
```
290
291
### Buffered Output Handling
292
293
```javascript
294
class BufferedOutputStream {
295
constructor(flushCallback, bufferSize = 1024) {
296
this.buffer = '';
297
this.flushCallback = flushCallback;
298
this.bufferSize = bufferSize;
299
this.timer = null;
300
}
301
302
write(message) {
303
this.buffer += message;
304
305
// Flush if buffer is full
306
if (this.buffer.length >= this.bufferSize) {
307
this.flush();
308
} else {
309
// Schedule flush after short delay
310
if (this.timer) {
311
clearTimeout(this.timer);
312
}
313
this.timer = setTimeout(() => this.flush(), 10);
314
}
315
}
316
317
flush() {
318
if (this.buffer.length > 0) {
319
this.flushCallback(this.buffer);
320
this.buffer = '';
321
}
322
if (this.timer) {
323
clearTimeout(this.timer);
324
this.timer = null;
325
}
326
}
327
}
328
329
const bufferedOutput = new BufferedOutputStream((content) => {
330
console.log('Flushed output:', content);
331
});
332
333
const bufferedError = new BufferedOutputStream((content) => {
334
console.error('Flushed errors:', content);
335
});
336
337
pyodide.setStdout((msg) => bufferedOutput.write(msg + '\n'));
338
pyodide.setStderr((msg) => bufferedError.write(msg + '\n'));
339
340
// Test with rapid output
341
pyodide.runPython(`
342
for i in range(100):
343
print(f"Line {i}")
344
`);
345
346
// Ensure final flush
347
setTimeout(() => {
348
bufferedOutput.flush();
349
bufferedError.flush();
350
}, 100);
351
```
352
353
### File-based I/O Redirection
354
355
```javascript
356
// Redirect output to virtual files
357
pyodide.FS.writeFile('/tmp/output.log', '');
358
pyodide.FS.writeFile('/tmp/error.log', '');
359
360
pyodide.setStdout((message) => {
361
const current = pyodide.FS.readFile('/tmp/output.log', { encoding: 'utf8' });
362
pyodide.FS.writeFile('/tmp/output.log', current + message + '\n');
363
});
364
365
pyodide.setStderr((message) => {
366
const current = pyodide.FS.readFile('/tmp/error.log', { encoding: 'utf8' });
367
pyodide.FS.writeFile('/tmp/error.log', current + message + '\n');
368
});
369
370
// Run Python code
371
pyodide.runPython(`
372
print("This goes to output.log")
373
import sys
374
sys.stderr.write("This goes to error.log\\n")
375
376
import warnings
377
warnings.warn("This is a warning")
378
`);
379
380
// Read logged output
381
const outputLog = pyodide.FS.readFile('/tmp/output.log', { encoding: 'utf8' });
382
const errorLog = pyodide.FS.readFile('/tmp/error.log', { encoding: 'utf8' });
383
384
console.log('Output log:', outputLog);
385
console.log('Error log:', errorLog);
386
```
387
388
### Stream Multiplexing
389
390
```javascript
391
class StreamMultiplexer {
392
constructor() {
393
this.handlers = {
394
stdout: [],
395
stderr: [],
396
stdin: []
397
};
398
}
399
400
addOutputHandler(handler) {
401
this.handlers.stdout.push(handler);
402
}
403
404
addErrorHandler(handler) {
405
this.handlers.stderr.push(handler);
406
}
407
408
addInputHandler(handler) {
409
this.handlers.stdin.push(handler);
410
}
411
412
handleOutput(message) {
413
this.handlers.stdout.forEach(handler => handler(message));
414
}
415
416
handleError(message) {
417
this.handlers.stderr.forEach(handler => handler(message));
418
}
419
420
handleInput() {
421
// Use first available input handler
422
if (this.handlers.stdin.length > 0) {
423
return this.handlers.stdin[0]();
424
}
425
return '';
426
}
427
}
428
429
const multiplexer = new StreamMultiplexer();
430
431
// Add multiple output handlers
432
multiplexer.addOutputHandler((msg) => console.log(`[Console] ${msg}`));
433
multiplexer.addOutputHandler((msg) => {
434
// Send to websocket, log file, etc.
435
// websocket.send(JSON.stringify({type: 'stdout', message: msg}));
436
});
437
438
multiplexer.addErrorHandler((msg) => console.error(`[Console Error] ${msg}`));
439
multiplexer.addErrorHandler((msg) => {
440
// Send errors to monitoring service
441
// monitoringService.reportError(msg);
442
});
443
444
// Set up Pyodide with multiplexer
445
pyodide.setStdout((msg) => multiplexer.handleOutput(msg));
446
pyodide.setStderr((msg) => multiplexer.handleError(msg));
447
pyodide.setStdin(() => multiplexer.handleInput());
448
```
449
450
### Stream Restoration
451
452
```javascript
453
// Save original stream handlers for restoration
454
const originalHandlers = {
455
stdout: null,
456
stderr: null,
457
stdin: null
458
};
459
460
function saveStreamHandlers() {
461
// Note: Pyodide doesn't expose current handlers directly
462
// This is conceptual - you'd need to track them in your app
463
originalHandlers.stdout = console.log;
464
originalHandlers.stderr = console.error;
465
originalHandlers.stdin = () => prompt("Input:");
466
}
467
468
function restoreStreamHandlers() {
469
pyodide.setStdout(originalHandlers.stdout);
470
pyodide.setStderr(originalHandlers.stderr);
471
pyodide.setStdin(originalHandlers.stdin);
472
}
473
474
// Custom temporary handlers
475
function withCustomStreams(customHandlers, callback) {
476
saveStreamHandlers();
477
478
if (customHandlers.stdout) pyodide.setStdout(customHandlers.stdout);
479
if (customHandlers.stderr) pyodide.setStderr(customHandlers.stderr);
480
if (customHandlers.stdin) pyodide.setStdin(customHandlers.stdin);
481
482
try {
483
return callback();
484
} finally {
485
restoreStreamHandlers();
486
}
487
}
488
489
// Usage
490
withCustomStreams({
491
stdout: (msg) => console.log(`[CUSTOM] ${msg}`),
492
stderr: (msg) => console.error(`[CUSTOM ERROR] ${msg}`)
493
}, () => {
494
pyodide.runPython(`
495
print("This uses custom handlers")
496
import sys
497
sys.stderr.write("Custom error handler\\n")
498
`);
499
});
500
501
// Original handlers are restored automatically
502
pyodide.runPython(`print("Back to original handlers")`);
503
```