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

ffi.mddocs/

0

# Foreign Function Interface (FFI)

1

2

Pyodide's Foreign Function Interface provides seamless interoperability between JavaScript and Python, enabling automatic type conversion and proxy objects for cross-language operation.

3

4

## FFI Module

5

6

The FFI module exports type classes and utilities for working with Python objects from JavaScript.

7

8

```javascript { .api }

9

import { ffi } from "pyodide/ffi";

10

11

const ffi: {

12

PyProxy: typeof PyProxy;

13

PyProxyWithLength: typeof PyProxyWithLength;

14

PyProxyWithGet: typeof PyProxyWithGet;

15

PyProxyWithSet: typeof PyProxyWithSet;

16

PyProxyWithHas: typeof PyProxyWithHas;

17

PyDict: typeof PyDict;

18

PyIterable: typeof PyIterable;

19

PyAsyncIterable: typeof PyAsyncIterable;

20

PyIterator: typeof PyIterator;

21

PyAsyncIterator: typeof PyAsyncIterator;

22

PyGenerator: typeof PyGenerator;

23

PyAsyncGenerator: typeof PyAsyncGenerator;

24

PyAwaitable: typeof PyAwaitable;

25

PyCallable: typeof PyCallable;

26

PyBuffer: typeof PyBuffer;

27

PyBufferView: typeof PyBufferView;

28

PySequence: typeof PySequence;

29

PyMutableSequence: typeof PyMutableSequence;

30

PythonError: typeof PythonError;

31

};

32

```

33

34

## Core FFI Types

35

36

### PyProxy

37

38

Base proxy type for all Python objects accessible from JavaScript.

39

40

```javascript { .api }

41

interface PyProxy {

42

/** Check if object has been destroyed */

43

readonly destroyed: boolean;

44

/** Destroy the proxy and release Python reference */

45

destroy(): void;

46

/** Convert to JavaScript object */

47

toJs(options?: ConversionOptions): any;

48

/** Create a copy with new conversion options */

49

copy(): PyProxy;

50

/** Get string representation (str() in Python) */

51

toString(): string;

52

/** Get detailed representation (repr() in Python) */

53

supportsLength(): this is PyProxyWithLength;

54

supportsGet(): this is PyProxyWithGet;

55

supportsSet(): this is PyProxyWithSet;

56

supportsHas(): this is PyProxyWithHas;

57

}

58

```

59

60

### PyProxyWithLength

61

62

Proxy for Python objects that support `len()`.

63

64

```javascript { .api }

65

interface PyProxyWithLength extends PyProxy {

66

readonly length: number;

67

}

68

```

69

70

### PyProxyWithGet

71

72

Proxy for Python objects that support item access (`obj[key]`).

73

74

```javascript { .api }

75

interface PyProxyWithGet extends PyProxy {

76

get(key: any): any;

77

}

78

```

79

80

### PyProxyWithSet

81

82

Proxy for Python objects that support item assignment (`obj[key] = value`).

83

84

```javascript { .api }

85

interface PyProxyWithSet extends PyProxy {

86

set(key: any, value: any): void;

87

delete(key: any): void;

88

}

89

```

90

91

### PyProxyWithHas

92

93

Proxy for Python objects that support membership testing (`key in obj`).

94

95

```javascript { .api }

96

interface PyProxyWithHas extends PyProxy {

97

has(key: any): boolean;

98

}

99

```

100

101

## Collection Types

102

103

### PyDict

104

105

Proxy for Python dictionaries with JavaScript Map-like interface.

106

107

```javascript { .api }

108

interface PyDict extends PyProxy {

109

get(key: any): any;

110

set(key: any, value: any): void;

111

delete(key: any): boolean;

112

has(key: any): boolean;

113

keys(): PyProxy;

114

values(): PyProxy;

115

items(): PyProxy;

116

}

117

```

118

119

**Usage Example:**

120

121

```javascript

122

// Create Python dict and work with it

123

const pythonDict = pyodide.runPython(`

124

{'name': 'Alice', 'age': 30, 'city': 'New York'}

125

`);

126

127

console.log(pythonDict.get('name')); // 'Alice'

128

pythonDict.set('age', 31);

129

console.log(pythonDict.has('city')); // true

130

pythonDict.delete('city');

131

132

// Convert to JavaScript object

133

const jsObject = pythonDict.toJs();

134

console.log(jsObject); // {name: 'Alice', age: 31}

135

```

136

137

### PySequence

138

139

Proxy for Python sequences (lists, tuples, strings) with array-like interface.

140

141

```javascript { .api }

142

interface PySequence extends PyProxy {

143

readonly length: number;

144

get(index: number): any;

145

slice(start?: number, stop?: number): PySequence;

146

}

147

```

148

149

### PyMutableSequence

150

151

Proxy for mutable Python sequences (lists) with modification capabilities.

152

153

```javascript { .api }

154

interface PyMutableSequence extends PySequence {

155

set(index: number, value: any): void;

156

delete(index: number): void;

157

append(value: any): void;

158

extend(iterable: any): void;

159

insert(index: number, value: any): void;

160

pop(index?: number): any;

161

reverse(): void;

162

sort(compare?: (a: any, b: any) => number): void;

163

}

164

```

165

166

**Usage Example:**

167

168

```javascript

169

// Create Python list and manipulate it

170

const pythonList = pyodide.runPython('[1, 2, 3, 4, 5]');

171

172

console.log(pythonList.length); // 5

173

console.log(pythonList.get(0)); // 1

174

175

pythonList.append(6);

176

pythonList.insert(0, 0);

177

console.log(pythonList.toJs()); // [0, 1, 2, 3, 4, 5, 6]

178

179

const sliced = pythonList.slice(1, 4);

180

console.log(sliced.toJs()); // [1, 2, 3]

181

```

182

183

## Async and Iterator Types

184

185

### PyIterable

186

187

Proxy for Python iterable objects.

188

189

```javascript { .api }

190

interface PyIterable extends PyProxy {

191

[Symbol.iterator](): Iterator<any>;

192

}

193

```

194

195

### PyAsyncIterable

196

197

Proxy for Python async iterable objects.

198

199

```javascript { .api }

200

interface PyAsyncIterable extends PyProxy {

201

[Symbol.asyncIterator](): AsyncIterator<any>;

202

}

203

```

204

205

### PyIterator

206

207

Proxy for Python iterator objects.

208

209

```javascript { .api }

210

interface PyIterator extends PyProxy {

211

next(): IteratorResult<any>;

212

[Symbol.iterator](): PyIterator;

213

}

214

```

215

216

### PyAsyncIterator

217

218

Proxy for Python async iterator objects.

219

220

```javascript { .api }

221

interface PyAsyncIterator extends PyProxy {

222

next(): Promise<IteratorResult<any>>;

223

[Symbol.asyncIterator](): PyAsyncIterator;

224

}

225

```

226

227

## Function and Callable Types

228

229

### PyCallable

230

231

Proxy for Python callable objects (functions, methods, classes).

232

233

```javascript { .api }

234

interface PyCallable extends PyProxy {

235

(...args: any[]): any;

236

callKwargs(...args: any[], kwargs?: { [key: string]: any }): any;

237

apply(thisArg: any, args: any[]): any;

238

call(thisArg: any, ...args: any[]): any;

239

}

240

```

241

242

**Usage Example:**

243

244

```javascript

245

// Call Python functions from JavaScript

246

const mathFunc = pyodide.runPython(`

247

def calculate(x, y, operation='add'):

248

if operation == 'add':

249

return x + y

250

elif operation == 'multiply':

251

return x * y

252

return 0

253

calculate

254

`);

255

256

// Call with positional arguments

257

const result1 = mathFunc(5, 3);

258

console.log(result1); // 8

259

260

// Call with keyword arguments

261

const result2 = mathFunc.callKwargs(5, 3, { operation: 'multiply' });

262

console.log(result2); // 15

263

```

264

265

### PyGenerator

266

267

Proxy for Python generator objects.

268

269

```javascript { .api }

270

interface PyGenerator extends PyProxy {

271

next(value?: any): IteratorResult<any>;

272

return(value?: any): IteratorResult<any>;

273

throw(error?: any): IteratorResult<any>;

274

[Symbol.iterator](): PyGenerator;

275

}

276

```

277

278

### PyAsyncGenerator

279

280

Proxy for Python async generator objects.

281

282

```javascript { .api }

283

interface PyAsyncGenerator extends PyProxy {

284

next(value?: any): Promise<IteratorResult<any>>;

285

return(value?: any): Promise<IteratorResult<any>>;

286

throw(error?: any): Promise<IteratorResult<any>>;

287

[Symbol.asyncIterator](): PyAsyncGenerator;

288

}

289

```

290

291

### PyAwaitable

292

293

Proxy for Python awaitable objects (coroutines, futures).

294

295

```javascript { .api }

296

interface PyAwaitable extends PyProxy {

297

then<TResult1 = any, TResult2 = never>(

298

onfulfilled?: (value: any) => TResult1 | PromiseLike<TResult1>,

299

onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>

300

): Promise<TResult1 | TResult2>;

301

}

302

```

303

304

**Usage Example:**

305

306

```javascript

307

// Work with async Python functions

308

const asyncFunc = pyodide.runPython(`

309

import asyncio

310

311

async def fetch_data():

312

await asyncio.sleep(0.1)

313

return "Data fetched!"

314

315

fetch_data

316

`);

317

318

// Await Python coroutine

319

const result = await asyncFunc();

320

console.log(result); // "Data fetched!"

321

```

322

323

## Buffer Types

324

325

### PyBuffer

326

327

Proxy for Python buffer protocol objects.

328

329

```javascript { .api }

330

interface PyBuffer extends PyProxy {

331

readonly byteLength: number;

332

readonly format: string;

333

readonly itemsize: number;

334

readonly ndim: number;

335

readonly readonly: boolean;

336

readonly shape: number[] | null;

337

readonly strides: number[] | null;

338

getBuffer(order?: 'C' | 'F'): ArrayBuffer;

339

}

340

```

341

342

### PyBufferView

343

344

A view into a Python buffer as a typed array.

345

346

```javascript { .api }

347

interface PyBufferView extends PyProxy {

348

readonly buffer: ArrayBuffer;

349

readonly byteLength: number;

350

readonly byteOffset: number;

351

readonly length: number;

352

readonly BYTES_PER_ELEMENT: number;

353

}

354

```

355

356

**Usage Example:**

357

358

```javascript

359

// Work with NumPy arrays through buffer interface

360

await pyodide.loadPackage('numpy');

361

const npArray = pyodide.runPython(`

362

import numpy as np

363

np.array([1, 2, 3, 4, 5], dtype=np.float32)

364

`);

365

366

console.log('Array shape:', npArray.shape);

367

console.log('Array dtype:', npArray.format);

368

369

// Get buffer view

370

const buffer = npArray.getBuffer();

371

const float32View = new Float32Array(buffer);

372

console.log('JavaScript view:', float32View); // [1, 2, 3, 4, 5]

373

```

374

375

## Error Handling

376

377

### PythonError

378

379

JavaScript representation of Python exceptions.

380

381

```javascript { .api }

382

interface PythonError extends Error {

383

readonly type: string;

384

readonly value: PyProxy;

385

readonly traceback: PyProxy;

386

}

387

```

388

389

**Usage Example:**

390

391

```javascript

392

import { ffi } from "pyodide/ffi";

393

394

try {

395

pyodide.runPython(`

396

raise ValueError("Something went wrong!")

397

`);

398

} catch (error) {

399

if (error instanceof ffi.PythonError) {

400

console.log('Python error type:', error.type);

401

console.log('Python error message:', error.message);

402

console.log('Python traceback:', error.traceback.toString());

403

}

404

}

405

```

406

407

## Type Conversion Options

408

409

```javascript { .api }

410

interface ConversionOptions {

411

/** Maximum depth for recursive conversion */

412

depth?: number;

413

/** Custom converter for objects without default conversion */

414

defaultConverter?: (

415

value: any,

416

converter: (value: any) => any,

417

cacheConversion: (input: any, output: any) => void

418

) => any;

419

/** Convert Python dictionaries to JavaScript Maps */

420

dictConverter?: (

421

items: Iterable<[key: string, value: any]>

422

) => Map<string, any> | object;

423

}

424

```

425

426

## Runtime Type Checking

427

428

Use FFI classes for runtime type checking:

429

430

```javascript

431

import { ffi } from "pyodide/ffi";

432

433

function processData(pythonObject) {

434

if (pythonObject instanceof ffi.PyDict) {

435

console.log('Processing Python dictionary...');

436

for (const [key, value] of pythonObject.items()) {

437

console.log(`${key}: ${value}`);

438

}

439

} else if (pythonObject instanceof ffi.PySequence) {

440

console.log('Processing Python sequence...');

441

for (let i = 0; i < pythonObject.length; i++) {

442

console.log(`Item ${i}: ${pythonObject.get(i)}`);

443

}

444

} else if (pythonObject instanceof ffi.PyCallable) {

445

console.log('Processing Python function...');

446

const result = pythonObject();

447

console.log('Function result:', result);

448

}

449

}

450

```