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

advanced-features.mddocs/

0

# Advanced Features

1

2

Advanced functionality including canvas integration, worker support, interrupt handling, and archive operations.

3

4

## Canvas Integration

5

6

### canvas

7

8

HTML5 canvas integration for matplotlib and graphics libraries.

9

10

```javascript { .api }

11

const canvas: {

12

setCanvas2D(canvas: HTMLCanvasElement): void;

13

getCanvas2D(): HTMLCanvasElement | undefined;

14

setCanvas3D(canvas: HTMLCanvasElement): void;

15

getCanvas3D(): HTMLCanvasElement | undefined;

16

};

17

```

18

19

## Interrupt Handling

20

21

### setInterruptBuffer

22

23

Set interrupt signal buffer for webworker communication.

24

25

```javascript { .api }

26

function setInterruptBuffer(buffer: TypedArray): void;

27

```

28

29

**Parameters:**

30

- `buffer` - SharedArrayBuffer for interrupt signals between threads

31

32

### checkInterrupt

33

34

Manually check for pending interrupt signals.

35

36

```javascript { .api }

37

function checkInterrupt(): void;

38

```

39

40

## Worker Communication

41

42

### registerComlink

43

44

Register Comlink for seamless worker proxy communication.

45

46

```javascript { .api }

47

function registerComlink(Comlink: any): void;

48

```

49

50

**Parameters:**

51

- `Comlink` - Comlink library instance

52

53

## Archive Operations

54

55

### unpackArchive

56

57

Extract archive files (tar, zip, wheel) into the file system.

58

59

```javascript { .api }

60

function unpackArchive(

61

buffer: TypedArray | ArrayBuffer,

62

format: string,

63

options?: {

64

extractDir?: string;

65

}

66

): void;

67

```

68

69

**Parameters:**

70

- `buffer` - Archive file data

71

- `format` - Archive format ('tar', 'zip', 'wheel', 'gztar', etc.)

72

- `options.extractDir` - Directory to extract to (default: current directory)

73

74

## Error Constants

75

76

### ERRNO_CODES

77

78

POSIX error code constants for file system operations.

79

80

```javascript { .api }

81

const ERRNO_CODES: {

82

[code: string]: number;

83

};

84

```

85

86

## Usage Examples

87

88

### Canvas Integration for Matplotlib

89

90

```javascript

91

// Set up canvas for matplotlib

92

const canvas = document.getElementById('matplotlib-canvas');

93

pyodide.canvas.setCanvas2D(canvas);

94

95

await pyodide.loadPackage(['matplotlib']);

96

97

pyodide.runPython(`

98

import matplotlib.pyplot as plt

99

import numpy as np

100

101

# Create sample plot

102

x = np.linspace(0, 2 * np.pi, 100)

103

y = np.sin(x)

104

105

plt.figure(figsize=(8, 6))

106

plt.plot(x, y, label='sin(x)')

107

plt.plot(x, np.cos(x), label='cos(x)')

108

plt.legend()

109

plt.title('Trigonometric Functions')

110

plt.xlabel('x')

111

plt.ylabel('y')

112

plt.grid(True)

113

plt.show() # This will render to the canvas

114

`);

115

```

116

117

### 3D Graphics with Three.js Integration

118

119

```javascript

120

// Set up 3D canvas

121

const canvas3d = document.getElementById('threejs-canvas');

122

pyodide.canvas.setCanvas3D(canvas3d);

123

124

// Register Three.js as a module

125

pyodide.registerJsModule('threejs', {

126

Scene: THREE.Scene,

127

PerspectiveCamera: THREE.PerspectiveCamera,

128

WebGLRenderer: THREE.WebGLRenderer,

129

BoxGeometry: THREE.BoxGeometry,

130

MeshBasicMaterial: THREE.MeshBasicMaterial,

131

Mesh: THREE.Mesh

132

});

133

134

pyodide.runPython(`

135

from threejs import Scene, PerspectiveCamera, WebGLRenderer

136

from threejs import BoxGeometry, MeshBasicMaterial, Mesh

137

138

# Create 3D scene

139

scene = Scene()

140

camera = PerspectiveCamera(75, 800/600, 0.1, 1000)

141

renderer = WebGLRenderer()

142

143

# Create geometry

144

geometry = BoxGeometry(1, 1, 1)

145

material = MeshBasicMaterial({'color': 0x00ff00})

146

cube = Mesh(geometry, material)

147

148

scene.add(cube)

149

camera.position.z = 5

150

151

# Render scene

152

renderer.render(scene, camera)

153

`);

154

```

155

156

### Interrupt Handling in Web Workers

157

158

```javascript

159

// Main thread

160

const sharedBuffer = new SharedArrayBuffer(4);

161

const interruptArray = new Int32Array(sharedBuffer);

162

163

// Send buffer to worker

164

worker.postMessage({ sharedBuffer });

165

166

// Worker code

167

self.onmessage = async function(e) {

168

if (e.data.sharedBuffer) {

169

const pyodide = await loadPyodide();

170

const interruptArray = new Int32Array(e.data.sharedBuffer);

171

172

// Set interrupt buffer

173

pyodide.setInterruptBuffer(interruptArray);

174

175

// Long running Python code

176

pyodide.runPython(`

177

import time

178

import signal

179

180

def signal_handler(signum, frame):

181

print(f"Received signal {signum}")

182

raise KeyboardInterrupt("Interrupted by signal")

183

184

# Install signal handler

185

signal.signal(signal.SIGINT, signal_handler)

186

187

# Long computation

188

for i in range(1000000):

189

# Check for interrupts periodically

190

if i % 10000 == 0:

191

print(f"Progress: {i}")

192

time.sleep(0.001)

193

`);

194

}

195

};

196

197

// Main thread - send interrupt

198

setTimeout(() => {

199

interruptArray[0] = 2; // SIGINT

200

}, 5000);

201

```

202

203

### Manual Interrupt Checking

204

205

```javascript

206

// JavaScript-controlled interrupt checking

207

let shouldStop = false;

208

209

pyodide.registerJsModule('jscontrol', {

210

checkStop: () => shouldStop

211

});

212

213

// Start long-running Python process

214

const runLongTask = async () => {

215

pyodide.runPythonAsync(`

216

from jscontrol import checkStop

217

import time

218

219

for i in range(1000000):

220

if i % 1000 == 0:

221

if checkStop():

222

print(f"Task interrupted at iteration {i}")

223

break

224

print(f"Processing: {i}")

225

226

time.sleep(0.001)

227

`);

228

};

229

230

// Stop button handler

231

document.getElementById('stop-button').onclick = () => {

232

shouldStop = true;

233

};

234

235

// Start button handler

236

document.getElementById('start-button').onclick = () => {

237

shouldStop = false;

238

runLongTask();

239

};

240

```

241

242

### Comlink Integration for Seamless Worker Communication

243

244

```javascript

245

// Main thread

246

import * as Comlink from 'comlink';

247

248

const worker = new Worker('pyodide-worker.js');

249

const PyodideWorker = Comlink.wrap(worker);

250

251

// Worker (pyodide-worker.js)

252

import * as Comlink from 'comlink';

253

import { loadPyodide } from 'pyodide';

254

255

class PyodideService {

256

constructor() {

257

this.pyodide = null;

258

}

259

260

async init() {

261

this.pyodide = await loadPyodide();

262

this.pyodide.registerComlink(Comlink);

263

return 'Pyodide loaded in worker';

264

}

265

266

runCode(code) {

267

return this.pyodide.runPython(code);

268

}

269

270

async runCodeAsync(code) {

271

return await this.pyodide.runPythonAsync(code);

272

}

273

274

// Return Python functions as Comlink proxies

275

getPythonFunction(code) {

276

this.pyodide.runPython(code);

277

return this.pyodide.globals.get('exported_function');

278

}

279

}

280

281

Comlink.expose(new PyodideService());

282

283

// Usage in main thread

284

const service = await new PyodideWorker();

285

await service.init();

286

287

const result = await service.runCode('2 + 2');

288

console.log(result); // 4

289

290

// Get Python function proxy

291

const pythonFunc = await service.getPythonFunction(`

292

def exported_function(x, y):

293

return x ** 2 + y ** 2

294

`);

295

296

const funcResult = await pythonFunc(3, 4);

297

console.log(funcResult); // 25

298

```

299

300

### Archive Extraction

301

302

```javascript

303

// Extract Python package archives

304

async function installCustomPackage(archiveUrl, format) {

305

try {

306

// Download archive

307

const response = await fetch(archiveUrl);

308

const buffer = await response.arrayBuffer();

309

310

// Extract to temporary directory

311

pyodide.FS.mkdir('/tmp/extract');

312

pyodide.unpackArchive(buffer, format, {

313

extractDir: '/tmp/extract'

314

});

315

316

// List extracted contents

317

const files = pyodide.FS.readdir('/tmp/extract');

318

console.log('Extracted files:', files);

319

320

// Move to site-packages if it's a Python package

321

if (files.includes('setup.py') || files.includes('pyproject.toml')) {

322

pyodide.runPython(`

323

import sys

324

import os

325

sys.path.insert(0, '/tmp/extract')

326

327

# Install package

328

os.chdir('/tmp/extract')

329

import subprocess

330

subprocess.run([sys.executable, 'setup.py', 'install'])

331

`);

332

}

333

334

} catch (error) {

335

console.error('Failed to install custom package:', error);

336

}

337

}

338

339

// Install from wheel file

340

await installCustomPackage('https://example.com/package.whl', 'wheel');

341

342

// Install from tar.gz

343

await installCustomPackage('https://example.com/package.tar.gz', 'gztar');

344

```

345

346

### Data Archive Processing

347

348

```javascript

349

// Process data archives

350

async function processDataArchive(archiveData, format) {

351

// Extract data archive

352

pyodide.unpackArchive(archiveData, format, {

353

extractDir: '/data'

354

});

355

356

// Process extracted data with Python

357

const results = pyodide.runPython(`

358

import os

359

import pandas as pd

360

361

data_files = []

362

for root, dirs, files in os.walk('/data'):

363

for file in files:

364

if file.endswith('.csv'):

365

file_path = os.path.join(root, file)

366

data_files.append(file_path)

367

368

# Load and combine CSV files

369

dataframes = []

370

for file_path in data_files:

371

df = pd.read_csv(file_path)

372

df['source_file'] = file_path

373

dataframes.append(df)

374

375

if dataframes:

376

combined_df = pd.concat(dataframes, ignore_index=True)

377

{

378

'total_rows': len(combined_df),

379

'columns': list(combined_df.columns),

380

'files_processed': len(data_files)

381

}

382

else:

383

{'error': 'No CSV files found'}

384

`);

385

386

return results;

387

}

388

389

// Usage with ZIP file containing CSV data

390

const zipData = await fetch('/data-archive.zip').then(r => r.arrayBuffer());

391

const results = await processDataArchive(zipData, 'zip');

392

console.log('Processing results:', results);

393

```

394

395

### Error Code Handling

396

397

```javascript

398

// Use ERRNO codes for file system error handling

399

function safeFileOperation(operation) {

400

try {

401

return operation();

402

} catch (error) {

403

const errno = error.errno;

404

405

if (errno === pyodide.ERRNO_CODES.ENOENT) {

406

console.error('File or directory does not exist');

407

} else if (errno === pyodide.ERRNO_CODES.EACCES) {

408

console.error('Permission denied');

409

} else if (errno === pyodide.ERRNO_CODES.EEXIST) {

410

console.error('File already exists');

411

} else if (errno === pyodide.ERRNO_CODES.ENOSPC) {

412

console.error('No space left on device');

413

} else {

414

console.error(`File system error (${errno}):`, error.message);

415

}

416

417

return null;

418

}

419

}

420

421

// Safe file operations

422

const content = safeFileOperation(() =>

423

pyodide.FS.readFile('/nonexistent/file.txt', { encoding: 'utf8' })

424

);

425

426

const success = safeFileOperation(() =>

427

pyodide.FS.writeFile('/protected/file.txt', 'data')

428

);

429

```

430

431

### Performance Monitoring

432

433

```javascript

434

// Monitor Pyodide performance

435

class PyodideMonitor {

436

constructor() {

437

this.metrics = {

438

executionTimes: [],

439

memoryUsage: [],

440

packageLoadTimes: []

441

};

442

}

443

444

async timeExecution(code, description = 'Execution') {

445

const startTime = performance.now();

446

const startMemory = this.getMemoryUsage();

447

448

try {

449

const result = await pyodide.runPython(code);

450

const endTime = performance.now();

451

const endMemory = this.getMemoryUsage();

452

453

const executionTime = endTime - startTime;

454

const memoryDelta = endMemory - startMemory;

455

456

this.metrics.executionTimes.push({

457

description,

458

time: executionTime,

459

timestamp: new Date().toISOString()

460

});

461

462

this.metrics.memoryUsage.push({

463

description,

464

memoryDelta,

465

totalMemory: endMemory,

466

timestamp: new Date().toISOString()

467

});

468

469

console.log(`${description}: ${executionTime.toFixed(2)}ms, Memory: ${memoryDelta}KB`);

470

return result;

471

472

} catch (error) {

473

console.error(`${description} failed:`, error);

474

throw error;

475

}

476

}

477

478

getMemoryUsage() {

479

// Approximation - actual memory usage varies

480

return performance.memory?.usedJSHeapSize / 1024 || 0;

481

}

482

483

getReport() {

484

return {

485

averageExecutionTime: this.metrics.executionTimes.reduce((sum, entry) =>

486

sum + entry.time, 0) / this.metrics.executionTimes.length,

487

totalExecutions: this.metrics.executionTimes.length,

488

memoryMetrics: this.metrics.memoryUsage,

489

packageMetrics: this.metrics.packageLoadTimes

490

};

491

}

492

}

493

494

// Usage

495

const monitor = new PyodideMonitor();

496

497

await monitor.timeExecution(`

498

import numpy as np

499

arr = np.random.random((1000, 1000))

500

result = np.sum(arr)

501

result

502

`, 'NumPy Random Sum');

503

504

await monitor.timeExecution(`

505

import pandas as pd

506

df = pd.DataFrame({'A': range(10000), 'B': range(10000, 20000)})

507

result = df.groupby('A').sum()

508

len(result)

509

`, 'Pandas GroupBy');

510

511

console.log('Performance Report:', monitor.getReport());

512

```

513

514

## Debug Control

515

516

### setDebug

517

518

Enable or disable debug mode for enhanced error messages at a performance cost.

519

520

```javascript { .api }

521

function setDebug(debug: boolean): boolean;

522

```

523

524

**Parameters:**

525

- `debug` - If true, enable debug mode; if false, disable debug mode

526

527

**Returns:** The previous value of the debug flag

528

529

**Usage Example:**

530

531

```javascript

532

// Enable debug mode for development

533

const previousDebugState = pyodide.setDebug(true);

534

console.log('Debug mode enabled, previous state was:', previousDebugState);

535

536

try {

537

// Your code that might produce complex errors

538

pyodide.runPython('some_complex_python_code()');

539

} catch (error) {

540

console.error('Enhanced error info:', error);

541

} finally {

542

// Restore previous debug state

543

pyodide.setDebug(previousDebugState);

544

}

545

```

546

547

## Package Information Access

548

549

### lockfile

550

551

Access the complete lockfile used to load the current Pyodide instance, containing package metadata and dependency information.

552

553

```javascript { .api }

554

const lockfile: Lockfile;

555

```

556

557

**Usage Example:**

558

559

```javascript

560

// Access lockfile information

561

const lockfile = pyodide.lockfile;

562

console.log('Pyodide version:', lockfile.info.version);

563

console.log('Python version:', lockfile.info.python);

564

console.log('Available packages:', Object.keys(lockfile.packages));

565

566

// Get information about a specific package

567

const numpyInfo = lockfile.packages['numpy'];

568

if (numpyInfo) {

569

console.log('NumPy version:', numpyInfo.version);

570

console.log('NumPy dependencies:', numpyInfo.depends);

571

}

572

```

573

574

### lockfileBaseUrl

575

576

Get the base URL used for resolving relative package paths in the lockfile.

577

578

```javascript { .api }

579

const lockfileBaseUrl: string | undefined;

580

```

581

582

**Usage Example:**

583

584

```javascript

585

const baseUrl = pyodide.lockfileBaseUrl;

586

console.log('Package base URL:', baseUrl);

587

588

// This is useful for understanding where packages are loaded from

589

if (baseUrl) {

590

console.log('Packages are loaded from:', baseUrl);

591

} else {

592

console.log('Using default package locations');

593

}

594

```