CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-pyodide

Python distribution for the browser and Node.js based on WebAssembly that enables running Python code with full JavaScript interoperability

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

file-system.mddocs/

File System

Access and manipulate the Emscripten virtual file system with support for mounting host directories and browser file systems.

File System API

FS

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

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;
};

Mounting External File Systems

mountNodeFS

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 mount

mountNativeFS

Mount 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 FileSystemDirectoryHandle

Returns: Promise resolving to NativeFS object with sync capabilities

Usage Examples

Basic File Operations

// 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);

Directory Operations

// 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));

Path Manipulation

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

Working with Python Files

// 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}")
`);

Node.js Host Directory Mounting

// 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);
}

Browser File System Access

// 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);
  }
}

Persistent Storage with IDBFS

// 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");
    }
  });
});

File Streaming and Large Files

// 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;
}

File System Utilities

// 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}%`);
  }
);

File System Types

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

docs

advanced-features.md

code-execution.md

ffi.md

file-system.md

index.md

interoperability.md

io-streams.md

package-management.md

runtime-loading.md

tile.json