.. include:: pending-removal-in-3.17.rst
+.. include:: pending-removal-in-3.19.rst
+
.. include:: pending-removal-in-future.rst
C API deprecations
--- /dev/null
+Pending removal in Python 3.19
+------------------------------
+
+* :mod:`ctypes`:
+
+ * Implicitly switching to the MSVC-compatible struct layout by setting
+ :attr:`~ctypes.Structure._pack_` but not :attr:`~ctypes.Structure._layout_`
+ on non-Windows platforms.
when :attr:`_fields_` is assigned, otherwise it will have no effect.
Setting this attribute to 0 is the same as not setting it at all.
+ This is only implemented for the MSVC-compatible memory layout.
+
+ .. deprecated-removed:: next 3.19
+
+ For historical reasons, if :attr:`!_pack_` is non-zero,
+ the MSVC-compatible layout will be used by default.
+ On non-Windows platforms, this default is deprecated and is slated to
+ become an error in Python 3.19.
+ If it is intended, set :attr:`~Structure._layout_` to ``'ms'``
+ explicitly.
.. attribute:: _align_
Currently the default will be:
- On Windows: ``"ms"``
- - When :attr:`~Structure._pack_` is specified: ``"ms"``
+ - When :attr:`~Structure._pack_` is specified: ``"ms"``.
+ (This is deprecated; see :attr:`~Structure._pack_` documentation.)
- Otherwise: ``"gcc-sysv"``
:attr:`!_layout_` must already be defined when
:attr:`~Structure._fields_` is assigned, otherwise it will have no effect.
+ .. versionadded:: next
+
.. attribute:: _anonymous_
An optional sequence that lists the names of unnamed (anonymous) fields.
:func:`codecs.open` is now deprecated. Use :func:`open` instead.
(Contributed by Inada Naoki in :gh:`133036`.)
+* :mod:`ctypes`:
+ On non-Windows platforms, setting :attr:`.Structure._pack_` to use a
+ MSVC-compatible default memory layout is deprecated in favor of setting
+ :attr:`.Structure._layout_` to ``'ms'``.
+ (Contributed by Petr Viktorin in :gh:`131747`.)
+
* :mod:`ctypes`:
Calling :func:`ctypes.POINTER` on a string is deprecated.
Use :ref:`ctypes-incomplete-types` for self-referential structures.
.. include:: ../deprecations/pending-removal-in-3.17.rst
+.. include:: ../deprecations/pending-removal-in-3.19.rst
+
.. include:: ../deprecations/pending-removal-in-future.rst
Removed
"""
import sys
+import warnings
from _ctypes import CField, buffer_info
import ctypes
# For clarity, variables that count bits have `bit` in their names.
+ pack = getattr(cls, '_pack_', None)
+
layout = getattr(cls, '_layout_', None)
if layout is None:
- if sys.platform == 'win32' or getattr(cls, '_pack_', None):
+ if sys.platform == 'win32':
+ gcc_layout = False
+ elif pack:
+ if is_struct:
+ base_type_name = 'Structure'
+ else:
+ base_type_name = 'Union'
+ warnings._deprecated(
+ '_pack_ without _layout_',
+ f"Due to '_pack_', the '{cls.__name__}' {base_type_name} will "
+ + "use memory layout compatible with MSVC (Windows). "
+ + "If this is intended, set _layout_ to 'ms'. "
+ + "The implicit default is deprecated and slated to become "
+ + "an error in Python {remove}.",
+ remove=(3, 19),
+ )
gcc_layout = False
else:
gcc_layout = True
else:
big_endian = sys.byteorder == 'big'
- pack = getattr(cls, '_pack_', None)
if pack is not None:
try:
pack = int(pack)
class Main(sbase):
_pack_ = 1
+ _layout_ = "ms"
_fields_ = [
("a", c_ubyte),
("b", Inner),
def test_gh_84039(self):
class Bad(Structure):
_pack_ = 1
+ _layout_ = "ms"
_fields_ = [
("a0", c_uint8, 1),
("a1", c_uint8, 1),
("b1", c_uint16, 12),
]
-
class GoodA(Structure):
_pack_ = 1
+ _layout_ = "ms"
_fields_ = [
("a0", c_uint8, 1),
("a1", c_uint8, 1),
class Good(Structure):
_pack_ = 1
+ _layout_ = "ms"
_fields_ = [
("a", GoodA),
("b0", c_uint16, 4),
def test_gh_73939(self):
class MyStructure(Structure):
_pack_ = 1
+ _layout_ = "ms"
_fields_ = [
("P", c_uint16),
("L", c_uint16, 9),
class S(base):
_pack_ = 1
+ _layout_ = "ms"
_fields_ = [("b", c_byte),
("h", c_short),
class S(Structure):
_pack_ = 1
+ _layout_ = "ms"
_fields_ = [("b", c_byte),
("h", c_short),
class Packed1(Structure):
_fields_ = [('a', c_int8), ('b', c_int64)]
_pack_ = 1
+ _layout_ = 'ms'
@register()
class Packed2(Structure):
_fields_ = [('a', c_int8), ('b', c_int64)]
_pack_ = 2
+ _layout_ = 'ms'
@register()
class Packed3(Structure):
_fields_ = [('a', c_int8), ('b', c_int64)]
_pack_ = 4
+ _layout_ = 'ms'
@register()
_fields_ = [('a', c_int8), ('b', c_int64)]
_pack_ = 8
+ _layout_ = 'ms'
@register()
class X86_32EdgeCase(Structure):
@register()
class Example_gh_84039_bad(Structure):
_pack_ = 1
+ _layout_ = 'ms'
_fields_ = [("a0", c_uint8, 1),
("a1", c_uint8, 1),
("a2", c_uint8, 1),
@register()
class Example_gh_84039_good_a(Structure):
_pack_ = 1
+ _layout_ = 'ms'
_fields_ = [("a0", c_uint8, 1),
("a1", c_uint8, 1),
("a2", c_uint8, 1),
@register()
class Example_gh_84039_good(Structure):
_pack_ = 1
+ _layout_ = 'ms'
_fields_ = [("a", Example_gh_84039_good_a),
("b0", c_uint16, 4),
("b1", c_uint16, 12)]
@register()
class Example_gh_73939(Structure):
_pack_ = 1
+ _layout_ = 'ms'
_fields_ = [("P", c_uint16),
("L", c_uint16, 9),
("Pro", c_uint16, 1),
@register()
class Example_gh_86098_pack(Structure):
_pack_ = 1
+ _layout_ = 'ms'
_fields_ = [("a", c_uint8, 8),
("b", c_uint8, 8),
("c", c_uint32, 16)]
pushes.append(f'#pragma pack(push, {pack})')
pops.append(f'#pragma pack(pop)')
layout = getattr(tp, '_layout_', None)
- if layout == 'ms' or pack:
+ if layout == 'ms':
# The 'ms_struct' attribute only works on x86 and PowerPC
requires.add(
'defined(MS_WIN32) || ('
class PackedPoint(Structure):
_pack_ = 2
+ _layout_ = 'ms'
_fields_ = [("x", c_long), ("y", c_long)]
class PointMidPad(Structure):
class PackedPointMidPad(Structure):
_pack_ = 2
+ _layout_ = 'ms'
_fields_ = [("x", c_byte), ("y", c_uint64)]
class PointEndPad(Structure):
class PackedPointEndPad(Structure):
_pack_ = 2
+ _layout_ = 'ms'
_fields_ = [("x", c_uint64), ("y", c_byte)]
class Point2(Structure):
Py_TPFLAGS_DISALLOW_INSTANTIATION,
Py_TPFLAGS_IMMUTABLETYPE)
from struct import calcsize
+import contextlib
+from test.support import MS_WINDOWS
class StructUnionTestBase:
self.assertIn("from_address", dir(type(self.cls)))
self.assertIn("in_dll", dir(type(self.cls)))
+ def test_pack_layout_switch(self):
+ # Setting _pack_ implicitly sets default layout to MSVC;
+ # this is deprecated on non-Windows platforms.
+ if MS_WINDOWS:
+ warn_context = contextlib.nullcontext()
+ else:
+ warn_context = self.assertWarns(DeprecationWarning)
+ with warn_context:
+ class X(self.cls):
+ _pack_ = 1
+ # _layout_ missing
+ _fields_ = [('a', c_int8, 1), ('b', c_int16, 2)]
+
+ # Check MSVC layout (bitfields of different types aren't combined)
+ self.check_sizeof(X, struct_size=3, union_size=2)
+
class StructureTestCase(unittest.TestCase, StructUnionTestBase):
cls = Structure
_fields_ = [("a", c_byte),
("b", c_longlong)]
_pack_ = 1
+ _layout_ = 'ms'
self.check_struct(X)
self.assertEqual(sizeof(X), 9)
_fields_ = [("a", c_byte),
("b", c_longlong)]
_pack_ = 2
+ _layout_ = 'ms'
self.check_struct(X)
self.assertEqual(sizeof(X), 10)
self.assertEqual(X.b.offset, 2)
_fields_ = [("a", c_byte),
("b", c_longlong)]
_pack_ = 4
+ _layout_ = 'ms'
self.check_struct(X)
self.assertEqual(sizeof(X), min(4, longlong_align) + longlong_size)
self.assertEqual(X.b.offset, min(4, longlong_align))
_fields_ = [("a", c_byte),
("b", c_longlong)]
_pack_ = 8
+ _layout_ = 'ms'
self.check_struct(X)
self.assertEqual(sizeof(X), min(8, longlong_align) + longlong_size)
self.assertEqual(X.b.offset, min(8, longlong_align))
-
- d = {"_fields_": [("a", "b"),
- ("b", "q")],
- "_pack_": -1}
- self.assertRaises(ValueError, type(Structure), "X", (Structure,), d)
+ with self.assertRaises(ValueError):
+ class X(Structure):
+ _fields_ = [("a", "b"), ("b", "q")]
+ _pack_ = -1
+ _layout_ = "ms"
@support.cpython_only
def test_packed_c_limits(self):
# Issue 15989
import _testcapi
- d = {"_fields_": [("a", c_byte)],
- "_pack_": _testcapi.INT_MAX + 1}
- self.assertRaises(ValueError, type(Structure), "X", (Structure,), d)
- d = {"_fields_": [("a", c_byte)],
- "_pack_": _testcapi.UINT_MAX + 2}
- self.assertRaises(ValueError, type(Structure), "X", (Structure,), d)
+ with self.assertRaises(ValueError):
+ class X(Structure):
+ _fields_ = [("a", c_byte)]
+ _pack_ = _testcapi.INT_MAX + 1
+ _layout_ = "ms"
+
+ with self.assertRaises(ValueError):
+ class X(Structure):
+ _fields_ = [("a", c_byte)]
+ _pack_ = _testcapi.UINT_MAX + 2
+ _layout_ = "ms"
def test_initializers(self):
class Person(Structure):
c_ushort, c_uint, c_ulong, c_ulonglong]:
class X(Structure):
_pack_ = 1
+ _layout_ = 'ms'
_fields_ = [("pad", c_byte),
("value", typ)]
class Y(SwappedStructure):
_pack_ = 1
+ _layout_ = 'ms'
_fields_ = [("pad", c_byte),
("value", typ)]
structures.append(X)
--- /dev/null
+On non-Windows platforms, deprecate using :attr:`ctypes.Structure._pack_` to
+use a Windows-compatible layout on non-Windows platforms. The layout should
+be specified explicitly by setting :attr:`ctypes.Structure._layout_` to
+``'ms'``.