GH-158628: Implement `sys._wasi_info` (GH-159039)

Starting off with WASI version, WASI SDK version, and whether cooperative multithreading is available. All values are required as WASI SDK is the officially  supported compiler according to PEP 11.
diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
index b447e42..d1b23df 100644
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -418,6 +418,29 @@
    .. versionadded:: 3.11
 
 
+.. data:: _wasi_info
+
+   An object containing information about the build on the WebAssembly WASI
+   platform. The object is provisional and may change in the future.
+
+   .. attribute:: _wasi_info.wasi_version
+
+      The WASI target version as a string: ``'p1'``, ``'p2'``, or ``'p3'``.
+
+   .. attribute:: _wasi_info.wasi_sdk_version
+
+      The full WASI SDK version as a string, such as ``'34.0'``.
+
+   .. attribute:: _wasi_info.cooperative_threads
+
+      ``True`` if wasi-libc was compiled with cooperative multithreading
+      support, ``False`` otherwise.
+
+   .. availability:: WASI.
+
+   .. versionadded:: next
+
+
 .. data:: pycache_prefix
 
    If this is set (not ``None``), Python will write bytecode-cache ``.pyc``
diff --git a/Include/internal/mimalloc/mimalloc/internal.h b/Include/internal/mimalloc/mimalloc/internal.h
index a7daa3a..423693f 100644
--- a/Include/internal/mimalloc/mimalloc/internal.h
+++ b/Include/internal/mimalloc/mimalloc/internal.h
@@ -23,10 +23,6 @@ terms of the MIT license. A copy of the license can be found in the file
 #define mi_trace_message(...)
 #endif
 
-#if defined(__EMSCRIPTEN__) && !defined(__wasi__)
-#define __wasi__
-#endif
-
 #if defined(__cplusplus)
 #define mi_decl_externc       extern "C"
 #else
@@ -34,7 +30,7 @@ terms of the MIT license. A copy of the license can be found in the file
 #endif
 
 // pthreads
-#if !defined(_WIN32) && !defined(__wasi__)
+#if !defined(_WIN32) && !defined(__wasm__)
 #define  MI_USE_PTHREADS
 #include <pthread.h>
 #endif
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index c74ca05..af99437 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -824,6 +824,16 @@ def test_emscripten_info(self):
         self.assertIsInstance(sys._emscripten_info.pthreads, bool)
         self.assertIsInstance(sys._emscripten_info.shared_memory, bool)
 
+    @unittest.skipUnless(support.is_wasi, "only available on WASI")
+    def test_wasi_info(self):
+        info = sys._wasi_info
+        self.assertIn(info.wasi_version, {'p1', 'p2', 'p3'})
+        self.assertIn('.', info.wasi_sdk_version)
+        sdk_major, _, sdk_minor = info.wasi_sdk_version.partition('.')
+        self.assertGreaterEqual(int(sdk_major), 1)
+        self.assertGreaterEqual(int(sdk_minor), 0)
+        self.assertIsInstance(info.cooperative_threads, 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/2026-10-08-11-58-52.gh-issue-158628.NJxwuw.rst b/Misc/NEWS.d/next/Library/2026-10-08-11-58-52.gh-issue-158628.NJxwuw.rst
new file mode 100644
index 0000000..eb1e0f1
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-08-11-58-52.gh-issue-158628.NJxwuw.rst
@@ -0,0 +1 @@
+Implement :data:`sys._wasi_info` for exposing WASI-specific system details.
diff --git a/Platforms/WASI/README.md b/Platforms/WASI/README.md
index a62efbd..e56645b 100644
--- a/Platforms/WASI/README.md
+++ b/Platforms/WASI/README.md
@@ -279,10 +279,16 @@
 
 ### C code
 
-WASI SDK defines several built-in macros. You can dump a full list of built-ins
-with ``/path/to/wasi-sdk/bin/clang -dM -E - < /dev/null``.
+The following built-in macros identify WebAssembly targets and environments.
+For WASI SDK, you can dump a full list of built-ins with
+`/path/to/wasi-sdk/bin/clang -dM -E - < /dev/null`.
 
-* WebAssembly ``__wasm__`` (also ``__wasm``)
-* wasm32 ``__wasm32__`` (also ``__wasm32``)
-* wasm64 ``__wasm64__``
-* WASI ``__wasi__``
+#### Shared with Emscripten
+
+- WebAssembly target: `__wasm__` (also `__wasm`)
+- wasm32 target: `__wasm32__` (also `__wasm32`)
+- wasm64 target: `__wasm64__`
+
+#### Unique to WASI
+
+- WASI environment: `__wasi__`
diff --git a/Python/marshal.c b/Python/marshal.c
index 7109e5c..e8ce70f 100644
--- a/Python/marshal.c
+++ b/Python/marshal.c
@@ -44,7 +44,7 @@ module marshal
  */
 #if defined(MS_WINDOWS)
 #  define MAX_MARSHAL_STACK_DEPTH 1000
-#elif defined(__wasi__)
+#elif defined(__wasm__)
 #  define MAX_MARSHAL_STACK_DEPTH 1500
 // TARGET_OS_IPHONE covers any non-macOS Apple platform.
 // It won't be defined on older macOS SDKs
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index afc90d9..a284c83 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -64,6 +64,10 @@ extern const char *PyWin_DLLVersionString;
 #  include <emscripten.h>
 #endif
 
+#ifdef __wasi__
+#  include <wasi/version.h>
+#endif
+
 #ifdef HAVE_FCNTL_H
 #  include <fcntl.h>
 #endif
@@ -3837,6 +3841,49 @@ make_abi_info(void)
 }
 
 
+#ifdef __wasi__
+
+static PyObject *
+make_wasi_info(void)
+{
+#if defined(__wasip1__)
+    const char *wasi_version = "p1";
+#elif defined(__wasip2__)
+    const char *wasi_version = "p2";
+#elif defined(__wasip3__)
+    const char *wasi_version = "p3";
+#else
+#  error "Unknown WASI target version"
+#endif
+
+    // The SDK version is only defined for SDK builds of wasi-libc.
+#ifdef __wasi_sdk_version__
+    const char *wasi_sdk_version = __wasi_sdk_version__;
+#else
+#  error "Unknown WASI SDK version"
+#endif
+
+#ifdef __wasi_cooperative_threads__
+    PyObject *cooperative_threads = Py_True;
+#else
+    PyObject *cooperative_threads = Py_False;
+#endif
+
+    PyObject *wasi_info = Py_BuildValue(
+        "{s:s,s:s,s:O}",
+        "wasi_version", wasi_version,
+        "wasi_sdk_version", wasi_sdk_version,
+        "cooperative_threads", cooperative_threads);
+    if (wasi_info == NULL) {
+        return NULL;
+    }
+    PyObject *ns = _PyNamespace_New(wasi_info);
+    Py_DECREF(wasi_info);
+    return ns;
+}
+
+#endif // __wasi__
+
 #ifdef __EMSCRIPTEN__
 
 PyDoc_STRVAR(emscripten_info__doc__,
@@ -4084,6 +4131,10 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict)
 
     SET_SYS("abi_info", make_abi_info());
 
+#ifdef __wasi__
+    SET_SYS("_wasi_info", make_wasi_info());
+#endif
+
     /* initialize asyncgen_hooks */
     if (_PyStructSequence_InitBuiltin(interp, &AsyncGenHooksType,
                                       &asyncgen_hooks_desc) < 0)