123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- import { loadPyodide, type PyodideInterface } from 'pyodide';
- declare global {
- interface Window {
- stdout: string | null;
- stderr: string | null;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- result: any;
- pyodide: PyodideInterface;
- packages: string[];
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- [key: string]: any;
- }
- }
- async function loadPyodideAndPackages(packages: string[] = []) {
- self.stdout = null;
- self.stderr = null;
- self.result = null;
- self.pyodide = await loadPyodide({
- indexURL: '/pyodide/',
- stdout: (text) => {
- console.log('Python output:', text);
- if (self.stdout) {
- self.stdout += `${text}\n`;
- } else {
- self.stdout = `${text}\n`;
- }
- },
- stderr: (text) => {
- console.log('An error occurred:', text);
- if (self.stderr) {
- self.stderr += `${text}\n`;
- } else {
- self.stderr = `${text}\n`;
- }
- },
- packages: ['micropip']
- });
- const micropip = self.pyodide.pyimport('micropip');
- // await micropip.set_index_urls('https://pypi.org/pypi/{package_name}/json');
- await micropip.install(packages);
- }
- self.onmessage = async (event) => {
- const { id, code, ...context } = event.data;
- console.log(event.data);
- // The worker copies the context in its own "memory" (an object mapping name to values)
- for (const key of Object.keys(context)) {
- self[key] = context[key];
- }
- // make sure loading is done
- await loadPyodideAndPackages(self.packages);
- try {
- self.result = await self.pyodide.runPythonAsync(code);
- // Safely process and recursively serialize the result
- self.result = processResult(self.result);
- console.log('Python result:', self.result);
- } catch (error) {
- self.stderr = error.toString();
- }
- self.postMessage({ id, result: self.result, stdout: self.stdout, stderr: self.stderr });
- };
- function processResult(result: any): any {
- // Catch and always return JSON-safe string representations
- try {
- if (result == null) {
- // Handle null and undefined
- return null;
- }
- if (typeof result === 'string' || typeof result === 'number' || typeof result === 'boolean') {
- // Handle primitive types directly
- return result;
- }
- if (Array.isArray(result)) {
- // If it's an array, recursively process items
- return result.map((item) => processResult(item));
- }
- if (typeof result.toJs === 'function') {
- // If it's a Pyodide proxy object (e.g., Pandas DF, Numpy Array), convert to JS and process recursively
- return processResult(result.toJs());
- }
- if (typeof result === 'object') {
- // Convert JS objects to a recursively serialized representation
- const processedObject: { [key: string]: any } = {};
- for (const key in result) {
- if (Object.prototype.hasOwnProperty.call(result, key)) {
- processedObject[key] = processResult(result[key]);
- }
- }
- return processedObject;
- }
- // Stringify anything that's left (e.g., Proxy objects that cannot be directly processed)
- return JSON.stringify(result);
- } catch (err) {
- // In case something unexpected happens, we return a stringified fallback
- return `[processResult error]: ${err.toString()}`;
- }
- }
- export default {};
|