From e9ac834c1a3e281f3dd9b654256ab895327fcbb7 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 18 Apr 2025 14:35:10 +0300 Subject: [PATCH 1/3] gh-132673: Fix `ctypes.Structure` with `_align_=0` --- Lib/ctypes/_layout.py | 2 +- .../test_ctypes/test_aligned_structures.py | 25 ++++++++++++++++++- ...-04-18-14-34-43.gh-issue-132673.0sliCv.rst | 2 ++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-04-18-14-34-43.gh-issue-132673.0sliCv.rst diff --git a/Lib/ctypes/_layout.py b/Lib/ctypes/_layout.py index beb3b86414c010..0719e72cfed312 100644 --- a/Lib/ctypes/_layout.py +++ b/Lib/ctypes/_layout.py @@ -84,7 +84,7 @@ def get_layout(cls, input_fields, is_struct, base): raise ValueError('_align_ must be a non-negative integer') elif align == 0: # Setting `_align_ = 0` amounts to using the default alignment - align == 1 + align = 1 if base: align = max(ctypes.alignment(base), align) diff --git a/Lib/test/test_ctypes/test_aligned_structures.py b/Lib/test/test_ctypes/test_aligned_structures.py index 26d24f31b29f7b..cac7226ddefdb9 100644 --- a/Lib/test/test_ctypes/test_aligned_structures.py +++ b/Lib/test/test_ctypes/test_aligned_structures.py @@ -1,7 +1,7 @@ from ctypes import ( c_char, c_uint32, c_uint16, c_ubyte, c_byte, alignment, sizeof, BigEndianStructure, LittleEndianStructure, - BigEndianUnion, LittleEndianUnion, + BigEndianUnion, LittleEndianUnion, Structure, ) import struct import unittest @@ -69,6 +69,29 @@ class Main(base): self.assertEqual(Main.z.offset, 8) self.assertEqual(main.z, 7) + def test_negative_align(self): + for base in (Structure, LittleEndianStructure, BigEndianStructure): + with ( + self.subTest(base=base), + self.assertRaisesRegex( + ValueError, + '_align_ must be a non-negative integer', + ) + ): + class MyStructure(base): + _align_ = -1 + _fields_ = [] + + def test_zero_align(self): + for base in (Structure, LittleEndianStructure, BigEndianStructure): + with self.subTest(base=base): + class MyStructure(base): + _align_ = 0 + _fields_ = [] + + self.assertEqual(alignment(MyStructure), 1) + self.assertEqual(alignment(MyStructure()), 1) + def test_oversized_structure(self): data = bytearray(b"\0" * 8) for base in (LittleEndianStructure, BigEndianStructure): diff --git a/Misc/NEWS.d/next/Library/2025-04-18-14-34-43.gh-issue-132673.0sliCv.rst b/Misc/NEWS.d/next/Library/2025-04-18-14-34-43.gh-issue-132673.0sliCv.rst new file mode 100644 index 00000000000000..3c6e6afc68fee1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-18-14-34-43.gh-issue-132673.0sliCv.rst @@ -0,0 +1,2 @@ +Fix :exc:`AssertionError` raised on :class:`ctypes.Structure` with a +``_align_ = 0``. From d724c4666591f1a70e8f5a8ac9abb108f6ac1a73 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 18 Apr 2025 16:16:00 +0300 Subject: [PATCH 2/3] Update Misc/NEWS.d/next/Library/2025-04-18-14-34-43.gh-issue-132673.0sliCv.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- .../Library/2025-04-18-14-34-43.gh-issue-132673.0sliCv.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2025-04-18-14-34-43.gh-issue-132673.0sliCv.rst b/Misc/NEWS.d/next/Library/2025-04-18-14-34-43.gh-issue-132673.0sliCv.rst index 3c6e6afc68fee1..4d5a26caf0ea90 100644 --- a/Misc/NEWS.d/next/Library/2025-04-18-14-34-43.gh-issue-132673.0sliCv.rst +++ b/Misc/NEWS.d/next/Library/2025-04-18-14-34-43.gh-issue-132673.0sliCv.rst @@ -1,2 +1,2 @@ -Fix :exc:`AssertionError` raised on :class:`ctypes.Structure` with a -``_align_ = 0``. +Fix :exc:`AssertionError` raised on :class:`ctypes.Structure` with +``_align_ = 0`` and ``_fields_ = []``. From 2619a90584384aa1624ebaff7d286aa4b8d5e5af Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 18 Apr 2025 16:37:15 +0300 Subject: [PATCH 3/3] Add a test case with fields --- Lib/test/test_ctypes/test_aligned_structures.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_ctypes/test_aligned_structures.py b/Lib/test/test_ctypes/test_aligned_structures.py index cac7226ddefdb9..0c563ab80559a6 100644 --- a/Lib/test/test_ctypes/test_aligned_structures.py +++ b/Lib/test/test_ctypes/test_aligned_structures.py @@ -82,7 +82,7 @@ class MyStructure(base): _align_ = -1 _fields_ = [] - def test_zero_align(self): + def test_zero_align_no_fields(self): for base in (Structure, LittleEndianStructure, BigEndianStructure): with self.subTest(base=base): class MyStructure(base): @@ -92,6 +92,18 @@ class MyStructure(base): self.assertEqual(alignment(MyStructure), 1) self.assertEqual(alignment(MyStructure()), 1) + def test_zero_align_with_fields(self): + for base in (Structure, LittleEndianStructure, BigEndianStructure): + with self.subTest(base=base): + class MyStructure(base): + _align_ = 0 + _fields_ = [ + ("x", c_ubyte), + ] + + self.assertEqual(alignment(MyStructure), 1) + self.assertEqual(alignment(MyStructure()), 1) + def test_oversized_structure(self): data = bytearray(b"\0" * 8) for base in (LittleEndianStructure, BigEndianStructure):