[Python-checkins] gh-84461: Add sys._emscripten_info, improve docs and build (gh-91781)

tiran webhook-mailer at python.org
Sat Apr 23 03:52:26 EDT 2022


https://github.com/python/cpython/commit/9b5ca5405e5a2786b5b3acc0de578f80f8dc9e36
commit: 9b5ca5405e5a2786b5b3acc0de578f80f8dc9e36
branch: main
author: Christian Heimes <christian at python.org>
committer: tiran <christian at python.org>
date: 2022-04-23T09:52:16+02:00
summary:

gh-84461: Add sys._emscripten_info, improve docs and build (gh-91781)

files:
A Misc/NEWS.d/next/Library/2022-04-21-11-57-23.gh-issue-84461.S7dbt4.rst
M Doc/library/sys.rst
M Lib/test/support/threading_helper.py
M Lib/test/test_sys.py
M Python/sysmodule.c
M Tools/wasm/README.md
M configure
M configure.ac

diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
index 2a8b532b592e8..f43344887e421 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 7b636f0ccf097..26cbc6f4d2439 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 b70871ff551d9..bbf01b6a39011 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -629,6 +629,14 @@ def test_thread_info(self):
         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 0000000000000..08448d7d7ce21
--- /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 ac44b803b23f5..d5a62fc12b668 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 @@ make_impl_info(PyObject *version_info)
     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 fa99703acfc5b..9d3680a53d28e 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 94adc6fdf015e..25e767be82fa4 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 9b60b98d1434e..82211f4ee1d68 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])]



More information about the Python-checkins mailing list