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

interoperability.mddocs/

0

# Interoperability

1

2

Seamless bidirectional conversion and communication between JavaScript and Python objects, modules, and functions.

3

4

## Type Conversion

5

6

### toPy

7

8

Convert JavaScript objects to Python equivalents.

9

10

```javascript { .api }

11

function toPy(

12

obj: any,

13

options?: {

14

depth?: number;

15

defaultConverter?: (

16

value: any,

17

converter: (value: any) => any,

18

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

19

) => any;

20

}

21

): any;

22

```

23

24

**Parameters:**

25

- `obj` - JavaScript object to convert

26

- `options.depth` - Maximum conversion depth (default: -1 for unlimited)

27

- `options.defaultConverter` - Custom converter for objects with no default conversion

28

29

**Returns:** Python object (may be a PyProxy)

30

31

## Module System

32

33

### pyimport

34

35

Import Python modules and make them available in JavaScript.

36

37

```javascript { .api }

38

function pyimport(moduleName: string): any;

39

```

40

41

**Parameters:**

42

- `moduleName` - Python module name to import (supports dot notation)

43

44

**Returns:** PyProxy wrapping the imported Python module

45

46

### registerJsModule

47

48

Register JavaScript objects as importable Python modules.

49

50

```javascript { .api }

51

function registerJsModule(name: string, module: object): void;

52

```

53

54

**Parameters:**

55

- `name` - Module name for Python imports

56

- `module` - JavaScript object to expose as module

57

58

### unregisterJsModule

59

60

Remove a previously registered JavaScript module.

61

62

```javascript { .api }

63

function unregisterJsModule(name: string): void;

64

```

65

66

**Parameters:**

67

- `name` - Module name to unregister

68

69

## Global Access

70

71

### globals

72

73

Access the Python global namespace.

74

75

```javascript { .api }

76

const globals: PyProxy;

77

```

78

79

### pyodide_py

80

81

Access the Python-side pyodide module.

82

83

```javascript { .api }

84

const pyodide_py: PyProxy;

85

```

86

87

## Usage Examples

88

89

### Basic Type Conversion

90

91

```javascript

92

// JavaScript to Python conversion

93

const jsArray = [1, 2, 3, 4, 5];

94

const pyList = pyodide.toPy(jsArray);

95

96

pyodide.globals.set("my_list", pyList);

97

pyodide.runPython(`

98

print(type(my_list)) # <class 'list'>

99

print(sum(my_list)) # 15

100

`);

101

102

// JavaScript objects become Python dictionaries

103

const jsObject = { name: "Alice", age: 30, active: true };

104

const pyDict = pyodide.toPy(jsObject);

105

106

pyodide.globals.set("user", pyDict);

107

pyodide.runPython(`

108

print(user["name"]) # Alice

109

print(user["age"]) # 30

110

print(type(user)) # <class 'dict'>

111

`);

112

```

113

114

### Advanced Type Conversion

115

116

```javascript

117

// Nested objects with depth limit

118

const nestedObj = {

119

level1: {

120

level2: {

121

level3: {

122

data: "deep"

123

}

124

}

125

}

126

};

127

128

const shallowConversion = pyodide.toPy(nestedObj, { depth: 2 });

129

// level3 remains as JavaScript proxy

130

131

// Custom converter for special objects

132

class CustomClass {

133

constructor(value) {

134

this.value = value;

135

}

136

}

137

138

const customObj = new CustomClass(42);

139

const converted = pyodide.toPy(customObj, {

140

defaultConverter: (obj, convert, cache) => {

141

if (obj instanceof CustomClass) {

142

return convert({ custom_value: obj.value });

143

}

144

return obj;

145

}

146

});

147

```

148

149

### Python Module Imports

150

151

```javascript

152

// Import standard library modules

153

const math = pyodide.pyimport("math");

154

console.log(math.pi); // 3.141592653589793

155

console.log(math.sqrt(16)); // 4

156

157

const sys = pyodide.pyimport("sys");

158

console.log(sys.version);

159

160

// Import installed packages

161

await pyodide.loadPackage("numpy");

162

const np = pyodide.pyimport("numpy");

163

164

const array = np.array([1, 2, 3, 4, 5]);

165

console.log(array.shape); // [5]

166

console.log(array.sum()); // 15

167

168

// Import submodules

169

const pyplot = pyodide.pyimport("matplotlib.pyplot");

170

pyplot.figure();

171

pyplot.plot([1, 2, 3], [1, 4, 9]);

172

```

173

174

### JavaScript Module Registration

175

176

```javascript

177

// Register utility functions

178

const jsUtils = {

179

formatNumber: (num, decimals = 2) => num.toFixed(decimals),

180

getCurrentTime: () => new Date().toISOString(),

181

logMessage: (msg) => console.log(`[JS] ${msg}`),

182

183

// Nested module structure

184

string: {

185

reverse: (str) => str.split('').reverse().join(''),

186

capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1)

187

}

188

};

189

190

pyodide.registerJsModule("jsutils", jsUtils);

191

192

pyodide.runPython(`

193

import jsutils

194

195

# Use top-level functions

196

print(jsutils.formatNumber(3.14159, 3)) # 3.142

197

print(jsutils.getCurrentTime())

198

jsutils.logMessage("Hello from Python!")

199

200

# Use nested modules

201

from jsutils.string import reverse, capitalize

202

print(reverse("hello")) # olleh

203

print(capitalize("world")) # World

204

`);

205

```

206

207

### Working with JavaScript APIs

208

209

```javascript

210

// Register browser APIs for Python use

211

pyodide.registerJsModule("browser", {

212

fetch: fetch,

213

localStorage: {

214

getItem: (key) => localStorage.getItem(key),

215

setItem: (key, value) => localStorage.setItem(key, value),

216

removeItem: (key) => localStorage.removeItem(key)

217

},

218

document: {

219

getElementById: (id) => document.getElementById(id),

220

createElement: (tag) => document.createElement(tag)

221

}

222

});

223

224

pyodide.runPython(`

225

from browser import fetch, localStorage, document

226

227

# Use fetch API

228

response = await fetch("https://api.example.com/data")

229

data = await response.json()

230

231

# Use localStorage

232

localStorage.setItem("user_data", "some_value")

233

stored = localStorage.getItem("user_data")

234

235

# Use DOM APIs

236

element = document.getElementById("output")

237

if element:

238

element.innerHTML = "Updated from Python!"

239

`);

240

```

241

242

### Bidirectional Communication

243

244

```javascript

245

// Python functions callable from JavaScript

246

pyodide.runPython(`

247

def python_processor(data):

248

# Process data in Python

249

return [x * 2 for x in data if x > 0]

250

251

def async_python_function():

252

import asyncio

253

await asyncio.sleep(0.1)

254

return {"processed": True, "timestamp": "2024-01-01"}

255

`);

256

257

const pythonProcessor = pyodide.globals.get("python_processor");

258

const result = pythonProcessor([1, -2, 3, -4, 5]);

259

console.log(result); // [2, 6, 10]

260

261

const asyncPythonFunc = pyodide.globals.get("async_python_function");

262

const asyncResult = await asyncPythonFunc();

263

console.log(asyncResult); // {processed: true, timestamp: "2024-01-01"}

264

```

265

266

### Global Namespace Management

267

268

```javascript

269

// Set values in Python global namespace

270

pyodide.globals.set("api_key", "your-secret-key");

271

pyodide.globals.set("config", pyodide.toPy({

272

debug: true,

273

timeout: 5000,

274

endpoints: ["api1", "api2"]

275

}));

276

277

// Get values from Python global namespace

278

pyodide.runPython(`

279

import os

280

os.environ["API_KEY"] = api_key

281

282

processed_config = {

283

"debug_mode": config["debug"],

284

"timeout_ms": config["timeout"],

285

"endpoint_count": len(config["endpoints"])

286

}

287

`);

288

289

const processedConfig = pyodide.globals.get("processed_config");

290

console.log(processedConfig.toJs()); // Convert PyProxy to JavaScript

291

```

292

293

### Class and Object Interoperability

294

295

```javascript

296

// JavaScript class available in Python

297

class JavaScriptCalculator {

298

constructor() {

299

this.history = [];

300

}

301

302

add(a, b) {

303

const result = a + b;

304

this.history.push(`${a} + ${b} = ${result}`);

305

return result;

306

}

307

308

getHistory() {

309

return this.history;

310

}

311

}

312

313

pyodide.registerJsModule("calculator", {

314

Calculator: JavaScriptCalculator

315

});

316

317

pyodide.runPython(`

318

from calculator import Calculator

319

320

calc = Calculator()

321

result1 = calc.add(5, 3)

322

result2 = calc.add(10, 7)

323

324

print("Results:", result1, result2)

325

print("History:", calc.getHistory())

326

`);

327

328

// Python class instances in JavaScript

329

pyodide.runPython(`

330

class PythonCounter:

331

def __init__(self, start=0):

332

self.value = start

333

334

def increment(self, step=1):

335

self.value += step

336

return self.value

337

338

def get_value(self):

339

return self.value

340

341

counter = PythonCounter(10)

342

`);

343

344

const pythonCounter = pyodide.globals.get("counter");

345

console.log(pythonCounter.increment(5)); // 15

346

console.log(pythonCounter.get_value()); // 15

347

```

348

349

### Error Handling Across Languages

350

351

```javascript

352

// JavaScript errors in Python

353

const faultyJsFunction = () => {

354

throw new Error("JavaScript error occurred");

355

};

356

357

pyodide.registerJsModule("faulty", { faultyFunction: faultyJsFunction });

358

359

try {

360

pyodide.runPython(`

361

from faulty import faultyFunction

362

faultyFunction() # This will propagate the JS error

363

`);

364

} catch (error) {

365

console.log("Caught JS error in Python context:", error.message);

366

}

367

368

// Python errors in JavaScript

369

pyodide.runPython(`

370

def failing_python_function():

371

raise ValueError("Python error occurred")

372

`);

373

374

const failingPyFunction = pyodide.globals.get("failing_python_function");

375

376

try {

377

failingPyFunction();

378

} catch (error) {

379

console.log("Caught Python error in JS context:", error.message);

380

}

381

```

382

383

### Memory Management

384

385

```javascript

386

// Proper cleanup of PyProxy objects

387

const largePythonObject = pyodide.runPython(`

388

# Create large object

389

large_data = list(range(1000000))

390

{"data": large_data, "size": len(large_data)}

391

`);

392

393

// Use the object

394

console.log(largePythonObject.get("size"));

395

396

// Clean up when done

397

largePythonObject.destroy();

398

399

// Automatic cleanup with context managers

400

function withPyObject(obj, callback) {

401

try {

402

return callback(obj);

403

} finally {

404

if (obj && obj.destroy) {

405

obj.destroy();

406

}

407

}

408

}

409

410

const result = withPyObject(

411

pyodide.runPython("{'result': 42}"),

412

(obj) => obj.get("result")

413

);

414

console.log(result); // 42

415

```