From 5dd97fd2a6acbc1427c216207d8c75eab46bdc24 Mon Sep 17 00:00:00 2001 From: youknowdot Date: Wed, 28 Oct 2026 22:39:35 +0900 Subject: [PATCH] Sync remaining call, buffer and annotation tests for Python 3.15 Run update_lib against final CPython 3.15.0 for four disjoint test sources. Preserve the existing Proactor sendfile override and capability gates. Assisted-by: OpenAI Codex: model version unavailable --- Lib/test/test_asyncio/test_sendfile.py | 7 +- Lib/test/test_buffer.py | 85 +++++++++++++++++++++--- Lib/test/test_call.py | 57 ++++++++++++++-- Lib/test/test_future_stmt/test_future.py | 2 + 4 files changed, 136 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_asyncio/test_sendfile.py b/Lib/test/test_asyncio/test_sendfile.py index a95e7d086ff..e4aaeaa9adc 100644 --- a/Lib/test/test_asyncio/test_sendfile.py +++ b/Lib/test/test_asyncio/test_sendfile.py @@ -98,7 +98,12 @@ class SendfileBase: # 64 KiB page configuration. DATA = b"x" * (1024 * 17 * 64 + 1) # Reduce socket buffer size to test on relative small data sets. - BUF_SIZE = 4 * 1024 # 4 KiB + if sys.platform.startswith('dragonfly'): + # A smaller buffer makes every window update wait 100 ms for the + # delayed ACK timer. + BUF_SIZE = 32 * 1024 # 32 KiB + else: + BUF_SIZE = 4 * 1024 # 4 KiB def create_event_loop(self): raise NotImplementedError diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py index 19582e75716..7454c8a1539 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -66,7 +66,8 @@ '?':0, 'c':0, 'b':0, 'B':0, 'h':0, 'H':0, 'i':0, 'I':0, 'l':0, 'L':0, 'n':0, 'N':0, - 'e':0, 'f':0, 'd':0, 'P':0 + 'e':0, 'f':0, 'd':0, 'P':0, + 'Zf':0, 'Zd':0, } # NumPy does not have 'n' or 'N': @@ -92,7 +93,9 @@ 'l':(-(1<<31), 1<<31), 'L':(0, 1<<32), 'q':(-(1<<63), 1<<63), 'Q':(0, 1<<64), 'e':(-65519, 65520), 'f':(-(1<<63), 1<<63), - 'd':(-(1<<1023), 1<<1023) + 'd':(-(1<<1023), 1<<1023), + 'Zf':(-(1<<63), 1<<63), + 'Zd':(-(1<<1023), 1<<1023), } def native_type_range(fmt): @@ -107,6 +110,10 @@ def native_type_range(fmt): lh = (-(1<<63), 1<<63) elif fmt == 'd': lh = (-(1<<1023), 1<<1023) + elif fmt == 'Zf': + lh = (-(1<<63), 1<<63) + elif fmt == 'Zd': + lh = (-(1<<1023), 1<<1023) else: for exp in (128, 127, 64, 63, 32, 31, 16, 15, 8, 7): try: @@ -136,7 +143,7 @@ def native_type_range(fmt): # Format codes supported by array.array ARRAY = NATIVE.copy() for k in NATIVE: - if not k in "bBhHiIlLfd": + if k not in list("bBhHiIlLefd") + ['Zf', 'Zd']: del ARRAY[k] BYTEFMT = NATIVE.copy() @@ -175,13 +182,28 @@ def randrange_fmt(mode, char, obj): if char in 'efd': x = struct.pack(char, x) x = struct.unpack(char, x)[0] + if char in ('Zf', 'Zd'): + y = randrange(*fmtdict[mode][char]) + x = complex(x, y) + x = struct.pack(char, x) + x = struct.unpack(char, x)[0] return x +def split_format(fmt): + i = 0 + while i < len(fmt): + if fmt[i] == 'Z': + n = 2 + else: + n = 1 + yield fmt[i:i + n] + i += n + def gen_item(fmt, obj): """Return single random item.""" mode, chars = fmt.split('#') x = [] - for c in chars: + for c in split_format(chars): x.append(randrange_fmt(mode, c, obj)) return x[0] if len(x) == 1 else tuple(x) @@ -242,9 +264,7 @@ def is_byte_format(fmt): def is_memoryview_format(fmt): """format suitable for memoryview""" - x = len(fmt) - return ((x == 1 or (x == 2 and fmt[0] == '@')) and - fmt[x-1] in MEMORYVIEW) + return fmt.removeprefix('@') in MEMORYVIEW NON_BYTE_FORMAT = [c for c in fmtdict['@'] if not is_byte_format(c)] @@ -636,14 +656,22 @@ def ndarray_from_structure(items, fmt, t, flags=0): return ndarray(items, shape=shape, strides=strides, format=fmt, offset=offset, flags=ND_WRITABLE|flags) +# Convert PEP 3118 formats to numpy dtypes +FORMAT_TO_DTYPE = { + 'Zf': 'F', + 'Zd': 'D', +} + def numpy_array_from_structure(items, fmt, t): """Return numpy_array from the tuple returned by rand_structure()""" memlen, itemsize, ndim, shape, strides, offset = t buf = bytearray(memlen) for j, v in enumerate(items): struct.pack_into(fmt, buf, j*itemsize, v) + # Replace Zd/Zf formats with D/F dtypes + dtype = FORMAT_TO_DTYPE.get(fmt, fmt) return numpy_array(buffer=buf, shape=shape, strides=strides, - dtype=fmt, offset=offset) + dtype=dtype, offset=offset) # ====================================================================== @@ -3015,7 +3043,7 @@ def test_memoryview_assign(self): m = memoryview(nd) self.assertRaises(TypeError, m.__setitem__, 0, 100) - ex = ndarray(list(range(120)), shape=[1,2,3,4,5], flags=ND_WRITABLE) + ex = ndarray(list(range(144)), shape=[1,2,3,4,6], flags=ND_WRITABLE) m1 = memoryview(ex) for fmt, _range in fmtdict['@'].items(): @@ -3025,7 +3053,7 @@ def test_memoryview_assign(self): continue m2 = m1.cast(fmt) lo, hi = _range - if fmt == 'd' or fmt == 'f': + if fmt in ("d", "f", "Zd", "Zf"): lo, hi = -2**1024, 2**1024 if fmt != 'P': # PyLong_AsVoidPtr() accepts negative numbers self.assertRaises(ValueError, m2.__setitem__, 0, lo-1) @@ -4447,6 +4475,43 @@ def test_bytearray_release_buffer_read_flag(self): with self.assertRaises(SystemError): obj.__buffer__(inspect.BufferFlags.WRITE) + @support.cpython_only + @unittest.skipIf(_testcapi is None, "requires _testcapi") + def test_bytearray_alignment(self): + # gh-140557: pointer alignment of buffers including empty allocation + # should be at least to `size_t`. + align = struct.calcsize("N") + cases = [ + bytearray(), + bytearray(1), + bytearray(b"0123456789abcdef"), + bytearray(16), + ] + ptrs = [_testcapi.buffer_pointer_as_int(array) for array in cases] + self.assertEqual([ptr % align for ptr in ptrs], [0]*len(ptrs)) + + @support.cpython_only + @unittest.skipIf(_testcapi is None, "requires _testcapi") + def test_array_alignment(self): + # gh-140557: pointer alignment of buffers including empty allocation + # should match the maximum array alignment. + formats = [fmt for fmt in ARRAY + if struct.calcsize(fmt) <= struct.calcsize('P')] + align = max(struct.calcsize(fmt) for fmt in formats) + cases = [array.array(fmt) for fmt in formats] + # Empty arrays + self.assertEqual( + [_testcapi.buffer_pointer_as_int(case) % align for case in cases], + [0] * len(cases), + ) + for case in cases: + case.append(0) + # Allocated arrays + self.assertEqual( + [_testcapi.buffer_pointer_as_int(case) % align for case in cases], + [0] * len(cases), + ) + @support.cpython_only def test_pybuffer_size_from_format(self): # basic tests diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index b18f16a6efc..76f1c351e15 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -3,7 +3,7 @@ set_recursion_limit, skip_on_s390x, skip_emscripten_stack_overflow, skip_wasi_stack_overflow, skip_if_sanitizer, - import_helper) + skip_if_huge_c_stack, import_helper) try: import _testcapi except ImportError: @@ -47,6 +47,54 @@ def fn(**kw): self.assertIsInstance(res, dict) self.assertEqual(list(res.items()), expected) + def test_kwargs_order_preserved(self): + # PEP 468: Preserving Keyword Argument Order + def fn(**kw): + return list(kw) + + self.assertEqual(fn(b=1, a=2, c=3), ['b', 'a', 'c']) + self.assertEqual(fn(c=3, a=2, b=1), ['c', 'a', 'b']) + # Unpacked mappings are merged in place, keeping their own order. + self.assertEqual(fn(z=0, **{'x': 1, 'a': 2}, y=3), + ['z', 'x', 'a', 'y']) + self.assertEqual(fn(**{'b': 1}, **{'a': 2}), ['b', 'a']) + # Named parameters are removed from **kwargs without reordering + # the rest. + def fn2(a, c=None, **kw): + return list(kw) + + self.assertEqual(fn2(d=1, a=2, b=3, c=4, e=5), ['d', 'b', 'e']) + + def test_kwargs_order_preserved_in_methods(self): + # PEP 468: Preserving Keyword Argument Order + class C: + def __init__(self, **kw): + self.init_kw = list(kw) + + def meth(self, **kw): + return list(kw) + + @classmethod + def cmeth(cls, **kw): + return list(kw) + + @staticmethod + def smeth(**kw): + return list(kw) + + c = C(b=1, a=2, c=3) + self.assertEqual(c.init_kw, ['b', 'a', 'c']) + self.assertEqual(c.meth(b=1, a=2, c=3), ['b', 'a', 'c']) + self.assertEqual(C.cmeth(b=1, a=2, c=3), ['b', 'a', 'c']) + self.assertEqual(C.smeth(b=1, a=2, c=3), ['b', 'a', 'c']) + + def test_kwargs_order_preserved_in_c_functions(self): + # PEP 468: Preserving Keyword Argument Order + self.assertEqual(list(dict(b=1, a=2, c=3)), ['b', 'a', 'c']) + self.assertEqual(list(dict(**{'b': 1}, a=2)), ['b', 'a']) + self.assertEqual(list(collections.OrderedDict(b=1, a=2, c=3)), + ['b', 'a', 'c']) + def test_frames_are_popped_after_failed_calls(self): # GH-93252: stuff blows up if we don't pop the new frame after # recovering from failed calls: @@ -1052,16 +1100,17 @@ def get_sp(): self.assertLess(lower_sp, this_sp) safe_margin = this_sp - lower_sp else: - self.assertLess(this_sp, lower_sp) + self.assertGreater(lower_sp, this_sp) safe_margin = lower_sp - this_sp - # Add an (arbitrary) extra 20% for safety - safe_margin *= 6 / 5 + # Add an (arbitrary) extra 25% for safety + safe_margin = safe_margin * 5 / 4 self.assertLess(safe_margin, _testinternalcapi.get_stack_margin()) @skip_on_s390x @unittest.skipIf(is_wasi and Py_DEBUG, "requires deep stack") @skip_if_sanitizer("requires deep stack", thread=True) @unittest.skipIf(_testcapi is None, "requires _testcapi") + @skip_if_huge_c_stack(90_000) @skip_emscripten_stack_overflow() @skip_wasi_stack_overflow() def test_super_deep(self): diff --git a/Lib/test/test_future_stmt/test_future.py b/Lib/test/test_future_stmt/test_future.py index 71f1e616116..faa3a2bfe12 100644 --- a/Lib/test/test_future_stmt/test_future.py +++ b/Lib/test/test_future_stmt/test_future.py @@ -349,6 +349,8 @@ def test_annotations(self): eq("(i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3))") eq("{i: 0 for i in (1, 2, 3)}") eq("{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))}") + eq("{**x for x in ()}") + eq("[*x for x in ()]") eq("[(x, y) for x, y in (a, b)]") eq("[(x,) for x, in (a,)]") eq("Python3 > Python2 > COBOL")