[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