Merge pull request #5702 from arihant2math/struct-313

Update struct to 3.13.3 and update parts of test.support
This commit is contained in:
Noa authored and GitHub committed 2025-04-16 17:09:41 -05:00
commit 2ca52bf3ba
5 files changed
+180 -168

No files matched your search

+95 -51
View File
@@ -74,13 +74,7 @@ __all__ = [
#
# The timeout should be long enough for connect(), recv() and send() methods
# of socket.socket.
LOOPBACK_TIMEOUT = 5.0
if sys.platform == 'win32' and ' 32 bit (ARM)' in sys.version:
# bpo-37553: test_socket.SendfileUsingSendTest is taking longer than 2
# seconds on Windows ARM32 buildbot
LOOPBACK_TIMEOUT = 10
elif sys.platform == 'vxworks':
LOOPBACK_TIMEOUT = 10
LOOPBACK_TIMEOUT = 10.0
# Timeout in seconds for network requests going to the internet. The timeout is
# short enough to prevent a test to wait for too long if the internet request
@@ -113,7 +107,6 @@ TEST_HOME_DIR = os.path.dirname(TEST_SUPPORT_DIR)
STDLIB_DIR = os.path.dirname(TEST_HOME_DIR)
REPO_ROOT = os.path.dirname(STDLIB_DIR)
class Error(Exception):
"""Base class for regression test exceptions."""
@@ -259,22 +252,16 @@ def _is_gui_available():
# process not running under the same user id as the current console
# user. To avoid that, raise an exception if the window manager
# connection is not available.
from ctypes import cdll, c_int, pointer, Structure
from ctypes.util import find_library
app_services = cdll.LoadLibrary(find_library("ApplicationServices"))
if app_services.CGMainDisplayID() == 0:
reason = "gui tests cannot run without OS X window manager"
import subprocess
try:
rc = subprocess.run(["launchctl", "managername"],
capture_output=True, check=True)
managername = rc.stdout.decode("utf-8").strip()
except subprocess.CalledProcessError:
reason = "unable to detect macOS launchd job manager"
else:
class ProcessSerialNumber(Structure):
_fields_ = [("highLongOfPSN", c_int),
("lowLongOfPSN", c_int)]
psn = ProcessSerialNumber()
psn_p = pointer(psn)
if ( (app_services.GetCurrentProcess(psn_p) < 0) or
(app_services.SetFrontProcess(psn_p) < 0) ):
reason = "cannot run without OS X gui process"
if managername != "Aqua":
reason = f"{managername=} -- can only run in a macOS GUI session"
# check on every platform whether tkinter can actually do anything
if not reason:
@@ -391,11 +378,12 @@ def requires_mac_ver(*min_version):
def skip_if_buildbot(reason=None):
"""Decorator raising SkipTest if running on a buildbot."""
import getpass
if not reason:
reason = 'not suitable for buildbots'
try:
isbuildbot = getpass.getuser().lower() == 'buildbot'
except (KeyError, EnvironmentError) as err:
except (KeyError, OSError) as err:
warnings.warn(f'getpass.getuser() failed {err}.', RuntimeWarning)
isbuildbot = False
return unittest.skipIf(isbuildbot, reason)
@@ -409,35 +397,48 @@ def check_sanitizer(*, address=False, memory=False, ub=False, thread=False):
cflags = sysconfig.get_config_var('CFLAGS') or ''
config_args = sysconfig.get_config_var('CONFIG_ARGS') or ''
memory_sanitizer = (
'-fsanitize=memory' in cflags or
'--with-memory-sanitizer' in config_args
'-fsanitize=memory' in cflags or
'--with-memory-sanitizer' in config_args
)
address_sanitizer = (
'-fsanitize=address' in cflags or
'--with-address-sanitizer' in config_args
'-fsanitize=address' in cflags or
'--with-address-sanitizer' in config_args
)
ub_sanitizer = (
'-fsanitize=undefined' in cflags or
'--with-undefined-behavior-sanitizer' in config_args
'-fsanitize=undefined' in cflags or
'--with-undefined-behavior-sanitizer' in config_args
)
thread_sanitizer = (
'-fsanitize=thread' in cflags or
'--with-thread-sanitizer' in config_args
'-fsanitize=thread' in cflags or
'--with-thread-sanitizer' in config_args
)
return (
(memory and memory_sanitizer) or
(address and address_sanitizer) or
(ub and ub_sanitizer) or
(thread and thread_sanitizer)
(memory and memory_sanitizer) or
(address and address_sanitizer) or
(ub and ub_sanitizer) or
(thread and thread_sanitizer)
)
def skip_if_sanitizer(reason=None, *, address=False, memory=False, ub=False, thread=False):
"""Decorator raising SkipTest if running with a sanitizer active."""
if not reason:
reason = 'not working with sanitizers active'
skip = check_sanitizer(address=address, memory=memory, ub=ub)
skip = check_sanitizer(address=address, memory=memory, ub=ub, thread=thread)
return unittest.skipIf(skip, reason)
# gh-89363: True if fork() can hang if Python is built with Address Sanitizer
# (ASAN): libasan race condition, dead lock in pthread_create().
HAVE_ASAN_FORK_BUG = check_sanitizer(address=True)
def set_sanitizer_env_var(env, option):
for name in ('ASAN_OPTIONS', 'MSAN_OPTIONS', 'UBSAN_OPTIONS', 'TSAN_OPTIONS'):
if name in env:
env[name] += f':{option}'
else:
env[name] = option
def system_must_validate_cert(f):
"""Skip the test on TLS certificate validation failures."""
@@ -510,21 +511,42 @@ def has_no_debug_ranges():
def requires_debug_ranges(reason='requires co_positions / debug_ranges'):
return unittest.skipIf(has_no_debug_ranges(), reason)
def requires_legacy_unicode_capi():
@contextlib.contextmanager
def suppress_immortalization(suppress=True):
"""Suppress immortalization of deferred objects."""
try:
from _testcapi import unicode_legacy_string
import _testinternalcapi
except ImportError:
unicode_legacy_string = None
yield
return
return unittest.skipUnless(unicode_legacy_string,
'requires legacy Unicode C API')
if not suppress:
yield
return
_testinternalcapi.suppress_immortalization(True)
try:
yield
finally:
_testinternalcapi.suppress_immortalization(False)
def skip_if_suppress_immortalization():
try:
import _testinternalcapi
except ImportError:
return
return unittest.skipUnless(_testinternalcapi.get_immortalize_deferred(),
"requires immortalization of deferred objects")
MS_WINDOWS = (sys.platform == 'win32')
# Is not actually used in tests, but is kept for compatibility.
is_jython = sys.platform.startswith('java')
is_android = hasattr(sys, 'getandroidapilevel')
is_android = sys.platform == "android"
if sys.platform not in ('win32', 'vxworks'):
if sys.platform not in {"win32", "vxworks", "ios", "tvos", "watchos"}:
unix_shell = '/system/bin/sh' if is_android else '/bin/sh'
else:
unix_shell = None
@@ -534,23 +556,44 @@ else:
is_emscripten = sys.platform == "emscripten"
is_wasi = sys.platform == "wasi"
has_fork_support = hasattr(os, "fork") and not is_emscripten and not is_wasi
is_apple_mobile = sys.platform in {"ios", "tvos", "watchos"}
is_apple = is_apple_mobile or sys.platform == "darwin"
# From python 3.12.6
is_s390x = hasattr(os, 'uname') and os.uname().machine == 's390x'
skip_on_s390x = unittest.skipIf(is_s390x, 'skipped on s390x')
has_fork_support = hasattr(os, "fork") and not (
# WASM and Apple mobile platforms do not support subprocesses.
is_emscripten
or is_wasi
or is_apple_mobile
# Although Android supports fork, it's unsafe to call it from Python because
# all Android apps are multi-threaded.
or is_android
)
def requires_fork():
return unittest.skipUnless(has_fork_support, "requires working os.fork()")
has_subprocess_support = not is_emscripten and not is_wasi
has_subprocess_support = not (
# WASM and Apple mobile platforms do not support subprocesses.
is_emscripten
or is_wasi
or is_apple_mobile
# Although Android supports subproceses, they're almost never useful in
# practice (see PEP 738). And most of the tests that use them are calling
# sys.executable, which won't work when Python is embedded in an Android app.
or is_android
)
def requires_subprocess():
"""Used for subprocess, os.spawn calls, fd inheritance"""
return unittest.skipUnless(has_subprocess_support, "requires subprocess support")
# Emscripten's socket emulation and WASI sockets have limitations.
has_socket_support = not is_emscripten and not is_wasi
has_socket_support = not (
is_emscripten
or is_wasi
)
def requires_working_socket(*, module=False):
"""Skip tests or modules that require working sockets
@@ -2551,7 +2594,8 @@ EXCEEDS_RECURSION_LIMIT = 5000
# The default C recursion limit (from Include/cpython/pystate.h).
C_RECURSION_LIMIT = 1500
#Windows doesn't have os.uname() but it doesn't support s390x.
# Windows doesn't have os.uname() but it doesn't support s390x.
is_s390x = hasattr(os, 'uname') and os.uname().machine == 's390x'
skip_on_s390x = unittest.skipIf(hasattr(os, 'uname') and os.uname().machine == 's390x',
'skipped on s390x')
HAVE_ASAN_FORK_BUG = check_sanitizer(address=True)
-12
View File
@@ -291,18 +291,6 @@ class Test_Csv(unittest.TestCase):
self.assertRaises(TypeError, writer.writerows, None)
self.assertRaises(OSError, writer.writerows, BadIterable())
@support.cpython_only
@support.requires_legacy_unicode_capi()
@warnings_helper.ignore_warnings(category=DeprecationWarning)
def test_writerows_legacy_strings(self):
import _testcapi
c = _testcapi.unicode_legacy_string('a')
with TemporaryFile("w+", encoding="utf-8", newline='') as fileobj:
writer = csv.writer(fileobj)
writer.writerows([[c]])
fileobj.seek(0)
self.assertEqual(fileobj.read(), "a\r\n")
def _read_test(self, input, expect, **kwargs):
reader = csv.reader(input, **kwargs)
result = list(reader)
+2 -73
View File
@@ -34,10 +34,10 @@ import numbers
import locale
from test.support import (is_resource_enabled,
requires_IEEE_754, requires_docstrings,
requires_legacy_unicode_capi, check_sanitizer)
check_disallow_instantiation)
from test.support import (TestFailed,
run_with_locale, cpython_only,
darwin_malloc_err_warning, is_emscripten)
darwin_malloc_err_warning)
from test.support.import_helper import import_fresh_module
from test.support import threading_helper
from test.support import warnings_helper
@@ -586,18 +586,6 @@ class ExplicitConstructionTest:
# underscores don't prevent errors
self.assertRaises(InvalidOperation, Decimal, "1_2_\u00003")
@cpython_only
@requires_legacy_unicode_capi()
@warnings_helper.ignore_warnings(category=DeprecationWarning)
def test_from_legacy_strings(self):
import _testcapi
Decimal = self.decimal.Decimal
context = self.decimal.Context()
s = _testcapi.unicode_legacy_string('9.999999')
self.assertEqual(str(Decimal(s)), '9.999999')
self.assertEqual(str(context.create_decimal(s)), '9.999999')
def test_explicit_from_tuples(self):
Decimal = self.decimal.Decimal
@@ -2928,23 +2916,6 @@ class ContextAPItests:
assert_signals(self, c, 'traps', [InvalidOperation, DivisionByZero,
Overflow])
@cpython_only
@requires_legacy_unicode_capi()
@warnings_helper.ignore_warnings(category=DeprecationWarning)
def test_from_legacy_strings(self):
import _testcapi
c = self.decimal.Context()
for rnd in RoundingModes:
c.rounding = _testcapi.unicode_legacy_string(rnd)
self.assertEqual(c.rounding, rnd)
s = _testcapi.unicode_legacy_string('')
self.assertRaises(TypeError, setattr, c, 'rounding', s)
s = _testcapi.unicode_legacy_string('ROUND_\x00UP')
self.assertRaises(TypeError, setattr, c, 'rounding', s)
def test_pickle(self):
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
@@ -5654,48 +5625,6 @@ class CWhitebox(unittest.TestCase):
self.assertEqual(Decimal.from_float(cls(101.1)),
Decimal.from_float(101.1))
# Issue 41540:
@unittest.skipIf(sys.platform.startswith("aix"),
"AIX: default ulimit: test is flaky because of extreme over-allocation")
@unittest.skipIf(is_emscripten, "Test is unstable on Emscripten")
@unittest.skipIf(check_sanitizer(address=True, memory=True),
"ASAN/MSAN sanitizer defaults to crashing "
"instead of returning NULL for malloc failure.")
def test_maxcontext_exact_arith(self):
# Make sure that exact operations do not raise MemoryError due
# to huge intermediate values when the context precision is very
# large.
# The following functions fill the available precision and are
# therefore not suitable for large precisions (by design of the
# specification).
MaxContextSkip = ['logical_invert', 'next_minus', 'next_plus',
'logical_and', 'logical_or', 'logical_xor',
'next_toward', 'rotate', 'shift']
Decimal = C.Decimal
Context = C.Context
localcontext = C.localcontext
# Here only some functions that are likely candidates for triggering a
# MemoryError are tested. deccheck.py has an exhaustive test.
maxcontext = Context(prec=C.MAX_PREC, Emin=C.MIN_EMIN, Emax=C.MAX_EMAX)
with localcontext(maxcontext):
self.assertEqual(Decimal(0).exp(), 1)
self.assertEqual(Decimal(1).ln(), 0)
self.assertEqual(Decimal(1).log10(), 0)
self.assertEqual(Decimal(10**2).log10(), 2)
self.assertEqual(Decimal(10**223).log10(), 223)
self.assertEqual(Decimal(10**19).logb(), 19)
self.assertEqual(Decimal(4).sqrt(), 2)
self.assertEqual(Decimal("40E9").sqrt(), Decimal('2.0E+5'))
self.assertEqual(divmod(Decimal(10), 3), (3, 1))
self.assertEqual(Decimal(10) // 3, 3)
self.assertEqual(Decimal(4) / 2, 2)
self.assertEqual(Decimal(400) ** -1, Decimal('0.0025'))
def test_c_signaldict_segfault(self):
# See gh-106263 for details.
SignalDict = type(C.Context().flags)
+83 -2
View File
@@ -9,7 +9,7 @@ import sys
import weakref
from test import support
from test.support import import_helper
from test.support import import_helper, suppress_immortalization
from test.support.script_helper import assert_python_ok
ISBIGENDIAN = sys.byteorder == "big"
@@ -96,6 +96,13 @@ class StructTest(unittest.TestCase):
('10s', b'helloworld', b'helloworld', b'helloworld', 0),
('11s', b'helloworld', b'helloworld\0', b'helloworld\0', 1),
('20s', b'helloworld', b'helloworld'+10*b'\0', b'helloworld'+10*b'\0', 1),
('0p', b'helloworld', b'', b'', 1),
('1p', b'helloworld', b'\x00', b'\x00', 1),
('2p', b'helloworld', b'\x01h', b'\x01h', 1),
('10p', b'helloworld', b'\x09helloworl', b'\x09helloworl', 1),
('11p', b'helloworld', b'\x0Ahelloworld', b'\x0Ahelloworld', 0),
('12p', b'helloworld', b'\x0Ahelloworld\0', b'\x0Ahelloworld\0', 1),
('20p', b'helloworld', b'\x0Ahelloworld'+9*b'\0', b'\x0Ahelloworld'+9*b'\0', 1),
('b', 7, b'\7', b'\7', 0),
('b', -7, b'\371', b'\371', 0),
('B', 7, b'\7', b'\7', 0),
@@ -339,6 +346,7 @@ class StructTest(unittest.TestCase):
def test_p_code(self):
# Test p ("Pascal string") code.
for code, input, expected, expectedback in [
('0p', b'abc', b'', b''),
('p', b'abc', b'\x00', b''),
('1p', b'abc', b'\x00', b''),
('2p', b'abc', b'\x01a', b'a'),
@@ -523,6 +531,9 @@ class StructTest(unittest.TestCase):
for c in [b'\x01', b'\x7f', b'\xff', b'\x0f', b'\xf0']:
self.assertTrue(struct.unpack('>?', c)[0])
self.assertTrue(struct.unpack('<?', c)[0])
self.assertTrue(struct.unpack('=?', c)[0])
self.assertTrue(struct.unpack('@?', c)[0])
def test_count_overflow(self):
hugecount = '{}b'.format(sys.maxsize+1)
@@ -582,6 +593,7 @@ class StructTest(unittest.TestCase):
self.check_sizeof('187s', 1)
self.check_sizeof('20p', 1)
self.check_sizeof('0s', 1)
self.check_sizeof('0p', 1)
self.check_sizeof('0c', 0)
def test_boundary_error_message(self):
@@ -678,6 +690,7 @@ class StructTest(unittest.TestCase):
self.assertIn(b"Exception ignored in:", stderr)
self.assertIn(b"C.__del__", stderr)
@suppress_immortalization()
def test__struct_reference_cycle_cleaned_up(self):
# Regression test for python/cpython#94207.
@@ -714,6 +727,74 @@ class StructTest(unittest.TestCase):
'embedded null character'):
struct.calcsize(s)
@support.cpython_only
def test_issue98248(self):
def test_error_msg(prefix, int_type, is_unsigned):
fmt_str = prefix + int_type
size = struct.calcsize(fmt_str)
if is_unsigned:
max_ = 2 ** (size * 8) - 1
min_ = 0
else:
max_ = 2 ** (size * 8 - 1) - 1
min_ = -2 ** (size * 8 - 1)
error_msg = f"'{int_type}' format requires {min_} <= number <= {max_}"
for number in [int(-1e50), min_ - 1, max_ + 1, int(1e50)]:
with self.subTest(format_str=fmt_str, number=number):
with self.assertRaisesRegex(struct.error, error_msg):
struct.pack(fmt_str, number)
error_msg = "required argument is not an integer"
not_number = ""
with self.subTest(format_str=fmt_str, number=not_number):
with self.assertRaisesRegex(struct.error, error_msg):
struct.pack(fmt_str, not_number)
for prefix in '@=<>':
for int_type in 'BHILQ':
test_error_msg(prefix, int_type, True)
for int_type in 'bhilq':
test_error_msg(prefix, int_type, False)
int_type = 'N'
test_error_msg('@', int_type, True)
int_type = 'n'
test_error_msg('@', int_type, False)
@support.cpython_only
def test_issue98248_error_propagation(self):
class Div0:
def __index__(self):
1 / 0
def test_error_propagation(fmt_str):
with self.subTest(format_str=fmt_str, exception="ZeroDivisionError"):
with self.assertRaises(ZeroDivisionError):
struct.pack(fmt_str, Div0())
for prefix in '@=<>':
for int_type in 'BHILQbhilq':
test_error_propagation(prefix + int_type)
test_error_propagation('N')
test_error_propagation('n')
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_struct_subclass_instantiation(self):
# Regression test for https://github.com/python/cpython/issues/112358
class MyStruct(struct.Struct):
def __init__(self):
super().__init__('>h')
my_struct = MyStruct()
self.assertEqual(my_struct.pack(12345), b'\x30\x39')
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_repr(self):
s = struct.Struct('=i2H')
self.assertEqual(repr(s), f'Struct({s.format!r})')
class UnpackIteratorTest(unittest.TestCase):
"""
@@ -895,4 +976,4 @@ class UnpackIteratorTest(unittest.TestCase):
if __name__ == '__main__':
unittest.main()
unittest.main()
-30
View File
@@ -818,16 +818,6 @@ class UnicodeTest(string_tests.CommonTest,
self.assertFalse("©".isidentifier())
self.assertFalse("0".isidentifier())
@support.cpython_only
@support.requires_legacy_unicode_capi()
@unittest.skipIf(_testcapi is None, 'need _testcapi module')
def test_isidentifier_legacy(self):
u = '𝖀𝖓𝖎𝖈𝖔𝖉𝖊'
self.assertTrue(u.isidentifier())
with warnings_helper.check_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
self.assertTrue(_testcapi.unicode_legacy_string(u).isidentifier())
def test_isprintable(self):
self.assertTrue("".isprintable())
self.assertTrue(" ".isprintable())
@@ -2522,26 +2512,6 @@ class UnicodeTest(string_tests.CommonTest,
self.assertEqual(args[0], text)
self.assertEqual(len(args), 1)
@support.cpython_only
@support.requires_legacy_unicode_capi()
@unittest.skipIf(_testcapi is None, 'need _testcapi module')
def test_resize(self):
for length in range(1, 100, 7):
# generate a fresh string (refcount=1)
text = 'a' * length + 'b'
# fill wstr internal field
with self.assertWarns(DeprecationWarning):
abc = _testcapi.getargs_u(text)
self.assertEqual(abc, text)
# resize text: wstr field must be cleared and then recomputed
text += 'c'
with self.assertWarns(DeprecationWarning):
abcdef = _testcapi.getargs_u(text)
self.assertNotEqual(abc, abcdef)
self.assertEqual(abcdef, text)
def test_compare(self):
# Issue #17615
N = 10