From 678b8e165646e72a2f39f017ad237210d3d481ef Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 18 Apr 2025 17:32:28 +0300 Subject: [PATCH] gh-132673: Fix `ctypes.Structure` with `_align_=0` (#132676) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/ctypes/_layout.py | 2 +- .../test_ctypes/test_aligned_structures.py | 37 ++++++++++++++++++- ...-04-18-14-34-43.gh-issue-132673.0sliCv.rst | 2 + 3 files changed, 39 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 beb3b86414c0..0719e72cfed3 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 26d24f31b29f..0c563ab80559 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,41 @@ class TestAlignedStructures(unittest.TestCase, StructCheckMixin): 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_no_fields(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_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): 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 000000000000..4d5a26caf0ea --- /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 +``_align_ = 0`` and ``_fields_ = []``. -- 2.47.3