[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