struct pyruntimestate;
struct _ceval_runtime_state;
+/* WASI has limited call stack. wasmtime 0.36 can handle sufficient amount of
+ C stack frames for little more than 750 recursions. */
#ifndef Py_DEFAULT_RECURSION_LIMIT
-# define Py_DEFAULT_RECURSION_LIMIT 1000
+# ifdef __wasi__
+# define Py_DEFAULT_RECURSION_LIMIT 750
+# else
+# define Py_DEFAULT_RECURSION_LIMIT 1000
+# endif
#endif
#include "pycore_interp.h" // PyInterpreterState.eval_frame
executable = sys.executable
+ if not executable:
+ # sys.executable is not set.
+ return lib, version
+
V = _comparable_version
# We use os.path.realpath()
# here to work around problems with Cygwin not being
def collect_socket(info_add):
import socket
- hostname = socket.gethostname()
- info_add('socket.hostname', hostname)
+ try:
+ hostname = socket.gethostname()
+ except OSError:
+ # WASI SDK 15.0 does not have gethostname(2).
+ if sys.platform != "wasi":
+ raise
+ else:
+ info_add('socket.hostname', hostname)
def collect_sqlite(info_add):
def _force_run(path, func, *args):
try:
return func(*args)
+ except FileNotFoundError as err:
+ # chmod() won't fix a missing file.
+ if verbose >= 2:
+ print('%s: %s' % (err.__class__.__name__, err))
+ raise
except OSError as err:
if verbose >= 2:
print('%s: %s' % (err.__class__.__name__, err))
self.assertEqual(d['z'], 12)
def test_extended_arg(self):
- longexpr = 'x = x or ' + '-x' * 2500
+ # default: 1000 * 2.5 = 2500 repetitions
+ repeat = int(sys.getrecursionlimit() * 2.5)
+ longexpr = 'x = x or ' + '-x' * repeat
g = {}
code = '''
def f(x):
from weakref import proxy
from functools import wraps
-from test.support import cpython_only, swap_attr, gc_collect, is_emscripten
+from test.support import (
+ cpython_only, swap_attr, gc_collect, is_emscripten, is_wasi
+)
from test.support.os_helper import (TESTFN, TESTFN_UNICODE, make_bad_fd)
from test.support.warnings_helper import check_warnings
from collections import UserList
self.assertRaises((AttributeError, TypeError),
setattr, f, attr, 'oops')
+ @unittest.skipIf(is_wasi, "WASI does not expose st_blksize.")
def testBlksize(self):
# test private _blksize attribute
blksize = io.DEFAULT_BUFFER_SIZE
def skip_no_disk_space(path, required):
def decorator(fun):
def wrapper(*args, **kwargs):
+ if not hasattr(shutil, "disk_usage"):
+ raise unittest.SkipTest("requires shutil.disk_usage")
if shutil.disk_usage(os.path.realpath(path)).free < required:
hsize = int(required / 1024 / 1024)
raise unittest.SkipTest(
self.assertEqual(fp.read().strip(), '1')
class RotatingFileHandlerTest(BaseFileTest):
+ @unittest.skipIf(support.is_wasi, "WASI does not have /dev/null.")
def test_should_not_rollover(self):
# If maxbytes is zero rollover never occurs
rh = logging.handlers.RotatingFileHandler(
rh.close()
class TimedRotatingFileHandlerTest(BaseFileTest):
+ @unittest.skipIf(support.is_wasi, "WASI does not have /dev/null.")
def test_should_not_rollover(self):
# See bpo-45401. Should only ever rollover regular files
fh = logging.handlers.TimedRotatingFileHandler(
import fractions
import itertools
import locale
-import mmap
import os
import pickle
import select
except ImportError:
INT_MAX = PY_SSIZE_T_MAX = sys.maxsize
+try:
+ import mmap
+except ImportError:
+ mmap = None
from test.support.script_helper import assert_python_ok
from test.support import unix_shell
@unittest.skipUnless(hasattr(os, 'fpathconf'), 'test needs os.fpathconf()')
@unittest.skipIf(
- support.is_emscripten, "musl libc issue on Emscripten, bpo-46390"
+ support.is_emscripten or support.is_wasi,
+ "musl libc issue on Emscripten/WASI, bpo-46390"
)
def test_fpathconf(self):
self.check(os.pathconf, "PC_NAME_MAX")
# os.kill on Windows can take an int which gets set as the exit code
self._kill(100)
+ @unittest.skipIf(mmap is None, "requires mmap")
def _kill_with_event(self, event, name):
tagname = "test_os_%s" % uuid.uuid1()
m = mmap.mmap(-1, 1, tagname)
script = os.path.join(dirname, 'signalinterproctester.py')
assert_python_ok(script)
+ @unittest.skipUnless(
+ hasattr(signal, "valid_signals"),
+ "requires signal.valid_signals"
+ )
def test_valid_signals(self):
s = signal.valid_signals()
self.assertIsInstance(s, set)
self.assertRaises((ValueError, OSError),
signal.set_wakeup_fd, fd)
+ @unittest.skipUnless(support.has_socket_support, "needs working sockets.")
def test_invalid_socket(self):
sock = socket.socket()
fd = sock.fileno()
self.assertEqual(signal.set_wakeup_fd(-1), -1)
@unittest.skipIf(support.is_emscripten, "Emscripten cannot fstat pipes.")
+ @unittest.skipUnless(support.has_socket_support, "needs working sockets.")
def test_set_wakeup_fd_socket_result(self):
sock1 = socket.socket()
self.addCleanup(sock1.close)
import datetime
from decimal import Decimal as D
from pathlib import Path
+import sys
import tempfile
import unittest
self.assertEqual(obj_copy, expected_obj)
def test_inline_array_recursion_limit(self):
- nest_count = 470
+ # 470 with default recursion limit
+ nest_count = int(sys.getrecursionlimit() * 0.47)
recursive_array_toml = "arr = " + nest_count * "[" + nest_count * "]"
tomllib.loads(recursive_array_toml)
def test_inline_table_recursion_limit(self):
- nest_count = 310
+ # 310 with default recursion limit
+ nest_count = int(sys.getrecursionlimit() * 0.31)
recursive_table_toml = nest_count * "key = {" + nest_count * "}"
tomllib.loads(recursive_table_toml)
os_helper.create_empty_file(TESTMOD)
self.assertZipFailure(TESTMOD)
+ @unittest.skipIf(support.is_wasi, "mode 000 not supported.")
def testFileUnreadable(self):
os_helper.unlink(TESTMOD)
fd = os.open(TESTMOD, os.O_CREAT, 000)
--- /dev/null
+Decrease default recursion limit on WASI to address limited call stack size.
#elif defined(HAVE_CLOCK_GETTIME) && \
defined(CLOCK_PROCESS_CPUTIME_ID) && \
- !defined(__EMSCRIPTEN__)
+ !defined(__EMSCRIPTEN__) && !defined(__wasi__)
#define HAVE_THREAD_TIME
#if defined(__APPLE__) && defined(__has_attribute) && __has_attribute(availability)
# define D(x)
#endif
-# define MAXSTACK 6000
+#ifdef __wasi__
+# define MAXSTACK 4000
+#else
+# define MAXSTACK 6000
+#endif
static const int n_keyword_lists = 9;
static KeywordToken *reserved_keywords[] = {
(KeywordToken[]) {{NULL, -1}},
# define D(x)
#endif
-# define MAXSTACK 6000
+#ifdef __wasi__
+# define MAXSTACK 4000
+#else
+# define MAXSTACK 6000
+#endif
"""
# 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
+WASI builds require [WASI SDK](https://github.com/WebAssembly/wasi-sdk) 15.0+
+and currently [wasix](https://github.com/singlestore-labs/wasix) for POSIX
compatibility stubs.
+## WASI limitations and issues (WASI SDK 15.0)
+
+A lot of Emscripten limitations also apply to WASI. Noticable restrictions
+are:
+
+- Call stack size is limited. Default recursion limit and parser stack size
+ are smaller than in regular Python builds.
+- ``socket(2)`` cannot create new socket file descriptors. WASI programs can
+ call read/write/accept on a file descriptor that is passed into the process.
+- ``socket.gethostname()`` and host name resolution APIs like
+ ``socket.gethostbyname()`` are not implemented and always fail.
+- ``chmod(2)`` is not available. It's not possible to modify file permissions,
+ yet. A future version of WASI may provide a limited ``set_permissions`` API.
+- File locking (``fcntl``) is not available.
+- ``os.pipe()``, ``os.mkfifo()``, and ``os.mknod()`` are not supported.
+
+
# Detect WebAssembly builds
## Python code
# undefined symbols / unsupported features
ac_cv_func_eventfd=no
+
+# WASI SDK 15.0 has no pipe syscall.
+ac_cv_func_pipe=no
+
+# WASI SDK 15.0 cannot create fifos and special files.
+ac_cv_func_mkfifo=no
+ac_cv_func_mkfifoat=no
+ac_cv_func_mknod=no
+ac_cv_func_mknodat=no
+
+# fdopendir() fails on SDK 15.0,
+# OSError: [Errno 28] Invalid argument: '.'
+ac_cv_func_fdopendir=no
+
+# WASIX stubs we don't want to use.
+ac_cv_func_kill=no
+
+# WASI sockets are limited to operations on given socket fd and inet sockets.
+# Disable AF_UNIX and AF_PACKET support, see socketmodule.h.
+ac_cv_header_sys_un_h=no
+ac_cv_header_netpacket_packet_h=no
py_cv_module__ctypes_test=n/a
+ py_cv_module_fcntl=n/a
py_cv_module_=n/a
],
[Emscripten/node*], [],
[WASI/*], [
+ dnl WASI SDK 15.0 does not support file locking.
PY_STDLIB_MOD_SET_NA(
[_ctypes_test],
+ [fcntl],
)
]
)