Commits

Armin Rigo  committed 281e587

Improve the repr to show if a cdata "owns" memory or not.

  • Participants
  • Parent commits 6a138e3

Comments (0)

Files changed (3)

File ffi/backend_ctypes.py

         cls.__name__ = 'CData<%s>' % (cls._get_c_name(),)
         cls.__module__ = 'ffi'
 
-    def __repr__(self):
-        return '<cdata %r>' % (self._get_c_name(),)
+    def _get_own_repr(self):
+        return None
+
+    def __repr__(self, c_name=None):
+        own = self._get_own_repr()
+        if own is None:
+            own = ''
+        else:
+            own = ' owning %s' % (own,)
+        return '<cdata %r%s>' % (c_name or self._get_c_name(), own)
 
     def _convert_to_address_of(self, BClass):
         raise TypeError("cannot convert %r to %r" % (
                 _ctype = ctypes.POINTER(BItem._ctype)
             else:
                 _ctype = ctypes.c_void_p
-            _reftypename = BItem._get_c_name(' * &')
+            if kind != 'constcharp':
+                _reftypename = BItem._get_c_name(' * &')
+            else:
+                _reftypename = 'const char * &'
+                _keepalive_string = None
 
             def __init__(self, init):
                 if init is None:
             else:
                 def __getitem__(self, index):
                     # note that we allow access to the terminating NUL byte
-                    if not (0 <= index <= len(self._keepalive_string)):
+                    if index < 0:
+                        raise IndexError
+                    if (self._keepalive_string is not None and
+                            index > len(self._keepalive_string)):
                         raise IndexError
                     return self._as_ctype_ptr[index]
 
                         return ctypes.c_char_p(value)
                     else:
                         return super(CTypesPtr, cls)._arg_to_ctypes(value)
+                def _get_own_repr(self):
+                    if self._keepalive_string is not None:
+                        return 'a %d-bytes string' % (
+                            len(self._keepalive_string) + 1,)
+                    return None
 
             @staticmethod
             def _from_ctypes(ctypes_ptr):
             if length is not None:
                 _ctype = BItem._ctype * length
             _reftypename = BItem._get_c_name(brackets)
+            _own = False
 
             def __init__(self, init):
                 if length is None:
                         len1 = len(init) + extra_null
                     self._ctype = BItem._ctype * len1
                 self._blob = self._ctype()
+                self._own = True
                 if init is not None:
                     for i, value in enumerate(init):
                         self[i] = value
                         pass
                     return s
 
+            def _get_own_repr(self):
+                if self._own:
+                    return 'a %d-bytes array' % (ctypes.sizeof(self._blob),)
+                return None
+
             def _convert_to_address_of(self, BClass):
                 if BItem is BClass or BClass is CTypesVoid:
                     return ctypes.addressof(self._blob)
         class CTypesStructOrUnion(CTypesData):
             _ctype = struct_or_union
             _reftypename = '%s %s &' % (kind, name)
+            _own = False
 
             def __init__(self, init):
                 if fnames is None:
                     raise TypeError("cannot instantiate opaque type %s" % (
                         CTypesStructOrUnion,))
                 self._blob = struct_or_union()
+                self._own = True
                 if init is not None:
                     initializer(self, init)
+
+            def _get_own_repr(self):
+                if self._own:
+                    return '%d bytes' % (ctypes.sizeof(self._blob),)
+                return None
         #
         if fnames is not None:
             for fname, BField in zip(fnames, BFieldTypes):
             _ctype = ctypes.CFUNCTYPE(*ctypes_stuff)
             _reftypename = '%s(* &)(%s)' % (BResult._get_c_name(), nameargs)
             _name = None
+            _own_callback = None
 
             def __init__(self, init):
                 if init is None:
                 elif callable(init):
                     # create a callback to the Python callable init()
                     self._ctypes_func = CTypesFunction._ctype(init)
+                    self._own_callback = init
                 else:
                     raise TypeError("argument must be a callable object")
                 self._address = ctypes.cast(self._ctypes_func,
                 return not self.__eq__(other)
 
             def __repr__(self):
-                name = self._name
-                if name:
+                c_name = self._name
+                if c_name:
                     i = self._reftypename.index('(* &)')
                     if self._reftypename[i-1] not in ' )*':
-                        name = ' ' + name
-                    name = self._reftypename.replace('(* &)', name)
-                else:
-                    name = self._get_c_name()
-                return '<cfunc %r>' % (name,)
+                        c_name = ' ' + c_name
+                    c_name = self._reftypename.replace('(* &)', c_name)
+                return CTypesData.__repr__(self, c_name)
+
+            def _get_own_repr(self):
+                return self._own_callback
 
             @staticmethod
             def _from_ctypes(c_func):

File testing/backend_tests.py

         assert repr(p) == "<cdata 'int *'>"
         assert repr(type(p)) == "<class 'ffi.CData<int *>'>"
         p = ffi.new("int [2]")
-        assert repr(p) == "<cdata 'int[2]'>"
+        assert repr(p) == "<cdata 'int[2]' owning a 8-bytes array>"
         assert repr(type(p)) == "<class 'ffi.CData<int[2]>'>"
         p = ffi.new("int*[2][3]")
-        assert repr(p) == "<cdata 'int *[2][3]'>"
+        assert repr(p) == "<cdata 'int *[2][3]' owning a %d-bytes array>" % (
+            6 * SIZE_OF_LONG,)
         assert repr(type(p)) == "<class 'ffi.CData<int *[2][3]>'>"
 
     def test_new_array_of_array(self):
         assert s.b == -3
         assert s.c == 0
         py.test.raises((AttributeError, TypeError), "del s.a")
+        assert repr(s) == "<cdata 'struct foo' owning 8 bytes>"
         #
         py.test.raises(ValueError, ffi.new, "struct foo", [1, 2, 3, 4])
 
         u = ffi.new("union foo", -2)
         assert u.a == -2
         py.test.raises((AttributeError, TypeError), "del u.a")
+        assert repr(u) == "<cdata 'union foo' owning 4 bytes>"
 
     def test_union_opaque(self):
         ffi = FFI(backend=self.Backend())
         py.test.raises(TypeError, "p[3] = '?'")
         py.test.raises(TypeError, ffi.new, "char *", "some string")
         py.test.raises(ValueError, ffi.new, "const char *", "a\x00b")
+        assert repr(p) == "<cdata 'const char *' owning a 9-bytes string>"
 
     def test_voidp(self):
         ffi = FFI(backend=self.Backend())
         ffi = FFI(backend=self.Backend())
         p = ffi.new("int(*)(int)")
         assert not p
-        p = ffi.new("int(*)(int)", lambda n: n + 1)
+        assert repr(p) == "<cdata 'int(*)(int)'>"
+        def cb(n):
+            return n + 1
+        p = ffi.new("int(*)(int)", cb)
         res = p(41)
         assert res == 42 and type(res) is int
         res = p(ffi.new("int", -41))
         assert res == -40 and type(res) is int
+        assert repr(p).startswith(
+            "<cdata 'int(*)(int)' owning <function cb at 0x")

File testing/test_function.py

     """)
     fptr = ffi.C.puts
     assert type(fptr) == ffi.typeof("int(*)(const char*)")
+    assert repr(fptr) == "<cdata 'int puts(const char *)'>"
 
 def test_function_pointer():
     py.test.skip("in-progress")