Commits

Armin Rigo committed c04cefc

Allow weakrefs to any cdata object. Simplifies a bit the
class hierarchy in cdataobj.

Comments (0)

Files changed (7)

pypy/module/_cffi_backend/ccallback.py

 from pypy.rlib import clibffi, rweakref, rgc
 from pypy.rlib.rarithmetic import r_ulonglong
 
-from pypy.module._cffi_backend.cdataobj import W_CData, W_CDataApplevelOwning
+from pypy.module._cffi_backend.cdataobj import W_CData
 from pypy.module._cffi_backend.ctypefunc import SIZE_OF_FFI_ARG, BIG_ENDIAN
 from pypy.module._cffi_backend.ctypefunc import W_CTypeFunc
 from pypy.module._cffi_backend.ctypeprim import W_CTypePrimitiveSigned
 # ____________________________________________________________
 
 
-class W_CDataCallback(W_CDataApplevelOwning):
+class W_CDataCallback(W_CData):
     #_immutable_fields_ = ...
     ll_error = lltype.nullptr(rffi.CCHARP.TO)
 

pypy/module/_cffi_backend/cdataobj.py

 
 
 class W_CData(Wrappable):
-    _attrs_ = ['space', '_cdata', 'ctype']
+    _attrs_ = ['space', '_cdata', 'ctype', '_lifeline_']
     _immutable_fields_ = ['_cdata', 'ctype']
     _cdata = lltype.nullptr(rffi.CCHARP.TO)
 
         keepalive_until_here(self)
         return extra
 
+    def _repr_extra_owning(self):
+        from pypy.module._cffi_backend.ctypeptr import W_CTypePointer
+        ctype = self.ctype
+        if isinstance(ctype, W_CTypePointer):
+            num_bytes = ctype.ctitem.size
+        else:
+            num_bytes = self._sizeof()
+        return 'owning %d bytes' % num_bytes
+
     def repr(self):
         extra2 = self._repr_extra()
         extra1 = ''
-        if not isinstance(self, W_CDataApplevelOwning):
+        if not isinstance(self, W_CDataNewOwning):
             # it's slightly confusing to get "<cdata 'struct foo' 0x...>"
             # because the struct foo is not owned.  Trying to make it
             # clearer, write in this case "<cdata 'struct foo &' 0x...>".
         return self.ctype.size
 
 
-class W_CDataApplevelOwning(W_CData):
-    """This is the abstract base class for classes that are of the app-level
-    type '_cffi_backend.CDataOwn'.  These are weakrefable."""
-    _attrs_ = ['_lifeline_']    # for weakrefs
-
-    def _repr_extra(self):
-        from pypy.module._cffi_backend.ctypeptr import W_CTypePointer
-        ctype = self.ctype
-        if isinstance(ctype, W_CTypePointer):
-            num_bytes = ctype.ctitem.size
-        else:
-            num_bytes = self._sizeof()
-        return 'owning %d bytes' % num_bytes
-
-
-class W_CDataNewOwning(W_CDataApplevelOwning):
-    """This is the class used for the app-level type
-    '_cffi_backend.CDataOwn' created by newp()."""
+class W_CDataMem(W_CData):
+    """This is the base class used for cdata objects that own and free
+    their memory.  Used directly by the results of cffi.cast('int', x)
+    or other primitive explicitly-casted types.  It is further subclassed
+    by W_CDataNewOwning."""
     _attrs_ = []
 
     def __init__(self, space, size, ctype):
         cdata = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw', zero=True)
-        W_CDataApplevelOwning.__init__(self, space, cdata, ctype)
+        W_CData.__init__(self, space, cdata, ctype)
 
     @rgc.must_be_light_finalizer
     def __del__(self):
         lltype.free(self._cdata, flavor='raw')
 
 
+class W_CDataNewOwning(W_CDataMem):
+    """This is the class used for the cata objects created by newp()."""
+    _attrs_ = []
+
+    def _repr_extra(self):
+        return self._repr_extra_owning()
+
+
 class W_CDataNewOwningLength(W_CDataNewOwning):
     """Subclass with an explicit length, for allocated instances of
     the C type 'foo[]'."""
         return self.length
 
 
-class W_CDataPtrToStructOrUnion(W_CDataApplevelOwning):
+class W_CDataPtrToStructOrUnion(W_CData):
     """This subclass is used for the pointer returned by new('struct foo').
     It has a strong reference to a W_CDataNewOwning that really owns the
-    struct, which is the object returned by the app-level expression 'p[0]'."""
+    struct, which is the object returned by the app-level expression 'p[0]'.
+    But it is not itself owning any memory, although its repr says so;
+    it is merely a co-owner."""
     _attrs_ = ['structobj']
     _immutable_fields_ = ['structobj']
 
     def __init__(self, space, cdata, ctype, structobj):
-        W_CDataApplevelOwning.__init__(self, space, cdata, ctype)
+        W_CData.__init__(self, space, cdata, ctype)
         self.structobj = structobj
 
+    def _repr_extra(self):
+        return self._repr_extra_owning()
+
     def _do_getitem(self, ctype, i):
         assert i == 0
         return self.structobj
 
 
-class W_CDataCasted(W_CData):
-    """This subclass is used by the results of cffi.cast('int', x)
-    or other primitive explicitly-casted types.  Relies on malloc'ing
-    small bits of memory (e.g. just an 'int').  Its point is to not be
-    a subclass of W_CDataApplevelOwning."""
-    _attrs_ = []
-
-    def __init__(self, space, size, ctype):
-        cdata = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw', zero=True)
-        W_CData.__init__(self, space, cdata, ctype)
-
-    @rgc.must_be_light_finalizer
-    def __del__(self):
-        lltype.free(self._cdata, flavor='raw')
-
-
 W_CData.typedef = TypeDef(
     'CData',
     __module__ = '_cffi_backend',
     __setattr__ = interp2app(W_CData.setattr),
     __call__ = interp2app(W_CData.call),
     __iter__ = interp2app(W_CData.iter),
+    __weakref__ = make_weakref_descr(W_CData),
     )
 W_CData.typedef.acceptable_as_base_class = False
-
-W_CDataApplevelOwning.typedef = TypeDef(
-    'CDataOwn', W_CData.typedef,    # base typedef
-    __module__ = '_cffi_backend',
-    __weakref__ = make_weakref_descr(W_CDataApplevelOwning),
-    )
-W_CDataApplevelOwning.typedef.acceptable_as_base_class = False

pypy/module/_cffi_backend/ctypeprim.py

             value = r_ulonglong(value)
         else:
             value = misc.as_unsigned_long_long(space, w_ob, strict=False)
-        w_cdata = cdataobj.W_CDataCasted(space, self.size, self)
+        w_cdata = cdataobj.W_CDataMem(space, self.size, self)
         w_cdata.write_raw_integer_data(value)
         return w_cdata
 
             value = self.cast_str(w_ob)
         else:
             value = space.float_w(w_ob)
-        w_cdata = cdataobj.W_CDataCasted(space, self.size, self)
+        w_cdata = cdataobj.W_CDataMem(space, self.size, self)
         if not isinstance(self, W_CTypePrimitiveLongDouble):
             w_cdata.write_raw_float_data(value)
         else:
         return self.space.wrap(value)
 
     def convert_to_object(self, cdata):
-        w_cdata = cdataobj.W_CDataCasted(self.space, self.size, self)
+        w_cdata = cdataobj.W_CDataMem(self.space, self.size, self)
         self._copy_longdouble(cdata, w_cdata._cdata)
         keepalive_until_here(w_cdata)
         return w_cdata

pypy/module/_cffi_backend/ctypeptr.py

         return cdata
 
     def _check_subscript_index(self, w_cdata, i):
-        if isinstance(w_cdata, cdataobj.W_CDataApplevelOwning) and i != 0:
-            space = self.space
-            raise operationerrfmt(space.w_IndexError,
-                                  "cdata '%s' can only be indexed by 0",
-                                  self.name)
+        if (isinstance(w_cdata, cdataobj.W_CDataNewOwning) or
+            isinstance(w_cdata, cdataobj.W_CDataPtrToStructOrUnion)):
+            if i != 0:
+                space = self.space
+                raise operationerrfmt(space.w_IndexError,
+                                      "cdata '%s' can only be indexed by 0",
+                                      self.name)
         return self
 
     def add(self, cdata, i):

pypy/module/_cffi_backend/libraryobj.py

         else:
             mode = -1     # default value, corresponds to RTLD_LOCAL
         with rffi.scoped_str2charp(filename) as ll_libname:
+            if filename is None:
+                filename = "<None>"
             try:
                 self.handle = dlopen(ll_libname, mode)
             except DLOpenError, e:
                                   "function cdata expected, got '%s'",
                                   ctype.name)
         #
-        cdata = dlsym(self.handle, name)
-        if not cdata:
+        try:
+            cdata = dlsym(self.handle, name)
+        except KeyError:
             raise operationerrfmt(space.w_KeyError,
                                   "function '%s' not found in library '%s'",
                                   name, self.name)
     @unwrap_spec(ctype=W_CType, name=str)
     def read_variable(self, ctype, name):
         space = self.space
-        cdata = dlsym(self.handle, name)
-        if not cdata:
+        try:
+            cdata = dlsym(self.handle, name)
+        except KeyError:
             raise operationerrfmt(space.w_KeyError,
                                   "variable '%s' not found in library '%s'",
                                   name, self.name)
     @unwrap_spec(ctype=W_CType, name=str)
     def write_variable(self, ctype, name, w_value):
         space = self.space
-        cdata = dlsym(self.handle, name)
-        if not cdata:
+        try:
+            cdata = dlsym(self.handle, name)
+        except KeyError:
             raise operationerrfmt(space.w_KeyError,
                                   "variable '%s' not found in library '%s'",
                                   name, self.name)
 W_Library.acceptable_as_base_class = False
 
 
-@unwrap_spec(filename=str, is_global=int)
+@unwrap_spec(filename="str_or_None", is_global=int)
 def load_library(space, filename, is_global=0):
     lib = W_Library(space, filename, is_global)
     return space.wrap(lib)

pypy/module/_cffi_backend/test/_backend_test_c.py

 
 def find_and_load_library(name, is_global=0):
     import ctypes.util
-    path = ctypes.util.find_library(name)
+    if name is None:
+        path = None
+    else:
+        path = ctypes.util.find_library(name)
     return load_library(path, is_global)
 
 def test_load_library():
     p = newp(BIntPtrPtr, q)
     assert p[0][0] == 43
 
+def test_load_standard_library():
+    x = find_and_load_library(None)
+    BVoidP = new_pointer_type(new_void_type())
+    assert x.load_function(BVoidP, 'strcpy')
+    py.test.raises(KeyError, x.load_function,
+                   BVoidP, 'xxx_this_function_does_not_exist')
+
 def test_hash_differences():
     BChar = new_primitive_type("char")
     BInt = new_primitive_type("int")
     BPtr = new_pointer_type(BInt)
     weakref.ref(BInt)
     weakref.ref(newp(BPtr, 42))
-    py.test.raises(TypeError, weakref.ref, cast(BPtr, 42))
-    py.test.raises(TypeError, weakref.ref, cast(BInt, 42))
+    weakref.ref(cast(BPtr, 42))
+    weakref.ref(cast(BInt, 42))
 
 def test_no_inheritance():
     BInt = new_primitive_type("int")

pypy/module/_cffi_backend/test/test_c.py

         keepalive_funcs = []
 
         def find_and_load_library_for_test(space, w_name, w_is_global=0):
-            import ctypes.util
-            path = ctypes.util.find_library(space.str_w(w_name))
+            if space.is_w(w_name, space.w_None):
+                path = None
+            else:
+                import ctypes.util
+                path = ctypes.util.find_library(space.str_w(w_name))
             return space.appexec([space.wrap(path), w_is_global],
             """(path, is_global):
                 import _cffi_backend