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
Access and manipulate the Emscripten virtual file system with support for mounting host directories and browser file systems.
Complete Emscripten file system interface for file and directory operations.
const FS: {
// File operations
readFile(path: string, options?: { encoding?: string }): string | Uint8Array;
writeFile(path: string, data: string | ArrayBufferView, options?: { encoding?: string }): void;
unlink(path: string): void;
rename(oldPath: string, newPath: string): void;
// Directory operations
mkdir(path: string, mode?: number): void;
rmdir(path: string): void;
readdir(path: string): string[];
// File information
stat(path: string): FSStats;
lstat(path: string): FSStats;
exists(path: string): boolean;
// File system mounting
mount(type: any, options: any, mountpoint: string): void;
unmount(mountpoint: string): void;
// Additional utilities
mkdirTree(path: string): void;
lookupPath(path: string, options?: any): { path: string; node: any };
isFile(mode: number): boolean;
isDir(mode: number): boolean;
isLink(mode: number): boolean;
isChrdev(mode: number): boolean;
isBlkdev(mode: number): boolean;
isFIFO(mode: number): boolean;
isSocket(mode: number): boolean;
// File systems
filesystems: {
MEMFS: any;
IDBFS: any;
NODEFS: any;
PROXYFS: any;
WORKERFS: any;
NATIVEFS_ASYNC: any;
};
};Path manipulation utilities similar to Node.js path module.
const PATH: {
dirname(path: string): string;
basename(path: string, ext?: string): string;
extname(path: string): string;
join(...paths: string[]): string;
resolve(...paths: string[]): string;
normalize(path: string): string;
isAbsolute(path: string): boolean;
relative(from: string, to: string): string;
sep: string;
delimiter: string;
};Mount a host directory into the virtual file system (Node.js only).
function mountNodeFS(emscriptenPath: string, hostPath: string): void;Parameters:
emscriptenPath - Virtual file system mount point (must be absolute)hostPath - Host directory path to mountMount a browser FileSystemDirectoryHandle into the virtual file system.
function mountNativeFS(
path: string,
fileSystemHandle: FileSystemDirectoryHandle
): Promise<NativeFS>;Parameters:
path - Virtual file system mount point (must be absolute)fileSystemHandle - Browser FileSystemDirectoryHandleReturns: Promise resolving to NativeFS object with sync capabilities
// Create directories
pyodide.FS.mkdir("/workspace");
pyodide.FS.mkdir("/data");
// Write files
pyodide.FS.writeFile("/workspace/config.py", `
DEBUG = True
DATABASE_URL = "sqlite:///app.db"
SECRET_KEY = "development-key"
`);
pyodide.FS.writeFile("/data/sample.txt", "Hello from Pyodide file system!");
// Read files
const configContent = pyodide.FS.readFile("/workspace/config.py", { encoding: "utf8" });
console.log("Config file:", configContent);
const binaryData = pyodide.FS.readFile("/data/sample.txt"); // Returns Uint8Array
const textData = pyodide.FS.readFile("/data/sample.txt", { encoding: "utf8" });
console.log("Text data:", textData);// Create nested directories
pyodide.FS.mkdirTree("/project/src/utils");
// List directory contents
const files = pyodide.FS.readdir("/workspace");
console.log("Workspace files:", files);
// Check if path exists
if (pyodide.FS.exists("/workspace/config.py")) {
console.log("Config file exists");
}
// Get file information
const stats = pyodide.FS.stat("/workspace/config.py");
console.log("File size:", stats.size);
console.log("Modified:", new Date(stats.mtime));
console.log("Is file:", pyodide.FS.isFile(stats.mode));
console.log("Is directory:", pyodide.FS.isDir(stats.mode));// Join paths
const configPath = pyodide.PATH.join("/workspace", "config.py");
console.log("Config path:", configPath); // /workspace/config.py
// Get directory and filename
console.log("Directory:", pyodide.PATH.dirname(configPath)); // /workspace
console.log("Filename:", pyodide.PATH.basename(configPath)); // config.py
console.log("Extension:", pyodide.PATH.extname(configPath)); // .py
// Normalize paths
const messyPath = "/workspace/../workspace/./config.py";
console.log("Normalized:", pyodide.PATH.normalize(messyPath)); // /workspace/config.py
// Check if absolute
console.log("Is absolute:", pyodide.PATH.isAbsolute("/workspace")); // true
console.log("Is absolute:", pyodide.PATH.isAbsolute("config.py")); // false// Create Python module
pyodide.FS.writeFile("/workspace/mymodule.py", `
def calculate(x, y):
return x ** 2 + y ** 2
def greet(name):
return f"Hello, {name}!"
PI = 3.14159
`);
// Use the module in Python
pyodide.runPython(`
import sys
sys.path.append("/workspace")
import mymodule
result = mymodule.calculate(3, 4)
greeting = mymodule.greet("Pyodide")
print(f"Calculation result: {result}")
print(greeting)
print(f"PI value: {mymodule.PI}")
`);// Mount host directory (Node.js only)
try {
pyodide.mountNodeFS("/host", "/path/to/host/directory");
// Access host files from Python
pyodide.runPython(`
with open("/host/data.txt", "r") as f:
content = f.read()
print("Host file content:", content)
# Write to host directory
with open("/host/output.txt", "w") as f:
f.write("Generated by Pyodide")
`);
console.log("Host directory mounted successfully");
} catch (error) {
console.error("Failed to mount host directory:", error.message);
}// Request directory access (browser only)
async function setupWorkspaceAccess() {
try {
// Request directory picker
const dirHandle = await window.showDirectoryPicker();
// Mount the directory
const nativeFS = await pyodide.mountNativeFS("/workspace", dirHandle);
// Access files from the selected directory
pyodide.runPython(`
import os
files = os.listdir("/workspace")
print("Available files:", files)
# Read existing file if available
if "data.csv" in files:
import pandas as pd
df = pd.read_csv("/workspace/data.csv")
print("Data shape:", df.shape)
# Create new file
with open("/workspace/pyodide_output.txt", "w") as f:
f.write("Created by Pyodide in browser")
`);
// Sync changes back to native file system
await nativeFS.syncfs();
console.log("Files synced to native file system");
} catch (error) {
console.error("File system access denied:", error);
}
}// Mount IndexedDB file system for persistence (browser only)
pyodide.FS.mkdir("/persistent");
pyodide.FS.mount(pyodide.FS.filesystems.IDBFS, {}, "/persistent");
// Sync from IndexedDB
pyodide.FS.syncfs(true, (err) => {
if (err) {
console.error("Failed to sync from IndexedDB:", err);
return;
}
// Work with persistent files
pyodide.runPython(`
import os
persistent_file = "/persistent/user_data.json"
if os.path.exists(persistent_file):
import json
with open(persistent_file, "r") as f:
data = json.load(f)
print("Loaded persistent data:", data)
else:
# Create initial data
import json
data = {"visits": 1, "last_visit": "2024-01-01"}
with open(persistent_file, "w") as f:
json.dump(data, f)
print("Created initial persistent data")
`);
// Sync to IndexedDB
pyodide.FS.syncfs(false, (err) => {
if (err) {
console.error("Failed to sync to IndexedDB:", err);
} else {
console.log("Data persisted to IndexedDB");
}
});
});// Handle large files efficiently
function writeFileInChunks(path, data, chunkSize = 1024 * 1024) {
const encoder = new TextEncoder();
const bytes = encoder.encode(data);
// Open file for writing
const fd = pyodide.FS.open(path, "w");
try {
let offset = 0;
while (offset < bytes.length) {
const chunk = bytes.slice(offset, offset + chunkSize);
pyodide.FS.write(fd, chunk, 0, chunk.length);
offset += chunkSize;
}
} finally {
pyodide.FS.close(fd);
}
}
// Create large file
const largeContent = "x".repeat(10 * 1024 * 1024); // 10MB
writeFileInChunks("/data/large_file.txt", largeContent);
// Read file in chunks
function readFileInChunks(path, chunkSize = 1024 * 1024) {
const stats = pyodide.FS.stat(path);
const fd = pyodide.FS.open(path, "r");
const chunks = [];
try {
let bytesRead = 0;
while (bytesRead < stats.size) {
const buffer = new Uint8Array(Math.min(chunkSize, stats.size - bytesRead));
const read = pyodide.FS.read(fd, buffer, 0, buffer.length);
chunks.push(buffer.slice(0, read));
bytesRead += read;
}
} finally {
pyodide.FS.close(fd);
}
// Combine chunks
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
const result = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}
return result;
}// Recursive directory listing
function listFilesRecursive(dirPath) {
const result = [];
function traverse(currentPath) {
const items = pyodide.FS.readdir(currentPath);
for (const item of items) {
if (item === "." || item === "..") continue;
const fullPath = pyodide.PATH.join(currentPath, item);
const stats = pyodide.FS.stat(fullPath);
if (pyodide.FS.isDir(stats.mode)) {
result.push({ path: fullPath, type: "directory" });
traverse(fullPath);
} else {
result.push({
path: fullPath,
type: "file",
size: stats.size,
modified: new Date(stats.mtime)
});
}
}
}
traverse(dirPath);
return result;
}
// Copy file with progress
function copyFileWithProgress(src, dst, progressCallback) {
const stats = pyodide.FS.stat(src);
const srcFd = pyodide.FS.open(src, "r");
const dstFd = pyodide.FS.open(dst, "w");
try {
const chunkSize = 64 * 1024; // 64KB chunks
let bytesCopied = 0;
while (bytesCopied < stats.size) {
const remaining = stats.size - bytesCopied;
const currentChunkSize = Math.min(chunkSize, remaining);
const buffer = new Uint8Array(currentChunkSize);
pyodide.FS.read(srcFd, buffer, 0, currentChunkSize);
pyodide.FS.write(dstFd, buffer, 0, currentChunkSize);
bytesCopied += currentChunkSize;
progressCallback?.(bytesCopied, stats.size);
}
} finally {
pyodide.FS.close(srcFd);
pyodide.FS.close(dstFd);
}
}
// Usage
copyFileWithProgress("/data/large_file.txt", "/backup/large_file.txt",
(copied, total) => {
const percent = (copied / total * 100).toFixed(1);
console.log(`Copy progress: ${percent}%`);
}
);interface FSStats {
dev: number;
ino: number;
mode: number;
nlink: number;
uid: number;
gid: number;
rdev: number;
size: number;
atime: Date;
mtime: Date;
ctime: Date;
blksize: number;
blocks: number;
}
interface NativeFS {
syncfs(): Promise<void>;
}Install with Tessl CLI
npx tessl i tessl/npm-pyodide