summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/sys.rst29
-rw-r--r--Lib/test/support/threading_helper.py7
-rw-r--r--Lib/test/test_sys.py8
-rw-r--r--Misc/NEWS.d/next/Library/2022-04-21-11-57-23.gh-issue-84461.S7dbt4.rst2
-rw-r--r--Python/sysmodule.c118
-rw-r--r--Tools/wasm/README.md65
-rwxr-xr-xconfigure76
-rw-r--r--configure.ac81
8 files changed, 263 insertions, 123 deletions
diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
index 2a8b532..f433448 100644
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -314,6 +314,35 @@ always available.
yourself to control bytecode file generation.
+.. data:: _emscripten_info
+
+ A :term:`named tuple` holding information about the environment on the
+ *wasm32-emscripten* platform. The named tuple is provisional and may change
+ in the future.
+
+ .. tabularcolumns:: |l|L|
+
+ +-----------------------------+----------------------------------------------+
+ | Attribute | Explanation |
+ +=============================+==============================================+
+ | :const:`emscripten_version` | Emscripten version as tuple of ints |
+ | | (major, minor, micro), e.g. ``(3, 1, 8)``. |
+ +-----------------------------+----------------------------------------------+
+ | :const:`runtime` | Runtime string, e.g. browser user agent, |
+ | | ``'Node.js v14.18.2'``, or ``'UNKNOWN'``. |
+ +-----------------------------+----------------------------------------------+
+ | :const:`pthreads` | ``True`` if Python is compiled with |
+ | | Emscripten pthreads support. |
+ +-----------------------------+----------------------------------------------+
+ | :const:`shared_memory` | ``True`` if Python is compiled with shared |
+ | | memory support. |
+ +-----------------------------+----------------------------------------------+
+
+ .. availability:: WebAssembly Emscripten platform (*wasm32-emscripten*).
+
+ .. versionadded:: 3.11
+
+
.. data:: pycache_prefix
If this is set (not ``None``), Python will write bytecode-cache ``.pyc``
diff --git a/Lib/test/support/threading_helper.py b/Lib/test/support/threading_helper.py
index 7b636f0..26cbc6f 100644
--- a/Lib/test/support/threading_helper.py
+++ b/Lib/test/support/threading_helper.py
@@ -222,12 +222,7 @@ def _can_start_thread() -> bool:
support (-s USE_PTHREADS / __EMSCRIPTEN_PTHREADS__).
"""
if sys.platform == "emscripten":
- try:
- _thread.start_new_thread(lambda: None, ())
- except RuntimeError:
- return False
- else:
- return True
+ return sys._emscripten_info.pthreads
elif sys.platform == "wasi":
return False
else:
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index b70871f..bbf01b6 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -629,6 +629,14 @@ class SysModuleTest(unittest.TestCase):
self.assertIn(info.name, ('nt', 'pthread', 'solaris', None))
self.assertIn(info.lock, ('semaphore', 'mutex+cond', None))
+ @unittest.skipUnless(support.is_emscripten, "only available on Emscripten")
+ def test_emscripten_info(self):
+ self.assertEqual(len(sys._emscripten_info), 4)
+ self.assertIsInstance(sys._emscripten_info.emscripten_version, tuple)
+ self.assertIsInstance(sys._emscripten_info.runtime, (str, type(None)))
+ self.assertIsInstance(sys._emscripten_info.pthreads, bool)
+ self.assertIsInstance(sys._emscripten_info.shared_memory, bool)
+
def test_43581(self):
# Can't use sys.stdout, as this is a StringIO object when
# the test runs under regrtest.
diff --git a/Misc/NEWS.d/next/Library/2022-04-21-11-57-23.gh-issue-84461.S7dbt4.rst b/Misc/NEWS.d/next/Library/2022-04-21-11-57-23.gh-issue-84461.S7dbt4.rst
new file mode 100644
index 0000000..08448d7
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-04-21-11-57-23.gh-issue-84461.S7dbt4.rst
@@ -0,0 +1,2 @@
+Add provisional :data:`sys._emscripten_info` named tuple with build-time and
+run-time information about Emscripten platform.
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index ac44b80..d5a62fc 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -48,6 +48,10 @@ extern void *PyWin_DLLhModule;
extern const char *PyWin_DLLVersionString;
#endif
+#ifdef __EMSCRIPTEN__
+#include <emscripten.h>
+#endif
+
/*[clinic input]
module sys
[clinic start generated code]*/
@@ -2686,6 +2690,107 @@ error:
return NULL;
}
+#ifdef __EMSCRIPTEN__
+
+PyDoc_STRVAR(emscripten_info__doc__,
+"sys._emscripten_info\n\
+\n\
+WebAssembly Emscripten platform information.");
+
+static PyTypeObject *EmscriptenInfoType;
+
+static PyStructSequence_Field emscripten_info_fields[] = {
+ {"emscripten_version", "Emscripten version (major, minor, micro)"},
+ {"runtime", "Runtime (Node.JS version, browser user agent)"},
+ {"pthreads", "pthread support"},
+ {"shared_memory", "shared memory support"},
+ {0}
+};
+
+static PyStructSequence_Desc emscripten_info_desc = {
+ "sys._emscripten_info", /* name */
+ emscripten_info__doc__ , /* doc */
+ emscripten_info_fields, /* fields */
+ 4
+};
+
+EM_JS(char *, _Py_emscripten_runtime, (void), {
+ var info;
+ if (typeof navigator == 'object') {
+ info = navigator.userAgent;
+ } else if (typeof process == 'object') {
+ info = "Node.js ".concat(process.version)
+ } else {
+ info = "UNKNOWN"
+ }
+ var len = lengthBytesUTF8(info) + 1;
+ var res = _malloc(len);
+ stringToUTF8(info, res, len);
+ return res;
+});
+
+static PyObject *
+make_emscripten_info(void)
+{
+ PyObject *emscripten_info = NULL;
+ PyObject *version = NULL;
+ char *ua;
+ int pos = 0;
+
+ emscripten_info = PyStructSequence_New(EmscriptenInfoType);
+ if (emscripten_info == NULL) {
+ return NULL;
+ }
+
+ version = Py_BuildValue("(iii)",
+ __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__);
+ if (version == NULL) {
+ goto error;
+ }
+ PyStructSequence_SET_ITEM(emscripten_info, pos++, version);
+
+ ua = _Py_emscripten_runtime();
+ if (ua != NULL) {
+ PyObject *oua = PyUnicode_DecodeUTF8(ua, strlen(ua), "strict");
+ free(ua);
+ if (oua == NULL) {
+ goto error;
+ }
+ PyStructSequence_SET_ITEM(emscripten_info, pos++, oua);
+ } else {
+ Py_INCREF(Py_None);
+ PyStructSequence_SET_ITEM(emscripten_info, pos++, Py_None);
+ }
+
+#define SetBoolItem(flag) \
+ PyStructSequence_SET_ITEM(emscripten_info, pos++, PyBool_FromLong(flag))
+
+#ifdef __EMSCRIPTEN_PTHREADS__
+ SetBoolItem(1);
+#else
+ SetBoolItem(0);
+#endif
+
+#ifdef __EMSCRIPTEN_SHARED_MEMORY__
+ SetBoolItem(1);
+#else
+ SetBoolItem(0);
+#endif
+
+#undef SetBoolItem
+
+ if (PyErr_Occurred()) {
+ goto error;
+ }
+ return emscripten_info;
+
+ error:
+ Py_CLEAR(emscripten_info);
+ return NULL;
+}
+
+#endif // __EMSCRIPTEN__
+
static struct PyModuleDef sysmodule = {
PyModuleDef_HEAD_INIT,
"sys",
@@ -2821,6 +2926,16 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict)
}
}
+#ifdef __EMSCRIPTEN__
+ if (EmscriptenInfoType == NULL) {
+ EmscriptenInfoType = PyStructSequence_NewType(&emscripten_info_desc);
+ if (EmscriptenInfoType == NULL) {
+ goto type_init_failed;
+ }
+ }
+ SET_SYS("_emscripten_info", make_emscripten_info());
+#endif
+
/* adding sys.path_hooks and sys.path_importer_cache */
SET_SYS("meta_path", PyList_New(0));
SET_SYS("path_importer_cache", PyDict_New());
@@ -3066,6 +3181,9 @@ _PySys_Fini(PyInterpreterState *interp)
#endif
_PyStructSequence_FiniType(&Hash_InfoType);
_PyStructSequence_FiniType(&AsyncGenHooksType);
+#ifdef __EMSCRIPTEN__
+ Py_CLEAR(EmscriptenInfoType);
+#endif
}
}
diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md
index fa99703..9d3680a 100644
--- a/Tools/wasm/README.md
+++ b/Tools/wasm/README.md
@@ -4,11 +4,17 @@
This directory contains configuration and helpers to facilitate cross
compilation of CPython to WebAssembly (WASM). For now we support
-*wasm32-emscripten* builds for modern browser and for *Node.js*. It's not
-possible to build for *wasm32-wasi* out-of-the-box yet.
+*wasm32-emscripten* builds for modern browser and for *Node.js*. WASI
+(*wasm32-wasi*) is work-in-progress
## wasm32-emscripten build
+For now the build system has two target flavors. The ``Emscripten/browser``
+target (``--with-emscripten-target=browser``) is optimized for browsers.
+It comes with a reduced and preloaded stdlib without tests and threading
+support. The ``Emscripten/node`` target has threading enabled and can
+access the file system directly.
+
Cross compiling to the wasm32-emscripten platform needs the
[Emscripten](https://emscripten.org/) SDK and a build Python interpreter.
Emscripten 3.1.8 or newer are recommended. All commands below are relative
@@ -76,7 +82,7 @@ and header files with debug builds.
### Cross compile to wasm32-emscripten for node
-```
+```shell
mkdir -p builddir/emscripten-node
pushd builddir/emscripten-node
@@ -91,7 +97,7 @@ emmake make -j$(nproc)
popd
```
-```
+```shell
node --experimental-wasm-threads --experimental-wasm-bulk-memory builddir/emscripten-node/python.js
```
@@ -150,9 +156,9 @@ functions.
- Most stdlib modules with a dependency on external libraries are missing,
e.g. ``ctypes``, ``readline``, ``sqlite3``, ``ssl``, and more.
- Shared extension modules are not implemented yet. All extension modules
- are statically linked into the main binary.
- The experimental configure option ``--enable-wasm-dynamic-linking`` enables
- dynamic extensions.
+ are statically linked into the main binary. The experimental configure
+ option ``--enable-wasm-dynamic-linking`` enables dynamic extensions
+ supports. It's currently known to crash in combination with threading.
- glibc extensions for date and time formatting are not available.
- ``locales`` module is affected by musl libc issues,
[bpo-46390](https://bugs.python.org/issue46390).
@@ -167,8 +173,10 @@ functions.
distutils, multiprocessing, dbm, tests and similar modules
are not shipped. All other modules are bundled as pre-compiled
``pyc`` files.
-- Threading is not supported.
+- Threading is disabled.
- In-memory file system (MEMFS) is not persistent and limited.
+- Test modules are disabled by default. Use ``--enable-test-modules`` build
+ test modules like ``_testcapi``.
## wasm32-emscripten in node
@@ -205,11 +213,17 @@ AddType application/wasm wasm
</IfModule>
```
+# WASI (wasm32-wasi)
+
+WASI builds require [WASI SDK](https://github.com/WebAssembly/wasi-sdk) and
+currently [wasix](https://github.com/singlestore-labs/wasix) for POSIX
+compatibility stubs.
+
# Detect WebAssembly builds
## Python code
-```# python
+```python
import os, sys
if sys.platform == "emscripten":
@@ -222,7 +236,36 @@ if os.name == "posix":
# Windows does not provide os.uname().
machine = os.uname().machine
if machine.startswith("wasm"):
- # WebAssembly (wasm32 or wasm64)
+ # WebAssembly (wasm32, wasm64 in the future)
+```
+
+```python
+>>> import os, sys
+>>> os.uname()
+posix.uname_result(sysname='Emscripten', nodename='emscripten', release='1.0', version='#1', machine='wasm32')
+>>> os.name
+'posix'
+>>> sys.platform
+'emscripten'
+>>> sys._emscripten_info
+sys._emscripten_info(
+ emscripten_version=(3, 1, 8),
+ runtime='Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/99.0',
+ pthreads=False,
+ shared_memory=False
+)
+>>> sys._emscripten_info
+sys._emscripten_info(emscripten_version=(3, 1, 8), runtime='Node.js v14.18.2', pthreads=True, shared_memory=True)
+```
+
+```python
+>>> import os, sys
+>>> os.uname()
+posix.uname_result(sysname='wasi', nodename='(none)', release='0.0.0', version='0.0.0', machine='wasm32')
+>>> os.name
+'posix'
+>>> sys.platform
+'wasi'
```
## C code
@@ -231,7 +274,7 @@ Emscripten SDK and WASI SDK define several built-in macros. You can dump a
full list of built-ins with ``emcc -dM -E - < /dev/null`` and
``/path/to/wasi-sdk/bin/clang -dM -E - < /dev/null``.
-```# C
+```C
#ifdef __EMSCRIPTEN__
// Python on Emscripten
#endif
diff --git a/configure b/configure
index 94adc6f..25e767b 100755
--- a/configure
+++ b/configure
@@ -10513,6 +10513,9 @@ then
BLDSHARED="$LDSHARED"
fi
;;
+ Emscripten|WASI)
+ LDSHARED='$(CC) -shared'
+ LDCXXSHARED='$(CXX) -shared';;
Linux*|GNU*|QNX*|VxWorks*|Haiku*)
LDSHARED='$(CC) -shared'
LDCXXSHARED='$(CXX) -shared';;
@@ -22374,14 +22377,14 @@ $as_echo "$TEST_MODULES" >&6; }
# stdlib not available
-case $ac_sys_system/$ac_sys_emscripten_target in #(
- AIX/*) :
+case $ac_sys_system in #(
+ AIX) :
py_cv_module__scproxy=n/a
py_cv_module_spwd=n/a
;; #(
- VxWorks*/*) :
+ VxWorks*) :
py_cv_module__scproxy=n/a
@@ -22389,35 +22392,34 @@ case $ac_sys_system/$ac_sys_emscripten_target in #(
py_cv_module_termios=n/a
py_cv_module_grp=n/a
;; #(
- Darwin/*) :
+ Darwin) :
py_cv_module_ossaudiodev=n/a
py_cv_module_spwd=n/a
;; #(
- CYGWIN*/*) :
+ CYGWIN*) :
py_cv_module__scproxy=n/a
py_cv_module_nis=n/a
;; #(
- QNX*/*) :
+ QNX*) :
py_cv_module__scproxy=n/a
py_cv_module_nis=n/a
;; #(
- FreeBSD*/*) :
+ FreeBSD*) :
py_cv_module__scproxy=n/a
py_cv_module_spwd=n/a
;; #(
- Emscripten/browser*) :
+ Emscripten|WASI) :
- py_cv_module__ctypes=n/a
py_cv_module__curses=n/a
py_cv_module__curses_panel=n/a
py_cv_module__dbm=n/a
@@ -22428,64 +22430,34 @@ case $ac_sys_system/$ac_sys_emscripten_target in #(
py_cv_module__scproxy=n/a
py_cv_module__tkinter=n/a
py_cv_module__xxsubinterpreters=n/a
- py_cv_module_fcntl=n/a
py_cv_module_grp=n/a
py_cv_module_nis=n/a
py_cv_module_ossaudiodev=n/a
py_cv_module_pwd=n/a
- py_cv_module_resource=n/a
- py_cv_module_readline=n/a
py_cv_module_spwd=n/a
py_cv_module_syslog=n/a
- py_cv_module_termios=n/a
py_cv_module_=n/a
- ;; #(
- Emscripten/node*) :
+ case $ac_sys_system/$ac_sys_emscripten_target in #(
+ Emscripten/browser*) :
- py_cv_module__ctypes=n/a
- py_cv_module__curses=n/a
- py_cv_module__curses_panel=n/a
- py_cv_module__dbm=n/a
- py_cv_module__gdbm=n/a
- py_cv_module__multiprocessing=n/a
- py_cv_module__posixshmem=n/a
- py_cv_module__posixsubprocess=n/a
- py_cv_module__scproxy=n/a
- py_cv_module__tkinter=n/a
- py_cv_module__xxsubinterpreters=n/a
- py_cv_module_grp=n/a
- py_cv_module_nis=n/a
- py_cv_module_ossaudiodev=n/a
- py_cv_module_pwd=n/a
- py_cv_module_spwd=n/a
- py_cv_module_syslog=n/a
+ py_cv_module_fcntl=n/a
+ py_cv_module_resource=n/a
+ py_cv_module_readline=n/a
+ py_cv_module_termios=n/a
py_cv_module_=n/a
- ;; #(
+ ;; #(
+ Emscripten/node*) :
+ ;; #(
WASI/*) :
-
-
- py_cv_module__ctypes=n/a
- py_cv_module__ctypes_test=n/a
- py_cv_module__curses=n/a
- py_cv_module__curses_panel=n/a
- py_cv_module__dbm=n/a
- py_cv_module__gdbm=n/a
- py_cv_module__scproxy=n/a
- py_cv_module__tkinter=n/a
- py_cv_module__xxsubinterpreters=n/a
- py_cv_module_grp=n/a
- py_cv_module_nis=n/a
- py_cv_module_ossaudiodev=n/a
- py_cv_module_pwd=n/a
- py_cv_module_spwd=n/a
- py_cv_module_syslog=n/a
- py_cv_module_=n/a
-
+ ;; #(
+ *) :
+ ;;
+esac
;; #(
*) :
diff --git a/configure.ac b/configure.ac
index 9b60b98..82211f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2954,6 +2954,9 @@ then
BLDSHARED="$LDSHARED"
fi
;;
+ Emscripten|WASI)
+ LDSHARED='$(CC) -shared'
+ LDCXXSHARED='$(CXX) -shared';;
Linux*|GNU*|QNX*|VxWorks*|Haiku*)
LDSHARED='$(CC) -shared'
LDCXXSHARED='$(CXX) -shared';;
@@ -6585,43 +6588,19 @@ AC_DEFUN([PY_STDLIB_MOD_SET_NA], [
dnl Modules that are not available on some platforms
dnl AIX has shadow passwords, but access is not via getspent()
dnl VxWorks does not provide crypt() function
-AS_CASE([$ac_sys_system/$ac_sys_emscripten_target],
- [AIX/*], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])],
- [VxWorks*/*], [PY_STDLIB_MOD_SET_NA([_scproxy], [_crypt], [termios], [grp])],
- [Darwin/*], [PY_STDLIB_MOD_SET_NA([ossaudiodev], [spwd])],
- [CYGWIN*/*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])],
- [QNX*/*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])],
- [FreeBSD*/*], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])],
- [Emscripten/browser*], [
- PY_STDLIB_MOD_SET_NA(
- [_ctypes],
- [_curses],
- [_curses_panel],
- [_dbm],
- [_gdbm],
- [_multiprocessing],
- [_posixshmem],
- [_posixsubprocess],
- [_scproxy],
- [_tkinter],
- [_xxsubinterpreters],
- [fcntl],
- [grp],
- [nis],
- [ossaudiodev],
- [pwd],
- [resource],
- [readline],
- [spwd],
- [syslog],
- [termios],
- )
- ],
- dnl Some modules like _posixsubprocess do not work. We build them anyway
- dnl so imports in tests do not fail.
- [Emscripten/node*], [
+AS_CASE([$ac_sys_system],
+ [AIX], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])],
+ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [_crypt], [termios], [grp])],
+ [Darwin], [PY_STDLIB_MOD_SET_NA([ossaudiodev], [spwd])],
+ [CYGWIN*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])],
+ [QNX*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])],
+ [FreeBSD*], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])],
+ [Emscripten|WASI], [
+ dnl subprocess and multiprocessing are not supported (no fork syscall).
+ dnl curses and tkinter user interface are not available.
+ dnl dbm and gdbm aren't available, too.
+ dnl Emscripten and WASI provide only stubs for pwd, grp APIs.
PY_STDLIB_MOD_SET_NA(
- [_ctypes],
[_curses],
[_curses_panel],
[_dbm],
@@ -6639,24 +6618,18 @@ AS_CASE([$ac_sys_system/$ac_sys_emscripten_target],
[spwd],
[syslog],
)
- ],
- [WASI/*], [
- PY_STDLIB_MOD_SET_NA(
- [_ctypes],
- [_ctypes_test],
- [_curses],
- [_curses_panel],
- [_dbm],
- [_gdbm],
- [_scproxy],
- [_tkinter],
- [_xxsubinterpreters],
- [grp],
- [nis],
- [ossaudiodev],
- [pwd],
- [spwd],
- [syslog],
+ AS_CASE([$ac_sys_system/$ac_sys_emscripten_target],
+ [Emscripten/browser*], [
+ dnl These modules are not particularly useful in browsers.
+ PY_STDLIB_MOD_SET_NA(
+ [fcntl],
+ [resource],
+ [readline],
+ [termios],
+ )
+ ],
+ [Emscripten/node*], [],
+ [WASI/*], []
)
],
[PY_STDLIB_MOD_SET_NA([_scproxy])]