[pypy-commit] pypy default: Issue #2230: _anonymous_ fields were tested at the level of classes, but

arigo pypy.commits at gmail.com
Thu Mar 31 04:08:04 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r83449:3d53b5735ff2
Date: 2016-03-31 10:07 +0200
http://bitbucket.org/pypy/pypy/changeset/3d53b5735ff2/

Log:	Issue #2230: _anonymous_ fields were tested at the level of classes,
	but didn't work on instances

diff --git a/lib_pypy/_ctypes/structure.py b/lib_pypy/_ctypes/structure.py
--- a/lib_pypy/_ctypes/structure.py
+++ b/lib_pypy/_ctypes/structure.py
@@ -67,7 +67,8 @@
                     subvalue = subfield.ctype
                     fields[subname] = Field(subname,
                                             relpos, subvalue._sizeofinstances(),
-                                            subvalue, i, is_bitfield)
+                                            subvalue, i, is_bitfield,
+                                            inside_anon_field=fields[name])
             else:
                 resnames.append(name)
         names = resnames
@@ -77,13 +78,15 @@
 
 
 class Field(object):
-    def __init__(self, name, offset, size, ctype, num, is_bitfield):
+    def __init__(self, name, offset, size, ctype, num, is_bitfield,
+                 inside_anon_field=None):
         self.__dict__['name'] = name
         self.__dict__['offset'] = offset
         self.__dict__['size'] = size
         self.__dict__['ctype'] = ctype
         self.__dict__['num'] = num
         self.__dict__['is_bitfield'] = is_bitfield
+        self.__dict__['inside_anon_field'] = inside_anon_field
 
     def __setattr__(self, name, value):
         raise AttributeError(name)
@@ -95,6 +98,8 @@
     def __get__(self, obj, cls=None):
         if obj is None:
             return self
+        if self.inside_anon_field is not None:
+            return getattr(self.inside_anon_field.__get__(obj), self.name)
         if self.is_bitfield:
             # bitfield member, use direct access
             return obj._buffer.__getattr__(self.name)
@@ -105,6 +110,9 @@
             return fieldtype._CData_output(suba, obj, offset)
 
     def __set__(self, obj, value):
+        if self.inside_anon_field is not None:
+            setattr(self.inside_anon_field.__get__(obj), self.name, value)
+            return
         fieldtype = self.ctype
         cobj = fieldtype.from_param(value)
         key = keepalive_key(self.num)
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_anon.py b/pypy/module/test_lib_pypy/ctypes_tests/test_anon.py
--- a/pypy/module/test_lib_pypy/ctypes_tests/test_anon.py
+++ b/pypy/module/test_lib_pypy/ctypes_tests/test_anon.py
@@ -57,3 +57,32 @@
         assert Y.y.offset == sizeof(c_int) * 2
 
         assert Y._names_ == ['x', 'a', 'b', 'y']
+
+    def test_anonymous_fields_on_instance(self):
+        # this is about the *instance-level* access of anonymous fields,
+        # which you'd guess is the most common, but used not to work
+        # (issue #2230)
+
+        class B(Structure):
+            _fields_ = [("x", c_int), ("y", c_int), ("z", c_int)]
+        class A(Structure):
+            _anonymous_ = ["b"]
+            _fields_ = [("b", B)]
+
+        a = A()
+        a.x = 5
+        assert a.x == 5
+        assert a.b.x == 5
+        a.b.x += 1
+        assert a.x == 6
+
+        class C(Structure):
+            _anonymous_ = ["a"]
+            _fields_ = [("v", c_int), ("a", A)]
+
+        c = C()
+        c.v = 3
+        c.y = -8
+        assert c.v == 3
+        assert c.y == c.a.y == c.a.b.y == -8
+        assert not hasattr(c, 'b')


More information about the pypy-commit mailing list