diff options
-rw-r--r-- | Doc/library/sys.rst | 29 | ||||
-rw-r--r-- | Lib/test/support/threading_helper.py | 7 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 8 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2022-04-21-11-57-23.gh-issue-84461.S7dbt4.rst | 2 | ||||
-rw-r--r-- | Python/sysmodule.c | 118 | ||||
-rw-r--r-- | Tools/wasm/README.md | 65 | ||||
-rwxr-xr-x | configure | 76 | ||||
-rw-r--r-- | configure.ac | 81 |
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 @@ -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])] |