https://github.com/python/cpython/commit/83674483563c83550c87311adb557f8565916ce0
commit: 83674483563c83550c87311adb557f8565916ce0
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-07-19T12:05:22+02:00
summary:
gh-104533: Fix `@ctypes.util.struct` with string annotations (#154040)
Fix `@ctypes.util.struct` on structures declared in a module which uses
`from __future__ import annotations`. The decorator now calls
get_annotations() with eval_str=True.
files:
A Lib/test/test_ctypes/struct_str_ann.py
M Lib/ctypes/util.py
M Lib/test/test_ctypes/test_structures.py
diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py
index c65fc3032fdfad..e8db8c578bb360 100644
--- a/Lib/ctypes/util.py
+++ b/Lib/ctypes/util.py
@@ -512,7 +512,8 @@ def _process_struct(decorated_class, /, *, align, layout,
endian, pack):
fields.extend(decorated_class._fields_)
anonymous.extend(decorated_class._anonymous_)
- for name, hint in annotationlib.get_annotations(decorated_class).items():
+ annotations = annotationlib.get_annotations(decorated_class, eval_str=True)
+ for name, hint in annotations.items():
if get_origin(hint) is ClassVar:
continue
diff --git a/Lib/test/test_ctypes/struct_str_ann.py
b/Lib/test/test_ctypes/struct_str_ann.py
new file mode 100644
index 00000000000000..7a924825dd81ee
--- /dev/null
+++ b/Lib/test/test_ctypes/struct_str_ann.py
@@ -0,0 +1,15 @@
+from __future__ import annotations
+from ctypes.util import struct
+from ctypes import c_int
+
+class TestAnn:
+ x: c_int
+
+# Check that "from __future__ import annotations" works as expected
+if not isinstance(TestAnn.__annotations__['x'], str):
+ raise Exception("annotations must be strings")
+
+@struct
+class Point:
+ x: c_int
+ y: c_int
diff --git a/Lib/test/test_ctypes/test_structures.py
b/Lib/test/test_ctypes/test_structures.py
index 4dc1ea867c0700..04c3682e8308ec 100644
--- a/Lib/test/test_ctypes/test_structures.py
+++ b/Lib/test/test_ctypes/test_structures.py
@@ -88,13 +88,14 @@ class X(Structure):
self.assertEqual(sizeof(X), min(8, longlong_align) + longlong_size)
self.assertEqual(X.b.offset, min(8, longlong_align))
- with self.assertRaises(ValueError):
- if use_struct_util:
+ if use_struct_util:
+ with self.assertRaises(NameError):
@struct_util(pack=-1, layout='ms')
class X:
a: "b"
b: "q"
- else:
+ else:
+ with self.assertRaises(ValueError):
class X(Structure):
_fields_ = [("a", "b"), ("b", "q")]
_pack_ = -1
@@ -954,6 +955,12 @@ class Foo:
self.assertEqual(Foo.__name__, "Foo")
+ def test_string_annotations(self):
+ from test.test_ctypes import struct_str_ann
+ Point = struct_str_ann.Point
+ fields = [['x', c_int], ['y', c_int]]
+ self.assertEqual(Point._fields_, fields)
+
if __name__ == '__main__':
unittest.main()
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]