Spaces:
Running
Running
import { loadPyodide, type PyodideInterface } from 'pyodide'; | |
declare global { | |
interface Window { | |
stdout: string | null; | |
stderr: string | null; | |
pyodide: PyodideInterface; | |
cells: Record<string, CellState>; | |
indexURL: string; | |
} | |
} | |
type CellState = { | |
id: string; | |
status: 'idle' | 'running' | 'completed' | 'error'; | |
result: any; | |
stdout: string; | |
stderr: string; | |
}; | |
const initializePyodide = async () => { | |
// Ensure Pyodide is loaded once and cached in the worker's global scope | |
if (!self.pyodide) { | |
self.indexURL = '/pyodide/'; | |
self.stdout = ''; | |
self.stderr = ''; | |
self.cells = {}; | |
self.pyodide = await loadPyodide({ | |
indexURL: self.indexURL | |
}); | |
} | |
}; | |
const executeCode = async (id: string, code: string) => { | |
if (!self.pyodide) { | |
await initializePyodide(); | |
} | |
// Update the cell state to "running" | |
self.cells[id] = { | |
id, | |
status: 'running', | |
result: null, | |
stdout: '', | |
stderr: '' | |
}; | |
// Redirect stdout/stderr to stream updates | |
self.pyodide.setStdout({ | |
batched: (msg: string) => { | |
self.cells[id].stdout += msg; | |
self.postMessage({ type: 'stdout', id, message: msg }); | |
} | |
}); | |
self.pyodide.setStderr({ | |
batched: (msg: string) => { | |
self.cells[id].stderr += msg; | |
self.postMessage({ type: 'stderr', id, message: msg }); | |
} | |
}); | |
try { | |
// Dynamically load required packages based on imports in the Python code | |
await self.pyodide.loadPackagesFromImports(code, { | |
messageCallback: (msg: string) => { | |
self.postMessage({ type: 'stdout', id, package: true, message: `[package] ${msg}` }); | |
}, | |
errorCallback: (msg: string) => { | |
self.postMessage({ type: 'stderr', id, package: true, message: `[package] ${msg}` }); | |
} | |
}); | |
// Execute the Python code | |
const result = await self.pyodide.runPythonAsync(code); | |
self.cells[id].result = result; | |
self.cells[id].status = 'completed'; | |
} catch (error) { | |
self.cells[id].status = 'error'; | |
self.cells[id].stderr += `\n${error.toString()}`; | |
} finally { | |
// Notify parent thread when execution completes | |
self.postMessage({ | |
type: 'result', | |
id, | |
state: self.cells[id] | |
}); | |
} | |
}; | |
// Handle messages from the main thread | |
self.onmessage = async (event) => { | |
const { type, id, code, ...args } = event.data; | |
switch (type) { | |
case 'initialize': | |
await initializePyodide(); | |
self.postMessage({ type: 'initialized' }); | |
break; | |
case 'execute': | |
if (id && code) { | |
await executeCode(id, code); | |
} | |
break; | |
case 'getState': | |
self.postMessage({ | |
type: 'kernelState', | |
state: self.cells | |
}); | |
break; | |
case 'terminate': | |
// Explicitly clear the worker for cleanup | |
for (const key in self.cells) delete self.cells[key]; | |
self.close(); | |
break; | |
default: | |
console.error(`Unknown message type: ${type}`); | |
} | |
}; | |