summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHood Chatham <roberthoodchatham@gmail.com>2024-12-05 00:25:06 (GMT)
committerGitHub <noreply@github.com>2024-12-05 00:25:06 (GMT)
commit2f1cee8477e22bfc36a704310e4c0f409357e7e9 (patch)
treefe241cc2f29bd4c61fbc31cfcddd2e4642fdc101
parent94b8f8b40943bf38cf5c454773a3fb8f4ff71e01 (diff)
downloadcpython-2f1cee8477e22bfc36a704310e4c0f409357e7e9.zip
cpython-2f1cee8477e22bfc36a704310e4c0f409357e7e9.tar.gz
cpython-2f1cee8477e22bfc36a704310e4c0f409357e7e9.tar.bz2
gh-127111: Apply prettier formatter to Emscripten web example (#127551)
Cleaned up formatting (and a stray closing tag) of the web example HTML and JS.
-rw-r--r--Tools/wasm/emscripten/web_example/python.html782
-rw-r--r--Tools/wasm/emscripten/web_example/python.worker.mjs175
2 files changed, 511 insertions, 446 deletions
diff --git a/Tools/wasm/emscripten/web_example/python.html b/Tools/wasm/emscripten/web_example/python.html
index fae1e9a..078f86e 100644
--- a/Tools/wasm/emscripten/web_example/python.html
+++ b/Tools/wasm/emscripten/web_example/python.html
@@ -1,373 +1,433 @@
-<!DOCTYPE html>
+<!doctype html>
<html lang="en">
-<head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta name="author" content="Katie Bell">
- <meta name="description" content="Simple REPL for Python WASM">
- <title>wasm-python terminal</title>
- <link rel="stylesheet" href="https://unpkg.com/xterm@4.18.0/css/xterm.css" crossorigin integrity="sha384-4eEEn/eZgVHkElpKAzzPx/Kow/dTSgFk1BNe+uHdjHa+NkZJDh5Vqkq31+y7Eycd"/>
- <style>
- body {
- font-family: arial;
- max-width: 800px;
- margin: 0 auto
- }
- #code {
- width: 100%;
- height: 180px;
- }
- #info {
- padding-top: 20px;
- }
- .button-container {
- display: flex;
- justify-content: end;
- height: 50px;
- align-items: center;
- gap: 10px;
- }
- button {
- padding: 6px 18px;
- }
- </style>
- <script src="https://unpkg.com/xterm@4.18.0/lib/xterm.js" crossorigin integrity="sha384-yYdNmem1ioP5Onm7RpXutin5A8TimLheLNQ6tnMi01/ZpxXdAwIm2t4fJMx1Djs+"/></script>
- <script type="module">
-class WorkerManager {
- constructor(workerURL, standardIO, readyCallBack, finishedCallback) {
- this.workerURL = workerURL
- this.worker = null
- this.standardIO = standardIO
- this.readyCallBack = readyCallBack
- this.finishedCallback = finishedCallback
-
- this.initialiseWorker()
- }
-
- async initialiseWorker() {
- if (!this.worker) {
- this.worker = new Worker(this.workerURL, {type: "module"})
- this.worker.addEventListener('message', this.handleMessageFromWorker)
- }
- }
-
- async run(options) {
- this.worker.postMessage({
- type: 'run',
- args: options.args || [],
- files: options.files || {}
- })
- }
-
- reset() {
- if (this.worker) {
- this.worker.terminate()
- this.worker = null
- }
- this.standardIO.message('Worker process terminated.')
- this.initialiseWorker()
- }
-
- handleStdinData(inputValue) {
- if (this.stdinbuffer && this.stdinbufferInt) {
- let startingIndex = 1
- if (this.stdinbufferInt[0] > 0) {
- startingIndex = this.stdinbufferInt[0]
+ <head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta name="author" content="Katie Bell" />
+ <meta name="description" content="Simple REPL for Python WASM" />
+ <title>wasm-python terminal</title>
+ <link
+ rel="stylesheet"
+ href="https://unpkg.com/xterm@4.18.0/css/xterm.css"
+ crossorigin
+ integrity="sha384-4eEEn/eZgVHkElpKAzzPx/Kow/dTSgFk1BNe+uHdjHa+NkZJDh5Vqkq31+y7Eycd"
+ />
+ <style>
+ body {
+ font-family: arial;
+ max-width: 800px;
+ margin: 0 auto;
}
- const data = new TextEncoder().encode(inputValue)
- data.forEach((value, index) => {
- this.stdinbufferInt[startingIndex + index] = value
- })
-
- this.stdinbufferInt[0] = startingIndex + data.length - 1
- Atomics.notify(this.stdinbufferInt, 0, 1)
- }
- }
-
- handleMessageFromWorker = (event) => {
- const type = event.data.type
- if (type === 'ready') {
- this.readyCallBack()
- } else if (type === 'stdout') {
- this.standardIO.stdout(event.data.stdout)
- } else if (type === 'stderr') {
- this.standardIO.stderr(event.data.stderr)
- } else if (type === 'stdin') {
- // Leave it to the terminal to decide whether to chunk it into lines
- // or send characters depending on the use case.
- this.stdinbuffer = event.data.buffer
- this.stdinbufferInt = new Int32Array(this.stdinbuffer)
- this.standardIO.stdin().then((inputValue) => {
- this.handleStdinData(inputValue)
- })
- } else if (type === 'finished') {
- this.standardIO.message(`Exited with status: ${event.data.returnCode}`)
- this.finishedCallback()
- }
- }
-}
-
-class WasmTerminal {
-
- constructor() {
- this.inputBuffer = new BufferQueue();
- this.input = ''
- this.resolveInput = null
- this.activeInput = false
- this.inputStartCursor = null
-
- this.xterm = new Terminal(
- { scrollback: 10000, fontSize: 14, theme: { background: '#1a1c1f' }, cols: 100}
- );
-
- this.xterm.onKey((keyEvent) => {
- // Fix for iOS Keyboard Jumping on space
- if (keyEvent.key === " ") {
- keyEvent.domEvent.preventDefault();
+ #code {
+ width: 100%;
+ height: 180px;
}
- });
-
- this.xterm.onData(this.handleTermData)
- }
-
- open(container) {
- this.xterm.open(container);
- }
-
- handleTermData = (data) => {
- const ord = data.charCodeAt(0);
- data = data.replace(/\r(?!\n)/g, "\n") // Convert lone CRs to LF
-
- // Handle pasted data
- if (data.length > 1 && data.includes("\n")) {
- let alreadyWrittenChars = 0;
- // If line already had data on it, merge pasted data with it
- if (this.input != '') {
- this.inputBuffer.addData(this.input);
- alreadyWrittenChars = this.input.length;
- this.input = '';
+ #info {
+ padding-top: 20px;
}
- this.inputBuffer.addData(data);
- // If input is active, write the first line
- if (this.activeInput) {
- let line = this.inputBuffer.nextLine();
- this.writeLine(line.slice(alreadyWrittenChars));
- this.resolveInput(line);
- this.activeInput = false;
+ .button-container {
+ display: flex;
+ justify-content: end;
+ height: 50px;
+ align-items: center;
+ gap: 10px;
}
- // When input isn't active, add to line buffer
- } else if (!this.activeInput) {
- // Skip non-printable characters
- if (!(ord === 0x1b || ord == 0x7f || ord < 32)) {
- this.inputBuffer.addData(data);
+ button {
+ padding: 6px 18px;
}
- // TODO: Handle ANSI escape sequences
- } else if (ord === 0x1b) {
- // Handle special characters
- } else if (ord < 32 || ord === 0x7f) {
- switch (data) {
- case "\x0c": // CTRL+L
- this.clear();
- break;
- case "\n": // ENTER
- case "\x0a": // CTRL+J
- case "\x0d": // CTRL+M
- this.resolveInput(this.input + this.writeLine('\n'));
- this.input = '';
+ </style>
+ <script
+ src="https://unpkg.com/xterm@4.18.0/lib/xterm.js"
+ crossorigin
+ integrity="sha384-yYdNmem1ioP5Onm7RpXutin5A8TimLheLNQ6tnMi01/ZpxXdAwIm2t4fJMx1Djs+"
+ />
+ <script type="module">
+ class WorkerManager {
+ constructor(
+ workerURL,
+ standardIO,
+ readyCallBack,
+ finishedCallback,
+ ) {
+ this.workerURL = workerURL;
+ this.worker = null;
+ this.standardIO = standardIO;
+ this.readyCallBack = readyCallBack;
+ this.finishedCallback = finishedCallback;
+
+ this.initialiseWorker();
+ }
+
+ async initialiseWorker() {
+ if (!this.worker) {
+ this.worker = new Worker(this.workerURL, {
+ type: "module",
+ });
+ this.worker.addEventListener(
+ "message",
+ this.handleMessageFromWorker,
+ );
+ }
+ }
+
+ async run(options) {
+ this.worker.postMessage({
+ type: "run",
+ args: options.args || [],
+ files: options.files || {},
+ });
+ }
+
+ reset() {
+ if (this.worker) {
+ this.worker.terminate();
+ this.worker = null;
+ }
+ this.standardIO.message("Worker process terminated.");
+ this.initialiseWorker();
+ }
+
+ handleStdinData(inputValue) {
+ if (this.stdinbuffer && this.stdinbufferInt) {
+ let startingIndex = 1;
+ if (this.stdinbufferInt[0] > 0) {
+ startingIndex = this.stdinbufferInt[0];
+ }
+ const data = new TextEncoder().encode(inputValue);
+ data.forEach((value, index) => {
+ this.stdinbufferInt[startingIndex + index] = value;
+ });
+
+ this.stdinbufferInt[0] =
+ startingIndex + data.length - 1;
+ Atomics.notify(this.stdinbufferInt, 0, 1);
+ }
+ }
+
+ handleMessageFromWorker = (event) => {
+ const type = event.data.type;
+ if (type === "ready") {
+ this.readyCallBack();
+ } else if (type === "stdout") {
+ this.standardIO.stdout(event.data.stdout);
+ } else if (type === "stderr") {
+ this.standardIO.stderr(event.data.stderr);
+ } else if (type === "stdin") {
+ // Leave it to the terminal to decide whether to chunk it into lines
+ // or send characters depending on the use case.
+ this.stdinbuffer = event.data.buffer;
+ this.stdinbufferInt = new Int32Array(this.stdinbuffer);
+ this.standardIO.stdin().then((inputValue) => {
+ this.handleStdinData(inputValue);
+ });
+ } else if (type === "finished") {
+ this.standardIO.message(
+ `Exited with status: ${event.data.returnCode}`,
+ );
+ this.finishedCallback();
+ }
+ };
+ }
+
+ class WasmTerminal {
+ constructor() {
+ this.inputBuffer = new BufferQueue();
+ this.input = "";
+ this.resolveInput = null;
this.activeInput = false;
- break;
- case "\x7F": // BACKSPACE
- case "\x08": // CTRL+H
- this.handleCursorErase(true);
- break;
- case "\x04": // CTRL+D
- // Send empty input
- if (this.input === '') {
- this.resolveInput('')
- this.activeInput = false;
+ this.inputStartCursor = null;
+
+ this.xterm = new Terminal({
+ scrollback: 10000,
+ fontSize: 14,
+ theme: { background: "#1a1c1f" },
+ cols: 100,
+ });
+
+ this.xterm.onKey((keyEvent) => {
+ // Fix for iOS Keyboard Jumping on space
+ if (keyEvent.key === " ") {
+ keyEvent.domEvent.preventDefault();
+ }
+ });
+
+ this.xterm.onData(this.handleTermData);
+ }
+
+ open(container) {
+ this.xterm.open(container);
+ }
+
+ handleTermData = (data) => {
+ const ord = data.charCodeAt(0);
+ data = data.replace(/\r(?!\n)/g, "\n"); // Convert lone CRs to LF
+
+ // Handle pasted data
+ if (data.length > 1 && data.includes("\n")) {
+ let alreadyWrittenChars = 0;
+ // If line already had data on it, merge pasted data with it
+ if (this.input != "") {
+ this.inputBuffer.addData(this.input);
+ alreadyWrittenChars = this.input.length;
+ this.input = "";
+ }
+ this.inputBuffer.addData(data);
+ // If input is active, write the first line
+ if (this.activeInput) {
+ let line = this.inputBuffer.nextLine();
+ this.writeLine(line.slice(alreadyWrittenChars));
+ this.resolveInput(line);
+ this.activeInput = false;
+ }
+ // When input isn't active, add to line buffer
+ } else if (!this.activeInput) {
+ // Skip non-printable characters
+ if (!(ord === 0x1b || ord == 0x7f || ord < 32)) {
+ this.inputBuffer.addData(data);
+ }
+ // TODO: Handle ANSI escape sequences
+ } else if (ord === 0x1b) {
+ // Handle special characters
+ } else if (ord < 32 || ord === 0x7f) {
+ switch (data) {
+ case "\x0c": // CTRL+L
+ this.clear();
+ break;
+ case "\n": // ENTER
+ case "\x0a": // CTRL+J
+ case "\x0d": // CTRL+M
+ this.resolveInput(
+ this.input + this.writeLine("\n"),
+ );
+ this.input = "";
+ this.activeInput = false;
+ break;
+ case "\x7F": // BACKSPACE
+ case "\x08": // CTRL+H
+ this.handleCursorErase(true);
+ break;
+ case "\x04": // CTRL+D
+ // Send empty input
+ if (this.input === "") {
+ this.resolveInput("");
+ this.activeInput = false;
+ }
+ }
+ } else {
+ this.handleCursorInsert(data);
+ }
+ };
+
+ writeLine(line) {
+ this.xterm.write(line.slice(0, -1));
+ this.xterm.write("\r\n");
+ return line;
+ }
+
+ handleCursorInsert(data) {
+ this.input += data;
+ this.xterm.write(data);
+ }
+
+ handleCursorErase() {
+ // Don't delete past the start of input
+ if (
+ this.xterm.buffer.active.cursorX <=
+ this.inputStartCursor
+ ) {
+ return;
+ }
+ this.input = this.input.slice(0, -1);
+ this.xterm.write("\x1B[D");
+ this.xterm.write("\x1B[P");
+ }
+
+ prompt = async () => {
+ this.activeInput = true;
+ // Hack to allow stdout/stderr to finish before we figure out where input starts
+ setTimeout(() => {
+ this.inputStartCursor =
+ this.xterm.buffer.active.cursorX;
+ }, 1);
+ // If line buffer has a line ready, send it immediately
+ if (this.inputBuffer.hasLineReady()) {
+ return new Promise((resolve, reject) => {
+ resolve(
+ this.writeLine(this.inputBuffer.nextLine()),
+ );
+ this.activeInput = false;
+ });
+ // If line buffer has an incomplete line, use it for the active line
+ } else if (this.inputBuffer.lastLineIsIncomplete()) {
+ // Hack to ensure cursor input start doesn't end up after user input
+ setTimeout(() => {
+ this.handleCursorInsert(
+ this.inputBuffer.nextLine(),
+ );
+ }, 1);
}
+ return new Promise((resolve, reject) => {
+ this.resolveInput = (value) => {
+ resolve(value);
+ };
+ });
+ };
+
+ clear() {
+ this.xterm.clear();
+ }
+
+ print(charCode) {
+ let array = [charCode];
+ if (charCode == 10) {
+ array = [13, 10]; // Replace \n with \r\n
+ }
+ this.xterm.write(new Uint8Array(array));
+ }
}
- } else {
- this.handleCursorInsert(data);
- }
- }
-
- writeLine(line) {
- this.xterm.write(line.slice(0, -1))
- this.xterm.write('\r\n');
- return line;
- }
-
- handleCursorInsert(data) {
- this.input += data;
- this.xterm.write(data)
- }
-
- handleCursorErase() {
- // Don't delete past the start of input
- if (this.xterm.buffer.active.cursorX <= this.inputStartCursor) {
- return
- }
- this.input = this.input.slice(0, -1)
- this.xterm.write('\x1B[D')
- this.xterm.write('\x1B[P')
- }
-
- prompt = async () => {
- this.activeInput = true
- // Hack to allow stdout/stderr to finish before we figure out where input starts
- setTimeout(() => {this.inputStartCursor = this.xterm.buffer.active.cursorX}, 1)
- // If line buffer has a line ready, send it immediately
- if (this.inputBuffer.hasLineReady()) {
- return new Promise((resolve, reject) => {
- resolve(this.writeLine(this.inputBuffer.nextLine()));
- this.activeInput = false;
- })
- // If line buffer has an incomplete line, use it for the active line
- } else if (this.inputBuffer.lastLineIsIncomplete()) {
- // Hack to ensure cursor input start doesn't end up after user input
- setTimeout(() => {this.handleCursorInsert(this.inputBuffer.nextLine())}, 1);
- }
- return new Promise((resolve, reject) => {
- this.resolveInput = (value) => {
- resolve(value)
+
+ class BufferQueue {
+ constructor(xterm) {
+ this.buffer = [];
+ }
+
+ isEmpty() {
+ return this.buffer.length == 0;
+ }
+
+ lastLineIsIncomplete() {
+ return (
+ !this.isEmpty() &&
+ !this.buffer[this.buffer.length - 1].endsWith("\n")
+ );
+ }
+
+ hasLineReady() {
+ return !this.isEmpty() && this.buffer[0].endsWith("\n");
+ }
+
+ addData(data) {
+ let lines = data.match(/.*(\n|$)/g);
+ if (this.lastLineIsIncomplete()) {
+ this.buffer[this.buffer.length - 1] += lines.shift();
+ }
+ for (let line of lines) {
+ this.buffer.push(line);
+ }
+ }
+
+ nextLine() {
+ return this.buffer.shift();
+ }
}
- })
- }
-
- clear() {
- this.xterm.clear();
- }
-
- print(charCode) {
- let array = [charCode];
- if (charCode == 10) {
- array = [13, 10]; // Replace \n with \r\n
- }
- this.xterm.write(new Uint8Array(array));
- }
-}
-
-class BufferQueue {
- constructor(xterm) {
- this.buffer = []
- }
-
- isEmpty() {
- return this.buffer.length == 0
- }
-
- lastLineIsIncomplete() {
- return !this.isEmpty() && !this.buffer[this.buffer.length-1].endsWith("\n")
- }
-
- hasLineReady() {
- return !this.isEmpty() && this.buffer[0].endsWith("\n")
- }
-
- addData(data) {
- let lines = data.match(/.*(\n|$)/g)
- if (this.lastLineIsIncomplete()) {
- this.buffer[this.buffer.length-1] += lines.shift()
- }
- for (let line of lines) {
- this.buffer.push(line)
- }
- }
-
- nextLine() {
- return this.buffer.shift()
- }
-}
-
-const runButton = document.getElementById('run')
-const replButton = document.getElementById('repl')
-const stopButton = document.getElementById('stop')
-const clearButton = document.getElementById('clear')
-
-const codeBox = document.getElementById('codebox')
-
-window.onload = () => {
- const terminal = new WasmTerminal()
- terminal.open(document.getElementById('terminal'))
-
- const stdio = {
- stdout: (charCode) => { terminal.print(charCode) },
- stderr: (charCode) => { terminal.print(charCode) },
- stdin: async () => {
- return await terminal.prompt()
- },
- message: (text) => { terminal.writeLine(`\r\n${text}\r\n`) },
- }
-
- const programRunning = (isRunning) => {
- if (isRunning) {
- replButton.setAttribute('disabled', true)
- runButton.setAttribute('disabled', true)
- stopButton.removeAttribute('disabled')
- } else {
- replButton.removeAttribute('disabled')
- runButton.removeAttribute('disabled')
- stopButton.setAttribute('disabled', true)
- }
- }
-
- runButton.addEventListener('click', (e) => {
- terminal.clear()
- programRunning(true)
- const code = codeBox.value
- pythonWorkerManager.run({args: ['main.py'], files: {'main.py': code}})
- })
-
- replButton.addEventListener('click', (e) => {
- terminal.clear()
- programRunning(true)
- // Need to use "-i -" to force interactive mode.
- // Looks like isatty always returns false in emscripten
- pythonWorkerManager.run({args: ['-i', '-'], files: {}})
- })
-
- stopButton.addEventListener('click', (e) => {
- programRunning(false)
- pythonWorkerManager.reset()
- })
-
- clearButton.addEventListener('click', (e) => {
- terminal.clear()
- })
-
- const readyCallback = () => {
- replButton.removeAttribute('disabled')
- runButton.removeAttribute('disabled')
- clearButton.removeAttribute('disabled')
- }
-
- const finishedCallback = () => {
- programRunning(false)
- }
-
- const pythonWorkerManager = new WorkerManager('./python.worker.mjs', stdio, readyCallback, finishedCallback)
-}
- </script>
-</head>
-<body>
- <h1>Simple REPL for Python WASM</h1>
-<textarea id="codebox" cols="108" rows="16">
+
+ const runButton = document.getElementById("run");
+ const replButton = document.getElementById("repl");
+ const stopButton = document.getElementById("stop");
+ const clearButton = document.getElementById("clear");
+
+ const codeBox = document.getElementById("codebox");
+
+ window.onload = () => {
+ const terminal = new WasmTerminal();
+ terminal.open(document.getElementById("terminal"));
+
+ const stdio = {
+ stdout: (charCode) => {
+ terminal.print(charCode);
+ },
+ stderr: (charCode) => {
+ terminal.print(charCode);
+ },
+ stdin: async () => {
+ return await terminal.prompt();
+ },
+ message: (text) => {
+ terminal.writeLine(`\r\n${text}\r\n`);
+ },
+ };
+
+ const programRunning = (isRunning) => {
+ if (isRunning) {
+ replButton.setAttribute("disabled", true);
+ runButton.setAttribute("disabled", true);
+ stopButton.removeAttribute("disabled");
+ } else {
+ replButton.removeAttribute("disabled");
+ runButton.removeAttribute("disabled");
+ stopButton.setAttribute("disabled", true);
+ }
+ };
+
+ runButton.addEventListener("click", (e) => {
+ terminal.clear();
+ programRunning(true);
+ const code = codeBox.value;
+ pythonWorkerManager.run({
+ args: ["main.py"],
+ files: { "main.py": code },
+ });
+ });
+
+ replButton.addEventListener("click", (e) => {
+ terminal.clear();
+ programRunning(true);
+ // Need to use "-i -" to force interactive mode.
+ // Looks like isatty always returns false in emscripten
+ pythonWorkerManager.run({ args: ["-i", "-"], files: {} });
+ });
+
+ stopButton.addEventListener("click", (e) => {
+ programRunning(false);
+ pythonWorkerManager.reset();
+ });
+
+ clearButton.addEventListener("click", (e) => {
+ terminal.clear();
+ });
+
+ const readyCallback = () => {
+ replButton.removeAttribute("disabled");
+ runButton.removeAttribute("disabled");
+ clearButton.removeAttribute("disabled");
+ };
+
+ const finishedCallback = () => {
+ programRunning(false);
+ };
+
+ const pythonWorkerManager = new WorkerManager(
+ "./python.worker.mjs",
+ stdio,
+ readyCallback,
+ finishedCallback,
+ );
+ };
+ </script>
+ </head>
+ <body>
+ <h1>Simple REPL for Python WASM</h1>
+ <textarea id="codebox" cols="108" rows="16">
print('Welcome to WASM!')
-</textarea>
- <div class="button-container">
- <button id="run" disabled>Run</button>
- <button id="repl" disabled>Start REPL</button>
- <button id="stop" disabled>Stop</button>
- <button id="clear" disabled>Clear</button>
- </div>
- <div id="terminal"></div>
- <div id="info">
- The simple REPL provides a limited Python experience in the browser.
- <a href="https://github.com/python/cpython/blob/main/Tools/wasm/README.md">
- Tools/wasm/README.md</a> contains a list of known limitations and
- issues. Networking, subprocesses, and threading are not available.
- </div>
-</body>
+</textarea
+ >
+ <div class="button-container">
+ <button id="run" disabled>Run</button>
+ <button id="repl" disabled>Start REPL</button>
+ <button id="stop" disabled>Stop</button>
+ <button id="clear" disabled>Clear</button>
+ </div>
+ <div id="terminal"></div>
+ <div id="info">
+ The simple REPL provides a limited Python experience in the browser.
+ <a
+ href="https://github.com/python/cpython/blob/main/Tools/wasm/README.md"
+ >
+ Tools/wasm/README.md
+ </a>
+ contains a list of known limitations and issues. Networking,
+ subprocesses, and threading are not available.
+ </div>
+ </body>
</html>
diff --git a/Tools/wasm/emscripten/web_example/python.worker.mjs b/Tools/wasm/emscripten/web_example/python.worker.mjs
index 42c2e1e..8043e41 100644
--- a/Tools/wasm/emscripten/web_example/python.worker.mjs
+++ b/Tools/wasm/emscripten/web_example/python.worker.mjs
@@ -1,104 +1,109 @@
import createEmscriptenModule from "./python.mjs";
class StdinBuffer {
- constructor() {
- this.sab = new SharedArrayBuffer(128 * Int32Array.BYTES_PER_ELEMENT)
- this.buffer = new Int32Array(this.sab)
- this.readIndex = 1;
- this.numberOfCharacters = 0;
- this.sentNull = true
- }
+ constructor() {
+ this.sab = new SharedArrayBuffer(128 * Int32Array.BYTES_PER_ELEMENT);
+ this.buffer = new Int32Array(this.sab);
+ this.readIndex = 1;
+ this.numberOfCharacters = 0;
+ this.sentNull = true;
+ }
- prompt() {
- this.readIndex = 1
- Atomics.store(this.buffer, 0, -1)
- postMessage({
- type: 'stdin',
- buffer: this.sab
- })
- Atomics.wait(this.buffer, 0, -1)
- this.numberOfCharacters = this.buffer[0]
- }
+ prompt() {
+ this.readIndex = 1;
+ Atomics.store(this.buffer, 0, -1);
+ postMessage({
+ type: "stdin",
+ buffer: this.sab,
+ });
+ Atomics.wait(this.buffer, 0, -1);
+ this.numberOfCharacters = this.buffer[0];
+ }
- stdin = () => {
- while (this.numberOfCharacters + 1 === this.readIndex) {
- if (!this.sentNull) {
- // Must return null once to indicate we're done for now.
- this.sentNull = true
- return null
- }
- this.sentNull = false
- // Prompt will reset this.readIndex to 1
- this.prompt()
- }
- const char = this.buffer[this.readIndex]
- this.readIndex += 1
- return char
+ stdin = () => {
+ while (this.numberOfCharacters + 1 === this.readIndex) {
+ if (!this.sentNull) {
+ // Must return null once to indicate we're done for now.
+ this.sentNull = true;
+ return null;
+ }
+ this.sentNull = false;
+ // Prompt will reset this.readIndex to 1
+ this.prompt();
}
+ const char = this.buffer[this.readIndex];
+ this.readIndex += 1;
+ return char;
+ };
}
const stdout = (charCode) => {
- if (charCode) {
- postMessage({
- type: 'stdout',
- stdout: charCode,
- })
- } else {
- console.log(typeof charCode, charCode)
- }
-}
+ if (charCode) {
+ postMessage({
+ type: "stdout",
+ stdout: charCode,
+ });
+ } else {
+ console.log(typeof charCode, charCode);
+ }
+};
const stderr = (charCode) => {
- if (charCode) {
- postMessage({
- type: 'stderr',
- stderr: charCode,
- })
- } else {
- console.log(typeof charCode, charCode)
- }
-}
+ if (charCode) {
+ postMessage({
+ type: "stderr",
+ stderr: charCode,
+ });
+ } else {
+ console.log(typeof charCode, charCode);
+ }
+};
-const stdinBuffer = new StdinBuffer()
+const stdinBuffer = new StdinBuffer();
const emscriptenSettings = {
- noInitialRun: true,
- stdin: stdinBuffer.stdin,
- stdout: stdout,
- stderr: stderr,
- onRuntimeInitialized: () => {
- postMessage({type: 'ready', stdinBuffer: stdinBuffer.sab})
- },
- async preRun(Module) {
- const versionHex = Module.HEAPU32[Module._Py_Version/4].toString(16);
- const versionTuple = versionHex.padStart(8, "0").match(/.{1,2}/g).map((x) => parseInt(x, 16));
- const [major, minor, ..._] = versionTuple;
- // Prevent complaints about not finding exec-prefix by making a lib-dynload directory
- Module.FS.mkdirTree(`/lib/python${major}.${minor}/lib-dynload/`);
- Module.addRunDependency("install-stdlib");
- const resp = await fetch(`python${major}.${minor}.zip`);
- const stdlibBuffer = await resp.arrayBuffer();
- Module.FS.writeFile(`/lib/python${major}${minor}.zip`, new Uint8Array(stdlibBuffer), { canOwn: true });
- Module.removeRunDependency("install-stdlib");
- }
-}
+ noInitialRun: true,
+ stdin: stdinBuffer.stdin,
+ stdout: stdout,
+ stderr: stderr,
+ onRuntimeInitialized: () => {
+ postMessage({ type: "ready", stdinBuffer: stdinBuffer.sab });
+ },
+ async preRun(Module) {
+ const versionHex = Module.HEAPU32[Module._Py_Version / 4].toString(16);
+ const versionTuple = versionHex
+ .padStart(8, "0")
+ .match(/.{1,2}/g)
+ .map((x) => parseInt(x, 16));
+ const [major, minor, ..._] = versionTuple;
+ // Prevent complaints about not finding exec-prefix by making a lib-dynload directory
+ Module.FS.mkdirTree(`/lib/python${major}.${minor}/lib-dynload/`);
+ Module.addRunDependency("install-stdlib");
+ const resp = await fetch(`python${major}.${minor}.zip`);
+ const stdlibBuffer = await resp.arrayBuffer();
+ Module.FS.writeFile(
+ `/lib/python${major}${minor}.zip`,
+ new Uint8Array(stdlibBuffer),
+ { canOwn: true },
+ );
+ Module.removeRunDependency("install-stdlib");
+ },
+};
const modulePromise = createEmscriptenModule(emscriptenSettings);
-
onmessage = async (event) => {
- if (event.data.type === 'run') {
- const Module = await modulePromise;
- if (event.data.files) {
- for (const [filename, contents] of Object.entries(event.data.files)) {
- Module.FS.writeFile(filename, contents)
- }
- }
- const ret = Module.callMain(event.data.args);
- postMessage({
- type: 'finished',
- returnCode: ret
- })
+ if (event.data.type === "run") {
+ const Module = await modulePromise;
+ if (event.data.files) {
+ for (const [filename, contents] of Object.entries(event.data.files)) {
+ Module.FS.writeFile(filename, contents);
+ }
}
-}
-
+ const ret = Module.callMain(event.data.args);
+ postMessage({
+ type: "finished",
+ returnCode: ret,
+ });
+ }
+};