[pypy-svn] r48700 - in pypy/dist/pypy/lib: app_test ctypes

fijal at codespeak.net fijal at codespeak.net
Thu Nov 15 01:59:38 CET 2007


Author: fijal
Date: Thu Nov 15 01:59:38 2007
New Revision: 48700

Added:
   pypy/dist/pypy/lib/ctypes/util.py   (contents, props changed)
Modified:
   pypy/dist/pypy/lib/app_test/test_ctypes.py
   pypy/dist/pypy/lib/ctypes/__init__.py
Log:
A bit of cowboy-coding all over the place. The question is how far we
can go into implementing ctypes without being insane


Modified: pypy/dist/pypy/lib/app_test/test_ctypes.py
==============================================================================
--- pypy/dist/pypy/lib/app_test/test_ctypes.py	(original)
+++ pypy/dist/pypy/lib/app_test/test_ctypes.py	Thu Nov 15 01:59:38 2007
@@ -1,24 +1,71 @@
 
 from pypy.conftest import gettestobjspace
 import sys, py
+from pypy.module._ffi.test.test__ffi import AppTestFfi as BaseTestFfi
 
 def setup_module(mod):
     if sys.platform != 'linux2':
         py.test.skip("Linux only tests by now")
 
-class AppTestCtypes:
-    def setup_class(cls):
-        space = gettestobjspace(usemodules=('_ffi','struct'))
+class AppTestCtypes(BaseTestFfi):
+    def test_libload(self):
+        import ctypes
+        ctypes.CDLL('libc.so.6')
 
-    def test_rand(self):
+    def test_getattr(self):
         import ctypes
         libc = ctypes.CDLL('libc.so.6')
         rand = libc.rand
-        first = rand()
-        counter = 0
+        rand.restype = ctypes.c_int
+        assert libc.rand is rand
+        assert libc['rand'] is not rand
+        assert isinstance(rand, ctypes._CFuncPtr)
+        raises(AttributeError, 'libc.xxxxxxxxxxxxxxxx')
+
+    def test_short_addition(self):
+        import ctypes
+        lib = ctypes.CDLL(self.lib_name)
+        short_add = lib.add_shorts
+        short_add.argtypes = [ctypes.c_short, ctypes.c_short]
+        short_add.restype = ctypes.c_ushort
+        assert short_add(1, 2) == 3
+
+    def test_rand(self):
+        import ctypes
+        libc = ctypes.CDLL('libc.so.6')
+        func = libc.rand
+        first = func()
+        count = 0
         for i in range(100):
-            next = rand()
-            if next == first:
-                counter += 1
-        assert counter < 100
+            res = func()
+            if res == first:
+                count += 1
+        assert count != 100
+
+    def test_pow(self):
+        import ctypes
+        libm = ctypes.CDLL('libm.so')
+        pow = libm.pow
+        pow.argtypes = [ctypes.c_double, ctypes.c_double]
+        pow.restype = ctypes.c_double
+        assert pow(2.0, 2.0) == 4.0
+        assert pow(3.0, 3.0) == 27.0
+        assert pow(2, 2) == 4.0
+        raises(ctypes.ArgumentError, "pow('x', 2.0)")
+
+    def not_implemented(self):
+        skip("not implemented")
 
+    test_getchar = not_implemented
+    test_returning_str = not_implemented
+    test_strlen = not_implemented
+    test_time = not_implemented
+    test_gettimeofday = not_implemented
+    test_structreturn = not_implemented
+    test_nested_structures = not_implemented
+    test_array = not_implemented
+    test_array_of_structure = not_implemented
+    test_bad_parameters = not_implemented
+    test_implicit_structure = not_implemented
+    test_longs_ulongs = not_implemented
+    test_callback = not_implemented

Modified: pypy/dist/pypy/lib/ctypes/__init__.py
==============================================================================
--- pypy/dist/pypy/lib/ctypes/__init__.py	(original)
+++ pypy/dist/pypy/lib/ctypes/__init__.py	Thu Nov 15 01:59:38 2007
@@ -4,16 +4,23 @@
 
 DEFAULT_MODE = None # XXX, mode support in _ffi
 
+class ArgumentError(Exception):
+    pass
+
 class _CFuncPtr(object):
     def __init__(self, (name, lib)):
         self.name = name
         self.lib = lib
-    
+        self._update_handle()
+
     def __call__(self, *args):
-        if not hasattr(self, '_handle'):
-            self._update_handle()
-        # XXX eventually cast types here
-        return self._handle(*args)
+        # XXX right now always update handle in order to keep changing
+        #     argtypes and restype
+        self._update_handle()
+        try:
+            return self._handle(*args)
+        except TypeError, e:
+            raise ArgumentError("Wrong argument", e.__class__)
 
     def _update_handle(self):
         llargs = [i._lltype for i in self.argtypes]
@@ -21,9 +28,64 @@
         self._handle = self.lib._handle.ptr(self.name, llargs,
                                            self.restype._lltype)
 
-class c_int(object):
+class _SimpleCData(object):
+    def __init__(self, value):
+        self.value = value
+
+class c_ushort(_SimpleCData):
+    _lltype = 'H'
+
+class c_double(_SimpleCData):
+    _lltype = 'd'
+
+class c_ubyte(_SimpleCData):
+    _lltype = 'B'
+
+class c_float(_SimpleCData):
+    _lltype = 'f'
+
+class c_ulong(_SimpleCData):
+    _lltype = 'L'
+
+class c_short(_SimpleCData):
+    _lltype = 'h'
+
+class c_ubyte(_SimpleCData):
+    _lltype = 'b'
+
+class c_byte(_SimpleCData):
+    _lltype = 'B'
+
+class c_char(_SimpleCData):
+    _lltype = 'c'
+
+class c_long(_SimpleCData):
+    _lltype = 'l'
+
+class c_ulonglong(_SimpleCData):
+    _lltype = 'Q'
+
+class c_longlong(_SimpleCData):
+    _lltype = 'q'
+
+class c_int(_SimpleCData):
     _lltype = 'i'
 
+class c_uint(_SimpleCData):
+    _lltype = 'I'
+
+class c_double(_SimpleCData):
+    _lltype = 'd'
+
+class c_float(_SimpleCData):
+    _lltype = 'f'
+
+c_size_t = c_ulong # XXX
+
+class POINTER(object):
+    def __init__(self, cls):
+        self.cls = cls
+
 class CDLL(object):
     """An instance of this class represents a loaded dll/shared
     library, exporting functions using the standard C calling
@@ -66,3 +128,24 @@
         if not isinstance(name_or_ordinal, (int, long)):
             func.__name__ = name_or_ordinal
         return func
+
+
+class LibraryLoader(object):
+    def __init__(self, dlltype):
+        self._dlltype = dlltype
+
+    def __getattr__(self, name):
+        if name[0] == '_':
+            raise AttributeError(name)
+        dll = self._dlltype(name)
+        setattr(self, name, dll)
+        return dll
+
+    def __getitem__(self, name):
+        return getattr(self, name)
+
+    def LoadLibrary(self, name):
+        return self._dlltype(name)
+
+cdll = LibraryLoader(CDLL)
+

Added: pypy/dist/pypy/lib/ctypes/util.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/lib/ctypes/util.py	Thu Nov 15 01:59:38 2007
@@ -0,0 +1,154 @@
+######################################################################
+#  This file should be kept compatible with Python 2.3, see PEP 291. #
+######################################################################
+import sys, os
+
+# find_library(name) returns the pathname of a library, or None.
+if os.name == "nt":
+    def find_library(name):
+        # See MSDN for the REAL search order.
+        for directory in os.environ['PATH'].split(os.pathsep):
+            fname = os.path.join(directory, name)
+            if os.path.exists(fname):
+                return fname
+            if fname.lower().endswith(".dll"):
+                continue
+            fname = fname + ".dll"
+            if os.path.exists(fname):
+                return fname
+        return None
+
+if os.name == "ce":
+    # search path according to MSDN:
+    # - absolute path specified by filename
+    # - The .exe launch directory
+    # - the Windows directory
+    # - ROM dll files (where are they?)
+    # - OEM specified search path: HKLM\Loader\SystemPath
+    def find_library(name):
+        return name
+
+if os.name == "posix" and sys.platform == "darwin":
+    from ctypes.macholib.dyld import dyld_find as _dyld_find
+    def find_library(name):
+        possible = ['lib%s.dylib' % name,
+                    '%s.dylib' % name,
+                    '%s.framework/%s' % (name, name)]
+        for name in possible:
+            try:
+                return _dyld_find(name)
+            except ValueError:
+                continue
+        return None
+
+elif os.name == "posix":
+    # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump
+    import re, tempfile, errno
+
+    def _findLib_gcc(name):
+        expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name)
+        fdout, ccout = tempfile.mkstemp()
+        os.close(fdout)
+        cmd = 'if type gcc >/dev/null 2>&1; then CC=gcc; else CC=cc; fi;' \
+              '$CC -Wl,-t -o ' + ccout + ' 2>&1 -l' + name
+        try:
+            f = os.popen(cmd)
+            trace = f.read()
+            f.close()
+        finally:
+            try:
+                os.unlink(ccout)
+            except OSError, e:
+                if e.errno != errno.ENOENT:
+                    raise
+        res = re.search(expr, trace)
+        if not res:
+            return None
+        return res.group(0)
+
+    def _get_soname(f):
+        # assuming GNU binutils / ELF
+        if not f:
+            return None
+        cmd = "objdump -p -j .dynamic 2>/dev/null " + f
+        res = re.search(r'\sSONAME\s+([^\s]+)', os.popen(cmd).read())
+        if not res:
+            return None
+        return res.group(1)
+
+    if (sys.platform.startswith("freebsd")
+        or sys.platform.startswith("openbsd")
+        or sys.platform.startswith("dragonfly")):
+
+        def _num_version(libname):
+            # "libxyz.so.MAJOR.MINOR" => [ MAJOR, MINOR ]
+            parts = libname.split(".")
+            nums = []
+            try:
+                while parts:
+                    nums.insert(0, int(parts.pop()))
+            except ValueError:
+                pass
+            return nums or [ sys.maxint ]
+
+        def find_library(name):
+            ename = re.escape(name)
+            expr = r':-l%s\.\S+ => \S*/(lib%s\.\S+)' % (ename, ename)
+            res = re.findall(expr,
+                             os.popen('/sbin/ldconfig -r 2>/dev/null').read())
+            if not res:
+                return _get_soname(_findLib_gcc(name))
+            res.sort(cmp= lambda x,y: cmp(_num_version(x), _num_version(y)))
+            return res[-1]
+
+    else:
+
+        def _findLib_ldconfig(name):
+            # XXX assuming GLIBC's ldconfig (with option -p)
+            expr = r'/[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name)
+            res = re.search(expr,
+                            os.popen('/sbin/ldconfig -p 2>/dev/null').read())
+            if not res:
+                # Hm, this works only for libs needed by the python executable.
+                cmd = 'ldd %s 2>/dev/null' % sys.executable
+                res = re.search(expr, os.popen(cmd).read())
+                if not res:
+                    return None
+            return res.group(0)
+
+        def find_library(name):
+            return _get_soname(_findLib_ldconfig(name) or _findLib_gcc(name))
+
+################################################################
+# test code
+
+def test():
+    from ctypes import cdll
+    if os.name == "nt":
+        print cdll.msvcrt
+        print cdll.load("msvcrt")
+        print find_library("msvcrt")
+
+    if os.name == "posix":
+        # find and load_version
+        print find_library("m")
+        print find_library("c")
+        print find_library("bz2")
+
+        # getattr
+##        print cdll.m
+##        print cdll.bz2
+
+        # load
+        if sys.platform == "darwin":
+            print cdll.LoadLibrary("libm.dylib")
+            print cdll.LoadLibrary("libcrypto.dylib")
+            print cdll.LoadLibrary("libSystem.dylib")
+            print cdll.LoadLibrary("System.framework/System")
+        else:
+            print cdll.LoadLibrary("libm.so")
+            print cdll.LoadLibrary("libcrypt.so")
+            print find_library("crypt")
+
+if __name__ == "__main__":
+    test()



More information about the Pypy-commit mailing list