gh-90473: Decrease recursion limit and skip tests on WASI (GH-92803)

This commit is contained in:
Christian Heimes 2022-05-19 12:43:16 +02:00 committed by GitHub
parent e48ac9c100
commit 137fd3d88a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 109 additions and 14 deletions

View file

@ -12,8 +12,14 @@ extern "C" {
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

View file

@ -186,6 +186,10 @@ def libc_ver(executable=None, lib='', version='', chunksize=16384):
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

View file

@ -545,8 +545,14 @@ def format_attr(attr, value):
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):

View file

@ -199,6 +199,11 @@ def get_original_stdout():
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))

View file

@ -109,7 +109,9 @@ def __getitem__(self, key):
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):

View file

@ -9,7 +9,9 @@
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
@ -65,6 +67,7 @@ def testAttributes(self):
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

View file

@ -156,6 +156,8 @@ def test_seekable(self):
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(

View file

@ -5280,6 +5280,7 @@ def test_emit_after_closing_in_write_mode(self):
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(
@ -5387,6 +5388,7 @@ def rotator(source, dest):
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(

View file

@ -11,7 +11,6 @@
import fractions
import itertools
import locale
import mmap
import os
import pickle
import select
@ -59,6 +58,10 @@
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
@ -2167,7 +2170,8 @@ def test_fchown(self):
@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")
@ -2460,6 +2464,7 @@ def test_kill_int(self):
# 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)

View file

@ -107,6 +107,10 @@ def test_interprocess_signal(self):
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)
@ -212,6 +216,7 @@ def test_invalid_fd(self):
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()
@ -241,6 +246,7 @@ def test_set_wakeup_fd_result(self):
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)

View file

@ -6,6 +6,7 @@
import datetime
from decimal import Decimal as D
from pathlib import Path
import sys
import tempfile
import unittest
@ -91,11 +92,13 @@ def test_deepcopy(self):
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)

View file

@ -804,6 +804,7 @@ def testEmptyFile(self):
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)

View file

@ -0,0 +1 @@
Decrease default recursion limit on WASI to address limited call stack size.

View file

@ -1481,7 +1481,7 @@ _PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
#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)

6
Parser/parser.c generated
View file

@ -7,7 +7,11 @@
# 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}},

View file

@ -37,7 +37,11 @@
# define D(x)
#endif
# define MAXSTACK 6000
#ifdef __wasi__
# define MAXSTACK 4000
#else
# define MAXSTACK 6000
#endif
"""

View file

@ -220,10 +220,27 @@ # .htaccess
# 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

View file

@ -17,3 +17,24 @@ ac_cv_header_sys_resource_h=no
# 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

1
configure generated vendored
View file

@ -22611,6 +22611,7 @@ case $ac_sys_system in #(
py_cv_module__ctypes_test=n/a
py_cv_module_fcntl=n/a
py_cv_module_=n/a

View file

@ -6690,8 +6690,10 @@ AS_CASE([$ac_sys_system],
],
[Emscripten/node*], [],
[WASI/*], [
dnl WASI SDK 15.0 does not support file locking.
PY_STDLIB_MOD_SET_NA(
[_ctypes_test],
[fcntl],
)
]
)