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
```