Python distribution for the browser and Node.js based on WebAssembly that enables running Python code with full JavaScript interoperability
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Seamless bidirectional conversion and communication between JavaScript and Python objects, modules, and functions.
Convert JavaScript objects to Python equivalents.
function toPy(
obj: any,
options?: {
depth?: number;
defaultConverter?: (
value: any,
converter: (value: any) => any,
cacheConversion: (input: any, output: any) => void
) => any;
}
): any;Parameters:
obj - JavaScript object to convertoptions.depth - Maximum conversion depth (default: -1 for unlimited)options.defaultConverter - Custom converter for objects with no default conversionReturns: Python object (may be a PyProxy)
Import Python modules and make them available in JavaScript.
function pyimport(moduleName: string): any;Parameters:
moduleName - Python module name to import (supports dot notation)Returns: PyProxy wrapping the imported Python module
Register JavaScript objects as importable Python modules.
function registerJsModule(name: string, module: object): void;Parameters:
name - Module name for Python importsmodule - JavaScript object to expose as moduleRemove a previously registered JavaScript module.
function unregisterJsModule(name: string): void;Parameters:
name - Module name to unregisterAccess the Python global namespace.
const globals: PyProxy;Access the Python-side pyodide module.
const pyodide_py: PyProxy;// JavaScript to Python conversion
const jsArray = [1, 2, 3, 4, 5];
const pyList = pyodide.toPy(jsArray);
pyodide.globals.set("my_list", pyList);
pyodide.runPython(`
print(type(my_list)) # <class 'list'>
print(sum(my_list)) # 15
`);
// JavaScript objects become Python dictionaries
const jsObject = { name: "Alice", age: 30, active: true };
const pyDict = pyodide.toPy(jsObject);
pyodide.globals.set("user", pyDict);
pyodide.runPython(`
print(user["name"]) # Alice
print(user["age"]) # 30
print(type(user)) # <class 'dict'>
`);// Nested objects with depth limit
const nestedObj = {
level1: {
level2: {
level3: {
data: "deep"
}
}
}
};
const shallowConversion = pyodide.toPy(nestedObj, { depth: 2 });
// level3 remains as JavaScript proxy
// Custom converter for special objects
class CustomClass {
constructor(value) {
this.value = value;
}
}
const customObj = new CustomClass(42);
const converted = pyodide.toPy(customObj, {
defaultConverter: (obj, convert, cache) => {
if (obj instanceof CustomClass) {
return convert({ custom_value: obj.value });
}
return obj;
}
});// Import standard library modules
const math = pyodide.pyimport("math");
console.log(math.pi); // 3.141592653589793
console.log(math.sqrt(16)); // 4
const sys = pyodide.pyimport("sys");
console.log(sys.version);
// Import installed packages
await pyodide.loadPackage("numpy");
const np = pyodide.pyimport("numpy");
const array = np.array([1, 2, 3, 4, 5]);
console.log(array.shape); // [5]
console.log(array.sum()); // 15
// Import submodules
const pyplot = pyodide.pyimport("matplotlib.pyplot");
pyplot.figure();
pyplot.plot([1, 2, 3], [1, 4, 9]);// Register utility functions
const jsUtils = {
formatNumber: (num, decimals = 2) => num.toFixed(decimals),
getCurrentTime: () => new Date().toISOString(),
logMessage: (msg) => console.log(`[JS] ${msg}`),
// Nested module structure
string: {
reverse: (str) => str.split('').reverse().join(''),
capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1)
}
};
pyodide.registerJsModule("jsutils", jsUtils);
pyodide.runPython(`
import jsutils
# Use top-level functions
print(jsutils.formatNumber(3.14159, 3)) # 3.142
print(jsutils.getCurrentTime())
jsutils.logMessage("Hello from Python!")
# Use nested modules
from jsutils.string import reverse, capitalize
print(reverse("hello")) # olleh
print(capitalize("world")) # World
`);// Register browser APIs for Python use
pyodide.registerJsModule("browser", {
fetch: fetch,
localStorage: {
getItem: (key) => localStorage.getItem(key),
setItem: (key, value) => localStorage.setItem(key, value),
removeItem: (key) => localStorage.removeItem(key)
},
document: {
getElementById: (id) => document.getElementById(id),
createElement: (tag) => document.createElement(tag)
}
});
pyodide.runPython(`
from browser import fetch, localStorage, document
# Use fetch API
response = await fetch("https://api.example.com/data")
data = await response.json()
# Use localStorage
localStorage.setItem("user_data", "some_value")
stored = localStorage.getItem("user_data")
# Use DOM APIs
element = document.getElementById("output")
if element:
element.innerHTML = "Updated from Python!"
`);// Python functions callable from JavaScript
pyodide.runPython(`
def python_processor(data):
# Process data in Python
return [x * 2 for x in data if x > 0]
def async_python_function():
import asyncio
await asyncio.sleep(0.1)
return {"processed": True, "timestamp": "2024-01-01"}
`);
const pythonProcessor = pyodide.globals.get("python_processor");
const result = pythonProcessor([1, -2, 3, -4, 5]);
console.log(result); // [2, 6, 10]
const asyncPythonFunc = pyodide.globals.get("async_python_function");
const asyncResult = await asyncPythonFunc();
console.log(asyncResult); // {processed: true, timestamp: "2024-01-01"}// Set values in Python global namespace
pyodide.globals.set("api_key", "your-secret-key");
pyodide.globals.set("config", pyodide.toPy({
debug: true,
timeout: 5000,
endpoints: ["api1", "api2"]
}));
// Get values from Python global namespace
pyodide.runPython(`
import os
os.environ["API_KEY"] = api_key
processed_config = {
"debug_mode": config["debug"],
"timeout_ms": config["timeout"],
"endpoint_count": len(config["endpoints"])
}
`);
const processedConfig = pyodide.globals.get("processed_config");
console.log(processedConfig.toJs()); // Convert PyProxy to JavaScript// JavaScript class available in Python
class JavaScriptCalculator {
constructor() {
this.history = [];
}
add(a, b) {
const result = a + b;
this.history.push(`${a} + ${b} = ${result}`);
return result;
}
getHistory() {
return this.history;
}
}
pyodide.registerJsModule("calculator", {
Calculator: JavaScriptCalculator
});
pyodide.runPython(`
from calculator import Calculator
calc = Calculator()
result1 = calc.add(5, 3)
result2 = calc.add(10, 7)
print("Results:", result1, result2)
print("History:", calc.getHistory())
`);
// Python class instances in JavaScript
pyodide.runPython(`
class PythonCounter:
def __init__(self, start=0):
self.value = start
def increment(self, step=1):
self.value += step
return self.value
def get_value(self):
return self.value
counter = PythonCounter(10)
`);
const pythonCounter = pyodide.globals.get("counter");
console.log(pythonCounter.increment(5)); // 15
console.log(pythonCounter.get_value()); // 15// JavaScript errors in Python
const faultyJsFunction = () => {
throw new Error("JavaScript error occurred");
};
pyodide.registerJsModule("faulty", { faultyFunction: faultyJsFunction });
try {
pyodide.runPython(`
from faulty import faultyFunction
faultyFunction() # This will propagate the JS error
`);
} catch (error) {
console.log("Caught JS error in Python context:", error.message);
}
// Python errors in JavaScript
pyodide.runPython(`
def failing_python_function():
raise ValueError("Python error occurred")
`);
const failingPyFunction = pyodide.globals.get("failing_python_function");
try {
failingPyFunction();
} catch (error) {
console.log("Caught Python error in JS context:", error.message);
}// Proper cleanup of PyProxy objects
const largePythonObject = pyodide.runPython(`
# Create large object
large_data = list(range(1000000))
{"data": large_data, "size": len(large_data)}
`);
// Use the object
console.log(largePythonObject.get("size"));
// Clean up when done
largePythonObject.destroy();
// Automatic cleanup with context managers
function withPyObject(obj, callback) {
try {
return callback(obj);
} finally {
if (obj && obj.destroy) {
obj.destroy();
}
}
}
const result = withPyObject(
pyodide.runPython("{'result': 42}"),
(obj) => obj.get("result")
);
console.log(result); // 42Install with Tessl CLI
npx tessl i tessl/npm-pyodide