[pypy-svn] r45044 - in pypy/dist/pypy/rpython/lltypesystem: . test

arigo at codespeak.net arigo at codespeak.net
Sat Jul 14 10:17:46 CEST 2007


Author: arigo
Date: Sat Jul 14 10:17:46 2007
New Revision: 45044

Modified:
   pypy/dist/pypy/rpython/lltypesystem/ll2ctypes.py
   pypy/dist/pypy/rpython/lltypesystem/rffi.py
   pypy/dist/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
Log:
(lac, arigo)
First tests of calling an external rffi function via ctypes.


Modified: pypy/dist/pypy/rpython/lltypesystem/ll2ctypes.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/ll2ctypes.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/ll2ctypes.py	Sat Jul 14 10:17:46 2007
@@ -1,9 +1,12 @@
+import sys
 import ctypes
+import ctypes.util
 from pypy.rpython.lltypesystem import lltype
 
 
 _ctypes_cache = {
     lltype.Signed: ctypes.c_long,
+    lltype.Char:   ctypes.c_ubyte,
     }
 
 def build_ctypes_struct(S, max_n=None):
@@ -30,19 +33,24 @@
                     raise TypeError("%r is variable-sized" % (S,))
                 biggercls = build_ctypes_struct(S, n)
                 bigstruct = biggercls()
-                getattr(bigstruct, S._arrayfld).length = n
+                array = getattr(bigstruct, S._arrayfld)
+                if hasattr(array, 'length'):
+                    array.length = n
                 return bigstruct
         _malloc = classmethod(_malloc)
 
         def _getattr(self, field_name):
+            T = getattr(S, field_name)
             cobj = getattr(self, field_name)
-            return ctypes2lltype(cobj)
+            return ctypes2lltype(T, cobj)
 
         def _setattr(self, field_name, value):
             cobj = lltype2ctypes(value)
             setattr(self, field_name, cobj)
 
     CStruct.__name__ = 'ctypes_%s' % (S,)
+    if max_n is not None:
+        CStruct._normalized_ctype = get_ctypes_type(S)
     return CStruct
 
 def build_ctypes_array(A, max_n=0):
@@ -51,27 +59,33 @@
     ctypes_item = get_ctypes_type(ITEM)
 
     class CArray(ctypes.Structure):
-        _fields_ = [('length', ctypes.c_int),
-                    ('items',  max_n * ctypes_item)]
+        if not A._hints.get('nolength'):
+            _fields_ = [('length', ctypes.c_int),
+                        ('items',  max_n * ctypes_item)]
+        else:
+            _fields_ = [('items',  max_n * ctypes_item)]
 
         def _malloc(cls, n=None):
             if not isinstance(n, int):
                 raise TypeError, "array length must be an int"
             biggercls = build_ctypes_array(A, n)
             bigarray = biggercls()
-            bigarray.length = n
+            if hasattr(bigarray, 'length'):
+                bigarray.length = n
             return bigarray
         _malloc = classmethod(_malloc)
 
         def _getitem(self, index):
             cobj = self.items[index]
-            return ctypes2lltype(cobj)
+            return ctypes2lltype(ITEM, cobj)
 
         def _setitem(self, index, value):
             cobj = lltype2ctypes(value)
             self.items[index] = cobj
 
     CArray.__name__ = 'ctypes_%s*%d' % (A, max_n)
+    if max_n > 0:
+        CArray._normalized_ctype = get_ctypes_type(A)
     return CArray
 
 def get_ctypes_type(T):
@@ -112,7 +126,13 @@
         if not isinstance(item_value, lltype._uninitialized):
             carray.items[i] = lltype2ctypes(item_value)
 
-def lltype2ctypes(llobj):
+# ____________________________________________________________
+
+def lltype2ctypes(llobj, normalize=True):
+    """Convert the lltype object 'llobj' to its ctypes equivalent.
+    'normalize' should only be False in tests, where we want to
+    inspect the resulting ctypes object manually.
+    """
     T = lltype.typeOf(llobj)
     if isinstance(T, lltype.Ptr):
         container = llobj._obj
@@ -123,8 +143,84 @@
                 convert_array(container)
             else:
                 raise NotImplementedError(T)
-        return ctypes.pointer(container._ctypes_storage)
+        storage = container._ctypes_storage
+        p = ctypes.pointer(storage)
+        if normalize and hasattr(storage, '_normalized_ctype'):
+            p = ctypes.cast(p, ctypes.POINTER(storage._normalized_ctype))
+        return p
+
+    if T is lltype.Char:
+        return ord(llobj)
+
     return llobj
 
-def ctypes2lltype(cobj):
-    return cobj
+def ctypes2lltype(T, cobj):
+    """Convert the ctypes object 'cobj' to its lltype equivalent.
+    'T' is the expected lltype type.
+    """
+    if T is lltype.Char:
+        llobj = chr(cobj)
+    else:
+        llobj = cobj
+
+    assert lltype.typeOf(llobj) == T
+    return llobj
+
+# __________ the standard C library __________
+
+if sys.platform == 'win32':
+    standard_c_lib = ctypes.cdll.LoadLibrary('msvcrt.dll')
+else:
+    standard_c_lib = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
+
+# ____________________________________________
+
+def get_ctypes_callable(funcptr):
+    if getattr(funcptr._obj, 'source', None) is not None:
+        # give up - for tests with an inlined bit of C code
+        raise NotImplementedError("cannot call a C function defined in "
+                                  "a custom C source snippet")
+    FUNCTYPE = lltype.typeOf(funcptr).TO
+    funcname = funcptr._obj._name
+    libraries = getattr(funcptr._obj, 'libraries', None)
+    if not libraries:
+        cfunc = getattr(standard_c_lib, funcname, None)
+    else:
+        cfunc = None
+        for libname in libraries:
+            libpath = ctypes.util.find_library(libname)
+            if libpath:
+                clib = ctypes.cdll.LoadLibrary(libpath)
+                cfunc = getattr(clib, funcname, None)
+                if cfunc is not None:
+                    break
+
+    if cfunc is None:
+        # function name not found in any of the libraries
+        if not libraries:
+            place = 'the standard C library'
+        elif len(libraries) == 1:
+            place = 'library %r' % (libraries[0],)
+        else:
+            place = 'any of the libraries %r' % (libraries,)
+        raise NotImplementedError("function %r not found in %s" % (
+            funcname, place))
+
+    # get_ctypes_type() can raise NotImplementedError too
+    cfunc.argtypes = [get_ctypes_type(T) for T in FUNCTYPE.ARGS]
+    cfunc.restype = get_ctypes_type(FUNCTYPE.RESULT)
+    return cfunc
+
+def make_callable_via_ctypes(funcptr):
+    try:
+        cfunc = get_ctypes_callable(funcptr)
+    except NotImplementedError, e:
+        def invoke_via_ctypes(*argvalues):
+            raise NotImplementedError, e
+    else:
+        RESULT = lltype.typeOf(funcptr).TO.RESULT
+        def invoke_via_ctypes(*argvalues):
+            cargs = [lltype2ctypes(value) for value in argvalues]
+            cres = cfunc(*cargs)
+            return ctypes2lltype(RESULT, cres)
+    funcptr._obj._callable = invoke_via_ctypes

Modified: pypy/dist/pypy/rpython/lltypesystem/rffi.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/rffi.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/rffi.py	Sat Jul 14 10:17:46 2007
@@ -20,11 +20,15 @@
 
 def llexternal(name, args, result, _callable=None, sources=[], includes=[], libraries=[]):
     ext_type = lltype.FuncType(args, result)
-    return lltype.functionptr(ext_type, name, external='C',
-                              sources=tuple(sources),
-                              includes=tuple(includes),
-                              libraries=tuple(libraries),
-                              _callable=_callable)
+    funcptr = lltype.functionptr(ext_type, name, external='C',
+                                 sources=tuple(sources),
+                                 includes=tuple(includes),
+                                 libraries=tuple(libraries),
+                                 _callable=_callable)
+    if _callable is None:
+        from pypy.rpython.lltypesystem import ll2ctypes
+        ll2ctypes.make_callable_via_ctypes(funcptr)
+    return funcptr
 
 def setup():
     """ creates necessary c-level types

Modified: pypy/dist/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/test/test_ll2ctypes.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/test/test_ll2ctypes.py	Sat Jul 14 10:17:46 2007
@@ -1,11 +1,16 @@
+import py
 import ctypes
-from pypy.rpython.lltypesystem import lltype
-from pypy.rpython.lltypesystem.ll2ctypes import lltype2ctypes
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rpython.lltypesystem.ll2ctypes import lltype2ctypes, ctypes2lltype
 
 
 def test_primitive():
     assert lltype2ctypes(5) == 5
-    assert lltype2ctypes('?') == '?'
+    assert lltype2ctypes('?') == ord('?')
+    assert lltype2ctypes('\xE0') == 0xE0
+    assert ctypes2lltype(lltype.Signed, 5) == 5
+    assert ctypes2lltype(lltype.Char, ord('a')) == 'a'
+    assert ctypes2lltype(lltype.Char, 0xFF) == '\xFF'
 
 def test_simple_struct():
     S = lltype.Struct('S', ('x', lltype.Signed), ('y', lltype.Signed))
@@ -28,7 +33,7 @@
     a[0] = 100
     a[1] = 101
     a[2] = 102
-    ac = lltype2ctypes(a)
+    ac = lltype2ctypes(a, normalize=False)
     assert isinstance(ac.contents, ctypes.Structure)
     assert ac.contents.length == 10
     assert ac.contents.items[1] == 101
@@ -37,3 +42,58 @@
     a[3] = 789
     assert ac.contents.items[3] == 789
     lltype.free(a, flavor='raw')
+
+def test_array_nolength():
+    A = lltype.Array(lltype.Signed, hints={'nolength': True})
+    a = lltype.malloc(A, 10, flavor='raw')
+    a[0] = 100
+    a[1] = 101
+    a[2] = 102
+    ac = lltype2ctypes(a, normalize=False)
+    assert isinstance(ac.contents, ctypes.Structure)
+    assert ac.contents.items[1] == 101
+    ac.contents.items[2] = 456
+    assert a[2] == 456
+    a[3] = 789
+    assert ac.contents.items[3] == 789
+    assert ctypes.sizeof(ac.contents) == 10 * ctypes.sizeof(ctypes.c_long)
+    lltype.free(a, flavor='raw')
+
+def test_charp():
+    s = rffi.str2charp("hello")
+    sc = lltype2ctypes(s, normalize=False)
+    assert sc.contents.items[0] == ord('h')
+    assert sc.contents.items[1] == ord('e')
+    assert sc.contents.items[2] == ord('l')
+    assert sc.contents.items[3] == ord('l')
+    assert sc.contents.items[4] == ord('o')
+    assert sc.contents.items[5] == 0
+    assert not hasattr(sc.contents, 'length')
+    sc.contents.items[1] = ord('E')
+    assert s[1] == 'E'
+    s[0] = 'H'
+    assert sc.contents.items[0] == ord('H')
+
+def test_strlen():
+    strlen = rffi.llexternal('strlen', [rffi.CCHARP], lltype.Signed,
+                             includes=['string.h'])
+    s = rffi.str2charp("xxx")
+    res = strlen(s)
+    rffi.free_charp(s)
+    assert res == 3
+
+def test_func_not_in_clib():
+    foobar = rffi.llexternal('I_really_dont_exist', [], lltype.Signed)
+    py.test.raises(NotImplementedError, foobar)
+
+    foobar = rffi.llexternal('I_really_dont_exist', [], lltype.Signed,
+                             libraries=['m'])    # math library
+    py.test.raises(NotImplementedError, foobar)
+
+    foobar = rffi.llexternal('I_really_dont_exist', [], lltype.Signed,
+                             libraries=['m', 'z'])  # math and zlib libraries
+    py.test.raises(NotImplementedError, foobar)
+
+    foobar = rffi.llexternal('I_really_dont_exist', [], lltype.Signed,
+                             libraries=['I_really_dont_exist_either'])
+    py.test.raises(NotImplementedError, foobar)


More information about the pypy-svn mailing list