{"id":20660972,"url":"https://github.com/liatemplates/pyodide","last_synced_at":"2025-10-30T08:40:39.880Z","repository":{"id":107256842,"uuid":"178852177","full_name":"LiaTemplates/Pyodide","owner":"LiaTemplates","description":"A template for using Pyodide in LiaScript","archived":false,"fork":false,"pushed_at":"2025-05-21T15:49:54.000Z","size":63643,"stargazers_count":5,"open_issues_count":2,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-21T16:49:07.332Z","etag":null,"topics":["liascript","liascript-template","programming","pyodide","python","python-tutorial","template"],"latest_commit_sha":null,"homepage":"https://liascript.github.io/course/?https://raw.githubusercontent.com/liaTemplates/Pyodide/master/README.md","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LiaTemplates.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-04-01T11:50:21.000Z","updated_at":"2025-05-21T15:50:00.000Z","dependencies_parsed_at":"2025-03-05T15:34:40.301Z","dependency_job_id":null,"html_url":"https://github.com/LiaTemplates/Pyodide","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/LiaTemplates/Pyodide","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaTemplates%2FPyodide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaTemplates%2FPyodide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaTemplates%2FPyodide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaTemplates%2FPyodide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LiaTemplates","download_url":"https://codeload.github.com/LiaTemplates/Pyodide/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaTemplates%2FPyodide/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263074545,"owners_count":23409788,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["liascript","liascript-template","programming","pyodide","python","python-tutorial","template"],"created_at":"2024-11-16T19:06:40.161Z","updated_at":"2025-10-30T08:40:34.847Z","avatar_url":"https://github.com/LiaTemplates.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\n\nauthor:   André Dietrich\nemail:    LiaScript@web.de\nversion:  0.3.4\nlanguage: en\nnarrator: US English Male\n\nlogo:     logo.jpg\n\ncomment:  Use the real Python in your LiaScript courses, by loading this\n          template. For more information and to see, which Python-modules are\n          accessible visit the [pyodide-website](https://alpha.iodide.io).\n\nscript:   https://cdn.jsdelivr.net/pyodide/v0.27.3/full/pyodide.js\n\n@onload\nasync function loadPython() {\n    const pyodide = await loadPyodide({ fullStdLib: false })\n    await pyodide.runPythonAsync(`\n        def _lia_process_exception():\n            import sys\n            import traceback\n\n            summary = traceback.format_exception_only(sys.last_type, sys.last_value)[-1].strip()\n            if isinstance(sys.last_value, SyntaxError):\n                lines = [(sys.last_value.lineno, sys.last_value.offset)]\n            else:\n                frames = traceback.extract_tb(sys.last_traceback)\n                lines = [(frame.lineno, frame.colno) for frame in frames if frame.filename == '\u003cexec\u003e']\n            return summary, lines\n\n        def _lia_flush_streams():\n            import sys\n\n            sys.stdout.flush()\n            sys.stderr.flush()\n    `)\n    return pyodide\n}\n\nasync function runPython(code, io) {\n    const plot = document.getElementById(io.mplout)\n    plot.innerHTML = \"\"\n    document.pyodideMplTarget = plot\n\n    if (!window.pyodide) {\n        try {\n            window.pyodide = await loadPython()\n            window.pyodide_running = true\n        } catch (e) {\n            io.liaerr(e.message)\n            io.liaout(\"LIA: stop\")\n            return\n        }\n    }\n\n    try {\n        window.pyodide.setStdout(io.stdout)\n        window.pyodide.setStderr(io.stderr)\n        window.pyodide.setStdin({\n            stdin: () =\u003e {\n                return prompt(\"stdin\")\n            }\n        })\n\n        await window.pyodide.loadPackagesFromImports(code)\n        const rslt = await window.pyodide.runPythonAsync(code)\n\n        if (typeof rslt === 'string') {\n            io.liaout(rslt)\n        } else if (rslt !== undefined \u0026\u0026 typeof rslt.toString === 'function') {\n            io.liaout(rslt.toString())\n        } else if (io.clearOut) {\n            io.liaout(\"\")\n        }\n    } catch (e) {\n        if (e instanceof window.pyodide.ffi.PythonError) {\n            const out = await window.pyodide.runPythonAsync(\"_lia_process_exception()\")\n            const [errString, lines] = out.toJs({create_pyproxies : false})\n            io.liaerr(e.message, errString, lines)\n        } else {\n            io.liaerr(e.message)\n        }\n    }\n    await window.pyodide.runPythonAsync(\"_lia_flush_streams()\")\n    io.liaout(\"LIA: stop\")\n    window.pyodide_running = false\n}\n\nwindow.runPython = runPython\n@end\n\n\n@Pyodide.exec: @Pyodide.exec_(@uid,```@0```)\n\n@Pyodide.exec_\n\u003cscript run-once modify=\"# --python--\\n\" type=\"text/python\"\u003e\n\nasync function run_exec() {\n    const code = String.raw`# --python--\n@1\n# --python--\n`\n    if (!window.pyodide_running) {\n        window.pyodide_running = true\n\n        const io = {\n            stdout: {batched: console.log},\n            stderr: {batched: console.error},\n            liaout: send.lia,\n            liaerr: (text) =\u003e send.lia(`HTML: \u003cpre style='color: red'\u003e${text}\u003c/pre\u003e`),\n            clearOut: true,\n            mplout: \"target_@0\"\n        }\n\n        await window.runPython(code, io)\n    } else {\n        setTimeout(run_exec, 1000)\n    }\n}\n\nsetTimeout(run_exec, 500)\n\n\"calculating, please wait ...\"\n\n\u003c/script\u003e\n\n\u003cdiv id=\"target_@0\"\u003e\u003c/div\u003e\n@end\n\n\n@Pyodide.eval: @Pyodide.eval_(@uid)\n\n@Pyodide.eval_\n\u003cscript\u003e\n\nasync function run_eval() {\n    const code = \"@'input\"\n    const io = {\n        stdout: {\n            write: (buffer) =\u003e {\n                const decoder = new TextDecoder()\n                const string = decoder.decode(buffer)\n                console.stream(string)\n                return buffer.length\n            }\n        },\n        stderr: {\n            write: (buffer) =\u003e {\n                const decoder = new TextDecoder()\n                const string = decoder.decode(buffer)\n                send.log(\"error\", '', [string])\n                return buffer.length\n            }\n        },\n        liaout: send.lia,\n        liaerr: (fullMessage, lineMessage, lines) =\u003e {\n            window.console.log(lines)\n            let lineErrors = [[]]\n            for (const [i, line] of lines.entries()) {\n                const last = (i + 1 == lines.length)\n                lineErrors[0].push({\n                    row: line[0] - 1,  // Off-by-one; not sure why\n                    column: line[1],\n                    text: last ? lineMessage : \"Called from here\",\n                    type: last ? \"error\" : \"warning\"\n                })\n            }\n            send.lia(fullMessage, lineErrors, false)\n        },\n        clearOut: false,\n        mplout: \"target_@0\"\n    }\n\n    await window.runPython(code, io)\n}\n\nif (window.pyodide_running) {\n    setTimeout(() =\u003e {\n        console.warn(\"Another process is running, wait until finished\")\n    }, 500)\n\n    \"LIA: stop\"\n} else {\n    window.pyodide_running = true\n    setTimeout(run_eval, 500)\n\n    \"LIA: wait\"\n}\n\u003c/script\u003e\n\n\u003cdiv id=\"target_@0\"\u003e\u003c/div\u003e\n@end\n\n--\u003e\n\n# Pyodide - Template\n\n                                   --{{0}}--\nA template for executing Python code in [LiaScript](https://LiaScript.github.io)\nbased on the [Pyodide](https://github.com/iodide-project/pyodide) webassembly\nport to JavaScript. This port tries to make Python scientific programming\naccessible within the browser, see the [Iodide project](https://iodide.io) for\nmore information.\n\n__Try it on LiaScript:__\n\nhttps://liascript.github.io/course/?https://raw.githubusercontent.com/LiaTemplates/Pyodide/master/README.md\n\n\n![demo](demo.gif)\u003c!-- style=\"display:none\" --\u003e\n\n\n__See the project on Github:__\n\nhttps://github.com/LiaTemplates/pyodide\n\n                                   --{{1}}--\nThere are three ways to use this template. The easiest way is to use the\n`import` statement and the url of the raw text-file of the master branch or any\nother branch or version. But you can also copy the required functionality\ndirectly into the header of your Markdown document, see therefor the\n[Implementation](#3). And of course, you could also clone this project and\nchange it, as you wish.\n\n                                     {{1}}\n********************************************************************************\n\n1. Load the macros via\n\n   `import: https://raw.githubusercontent.com/LiaTemplates/Pyodide/master/README.md`\n\n2. Copy the definitions into your Project\n\n3. Clone this repository on GitHub\n\n********************************************************************************\n\n## `@Pyodide.eval`\n\n                                   --{{0}}--\nSimply attach the macro `@Pyodide.eval` to the end of your code-block to make\nyour Python code executable.\n\n```python\nimport sys\n\nfor i in range(5):\n\tprint(\"Hello\", 'World #', i)\n\nsys.version\n```\n@Pyodide.eval\n\n--------------------------------------------------------------------------------\n\n                                   --{{1}}--\nIf you want to use matplotlib, you will have to pass your figure to the `plot`\nfunction, as it is done in the last line below. This function converts your\nimage into a base64 representation and passes this string to the DOM. It is currently only possible to plot one figure per snippet.\n\n```python\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nt = np.arange(0.0, 2.0, 0.01)\ns = np.sin(2 * np.pi * t)\n\nfig, ax = plt.subplots()\nax.plot(t, s)\n\nax.grid(True, linestyle='-.')\nax.tick_params(labelcolor='r', labelsize='medium', width=3)\n\nplt.show()\n```\n@Pyodide.eval\n\n\n``` python\nimport pandas as pd\nd = {'col1': [1, 5, 7], 'col2': [3, .4, -2], 'col3':[\"yes\", \"no\",\"blue\"]};\ndf = pd.DataFrame(data=d);\ndf\nprint(df)\n```\n@Pyodide.eval\n\n\n## `@Pyodide.exec`\n\nThis macro works similar to the previous one, but the code is only passed as a parameter.\nThe user will only see the result and will not have the chance to directly modify the the Python code.\n\n```python   @Pyodide.exec\nimport sys\n\nfor i in range(5):\n\tprint(\"Hello\", 'World #', i)\n\nsys.version\n```\n\n\n```python   @Pyodide.exec\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nt = np.arange(0.0, 2.0, 0.01)\ns = np.sin(2 * np.pi * t)\n\nfig, ax = plt.subplots()\nax.plot(t, s)\n\nax.grid(True, linestyle='-.')\nax.tick_params(labelcolor='r', labelsize='medium', width=3)\n\nplt.show()\n```\n\n## Loading Libraries\n\n                                   --{{0}}--\n\nOnly the Python standard library and `six` are available at the beginning, other\nlibraries are globally loaded, if defined within the script.\n\n\u003e __Note:__ loading large packages such as `scipy` may take some time, since\n\u003e           they might require to download many MB of precompiled packages.\n\n## Implementation\n\n                                   --{{0}}--\nThis macro implementation only adds a simple script-tag that pushes the code of\nyour snippet directly to Pyodide. The `@onload` macro is required to instantiate\nPyodide and load the required libraries, which might require some time, since\nthe loaded packages might be quite large.\n\n\n```` js\nscript:   https://cdn.jsdelivr.net/pyodide/v0.24.0/full/pyodide.js\n\n@Pyodide.exec: @Pyodide.exec_(@uid,```@0```)\n\n@Pyodide.exec_\n\u003cscript\u003e\nasync function run(code, force=false) {\n    if (!window.pyodide_running || force) {\n        window.pyodide_running = true\n\n        const plot = document.getElementById('target_@0')\n        plot.innerHTML = \"\"\n        document.pyodideMplTarget = plot\n\n        if (!window.pyodide) {\n            try {\n                window.pyodide = await loadPyodide({fullStdLib: false})\n                window.pyodide_modules = []\n                window.pyodide_running = true\n            } catch(e) {\n                send.lia(e.message, false)\n                send.lia(\"LIA: stop\")\n            }\n        }\n\n        try {\n            window.pyodide.setStdout((text) =\u003e console.log(text))\n            window.pyodide.setStderr((text) =\u003e console.error(text))\n\n            window.pyodide.setStdin({stdin: () =\u003e {\n            return prompt(\"stdin\")\n            }})\n\n            const rslt = await window.pyodide.runPython(code)\n\n            if (rslt !== undefined) {\n                send.lia(rslt)\n            } else {\n                send.lia(\"\")\n            }\n        } catch(e) {\n            let module = e.message.match(/ModuleNotFoundError: No module named '([^']+)/i)\n\n            window.console.warn(\"Pyodide\", e.message)\n\n            if (!module) {\n                send.lia(e.message, false)\n\n            } else {\n                if (module.length \u003e 1) {\n                    module = module[1]\n\n                    if (window.pyodide_modules.includes(module)) {\n                        console.warn(e.message)\n                        send.lia(e.message, false)\n                    } else {\n                        send.lia(\"downloading module =\u003e \" + module)\n                        window.pyodide_modules.push(module)\n                        await window.pyodide.loadPackage(module)\n                        await run(code, true)\n                    }\n                }\n            }\n        }\n        send.lia(\"LIA: stop\")\n        window.pyodide_running = false\n    } else {\n        setTimeout(() =\u003e { run(code) }, 1000)\n    }\n}\n\nsetTimeout(() =\u003e { run(`@1`) }, 500)\n\n\"calculating, please wait ...\"\n\n\u003c/script\u003e\n\n\u003cdiv id=\"target_@0\"\u003e\u003c/div\u003e\n@end\n\n\n@Pyodide.eval: @Pyodide.eval_(@uid)\n\n@Pyodide.eval_\n\u003cscript\u003e\nasync function run(code) {\n\n    const plot = document.getElementById('target_@0')\n    plot.innerHTML = \"\"\n    document.pyodideMplTarget = plot\n\n    if (!window.pyodide) {\n        try {\n            window.pyodide = await loadPyodide({fullStdLib: false})\n            window.pyodide_modules = []\n            window.pyodide_running = true\n        } catch(e) {\n            console.error(e.message)\n            send.lia(\"LIA: stop\")\n        }\n    }\n\n    try {\n        window.pyodide.setStdout({ write: (buffer) =\u003e {\n            const decoder = new TextDecoder()\n            const string = decoder.decode(buffer)\n            console.stream(string)\n            return buffer.length\n        }})\n\n        window.pyodide.setStderr({ write: (buffer) =\u003e {\n            const decoder = new TextDecoder()\n            const string = decoder.decode(buffer)\n            console.error(string)\n            return buffer.length\n        }})\n\n        window.pyodide.setStdin({stdin: () =\u003e {\n          return prompt(\"stdin\")\n        }})\n\n        const rslt = await window.pyodide.runPython(code)\n\n        if (typeof rslt === 'string') {\n            send.lia(rslt)\n        }\n    } catch(e) {\n        let module = e.message.match(/ModuleNotFoundError: No module named '([^']+)/i)\n\n        window.console.warn(\"Pyodide\", e.message)\n\n        if (!module) {\n            const err = e.message.match(/File \"\u003cexec\u003e\", line (\\d+).*\\n((.*\\n){1,3})/i)\n\n            if (err!== null \u0026\u0026 err.length \u003e= 3) {\n                send.lia( e.message,\n                  [[{ row : parseInt(err[1]) - 1,\n                      column : 1,\n                      text : err[2],\n                      type : \"error\"\n                  }]],\n                  false)\n            } else {\n                console.error(e.message)\n            }\n        } else {\n            if (module.length \u003e 1) {\n                module = module[1]\n\n                if (window.pyodide_modules.includes(module)) {\n                    console.error(e.message)\n                } else {\n                    console.debug(\"downloading module =\u003e\", module)\n                    window.pyodide_modules.push(module)\n                    await window.pyodide.loadPackage(module)\n                    await run(code)\n                }\n            }\n        }\n    }\n    send.lia(\"LIA: stop\")\n    window.pyodide_running = false\n}\n\nif (window.pyodide_running) {\n  setTimeout(() =\u003e {\n    console.warn(\"Another process is running, wait until finished\")\n  }, 500)\n  \"LIA: stop\"\n} else {\n  window.pyodide_running = true\n\n  setTimeout(() =\u003e {\n    run(`@input`)\n  }, 500)\n\n  \"LIA: wait\"\n}\n\u003c/script\u003e\n\n\u003cdiv id=\"target_@0\"\u003e\u003c/div\u003e\n@end\n````\n\n                                   --{{1}}--\nIf you want to minimize loading effort in your LiaScript project, you can also\ncopy this code and paste it into your main comment header, see the code in the\nraw file of this document.\n\n\n                                     {{1}}\nhttps://raw.githubusercontent.com/LiaTemplates/pyodide/master/README.md\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliatemplates%2Fpyodide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliatemplates%2Fpyodide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliatemplates%2Fpyodide/lists"}