Building Wasm Components with Python
This cookbook guide shows you how to build WebAssembly components using Python that work with Wassette.
Quick Start
Prerequisites
- Python 3.10 or higher
- uv - Fast Python package manager
Install Tools
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install componentize-py
uv pip install componentize-py
Step-by-Step Guide
1. Create Your Project
mkdir my-python-tool
cd my-python-tool
mkdir wit wit_world
2. Define Your Interface (WIT)
Create wit/world.wit
:
package local:my-tool;
/// Example calculator tool
world calculator {
/// Add two numbers and return the result
export add: func(a: f64, b: f64) -> result<f64, string>;
/// Perform a calculation from a string expression
export calculate: func(expression: string) -> result<string, string>;
}
3. Generate Python Bindings
uv run componentize-py -d wit -w calculator bindings .
This creates Python bindings in the wit_world/
directory.
4. Implement Your Component
Create main.py
:
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
import wit_world
from wit_world.types import Err
import json
def handle_error(e: Exception) -> Err[str]:
"""Helper function to convert Python exceptions to WIT errors"""
message = str(e)
if message == "":
return Err(f"{type(e).__name__}")
else:
return Err(f"{type(e).__name__}: {message}")
class Calculator(wit_world.Calculator):
def add(self, a: float, b: float) -> float:
"""Add two numbers together"""
try:
result = a + b
return result
except Exception as e:
raise handle_error(e)
def calculate(self, expression: str) -> str:
"""Evaluate a mathematical expression and return JSON result"""
try:
# WARNING: eval() is unsafe for untrusted input
# In production, use ast.literal_eval() or a proper expression parser
result = eval(expression)
return json.dumps({"result": result})
except Exception as e:
raise handle_error(e)
5. Create Build Configuration
Create Justfile
:
install-uv:
if ! command -v uv &> /dev/null; then curl -LsSf https://astral.sh/uv/install.sh | sh; fi
install: install-uv
uv pip install componentize-py
bindings:
uv run componentize-py -d wit -w calculator bindings .
build:
uv run componentize-py -d wit -w calculator componentize -s main -o calculator.wasm
all: bindings build
6. Build Your Component
# Install build tools
just install
# Generate bindings and build Wasm component
just all
# Or run commands manually:
# uv run componentize-py -d wit -w calculator bindings .
# uv run componentize-py -d wit -w calculator componentize -s main -o calculator.wasm
7. Test Your Component
wassette serve --sse --plugin-dir .
Complete Examples
Simple Calculator
wit/world.wit:
package local:calculator;
world calculator {
export add: func(a: f64, b: f64) -> f64;
export subtract: func(a: f64, b: f64) -> f64;
export multiply: func(a: f64, b: f64) -> f64;
export divide: func(a: f64, b: f64) -> result<f64, string>;
}
main.py:
import wit_world
from wit_world.types import Err, Ok
class Calculator(wit_world.Calculator):
def add(self, a: float, b: float) -> float:
return a + b
def subtract(self, a: float, b: float) -> float:
return a - b
def multiply(self, a: float, b: float) -> float:
return a * b
def divide(self, a: float, b: float):
if b == 0:
return Err("Division by zero")
return Ok(a / b)
Data Processing Tool
wit/world.wit:
package local:data-processor;
world processor {
export process-csv: func(data: string) -> result<string, string>;
export analyze-data: func(data: string) -> result<string, string>;
}
main.py:
import wit_world
from wit_world.types import Ok, Err
import csv
import json
from io import StringIO
class Processor(wit_world.Processor):
def process_csv(self, data: str) -> str:
try:
reader = csv.DictReader(StringIO(data))
rows = list(reader)
return Ok(json.dumps(rows))
except Exception as e:
return Err(f"CSV processing error: {str(e)}")
def analyze_data(self, data: str) -> str:
try:
items = json.loads(data)
analysis = {
"count": len(items),
"summary": f"Processed {len(items)} items"
}
return Ok(json.dumps(analysis))
except Exception as e:
return Err(f"Analysis error: {str(e)}")
Error Handling
Python components use WIT’s result
type for error handling:
from wit_world.types import Ok, Err
# Success
return Ok(result_value)
# Error
return Err("Error message")
# Or raise an exception
raise handle_error(exception)
Working with WIT Types
Type Mappings
# WIT type -> Python type
# s32, s64, u32, u64 -> int
# f32, f64 -> float
# string -> str
# bool -> bool
# list<T> -> List[T]
# option<T> -> Optional[T]
# result<T, E> -> Ok[T] | Err[E]
# record -> dataclass or dict
Complex Types
from dataclasses import dataclass
from typing import Optional, List
@dataclass
class Person:
name: str
age: int
email: Optional[str]
def process_people(people: List[Person]) -> str:
return json.dumps([p.__dict__ for p in people])
Best Practices
- Use type hints - Python type hints help catch errors early
- Handle errors properly - Always return
Ok
orErr
for result types - Document your code - Use docstrings to explain functionality
- Test thoroughly - Validate edge cases and error conditions
- Keep it simple - Avoid complex dependencies that might not work in Wasm
- Avoid
eval()
for untrusted input - Useast.literal_eval()
or proper parsers instead ofeval()
to prevent code injection
Common Patterns
JSON Processing
import json
def process_json(data: str) -> str:
try:
parsed = json.loads(data)
# Process data
result = {"processed": True, "data": parsed}
return Ok(json.dumps(result))
except json.JSONDecodeError as e:
return Err(f"Invalid JSON: {str(e)}")
File Processing
def read_file(path: str) -> str:
try:
with open(path, 'r') as f:
content = f.read()
return Ok(content)
except FileNotFoundError:
return Err(f"File not found: {path}")
except PermissionError:
return Err(f"Permission denied: {path}")
Data Validation
def validate_input(data: str) -> str:
if not data:
return Err("Input cannot be empty")
if len(data) > 1000:
return Err("Input too large (max 1000 characters)")
return Ok(f"Valid input: {data}")
Troubleshooting
Build Errors
- Ensure Python 3.10+ is installed
- Verify
componentize-py
is installed via uv - Check that WIT interface matches your Python class
Runtime Errors
- Validate all imports are available in Wasm environment
- Check that file paths are correct
- Review Wassette logs for detailed errors
Import Errors
Some Python libraries may not work in Wasm. Stick to:
- Standard library modules (json, csv, math, etc.)
- Pure Python packages
- Modules explicitly tested with componentize-py
Full Documentation
For complete details, including advanced topics and more examples, see the Python Development Guide.
Working Examples
See this complete working example in the repository:
- eval-py - Python code execution component
Next Steps
- Review the complete Python guide
- Check out working examples
- Learn about componentize-py
- Read the FAQ