8000 gh-128715: Expose ctypes.CField, with info attributes by encukou · Pull Request #128950 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-128715: Expose ctypes.CField, with info attributes #128950

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 35 commits into from
Mar 24, 2025
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9d4be49
Restore max field size to sys.maxsize, as in Python 3.13 & below
encukou Dec 18, 2024
09c81a8
PyCField: Split out bit/byte sizes/offsets.
encukou Jan 9, 2025
c397cf4
Expose CField
encukou Jan 10, 2025
20ecd84
Add generic checks for all the test structs/unions
encukou Jan 17, 2025
60e7b32
More testing
encukou Jan 17, 2025
18334d8
Tests: import CField from ctypes
encukou Jan 17, 2025
294b1b8
Clarify bit_offset
encukou Jan 17, 2025
52114fa
Add a blurb
encukou Jan 17, 2025
32d41f3
Regen
encukou Jan 17, 2025
f6f596f
Merge in the main branch
encukou Jan 17, 2025
9b0e7eb
include <stdbool.h> in the common header
encukou Jan 17, 2025
d7bd835
Explicit casts
encukou Jan 17, 2025
06458f6
Remove problematic assert
encukou Jan 17, 2025
bb41481
Merge in the main branch
encukou Jan 24, 2025
91365b0
Add a test for the new info, and fix 'name' for nested anonymous structs
encukou Jan 24, 2025
b6d0510
Use subTest
encukou Jan 24, 2025
6e279e2
Use PyUnicode_FromObject to get an exact PyUnicode
encukou Jan 27, 2025
176de87
Normalize exception message
encukou Jan 31, 2025
085720e
Fix refcounting
encukou Jan 31, 2025
05c9591
Add pretty spaces
encukou Jan 31, 2025
b77074c
Merge in the main branch
encukou Jan 31, 2025
4e95755
Fix bit-packed size test for big-endian machines
encukou Jan 31, 2025
9cde20e
Remove `size` from _layout.py
encukou Jan 31, 2025
34865e8
Fix bit_offset for big-endian structs (where bitfields are laid out f…
encukou Feb 7, 2025
89fc44d
Merge in the main branch
encukou Feb 7, 2025
f327ffb
Name the magic constant
encukou Feb 7, 2025
14270ad
Remove unused variable
encukou Feb 7, 2025
7ce3cb9
Remove an unacceptable blank line
encukou Feb 7, 2025
d9d593c
Update documentation for tp_basicsize & tp_itemsize
encukou Feb 8, 2025
d52b8c9
Merge in the main branch
encukou Feb 21, 2025
2cdcb5b
Skip "is in" test for bitfields of underaligned types (bug filed)
encukou Feb 21, 2025
753090a
Merge in the main branch
encukou Mar 14, 2025
fde4204
Address review
encukou Mar 14, 2025
b2fddd8
One more alignment
encukou Mar 14, 2025
5ce595c
Don't use `self` while it's NULL
encukou Mar 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 98 additions & 5 deletions Doc/library/ctypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -657,12 +657,13 @@ Nested structures can also be initialized in the constructor in several ways::
>>> r = RECT((1, 2), (3, 4))

Field :term:`descriptor`\s can be retrieved from the *class*, they are useful
for debugging because they can provide useful information::
for debugging because they can provide useful information.
See :class:`CField`::

>>> print(POINT.x)
<Field type=c_long, ofs=0, size=4>
>>> print(POINT.y)
<Field type=c_long, ofs=4, size=4>
>>> POINT.x
<ctypes.CField 'x' type=c_int, ofs=0, size=4>
>>> POINT.y
<ctypes.CField 'y' type=c_int, ofs=4, size=4>
>>>


Expand Down Expand Up @@ -2776,6 +2777,98 @@ fields, or any other data types containing pointer type fields.
present in :attr:`_fields_`.


.. class:: CField(*args, **kw)

Descriptor for fields of a :class:`Structure` and :class:`Union`.
For example::

>>> class Color(Structure):
... _fields_ = (
... ('red', c_uint8),
... ('green', c_uint8),
... ('blue', c_uint8),
... ('intense', c_bool, 1),
... ('blinking', c_bool, 1),
... )
...
>>> Color.red
<ctypes.CField 'red' type=c_ubyte, ofs=0, size=1>
>>> Color.green.type
<class 'ctypes.c_ubyte'>
>>> Color.blue.byte_offset
2
>>> Color.intense
<ctypes.CField 'intense' type=c_bool, ofs=3, bit_size=1, bit_offset=0>
>>> Color.blinking.bit_offset
1

All attributes are read-only.

:class:`!CField` objects are created via :attr:`~Structure._fields_`;
do not instantiate the class directly.

.. versionadded:: next

Previously, descriptors only had ``offset`` and ``size`` attributes
and a readable string representation; the :class:`!CField` class was not
available directly.

.. attribute:: name

Name of the field, as a string.

.. attribute:: type

Type of the field, as a :ref:`ctypes class <ctypes-data-types>`.

.. attribute:: offset
byte_offset

Offset of the field, in bytes.

For bitfields, this excludes any :attr:`~CField.bit_offset`.

.. attribute:: byte_size

Size of the field, in bytes.

For bitfields, this is the size of the underlying type, which may be
much larger than the field itself.

.. attribute:: size

For non-bitfields, equivalent to :attr:`~CField.byte_size`.

For bitfields, this contains a backwards-compatible bit-packed
value that combines :attr:`~CField.bit_size` and
:attr:`~CField.bit_offset`.
Prefer using the explicit attributes instead.

.. attribute:: is_bitfield

True if this is a bitfield.

.. attribute:: bit_offset

Additional offset of a bitfield, in bits.
The value is relative to :attr:`byte_offset`. That is, the *total* bit
offset, from the start of the structure,
is ``byte_offset * 8 + bit_offset``.

Zero for non-bitfields.

.. attribute:: bit_size

Size of the field, in bits.

For non-bitfields, this is equal to ``byte_size * 8``.

.. attribute:: is_anonymous

True if this field is anonymous, that is, it contains nested sub-fields
that should be be merged into a containing structure or union.


.. _ctypes-arrays-pointers:

Arrays and pointers
Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,11 @@ ctypes
to help match a non-default ABI.
(Contributed by Petr Viktorin in :gh:`97702`.)

* The class of :class:`~ctypes.Structure`/:class:`~ctypes.Union`
field descriptors is now available as :class:`~ctypes.CField`,
and has new attributes to aid debugging and introspection.
(Contributed by Petr Viktorin in :gh:`128715`.)

* On Windows, the :exc:`~ctypes.COMError` exception is now public.
(Contributed by Jun Komoda in :gh:`126686`.)

Expand Down
5 changes: 5 additions & 0 deletions Include/internal/pycore_global_objects_fini_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Include/internal/pycore_global_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(_get_sourcefile)
STRUCT_FOR_ID(_handle_fromlist)
STRUCT_FOR_ID(_initializing)
STRUCT_FOR_ID(_internal_use)
STRUCT_FOR_ID(_io)
STRUCT_FOR_ID(_is_text_encoding)
STRUCT_FOR_ID(_isatty_open_only)
Expand Down Expand Up @@ -295,6 +296,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(before)
STRUCT_FOR_ID(big)
STRUCT_FOR_ID(binary_form)
STRUCT_FOR_ID(bit_offset)
STRUCT_FOR_ID(bit_size)
STRUCT_FOR_ID(block)
STRUCT_FOR_ID(bound)
Expand All @@ -305,6 +307,8 @@ struct _Py_global_strings {
STRUCT_FOR_ID(buffers)
STRUCT_FOR_ID(bufsize)
STRUCT_FOR_ID(builtins)
STRUCT_FOR_ID(byte_offset)
STRUCT_FOR_ID(byte_size)
STRUCT_FOR_ID(byteorder)
STRUCT_FOR_ID(bytes)
STRUCT_FOR_ID(bytes_per_sep)
Expand Down Expand Up @@ -440,6 +444,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(flush)
STRUCT_FOR_ID(fold)
STRUCT_FOR_ID(follow_symlinks)
STRUCT_FOR_ID(for_big_endian)
STRUCT_FOR_ID(format)
STRUCT_FOR_ID(format_spec)
STRUCT_FOR_ID(from_param)
Expand Down
5 changes: 5 additions & 0 deletions Include/internal/pycore_runtime_init_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions Include/internal/pycore_unicodeobject_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Lib/ctypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from _ctypes import RTLD_LOCAL, RTLD_GLOBAL
from _ctypes import ArgumentError
from _ctypes import SIZEOF_TIME_T
from _ctypes import CField

from struct import calcsize as _calcsize

Expand Down
14 changes: 11 additions & 3 deletions Lib/ctypes/_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ def get_layout(cls, input_fields, is_struct, base):
offset = round_down(next_bit_offset, type_bit_align) // 8
if is_bitfield:
effective_bit_offset = next_bit_offset - 8 * offset
bit_offset = effective_bit_offset
size = build_size(bit_size, effective_bit_offset,
big_endian, type_size)
assert effective_bit_offset <= type_bit_size
Expand Down Expand Up @@ -255,8 +256,9 @@ def get_layout(cls, input_fields, is_struct, base):
offset = next_byte_offset - last_field_bit_size // 8
if is_bitfield:
assert 0 <= (last_field_bit_size + next_bit_offset)
bit_offset = last_field_bit_size + next_bit_offset
size = build_size(bit_size,
last_field_bit_size + next_bit_offset,
bit_offset,
big_endian, type_size)
else:
size = type_size
Expand Down Expand Up @@ -294,10 +296,16 @@ def get_layout(cls, input_fields, is_struct, base):
result_fields.append(CField(
name=name,
type=ctype,
size=size,
offset=offset,
byte_size=type_size,
byte_offset=offset,
bit_size=bit_size if is_bitfield else None,
bit_offset=bit_offset if is_bitfield else None,
index=i,
for_big_endian=big_endian,

# Do not use CField outside ctypes, yet.
# The constructor is internal API and may change without warning.
_internal_use=True,
))
if is_bitfield and not gcc_layout:
assert type_bit_size > 0
Expand Down
Loading
Loading
0