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.
.. 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``

View file

@ -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:

View file

@ -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.

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;
#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
}
}

View file

@ -4,11 +4,17 @@ # Python WebAssembly (WASM) build
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 @@ ### Cross compile to wasm32-emscripten for browser
### Cross compile to wasm32-emscripten for node
```
```shell
mkdir -p builddir/emscripten-node
pushd builddir/emscripten-node
@ -91,7 +97,7 @@ ### Cross compile to wasm32-emscripten for node
popd
```
```
```shell
node --experimental-wasm-threads --experimental-wasm-bulk-memory builddir/emscripten-node/python.js
```
@ -150,9 +156,9 @@ ## Misc
- 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 @@ ## wasm32-emscripten in browsers
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 @@ # .htaccess
</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 @@ ## Python code
# 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 @@ ## C code
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

108
configure generated vendored
View file

@ -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,103 +22392,72 @@ 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|WASI) :
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
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_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*) :
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
;; #(
;; #(
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
;; #(
*) :

View file

@ -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*], [
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],
[_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*], [
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])]