Skip to content

gh-104533: Add a dataclass-like decorator for ctypes structures#153781

Draft
ZeroIntensity wants to merge 4 commits into
python:mainfrom
ZeroIntensity:ctypes/struct-decorator
Draft

gh-104533: Add a dataclass-like decorator for ctypes structures#153781
ZeroIntensity wants to merge 4 commits into
python:mainfrom
ZeroIntensity:ctypes/struct-decorator

Conversation

@ZeroIntensity

@ZeroIntensity ZeroIntensity commented Jul 15, 2026

Copy link
Copy Markdown
Member

@read-the-docs-community

read-the-docs-community Bot commented Jul 15, 2026

Copy link
Copy Markdown

Documentation build overview

📚 cpython-previews | 🛠️ Build #33605501 | 📁 Comparing bba7682 against main (b4662e8)

  🔍 Preview build  

3 files changed
± library/ctypes.html
± whatsnew/3.16.html
± whatsnew/changelog.html

Comment thread Lib/ctypes/util.py
Comment on lines +543 to +564
# These fields don't apply correctly when set later.
# As a workaround, we have this weird _StructData thing to set the attributes
# in advance.
class _StructData:
pass

if align is not None:
_StructData._align_ = align

if layout is not None:
_StructData._layout_ = layout

if pack is not None:
_StructData._pack_ = pack

for attr, value in klass.__dict__.items():
if attr != "__dict__":
setattr(_StructData, attr, value)

class _Struct(_StructData, endian_class):
_fields_ = fields
_anonymous_ = anonymous

@johnslavik johnslavik Jul 15, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works for me just fine:

Suggested change
# These fields don't apply correctly when set later.
# As a workaround, we have this weird _StructData thing to set the attributes
# in advance.
class _StructData:
pass
if align is not None:
_StructData._align_ = align
if layout is not None:
_StructData._layout_ = layout
if pack is not None:
_StructData._pack_ = pack
for attr, value in klass.__dict__.items():
if attr != "__dict__":
setattr(_StructData, attr, value)
class _Struct(_StructData, endian_class):
_fields_ = fields
_anonymous_ = anonymous
class _Struct(endian_class):
vars().update(vars(klass))
if align is not None:
_align_ = align
if layout is not None:
_layout_ = layout
if pack is not None:
_pack_ = pack
_fields_ = fields
_anonymous_ = anonymous

but maybe I'm missing sth

Comment thread Lib/ctypes/util.py

_Struct.__name__ = klass.__name__
_Struct.__qualname__ = klass.__qualname__
_Struct.__module__ = klass.__module__

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using lazy-imported functools.wraps for that.
You'd decorate the class with @wraps(klass, updated=()) (updated=() because __dict__ is unwritable).

Comment thread Lib/ctypes/util.py
Comment on lines +574 to +577
def inner(klass):
return _process_struct(klass, align=align, layout=layout, endian=endian, pack=pack)

return inner

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you decide to apply functools above, you could return a partial() here idiomatically.

Comment thread Lib/ctypes/util.py
def _process_struct(klass, /, *, align, layout, endian, pack):
fields = []
anonymous = []
if issubclass(klass, (Structure, LittleEndianStructure, BigEndianStructure)):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious why not

Suggested change
if issubclass(klass, (Structure, LittleEndianStructure, BigEndianStructure)):
if issubclass(klass, Structure):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants