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

This commit is contained in:
Christian Heimes 2022-04-23 10:52:16 +03:00 committed by GitHub
parent 0daa99f68b
commit 9b5ca5405e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 279 additions and 139 deletions

View file

@ -314,6 +314,35 @@ always available.
yourself to control bytecode file generation. 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 .. data:: pycache_prefix
If this is set (not ``None``), Python will write bytecode-cache ``.pyc`` If this is set (not ``None``), Python will write bytecode-cache ``.pyc``

View file

@ -222,12 +222,7 @@ def _can_start_thread() -> bool:
support (-s USE_PTHREADS / __EMSCRIPTEN_PTHREADS__). support (-s USE_PTHREADS / __EMSCRIPTEN_PTHREADS__).
""" """
if sys.platform == "emscripten": if sys.platform == "emscripten":
try: return sys._emscripten_info.pthreads
_thread.start_new_thread(lambda: None, ())
except RuntimeError:
return False
else:
return True
elif sys.platform == "wasi": elif sys.platform == "wasi":
return False return False
else: else:

View file

@ -629,6 +629,14 @@ class SysModuleTest(unittest.TestCase):
self.assertIn(info.name, ('nt', 'pthread', 'solaris', None)) self.assertIn(info.name, ('nt', 'pthread', 'solaris', None))
self.assertIn(info.lock, ('semaphore', 'mutex+cond', 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): def test_43581(self):
# Can't use sys.stdout, as this is a StringIO object when # Can't use sys.stdout, as this is a StringIO object when
# the test runs under regrtest. # the test runs under regrtest.

View file

@ -0,0 +1,2 @@
Add provisional :data:`sys._emscripten_info` named tuple with build-time and
run-time information about Emscripten platform.

View file

@ -48,6 +48,10 @@ extern void *PyWin_DLLhModule;
extern const char *PyWin_DLLVersionString; extern const char *PyWin_DLLVersionString;
#endif #endif
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
/*[clinic input] /*[clinic input]
module sys module sys
[clinic start generated code]*/ [clinic start generated code]*/
@ -2686,6 +2690,107 @@ error:
return NULL; 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 = { static struct PyModuleDef sysmodule = {
PyModuleDef_HEAD_INIT, PyModuleDef_HEAD_INIT,
"sys", "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 */ /* adding sys.path_hooks and sys.path_importer_cache */
SET_SYS("meta_path", PyList_New(0)); SET_SYS("meta_path", PyList_New(0));
SET_SYS("path_importer_cache", PyDict_New()); SET_SYS("path_importer_cache", PyDict_New());
@ -3066,6 +3181,9 @@ _PySys_Fini(PyInterpreterState *interp)
#endif #endif
_PyStructSequence_FiniType(&Hash_InfoType); _PyStructSequence_FiniType(&Hash_InfoType);
_PyStructSequence_FiniType(&AsyncGenHooksType); _PyStructSequence_FiniType(&AsyncGenHooksType);
#ifdef __EMSCRIPTEN__
Py_CLEAR(EmscriptenInfoType);
#endif
} }
} }

View file

@ -4,11 +4,17 @@
This directory contains configuration and helpers to facilitate cross This directory contains configuration and helpers to facilitate cross
compilation of CPython to WebAssembly (WASM). For now we support compilation of CPython to WebAssembly (WASM). For now we support
*wasm32-emscripten* builds for modern browser and for *Node.js*. It's not *wasm32-emscripten* builds for modern browser and for *Node.js*. WASI
possible to build for *wasm32-wasi* out-of-the-box yet. (*wasm32-wasi*) is work-in-progress
## wasm32-emscripten build ## 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 Cross compiling to the wasm32-emscripten platform needs the
[Emscripten](https://emscripten.org/) SDK and a build Python interpreter. [Emscripten](https://emscripten.org/) SDK and a build Python interpreter.
Emscripten 3.1.8 or newer are recommended. All commands below are relative 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 ### Cross compile to wasm32-emscripten for node
``` ```shell
mkdir -p builddir/emscripten-node mkdir -p builddir/emscripten-node
pushd builddir/emscripten-node pushd builddir/emscripten-node
@ -91,7 +97,7 @@ emmake make -j$(nproc)
popd popd
``` ```
``` ```shell
node --experimental-wasm-threads --experimental-wasm-bulk-memory builddir/emscripten-node/python.js 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, - Most stdlib modules with a dependency on external libraries are missing,
e.g. ``ctypes``, ``readline``, ``sqlite3``, ``ssl``, and more. e.g. ``ctypes``, ``readline``, ``sqlite3``, ``ssl``, and more.
- Shared extension modules are not implemented yet. All extension modules - Shared extension modules are not implemented yet. All extension modules
are statically linked into the main binary. are statically linked into the main binary. The experimental configure
The experimental configure option ``--enable-wasm-dynamic-linking`` enables option ``--enable-wasm-dynamic-linking`` enables dynamic extensions
dynamic extensions. supports. It's currently known to crash in combination with threading.
- glibc extensions for date and time formatting are not available. - glibc extensions for date and time formatting are not available.
- ``locales`` module is affected by musl libc issues, - ``locales`` module is affected by musl libc issues,
[bpo-46390](https://bugs.python.org/issue46390). [bpo-46390](https://bugs.python.org/issue46390).
@ -167,8 +173,10 @@ functions.
distutils, multiprocessing, dbm, tests and similar modules distutils, multiprocessing, dbm, tests and similar modules
are not shipped. All other modules are bundled as pre-compiled are not shipped. All other modules are bundled as pre-compiled
``pyc`` files. ``pyc`` files.
- Threading is not supported. - Threading is disabled.
- In-memory file system (MEMFS) is not persistent and limited. - 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 ## wasm32-emscripten in node
@ -205,11 +213,17 @@ AddType application/wasm wasm
</IfModule> </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 # Detect WebAssembly builds
## Python code ## Python code
```# python ```python
import os, sys import os, sys
if sys.platform == "emscripten": if sys.platform == "emscripten":
@ -222,7 +236,36 @@ if os.name == "posix":
# Windows does not provide os.uname(). # Windows does not provide os.uname().
machine = os.uname().machine machine = os.uname().machine
if machine.startswith("wasm"): 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 ## 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 full list of built-ins with ``emcc -dM -E - < /dev/null`` and
``/path/to/wasi-sdk/bin/clang -dM -E - < /dev/null``. ``/path/to/wasi-sdk/bin/clang -dM -E - < /dev/null``.
```# C ```C
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
// Python on Emscripten // Python on Emscripten
#endif #endif

78
configure generated vendored
View file

@ -10513,6 +10513,9 @@ then
BLDSHARED="$LDSHARED" BLDSHARED="$LDSHARED"
fi fi
;; ;;
Emscripten|WASI)
LDSHARED='$(CC) -shared'
LDCXXSHARED='$(CXX) -shared';;
Linux*|GNU*|QNX*|VxWorks*|Haiku*) Linux*|GNU*|QNX*|VxWorks*|Haiku*)
LDSHARED='$(CC) -shared' LDSHARED='$(CC) -shared'
LDCXXSHARED='$(CXX) -shared';; LDCXXSHARED='$(CXX) -shared';;
@ -22374,14 +22377,14 @@ $as_echo "$TEST_MODULES" >&6; }
# stdlib not available # stdlib not available
case $ac_sys_system/$ac_sys_emscripten_target in #( case $ac_sys_system in #(
AIX/*) : AIX) :
py_cv_module__scproxy=n/a py_cv_module__scproxy=n/a
py_cv_module_spwd=n/a py_cv_module_spwd=n/a
;; #( ;; #(
VxWorks*/*) : VxWorks*) :
py_cv_module__scproxy=n/a 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_termios=n/a
py_cv_module_grp=n/a py_cv_module_grp=n/a
;; #( ;; #(
Darwin/*) : Darwin) :
py_cv_module_ossaudiodev=n/a py_cv_module_ossaudiodev=n/a
py_cv_module_spwd=n/a py_cv_module_spwd=n/a
;; #( ;; #(
CYGWIN*/*) : CYGWIN*) :
py_cv_module__scproxy=n/a py_cv_module__scproxy=n/a
py_cv_module_nis=n/a py_cv_module_nis=n/a
;; #( ;; #(
QNX*/*) : QNX*) :
py_cv_module__scproxy=n/a py_cv_module__scproxy=n/a
py_cv_module_nis=n/a py_cv_module_nis=n/a
;; #( ;; #(
FreeBSD*/*) : FreeBSD*) :
py_cv_module__scproxy=n/a py_cv_module__scproxy=n/a
py_cv_module_spwd=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=n/a
py_cv_module__curses_panel=n/a py_cv_module__curses_panel=n/a
py_cv_module__dbm=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__scproxy=n/a
py_cv_module__tkinter=n/a py_cv_module__tkinter=n/a
py_cv_module__xxsubinterpreters=n/a py_cv_module__xxsubinterpreters=n/a
py_cv_module_fcntl=n/a
py_cv_module_grp=n/a py_cv_module_grp=n/a
py_cv_module_nis=n/a py_cv_module_nis=n/a
py_cv_module_ossaudiodev=n/a py_cv_module_ossaudiodev=n/a
py_cv_module_pwd=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_spwd=n/a
py_cv_module_syslog=n/a py_cv_module_syslog=n/a
py_cv_module_=n/a
case $ac_sys_system/$ac_sys_emscripten_target in #(
Emscripten/browser*) :
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_termios=n/a
py_cv_module_=n/a py_cv_module_=n/a
;; #( ;; #(
Emscripten/node*) : Emscripten/node*) :
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_=n/a
;; #( ;; #(
WASI/*) : WASI/*) :
;; #(
*) :
py_cv_module__ctypes=n/a ;;
py_cv_module__ctypes_test=n/a esac
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
;; #( ;; #(
*) : *) :

View file

@ -2954,6 +2954,9 @@ then
BLDSHARED="$LDSHARED" BLDSHARED="$LDSHARED"
fi fi
;; ;;
Emscripten|WASI)
LDSHARED='$(CC) -shared'
LDCXXSHARED='$(CXX) -shared';;
Linux*|GNU*|QNX*|VxWorks*|Haiku*) Linux*|GNU*|QNX*|VxWorks*|Haiku*)
LDSHARED='$(CC) -shared' LDSHARED='$(CC) -shared'
LDCXXSHARED='$(CXX) -shared';; LDCXXSHARED='$(CXX) -shared';;
@ -6585,16 +6588,19 @@ AC_DEFUN([PY_STDLIB_MOD_SET_NA], [
dnl Modules that are not available on some platforms dnl Modules that are not available on some platforms
dnl AIX has shadow passwords, but access is not via getspent() dnl AIX has shadow passwords, but access is not via getspent()
dnl VxWorks does not provide crypt() function dnl VxWorks does not provide crypt() function
AS_CASE([$ac_sys_system/$ac_sys_emscripten_target], AS_CASE([$ac_sys_system],
[AIX/*], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])], [AIX], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])],
[VxWorks*/*], [PY_STDLIB_MOD_SET_NA([_scproxy], [_crypt], [termios], [grp])], [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [_crypt], [termios], [grp])],
[Darwin/*], [PY_STDLIB_MOD_SET_NA([ossaudiodev], [spwd])], [Darwin], [PY_STDLIB_MOD_SET_NA([ossaudiodev], [spwd])],
[CYGWIN*/*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])], [CYGWIN*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])],
[QNX*/*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])], [QNX*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])],
[FreeBSD*/*], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])], [FreeBSD*], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])],
[Emscripten/browser*], [ [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( PY_STDLIB_MOD_SET_NA(
[_ctypes],
[_curses], [_curses],
[_curses_panel], [_curses_panel],
[_dbm], [_dbm],
@ -6605,58 +6611,25 @@ AS_CASE([$ac_sys_system/$ac_sys_emscripten_target],
[_scproxy], [_scproxy],
[_tkinter], [_tkinter],
[_xxsubinterpreters], [_xxsubinterpreters],
[fcntl],
[grp], [grp],
[nis], [nis],
[ossaudiodev], [ossaudiodev],
[pwd], [pwd],
[resource],
[readline],
[spwd], [spwd],
[syslog], [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], [termios],
) )
], ],
dnl Some modules like _posixsubprocess do not work. We build them anyway [Emscripten/node*], [],
dnl so imports in tests do not fail. [WASI/*], []
[Emscripten/node*], [
PY_STDLIB_MOD_SET_NA(
[_ctypes],
[_curses],
[_curses_panel],
[_dbm],
[_gdbm],
[_multiprocessing],
[_posixshmem],
[_posixsubprocess],
[_scproxy],
[_tkinter],
[_xxsubinterpreters],
[grp],
[nis],
[ossaudiodev],
[pwd],
[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],
) )
], ],
[PY_STDLIB_MOD_SET_NA([_scproxy])] [PY_STDLIB_MOD_SET_NA([_scproxy])]