WebAssembly support for Gradio applications enabling Python code execution in the browser through Pyodide integration
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Core functionality for managing WebAssembly workers that execute Python code through Pyodide integration. The WorkerProxy class provides a complete interface for initializing, communicating with, and managing web workers.
The main class for managing WebAssembly workers and Python code execution. Extends EventTarget for event-driven communication.
/**
* Main class for managing WebAssembly workers and Python code execution
* Extends EventTarget for event-driven communication patterns
*/
class WorkerProxy extends EventTarget {
/**
* Create a new WorkerProxy instance
* @param options - Configuration options for the worker
*/
constructor(options: WorkerProxyOptions);
/** The underlying worker instance (either dedicated or shared) */
readonly worker: globalThis.Worker | globalThis.SharedWorker;
/** Message target for communication with the worker */
readonly postMessageTarget: globalThis.Worker | MessagePort;
/**
* Execute Python code in the worker
* @param code - Python code string to execute
* @returns Promise that resolves when execution completes
*/
runPythonCode(code: string): Promise<void>;
/**
* Execute a Python file in the worker
* @param path - Path to the Python file to execute
* @returns Promise that resolves when execution completes
*/
runPythonFile(path: string): Promise<void>;
/**
* Send a message to the worker and wait for a response
* @param msg - Message to send to the worker
* @returns Promise that resolves with the worker's response
*/
postMessageAsync<T>(msg: InMessage): Promise<T>;
/**
* Initialize an ASGI connection for HTTP request handling
* @param scope - ASGI scope object containing request metadata
* @returns MessagePort for bidirectional communication
*/
requestAsgi(scope: Record<string, unknown>): MessagePort;
/**
* Make an HTTP request through the worker
* @param request - HTTP request object with method, path, headers, and body
* @returns Promise that resolves with the HTTP response
*/
httpRequest(request: HttpRequest): Promise<HttpResponse>;
/**
* Write a file to the worker's filesystem
* @param path - File path in the worker filesystem
* @param data - File data as string or binary data
* @param opts - Optional file writing options
* @returns Promise that resolves when file is written
*/
writeFile(path: string, data: string | ArrayBufferView, opts?: Record<string, unknown>): Promise<void>;
/**
* Rename a file in the worker's filesystem
* @param oldPath - Current file path
* @param newPath - New file path
* @returns Promise that resolves when file is renamed
*/
renameFile(oldPath: string, newPath: string): Promise<void>;
/**
* Delete a file from the worker's filesystem
* @param path - Path to the file to delete
* @returns Promise that resolves when file is deleted
*/
unlink(path: string): Promise<void>;
/**
* Install Python packages in the worker environment
* @param requirements - Array of package names to install
* @returns Promise that resolves when packages are installed
*/
install(requirements: string[]): Promise<void>;
/**
* Get code completions for Python code
* @param request - Code completion request with code, line, and column
* @returns Promise that resolves with completion suggestions
*/
getCodeCompletions(request: CodeCompletionRequest): Promise<CodeCompletionResponse>;
/**
* Terminate the worker and clean up resources
*/
terminate(): void;
}
/**
* Configuration options for WorkerProxy constructor
*/
interface WorkerProxyOptions {
/** URL to the Gradio wheel file for installation */
gradioWheelUrl: string;
/** URL to the Gradio client wheel file for installation */
gradioClientWheelUrl: string;
/** Files to make available in the worker filesystem */
files: Record<string, EmscriptenFile | EmscriptenFileUrl>;
/** Python packages to install during initialization */
requirements: string[];
/** Whether to use SharedWorker instead of DedicatedWorker */
sharedWorkerMode: boolean;
}Usage Examples:
import { WorkerProxy } from "@gradio/wasm";
// Create a worker with configuration
const worker = new WorkerProxy({
gradioWheelUrl: "https://example.com/gradio-4.0.0-py3-none-any.whl",
gradioClientWheelUrl: "https://example.com/gradio_client-1.0.0-py3-none-any.whl",
files: {
"data.json": {
data: JSON.stringify({ users: ["Alice", "Bob"] }),
opts: { encoding: "utf8" }
}
},
requirements: ["numpy", "pandas", "matplotlib"],
sharedWorkerMode: false
});
// Listen for initialization events
worker.addEventListener("initialization-completed", () => {
console.log("Worker initialized successfully");
});
worker.addEventListener("initialization-error", (event) => {
console.error("Worker initialization failed:", event.data);
});
// Execute Python code
await worker.runPythonCode(`
import json
import numpy as np
# Load data from virtual filesystem
with open('data.json', 'r') as f:
data = json.load(f)
print(f"Loaded {len(data['users'])} users")
# Use installed packages
arr = np.array([1, 2, 3, 4, 5])
print(f"NumPy array mean: {np.mean(arr)}")
`);
// Make HTTP requests through the worker
const response = await worker.httpRequest({
method: "POST",
path: "/api/predict",
query_string: "version=1",
headers: { "Content-Type": "application/json" },
body: new TextEncoder().encode(JSON.stringify({ input: "test" }))
});
console.log("Response status:", response.status);
console.log("Response body:", new TextDecoder().decode(response.body));
// Clean up
worker.terminate();WorkerProxy emits various events during its lifecycle and operation:
// Event types emitted by WorkerProxy
interface WorkerEvents {
/** Fired when worker initialization is completed successfully */
"initialization-completed": CustomEvent<void>;
/** Fired when worker initialization fails */
"initialization-error": CustomEvent<{ error: string }>;
/** Fired when worker reports progress updates */
"progress-update": CustomEvent<{ message: string; progress?: number }>;
/** Fired when Python modules are automatically loaded */
"modules-auto-loaded": CustomEvent<{ modules: string[] }>;
/** Fired when Python code writes to stdout */
"stdout": CustomEvent<{ data: string }>;
/** Fired when Python code writes to stderr */
"stderr": CustomEvent<{ data: string }>;
/** Fired when Python code execution encounters an error */
"python-error": CustomEvent<{ error: string; traceback: string }>;
}Event Handling Example:
import { WorkerProxy } from "@gradio/wasm";
const worker = new WorkerProxy(options);
// Handle all output events
worker.addEventListener("stdout", (event) => {
console.log("Python stdout:", event.data);
});
worker.addEventListener("stderr", (event) => {
console.warn("Python stderr:", event.data);
});
worker.addEventListener("python-error", (event) => {
console.error("Python error:", event.error);
console.error("Traceback:", event.traceback);
});
worker.addEventListener("progress-update", (event) => {
console.log("Progress:", event.message);
if (event.progress !== undefined) {
console.log(`Progress: ${event.progress}%`);
}
});Types for file handling in the worker environment:
/**
* File with inline data content
*/
interface EmscriptenFile {
/** File content as string or binary data */
data: string | ArrayBufferView;
/** Optional file system options */
opts?: Record<string, unknown>;
}
/**
* File with remote URL content
*/
interface EmscriptenFileUrl {
/** URL to fetch file content from */
url: string;
/** Optional file system options */
opts?: Record<string, unknown>;
}Internal message types used for worker communication:
/**
* Union type of all input messages sent to the worker
*/
type InMessage =
| InMessageInitEnv
| InMessageInitApp
| InMessageRunPythonCode
| InMessageRunPythonFile
| InMessageAsgiRequest
| InMessageFileWrite
| InMessageFileRename
| InMessageFileUnlink
| InMessageInstall
| InMessageCodeCompletion;
/**
* Initialize environment message
*/
interface InMessageInitEnv {
type: "init-env";
config: {
gradioWheelUrl: string;
gradioClientWheelUrl: string;
files: Record<string, EmscriptenFile | EmscriptenFileUrl>;
requirements: string[];
};
}
/**
* Run Python code message
*/
interface InMessageRunPythonCode {
type: "run-python-code";
code: string;
}
/**
* Run Python file message
*/
interface InMessageRunPythonFile {
type: "run-python-file";
path: string;
}