-
Notifications
You must be signed in to change notification settings - Fork 10
New in-browser code editor plugin #1659
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
3a3de09
69d92fd
5b9c825
ceb7876
3fda04a
26d0b8e
f252cf8
708b8ae
f9aa527
a1e72fc
3a26042
b7a6799
e6472af
62b0979
fc905c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| /** | ||
| * Classic Web Worker: runs the inlined Python test script in Pyodide. | ||
| * Loads Pyodide from CDN via importScripts, captures stdout, parses JSON RunResult, posts back. | ||
| * PYODIDE_INDEX_URL is injected at build time from src/util/pyodide-version.json. | ||
| */ | ||
| /* global importScripts, loadPyodide */ | ||
| var PYODIDE_INDEX_URL = "https://cdn.jsdelivr.net/pyodide/v0.29.3/full/" | ||
|
|
||
| importScripts(PYODIDE_INDEX_URL + "pyodide.js") | ||
|
|
||
| var pyodidePromise = null | ||
|
|
||
| function getPyodide() { | ||
| if (pyodidePromise !== null) { | ||
| return pyodidePromise | ||
| } | ||
| pyodidePromise = loadPyodide({ indexURL: PYODIDE_INDEX_URL }) | ||
| return pyodidePromise | ||
| } | ||
|
|
||
| self.onmessage = function (e) { | ||
| var script = e.data.script | ||
| getPyodide() | ||
| .then(function (pyodide) { | ||
| var stdout = "" | ||
| var stderr = "" | ||
| pyodide.setStdout({ | ||
| batched: function (msg) { | ||
| stdout += msg + "\n" | ||
| }, | ||
| }) | ||
| pyodide.setStderr({ | ||
| batched: function (msg) { | ||
| stderr += msg + "\n" | ||
| }, | ||
| }) | ||
| return pyodide.runPythonAsync(script).then(function () { | ||
| var lines = stdout.split("\n").filter(function (s) { | ||
| return s.trim().length > 0 | ||
| }) | ||
| var lastLine = lines.length > 0 ? lines[lines.length - 1] : "" | ||
| var runResult = JSON.parse(lastLine) | ||
| self.postMessage({ runResult: runResult }) | ||
| }) | ||
Ukonhattu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }) | ||
| .catch(function (err) { | ||
| var message = err && err.message ? err.message : String(err) | ||
| self.postMessage({ error: message }) | ||
| }) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| /** | ||
| * Run worker: executes user Python code in Pyodide with interactive stdin. | ||
| * When the program calls input(), the worker posts "stdin_request", waits for | ||
| * the main thread to send "stdin_line" via postMessage, then returns that line | ||
| * to Python. No SharedArrayBuffer required (works without cross-origin isolation). | ||
| * PYODIDE_INDEX_URL is injected at build time from src/util/pyodide-version.json. | ||
| */ | ||
| /* global importScripts, loadPyodide */ | ||
| var PYODIDE_INDEX_URL = "https://cdn.jsdelivr.net/pyodide/v0.29.3/full/" | ||
|
|
||
| importScripts(PYODIDE_INDEX_URL + "pyodide.js") | ||
|
|
||
| var pyodidePromise = null | ||
| var pendingStdinResolve = null | ||
| var templatePromise = null | ||
|
|
||
| function base64EncodeUtf8(str) { | ||
| var bytes = new TextEncoder().encode(str) | ||
| var binary = "" | ||
| for (var i = 0; i < bytes.length; i++) { | ||
| binary += String.fromCharCode(bytes[i]) | ||
| } | ||
| return btoa(binary) | ||
| } | ||
|
|
||
| function getTemplate() { | ||
| if (templatePromise !== null) { | ||
| return templatePromise | ||
| } | ||
| var templateUrl = new URL("runWorkerTemplate.py", self.location.href).href | ||
| templatePromise = fetch(templateUrl) | ||
| .then(function (r) { | ||
| if (!r.ok) { | ||
| templatePromise = null | ||
| throw new Error("Template fetch failed: " + r.status + " " + r.statusText) | ||
| } | ||
| return r.text() | ||
| }) | ||
| .catch(function (err) { | ||
| templatePromise = null | ||
| throw err | ||
| }) | ||
| return templatePromise | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| function getPyodide() { | ||
| if (pyodidePromise !== null) { | ||
| return pyodidePromise | ||
| } | ||
| pyodidePromise = loadPyodide({ indexURL: PYODIDE_INDEX_URL }).catch(function (err) { | ||
| pyodidePromise = null | ||
| throw err | ||
| }) | ||
| return pyodidePromise | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| self.inputPromise = function (prompt) { | ||
| self.postMessage({ | ||
| type: "stdin_request", | ||
| prompt: prompt != null ? String(prompt) : "", | ||
| }) | ||
| return new Promise(function (resolve) { | ||
| pendingStdinResolve = resolve | ||
| }) | ||
| } | ||
|
|
||
| self.printError = function (message, kind, line, tb) { | ||
| var msg = kind + " on line " + line + ": " + message | ||
| self.postMessage({ type: "run_error", message: msg, output: stdout }) | ||
| runHadError = true | ||
| } | ||
|
Comment on lines
+67
to
+71
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pipeline warning: unused
💡 Proposed fix-self.printError = function (message, kind, line, tb) {
+self.printError = function (message, kind, line, _tb) {🧰 Tools🪛 GitHub Actions: Code Style[warning] 67-67: eslint: warning: 'tb' is defined but never used. (no-unused-vars) 🤖 Prompt for AI Agents |
||
|
|
||
| var runHadError = false | ||
| var stdout = "" | ||
| var stderr = "" | ||
|
Comment on lines
+73
to
+75
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pipeline warning:
💡 Proposed fix var runHadError = false
var stdout = ""
-var stderr = "" stdout = ""
- stderr = ""
var stdoutDecoder = new TextDecoder("utf-8", { fatal: false })
var stderrDecoder = new TextDecoder("utf-8", { fatal: false }) if (chunk.length > 0) {
- stderr += chunk
self.postMessage({ type: "stderr", chunk: chunk })
}🧰 Tools🪛 GitHub Actions: Code Style[warning] 75-75: eslint: warning: 'stderr' is assigned a value but never used. (no-unused-vars) 🤖 Prompt for AI Agents |
||
|
|
||
| self.exit = function () { | ||
| if (!runHadError) { | ||
| self.postMessage({ type: "run_done", output: stdout }) | ||
| } | ||
| } | ||
|
|
||
| self.onmessage = function (e) { | ||
| var data = e.data | ||
|
|
||
| if (data.type === "stdin_line") { | ||
| if (pendingStdinResolve) { | ||
| var line = data.line != null ? String(data.line) : "" | ||
| if (line.length > 0 && line.charAt(line.length - 1) !== "\n") { | ||
| line += "\n" | ||
| } | ||
| pendingStdinResolve(line) | ||
| pendingStdinResolve = null | ||
| } | ||
| return | ||
| } | ||
|
|
||
| if (data.type !== "run") { | ||
| return | ||
| } | ||
|
|
||
| var script = data.script | ||
| runHadError = false | ||
| stdout = "" | ||
|
|
||
| getPyodide() | ||
| .then(function (pyodide) { | ||
| stdout = "" | ||
| stderr = "" | ||
| var stdoutDecoder = new TextDecoder("utf-8", { fatal: false }) | ||
| var stderrDecoder = new TextDecoder("utf-8", { fatal: false }) | ||
| pyodide.setStdout({ | ||
| raw: function (byte) { | ||
| var chunk = stdoutDecoder.decode(new Uint8Array([byte]), { stream: true }) | ||
| if (chunk.length > 0) { | ||
| stdout += chunk | ||
| self.postMessage({ type: "stdout", chunk: chunk }) | ||
| } | ||
| }, | ||
| }) | ||
| pyodide.setStderr({ | ||
| raw: function (byte) { | ||
| var chunk = stderrDecoder.decode(new Uint8Array([byte]), { stream: true }) | ||
| if (chunk.length > 0) { | ||
| stderr += chunk | ||
| self.postMessage({ type: "stderr", chunk: chunk }) | ||
| } | ||
| }, | ||
| }) | ||
Ukonhattu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| self.userScriptB64 = base64EncodeUtf8(script) | ||
| return getTemplate() | ||
| .then(function (template) { | ||
| return pyodide.runPythonAsync(template) | ||
| }) | ||
| .then(function () { | ||
| var flushOut = stdoutDecoder.decode(new Uint8Array(0), { stream: false }) | ||
| var flushErr = stderrDecoder.decode(new Uint8Array(0), { stream: false }) | ||
| if (flushOut.length > 0) { | ||
| stdout += flushOut | ||
| self.postMessage({ type: "stdout", chunk: flushOut }) | ||
| } | ||
| if (flushErr.length > 0) { | ||
| stderr += flushErr | ||
| self.postMessage({ type: "stderr", chunk: flushErr }) | ||
| } | ||
| }) | ||
| }) | ||
| .then(function () { | ||
| /* run_done is posted by exit() when user code finishes */ | ||
| }) | ||
| .catch(function (err) { | ||
| var message = err && err.message ? err.message : String(err) | ||
| self.postMessage({ type: "run_error", message: message }) | ||
| }) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pyodidePromiseis permanently cached on rejection — worker cannot recover.Unlike
runWorker.js(which resetspyodidePromise = nullin a.catch), thisgetPyodide()permanently holds a rejected promise. Any CDN or load failure makes all subsequent test runs fail immediately with no retry.🐛 Proposed fix
function getPyodide() { if (pyodidePromise !== null) { return pyodidePromise } - pyodidePromise = loadPyodide({ indexURL: PYODIDE_INDEX_URL }) - return pyodidePromise + pyodidePromise = loadPyodide({ indexURL: PYODIDE_INDEX_URL }).catch(function (err) { + pyodidePromise = null + throw err + }) + return pyodidePromise }📝 Committable suggestion
🤖 Prompt for AI Agents