from __future__ import annotations
import os
+import sys
# types
if False:
trace_file = open(trace_filename, "a")
-def trace(line: str, *k: object, **kw: object) -> None:
- if trace_file is None:
- return
- if k or kw:
- line = line.format(*k, **kw)
- trace_file.write(line + "\n")
- trace_file.flush()
+
+if sys.platform == "emscripten":
+ from posix import _emscripten_log
+
+ def trace(line: str, *k: object, **kw: object) -> None:
+ if "PYREPL_TRACE" not in os.environ:
+ return
+ if k or kw:
+ line = line.format(*k, **kw)
+ _emscripten_log(line)
+
+else:
+ def trace(line: str, *k: object, **kw: object) -> None:
+ if trace_file is None:
+ return
+ if k or kw:
+ line = line.format(*k, **kw)
+ trace_file.write(line + "\n")
+ trace_file.flush()
import sys
import unittest
from test.support import (
- cpython_only, get_pagesize, is_apple, requires_subprocess, verbose
+ cpython_only, get_pagesize, is_apple, requires_subprocess, verbose, is_emscripten
)
from test.support.import_helper import import_module
from test.support.os_helper import TESTFN, unlink, make_bad_fd
@unittest.skipUnless(
hasattr(fcntl, "F_SETPIPE_SZ") and hasattr(fcntl, "F_GETPIPE_SZ"),
"F_SETPIPE_SZ and F_GETPIPE_SZ are not available on all platforms.")
+ @unittest.skipIf(is_emscripten, "Emscripten pipefs doesn't support these")
def test_fcntl_f_pipesize(self):
test_pipe_r, test_pipe_w = os.pipe()
try:
@unittest.skipUnless(
hasattr(fcntl, "F_SETOWN_EX") and hasattr(fcntl, "F_GETOWN_EX"),
"requires F_SETOWN_EX and F_GETOWN_EX")
+ @unittest.skipIf(is_emscripten, "Emscripten doesn't actually support these")
def test_fcntl_small_buffer(self):
self._check_fcntl_not_mutate_len()
@unittest.skipUnless(
hasattr(fcntl, "F_SETOWN_EX") and hasattr(fcntl, "F_GETOWN_EX"),
"requires F_SETOWN_EX and F_GETOWN_EX")
+ @unittest.skipIf(is_emscripten, "Emscripten doesn't actually support these")
def test_fcntl_large_buffer(self):
self._check_fcntl_not_mutate_len(2024)
python-config checksharedmods
.PHONY: build_emscripten
-build_emscripten: build_wasm web_example
+build_emscripten: build_wasm web_example web_example_pyrepl_jspi
# Check that the source is clean when building out of source.
.PHONY: check-clean-src
# wasm32-emscripten browser web example
-WEBEX_DIR=$(srcdir)/Tools/wasm/emscripten/web_example/
+EMSCRIPTEN_DIR=$(srcdir)/Tools/wasm/emscripten
+WEBEX_DIR=$(EMSCRIPTEN_DIR)/web_example/
+
+ZIP_STDLIB=python$(VERSION)$(ABI_THREAD).zip
+$(ZIP_STDLIB): $(srcdir)/Lib/*.py $(srcdir)/Lib/*/*.py \
+ $(EMSCRIPTEN_DIR)/wasm_assets.py \
+ Makefile pybuilddir.txt Modules/Setup.local
+ $(PYTHON_FOR_BUILD) $(EMSCRIPTEN_DIR)/wasm_assets.py \
+ --buildroot . --prefix $(prefix) -o $@
+
web_example/index.html: $(WEBEX_DIR)/index.html
@mkdir -p web_example
@cp $< $@
-web_example/python.worker.mjs: $(WEBEX_DIR)/python.worker.mjs
+web_example/server.py: $(WEBEX_DIR)/server.py
@mkdir -p web_example
@cp $< $@
-web_example/server.py: $(WEBEX_DIR)/server.py
+web_example/$(ZIP_STDLIB): $(ZIP_STDLIB)
@mkdir -p web_example
@cp $< $@
-WEB_STDLIB=web_example/python$(VERSION)$(ABI_THREAD).zip
-$(WEB_STDLIB): $(srcdir)/Lib/*.py $(srcdir)/Lib/*/*.py \
- $(WEBEX_DIR)/wasm_assets.py \
- Makefile pybuilddir.txt Modules/Setup.local
- $(PYTHON_FOR_BUILD) $(WEBEX_DIR)/wasm_assets.py \
- --buildroot . --prefix $(prefix) -o $@
-
web_example/python.mjs web_example/python.wasm: $(BUILDPYTHON)
@if test $(HOST_GNU_TYPE) != 'wasm32-unknown-emscripten' ; then \
echo "Can only build web_example when target is Emscripten" ;\
cp python.wasm web_example/python.wasm
.PHONY: web_example
-web_example: web_example/python.mjs web_example/python.worker.mjs web_example/index.html web_example/server.py $(WEB_STDLIB)
+web_example: web_example/python.mjs web_example/index.html web_example/server.py web_example/$(ZIP_STDLIB)
+
+WEBEX2=web_example_pyrepl_jspi
+WEBEX2_DIR=$(EMSCRIPTEN_DIR)/$(WEBEX2)/
+
+$(WEBEX2)/python.mjs $(WEBEX2)/python.wasm: $(BUILDPYTHON)
+ @if test $(HOST_GNU_TYPE) != 'wasm32-unknown-emscripten' ; then \
+ echo "Can only build web_example when target is Emscripten" ;\
+ exit 1 ;\
+ fi
+ @mkdir -p $(WEBEX2)
+ @cp python.mjs $(WEBEX2)/python.mjs
+ @cp python.wasm $(WEBEX2)/python.wasm
+
+$(WEBEX2)/index.html: $(WEBEX2_DIR)/index.html
+ @mkdir -p $(WEBEX2)
+ @cp $< $@
+
+$(WEBEX2)/src.mjs: $(WEBEX2_DIR)/src.mjs
+ @mkdir -p $(WEBEX2)
+ @cp $< $@
+
+$(WEBEX2)/$(ZIP_STDLIB): $(ZIP_STDLIB)
+ @mkdir -p $(WEBEX2)
+ @cp $< $@
+
+.PHONY: web_example_pyrepl_jspi
+web_example_pyrepl_jspi: $(WEBEX2)/python.mjs $(WEBEX2)/index.html $(WEBEX2)/src.mjs $(WEBEX2)/$(ZIP_STDLIB)
+
############################################################################
# Header files
--- /dev/null
+pyrepl now works in Emscripten.
#endif /* defined(__EMSCRIPTEN__) */
+#if defined(__EMSCRIPTEN__)
+
+PyDoc_STRVAR(os__emscripten_log__doc__,
+"_emscripten_log($module, /, arg)\n"
+"--\n"
+"\n"
+"Log something to the JS console. Emscripten only.");
+
+#define OS__EMSCRIPTEN_LOG_METHODDEF \
+ {"_emscripten_log", _PyCFunction_CAST(os__emscripten_log), METH_FASTCALL|METH_KEYWORDS, os__emscripten_log__doc__},
+
+static PyObject *
+os__emscripten_log_impl(PyObject *module, const char *arg);
+
+static PyObject *
+os__emscripten_log(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+ #define NUM_KEYWORDS 1
+ static struct {
+ PyGC_Head _this_is_not_used;
+ PyObject_VAR_HEAD
+ Py_hash_t ob_hash;
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_hash = -1,
+ .ob_item = { &_Py_ID(arg), },
+ };
+ #undef NUM_KEYWORDS
+ #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+ #else // !Py_BUILD_CORE
+ # define KWTUPLE NULL
+ #endif // !Py_BUILD_CORE
+
+ static const char * const _keywords[] = {"arg", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "_emscripten_log",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[1];
+ const char *arg;
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
+ /*minpos*/ 1, /*maxpos*/ 1, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ if (!PyUnicode_Check(args[0])) {
+ _PyArg_BadArgument("_emscripten_log", "argument 'arg'", "str", args[0]);
+ goto exit;
+ }
+ Py_ssize_t arg_length;
+ arg = PyUnicode_AsUTF8AndSize(args[0], &arg_length);
+ if (arg == NULL) {
+ goto exit;
+ }
+ if (strlen(arg) != (size_t)arg_length) {
+ PyErr_SetString(PyExc_ValueError, "embedded null character");
+ goto exit;
+ }
+ return_value = os__emscripten_log_impl(module, arg);
+
+exit:
+ return return_value;
+}
+
+#endif /* defined(__EMSCRIPTEN__) */
+
#ifndef OS_TTYNAME_METHODDEF
#define OS_TTYNAME_METHODDEF
#endif /* !defined(OS_TTYNAME_METHODDEF) */
#ifndef OS__EMSCRIPTEN_DEBUGGER_METHODDEF
#define OS__EMSCRIPTEN_DEBUGGER_METHODDEF
#endif /* !defined(OS__EMSCRIPTEN_DEBUGGER_METHODDEF) */
-/*[clinic end generated code: output=6cfddb3b77dc7a40 input=a9049054013a1b77]*/
+
+#ifndef OS__EMSCRIPTEN_LOG_METHODDEF
+ #define OS__EMSCRIPTEN_LOG_METHODDEF
+#endif /* !defined(OS__EMSCRIPTEN_LOG_METHODDEF) */
+/*[clinic end generated code: output=608e9bc5f631f688 input=a9049054013a1b77]*/
emscripten_debugger();
Py_RETURN_NONE;
}
+
+EM_JS(void, emscripten_log_impl_js, (const char* arg), {
+ console.warn(UTF8ToString(arg));
+});
+
+/*[clinic input]
+os._emscripten_log
+ arg: str
+
+Log something to the JS console. Emscripten only.
+[clinic start generated code]*/
+
+static PyObject *
+os__emscripten_log_impl(PyObject *module, const char *arg)
+/*[clinic end generated code: output=9749e5e293c42784 input=350aa1f70bc1e905]*/
+{
+ emscripten_log_impl_js(arg);
+ Py_RETURN_NONE;
+}
#endif /* __EMSCRIPTEN__ */
OS__IS_INPUTHOOK_INSTALLED_METHODDEF
OS__CREATE_ENVIRON_METHODDEF
OS__EMSCRIPTEN_DEBUGGER_METHODDEF
+ OS__EMSCRIPTEN_LOG_METHODDEF
{NULL, NULL} /* Sentinel */
};
#include "emscripten.h"
+#include "stdio.h"
// If we're running in node, report the UID of the user in the native system as
// the UID of the user. Since the nodefs will report the uid correctly, if we
#include <wasi/api.h>
#include <errno.h>
-#undef errno
+#include <fcntl.h>
// Variant of EM_JS that does C preprocessor substitution on the body
#define EM_JS_MACROS(ret, func_name, args, body...) \
return;
}
const origResolveGlobalSymbol = resolveGlobalSymbol;
- if (!Module.onExit && globalThis?.process?.exit) {
+ if (ENVIRONMENT_IS_NODE && !Module.onExit) {
Module.onExit = (code) => process.exit(code);
}
// * wrap the main symbol with WebAssembly.promising,
orig.sym = (...args) => {
(async () => {
const ret = await main(...args);
- process?.exit?.(ret);
+ Module.onExit?.(ret);
})();
_emscripten_exit_with_live_runtime();
};
if (e.name !== 'ErrnoError') {
throw e;
}
- return e.errno;
+ return e["errno"];
}
})();
};
// Take a promise that resolves to __wasi_errno_t and suspend until it resolves,
// get the output.
-EM_JS(__wasi_errno_t, __block_for_errno, (__externref_t p), {
+EM_JS(int, __block_for_int, (__externref_t p), {
return p;
}
if (WebAssembly.Suspending) {
- __block_for_errno = new WebAssembly.Suspending(__block_for_errno);
+ __block_for_int = new WebAssembly.Suspending(__block_for_int);
}
)
// Replacement for fd_read syscall. Call __maybe_fd_read_async. If it returned
-// null, delegate back to __wasi_fd_read_orig. Otherwise, use __block_for_errno
+// null, delegate back to __wasi_fd_read_orig. Otherwise, use __block_for_int
// to get the result.
__wasi_errno_t __wasi_fd_read(__wasi_fd_t fd, const __wasi_iovec_t *iovs,
size_t iovs_len, __wasi_size_t *nread) {
if (__builtin_wasm_ref_is_null_extern(p)) {
return __wasi_fd_read_orig(fd, iovs, iovs_len, nread);
}
- __wasi_errno_t res = __block_for_errno(p);
- return res;
+ return __block_for_int(p);
+}
+
+#include <poll.h>
+#define POLLFD_FD 0
+#define POLLFD_EVENTS 4
+#define POLLFD_REVENTS 6
+#define POLLFD_SIZE 8
+_Static_assert(offsetof(struct pollfd, fd) == 0, "Unepxected pollfd struct layout");
+_Static_assert(offsetof(struct pollfd, events) == 4, "Unepxected pollfd struct layout");
+_Static_assert(offsetof(struct pollfd, revents) == 6, "Unepxected pollfd struct layout");
+_Static_assert(sizeof(struct pollfd) == 8, "Unepxected pollfd struct layout");
+
+EM_JS_MACROS(__externref_t, __maybe_poll_async, (intptr_t fds, int nfds, int timeout), {
+ if (!WebAssembly.promising) {
+ return null;
+ }
+ return (async function() {
+ try {
+ var nonzero = 0;
+ var promises = [];
+ for (var i = 0; i < nfds; i++) {
+ var pollfd = fds + POLLFD_SIZE * i;
+ var fd = HEAP32[(pollfd + POLLFD_FD)/4];
+ var events = HEAP16[(pollfd + POLLFD_EVENTS)/2];
+ var mask = POLLNVAL;
+ var stream = FS.getStream(fd);
+ if (stream) {
+ mask = POLLIN | POLLOUT;
+ if (stream.stream_ops.pollAsync) {
+ promises.push(stream.stream_ops.pollAsync(stream, timeout).then((mask) => {
+ mask &= events | POLLERR | POLLHUP;
+ HEAP16[(pollfd + POLLFD_REVENTS)/2] = mask;
+ if (mask) {
+ nonzero ++;
+ }
+ }));
+ } else if (stream.stream_ops.poll) {
+ var mask = stream.stream_ops.poll(stream, timeout);
+ mask &= events | POLLERR | POLLHUP;
+ HEAP16[(pollfd + POLLFD_REVENTS)/2] = mask;
+ if (mask) {
+ nonzero ++;
+ }
+ }
+ }
+ }
+ await Promise.all(promises);
+ return nonzero;
+ } catch(e) {
+ if (e?.name !== "ErrnoError") throw e;
+ return -e["errno"];
+ }
+ })();
+});
+
+// Bind original poll syscall to syscall_poll_orig().
+int syscall_poll_orig(intptr_t fds, int nfds, int timeout)
+ __attribute__((__import_module__("env"),
+ __import_name__("__syscall_poll"), __warn_unused_result__));
+
+int __syscall_poll(intptr_t fds, int nfds, int timeout) {
+ __externref_t p = __maybe_poll_async(fds, nfds, timeout);
+ if (__builtin_wasm_ref_is_null_extern(p)) {
+ return syscall_poll_orig(fds, nfds, timeout);
+ }
+ return __block_for_int(p);
+}
+
+#include <sys/ioctl.h>
+
+int syscall_ioctl_orig(int fd, int request, void* varargs)
+ __attribute__((__import_module__("env"),
+ __import_name__("__syscall_ioctl"), __warn_unused_result__));
+
+int __syscall_ioctl(int fd, int request, void* varargs) {
+ if (request == FIOCLEX || request == FIONCLEX) {
+ return 0;
+ }
+ if (request == FIONBIO) {
+ // Implement FIONBIO via fcntl.
+ // TODO: Upstream this.
+ int flags = fcntl(fd, F_GETFL, 0);
+ int nonblock = **((int**)varargs);
+ if (flags < 0) {
+ return errno;
+ }
+ if (nonblock) {
+ flags |= O_NONBLOCK;
+ } else {
+ flags &= (~O_NONBLOCK);
+ }
+ int res = fcntl(fd, F_SETFL, flags);
+ if (res < 0) {
+ return errno;
+ }
+ return res;
+ }
+ return syscall_ioctl_orig(fd, request, varargs);
}
# Syscalls that resulted in a segfault
ac_cv_func_utimensat=no
-ac_cv_header_sys_ioctl_h=no
# sockets are supported, but only AF_INET / AF_INET6 in non-blocking mode.
# Disable AF_UNIX and AF_PACKET support, see socketmodule.h.
from typing import Dict
# source directory
-SRCDIR = pathlib.Path(__file__).parents[4].absolute()
+SRCDIR = pathlib.Path(__file__).parents[3].absolute()
SRCDIR_LIB = SRCDIR / "Lib"
"_json": ["json/"],
"_multiprocessing": ["concurrent/futures/process.py", "multiprocessing/"],
"pyexpat": ["xml/", "xmlrpc/"],
- "readline": ["rlcompleter.py"],
"_sqlite3": ["sqlite3/"],
"_ssl": ["ssl.py"],
"_tkinter": ["idlelib/", "tkinter/", "turtle.py", "turtledemo/"],
--- /dev/null
+<!doctype html>
+<html>
+ <head>
+ <link
+ rel="stylesheet"
+ href="https://unpkg.com/xterm@4.18.0/css/xterm.css"
+ />
+ <style>
+ body {
+ background-color: #300a24;
+ }
+
+ .xterm-dom-renderer-owner-1 .xterm-fg-3 {
+ color: #c4a000 !important;
+ }
+
+ .xterm-dom-renderer-owner-1 .xterm-fg-6 {
+ color: #2aa1b3 !important;
+ }
+
+ .xterm-dom-renderer-owner-1 .xterm-fg-12 {
+ color: #1054a6 !important;
+ }
+
+ .xterm-dom-renderer-owner-1 .xterm-fg-13 {
+ color: #a347ba !important;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="terminal"></div>
+ <script type="module" src="src.mjs"></script>
+ </body>
+</html>
--- /dev/null
+// Much of this is adapted from here:
+// https://github.com/mame/xterm-pty/blob/main/emscripten-pty.js
+// Thanks to xterm-pty for making this possible!
+
+import createEmscriptenModule from "./python.mjs";
+import { openpty } from "https://unpkg.com/xterm-pty/index.mjs";
+import "https://unpkg.com/@xterm/xterm/lib/xterm.js";
+
+var term = new Terminal();
+term.open(document.getElementById("terminal"));
+const { master, slave: PTY } = openpty();
+term.loadAddon(master);
+globalThis.PTY = PTY;
+
+async function setupStdlib(Module) {
+ const versionInt = Module.HEAPU32[Module._Py_Version >>> 2];
+ const major = (versionInt >>> 24) & 0xff;
+ const minor = (versionInt >>> 16) & 0xff;
+ // Prevent complaints about not finding exec-prefix by making a lib-dynload directory
+ Module.FS.mkdirTree(`/lib/python${major}.${minor}/lib-dynload/`);
+ 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 },
+ );
+}
+
+const tty_ops = {
+ ioctl_tcgets: () => {
+ const termios = PTY.ioctl("TCGETS");
+ const data = {
+ c_iflag: termios.iflag,
+ c_oflag: termios.oflag,
+ c_cflag: termios.cflag,
+ c_lflag: termios.lflag,
+ c_cc: termios.cc,
+ };
+ return data;
+ },
+
+ ioctl_tcsets: (_tty, _optional_actions, data) => {
+ PTY.ioctl("TCSETS", {
+ iflag: data.c_iflag,
+ oflag: data.c_oflag,
+ cflag: data.c_cflag,
+ lflag: data.c_lflag,
+ cc: data.c_cc,
+ });
+ return 0;
+ },
+
+ ioctl_tiocgwinsz: () => PTY.ioctl("TIOCGWINSZ").reverse(),
+
+ get_char: () => {
+ throw new Error("Should not happen");
+ },
+ put_char: () => {
+ throw new Error("Should not happen");
+ },
+
+ fsync: () => {},
+};
+
+const POLLIN = 1;
+const POLLOUT = 4;
+
+const waitResult = {
+ READY: 0,
+ SIGNAL: 1,
+ TIMEOUT: 2,
+};
+
+function onReadable() {
+ var handle;
+ var promise = new Promise((resolve) => {
+ handle = PTY.onReadable(() => resolve(waitResult.READY));
+ });
+ return [promise, handle];
+}
+
+function onSignal() {
+ // TODO: signal handling
+ var handle = { dispose() {} };
+ var promise = new Promise((resolve) => {});
+ return [promise, handle];
+}
+
+function onTimeout(timeout) {
+ var id;
+ var promise = new Promise((resolve) => {
+ if (timeout > 0) {
+ id = setTimeout(resolve, timeout, waitResult.TIMEOUT);
+ }
+ });
+ var handle = {
+ dispose() {
+ if (id) {
+ clearTimeout(id);
+ }
+ },
+ };
+ return [promise, handle];
+}
+
+async function waitForReadable(timeout) {
+ let p1, p2, p3;
+ let h1, h2, h3;
+ try {
+ [p1, h1] = onReadable();
+ [p2, h2] = onTimeout(timeout);
+ [p3, h3] = onSignal();
+ return await Promise.race([p1, p2, p3]);
+ } finally {
+ h1.dispose();
+ h2.dispose();
+ h3.dispose();
+ }
+}
+
+const FIONREAD = 0x541b;
+
+const tty_stream_ops = {
+ async readAsync(stream, buffer, offset, length, pos /* ignored */) {
+ let readBytes = PTY.read(length);
+ if (length && !readBytes.length) {
+ const status = await waitForReadable(-1);
+ if (status === waitResult.READY) {
+ readBytes = PTY.read(length);
+ } else {
+ throw new Error("Not implemented");
+ }
+ }
+ buffer.set(readBytes, offset);
+ return readBytes.length;
+ },
+
+ write: (stream, buffer, offset, length) => {
+ // Note: default `buffer` is for some reason `HEAP8` (signed), while we want unsigned `HEAPU8`.
+ buffer = new Uint8Array(
+ buffer.buffer,
+ buffer.byteOffset,
+ buffer.byteLength,
+ );
+ const toWrite = Array.from(buffer.subarray(offset, offset + length));
+ PTY.write(toWrite);
+ return length;
+ },
+
+ async pollAsync(stream, timeout) {
+ if (!PTY.readable && timeout) {
+ await waitForReadable(timeout);
+ }
+ return (PTY.readable ? POLLIN : 0) | (PTY.writable ? POLLOUT : 0);
+ },
+ ioctl(stream, request, varargs) {
+ if (request === FIONREAD) {
+ const res = PTY.fromLdiscToUpperBuffer.length;
+ Module.HEAPU32[varargs / 4] = res;
+ return 0;
+ }
+ throw new Error("Unimplemented ioctl request");
+ },
+};
+
+async function setupStdio(Module) {
+ Object.assign(Module.TTY.default_tty_ops, tty_ops);
+ Object.assign(Module.TTY.stream_ops, tty_stream_ops);
+}
+
+const emscriptenSettings = {
+ async preRun(Module) {
+ Module.addRunDependency("pre-run");
+ Module.ENV.TERM = "xterm-256color";
+ // Uncomment next line to turn on tracing (messages go to browser console).
+ // Module.ENV.PYREPL_TRACE = "1";
+
+ // Leak module so we can try to show traceback if we crash on startup
+ globalThis.Module = Module;
+ await Promise.all([setupStdlib(Module), setupStdio(Module)]);
+ Module.removeRunDependency("pre-run");
+ },
+};
+
+try {
+ await createEmscriptenModule(emscriptenSettings);
+} catch (e) {
+ // Show JavaScript exception and traceback
+ console.warn(e);
+ // Show Python exception and traceback
+ Module.__Py_DumpTraceback(2, Module._PyGILState_GetThisThreadState());
+ process.exit(1);
+}
as_fn_append LDFLAGS_NODIST " -sWASM_BIGINT"
as_fn_append LINKFORSHARED " -sFORCE_FILESYSTEM -lidbfs.js -lnodefs.js -lproxyfs.js -lworkerfs.js"
- as_fn_append LINKFORSHARED " -sEXPORTED_RUNTIME_METHODS=FS,callMain,ENV,HEAPU32"
+ as_fn_append LINKFORSHARED " -sEXPORTED_RUNTIME_METHODS=FS,callMain,ENV,HEAPU32,TTY"
as_fn_append LINKFORSHARED " -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,__PyEM_EMSCRIPTEN_COUNT_ARGS_OFFSET,_PyGILState_GetThisThreadState,__Py_DumpTraceback"
as_fn_append LINKFORSHARED " -sSTACK_SIZE=5MB"
as_fn_append LINKFORSHARED " -sTEXTDECODER=2"
- py_cv_module_fcntl=n/a
py_cv_module_readline=n/a
- py_cv_module_termios=n/a
py_cv_module_=n/a
;; #(
dnl Include file system support
AS_VAR_APPEND([LINKFORSHARED], [" -sFORCE_FILESYSTEM -lidbfs.js -lnodefs.js -lproxyfs.js -lworkerfs.js"])
- AS_VAR_APPEND([LINKFORSHARED], [" -sEXPORTED_RUNTIME_METHODS=FS,callMain,ENV,HEAPU32"])
+ AS_VAR_APPEND([LINKFORSHARED], [" -sEXPORTED_RUNTIME_METHODS=FS,callMain,ENV,HEAPU32,TTY"])
AS_VAR_APPEND([LINKFORSHARED], [" -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,__PyEM_EMSCRIPTEN_COUNT_ARGS_OFFSET,_PyGILState_GetThisThreadState,__Py_DumpTraceback"])
AS_VAR_APPEND([LINKFORSHARED], [" -sSTACK_SIZE=5MB"])
dnl Avoid bugs in JS fallback string decoding path
)
dnl fcntl, readline, and termios are not particularly useful in browsers.
PY_STDLIB_MOD_SET_NA(
- [fcntl],
[readline],
- [termios],
)
],
[WASI], [