or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdcode-execution.mdffi.mdfile-system.mdindex.mdinteroperability.mdio-streams.mdpackage-management.mdruntime-loading.md

io-streams.mddocs/

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

```