Fix Python 3.12 compatibility for ctypes bitfield structures #82
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Python 3.12 Compatibility Changes
Summary
This document outlines the changes made to make the
open-dis-pythonlibrary compatible with Python 3.12. The primary issue addressed was ctypes structure padding and alignment, which became stricter in Python 3.12.Problem Statement
When running on Python 3.12, the library was failing due to changes in how
ctypeshandles bitfield structures. Python 3.12 enforced stricter rules about struct padding, requiring all bitfields within a singlectypes.Structureto use the same underlying C type to avoid unexpected padding bytes.Files Changed
1.
opendis/record/bitfield.py(CRITICAL FIX)Lines changed: 119 lines (new) vs 101 lines (old)
Key Changes:
OLD Implementation (Pattern Matching Approach):
Problem: Each field could potentially use a different base type (
c_uint8,c_uint16,c_uint32) based on its individual size. This caused padding issues in Python 3.12 when fields of different sizes were combined in the same structure.NEW Implementation (Two-Pass Approach):
NEW
bitfield()function logic:Solution:
base_ctypebased on the TOTAL size_field()function now takesbase_ctypeas a parameter rather than inferring it from individual field sizes2.
.gitignore(Housekeeping)Changes:
__pycache*→__pycache__*(added missing underscore)*.pyc(explicitly exclude compiled Python files)*.pyo(exclude optimized bytecode files)3. Files NOT Changed
The following files remained unchanged as they were already compatible with Python 3.12:
opendis/dis7.py- Already using modern type annotations (list[...],| None)opendis/DataInputStream.py- Binary I/O, version agnosticopendis/DataOutputStream.py- Binary I/O, version agnosticopendis/PduFactory.py- Factory pattern, compatibleopendis/RangeCoordinates.py- Math utilities, compatiblepyproject.toml- Already requires Python >=3.10Technical Details
Why This Fix Was Necessary
In Python 3.12, the
ctypesmodule became stricter about structure layout. When a bitfield structure contains multiple fields with different underlying C types, the C compiler may insert padding bytes to maintain proper alignment. This caused:The Solution
By ensuring all bitfields use the same underlying C type determined by the TOTAL structure size:
sizeof(Bitfield)equals the expected byte sizeExample
Consider a bitfield with these fields:
OLD (Python 3.11 and earlier):
c_uint8(3 bits <= 8)c_uint16(10 bits > 8, <= 16)NEW (Python 3.12 compatible):
c_uint16c_uint16Testing
The changes maintain backward compatibility with Python 3.10+ while fixing compatibility with Python 3.12.
Impact
This change affects all DIS records that use bitfields, including:
The fix ensures correct binary serialization/deserialization across all Python 3.10+ versions.
Credits
These changes enable the
open-dis-pythonlibrary to work correctly on Python 3.12 while maintaining compatibility with earlier Python 3.10+ versions.