From 6ff67ef09687611e810e20c23d5f7f7194693331 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 21 Apr 2006 16:51:04 +0000 Subject: [PATCH] Merge in changes from ctypes 0.9.9.6 upstream version. --- Lib/ctypes/__init__.py | 36 +++- Lib/ctypes/_loader.py | 262 ------------------------- Lib/ctypes/test/test_bitfields.py | 2 +- Lib/ctypes/test/test_byteswap.py | 2 +- Lib/ctypes/test/test_callbacks.py | 2 +- Lib/ctypes/test/test_cast.py | 27 +-- Lib/ctypes/test/test_cfuncs.py | 2 +- Lib/ctypes/test/test_checkretval.py | 2 +- Lib/ctypes/test/test_find.py | 90 +++++++++ Lib/ctypes/test/test_funcptr.py | 2 +- Lib/ctypes/test/test_functions.py | 4 +- Lib/ctypes/test/test_libc.py | 2 +- Lib/ctypes/test/test_loading.py | 38 ++-- Lib/ctypes/test/test_pointers.py | 8 +- Lib/ctypes/test/test_posix.py | 40 ---- Lib/ctypes/test/test_prototypes.py | 2 +- Lib/ctypes/test/test_refcounts.py | 2 +- Lib/ctypes/test/test_returnfuncptrs.py | 4 +- Lib/ctypes/test/test_slicing.py | 4 +- Lib/ctypes/test/test_stringptr.py | 2 +- Lib/ctypes/test/test_unicode.py | 4 +- Lib/ctypes/test/test_values.py | 4 +- Lib/ctypes/test/test_win32.py | 2 +- Lib/ctypes/util.py | 122 ++++++++++++ 24 files changed, 300 insertions(+), 365 deletions(-) delete mode 100644 Lib/ctypes/_loader.py create mode 100644 Lib/ctypes/test/test_find.py delete mode 100644 Lib/ctypes/test/test_posix.py create mode 100644 Lib/ctypes/util.py diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 28ac1807be2..f2ddbaacd92 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -3,7 +3,7 @@ import os as _os, sys as _sys from itertools import chain as _chain -__version__ = "0.9.9.4" +__version__ = "0.9.9.6" from _ctypes import Union, Structure, Array from _ctypes import _Pointer @@ -23,8 +23,6 @@ if _os.name in ("nt", "ce"): from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \ FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI -from ctypes._loader import LibraryLoader - """ WINOLEAPI -> HRESULT WINOLEAPI_(type) @@ -72,9 +70,11 @@ def CFUNCTYPE(restype, *argtypes): The function prototype can be called in three ways to create a callable object: - prototype(funct) - returns a C callable function calling funct - prototype(vtbl_index, method_name[, paramflags]) - a Python callable that calls a COM method - prototype(funct_name, dll[, paramflags]) - a Python callable that calls an exported function in a dll + prototype(integer address) -> foreign function + prototype(callable) -> create and return a C callable function from callable + prototype(integer index, method name[, paramflags]) -> foreign function calling a COM method + prototype((ordinal number, dll object)[, paramflags]) -> foreign function exported by ordinal + prototype((function name, dll object)[, paramflags]) -> foreign function exported by name """ try: return _c_functype_cache[(restype, argtypes)] @@ -352,6 +352,23 @@ if _os.name in ("nt", "ce"): _flags_ = _FUNCFLAG_STDCALL _restype_ = HRESULT +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) pydll = LibraryLoader(PyDLL) @@ -402,7 +419,12 @@ def PYFUNCTYPE(restype, *argtypes): _restype_ = restype _flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI return CFunctionType -cast = PYFUNCTYPE(py_object, c_void_p, py_object)(_cast_addr) +_cast = PYFUNCTYPE(py_object, c_void_p, py_object)(_cast_addr) + +def cast(obj, typ): + result = _cast(obj, typ) + result.__keepref = obj + return result _string_at = CFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr) def string_at(ptr, size=0): diff --git a/Lib/ctypes/_loader.py b/Lib/ctypes/_loader.py deleted file mode 100644 index 7a48c1c4ba4..00000000000 --- a/Lib/ctypes/_loader.py +++ /dev/null @@ -1,262 +0,0 @@ -import sys, os -import ctypes - -if os.name in ("nt", "ce"): - from _ctypes import LoadLibrary as dlopen -else: - from _ctypes import dlopen -from _ctypes import RTLD_LOCAL, RTLD_GLOBAL - -# _findLib(name) returns an iterable of possible names for a library. -if os.name in ("nt", "ce"): - def _findLib(name): - return [name] - -if os.name == "posix" and sys.platform == "darwin": - from ctypes.macholib.dyld import dyld_find as _dyld_find - def _findLib(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 [] - -elif os.name == "posix": - # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump - import re, tempfile - - def _findLib_gcc(name): - expr = '[^\(\)\s]*lib%s\.[^\(\)\s]*' % name - cmd = 'if type gcc &>/dev/null; then CC=gcc; else CC=cc; fi;' \ - '$CC -Wl,-t -o /dev/null 2>&1 -l' + name - try: - fdout, outfile = tempfile.mkstemp() - fd = os.popen(cmd) - trace = fd.read() - err = fd.close() - finally: - try: - os.unlink(outfile) - except OSError, e: - if e.errno != errno.ENOENT: - raise - res = re.search(expr, trace) - if not res: - return None - return res.group(0) - - def _findLib_ld(name): - expr = '/[^\(\)\s]*lib%s\.[^\(\)\s]*' % name - res = re.search(expr, os.popen('/sbin/ldconfig -p 2>/dev/null').read()) - if not res: - 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 _get_soname(f): - cmd = "objdump -p -j .dynamic 2>/dev/null " + f - res = re.search(r'\sSONAME\s+([^\s]+)', os.popen(cmd).read()) - if not res: - return f - return res.group(1) - - def _findLib(name): - lib = _findLib_ld(name) - if not lib: - lib = _findLib_gcc(name) - if not lib: - return [name] - return [_get_soname(lib)] - -class LibraryLoader(object): - """Loader for shared libraries. - - Shared libraries are accessed when compiling/linking a program, - and when the program is run. The purpose of the 'find' method is - to locate a library similar to what the compiler does (on machines - with several versions of a shared library the most recent should - be loaded), while 'load' acts like when the program is run, and - uses the runtime loader directly. 'load_version' works like - 'load' but tries to be platform independend (for cases where this - makes sense). Loading via attribute access is a shorthand - notation especially useful for interactive use.""" - - - def __init__(self, dlltype, mode=RTLD_LOCAL): - """Create a library loader instance which loads libraries by - creating an instance of 'dlltype'. 'mode' can be RTLD_LOCAL - or RTLD_GLOBAL, it is ignored on Windows. - """ - self._dlltype = dlltype - self._mode = mode - - def load(self, libname, mode=None): - """Load and return the library with the given libname. On - most systems 'libname' is the filename of the shared library; - when it's not a pathname it will be searched in a system - dependend list of locations (on many systems additional search - paths can be specified by an environment variable). Sometimes - the extension (like '.dll' on Windows) can be omitted. - - 'mode' allows to override the default flags specified in the - constructor, it is ignored on Windows. - """ - if mode is None: - mode = self._mode - return self._load(libname, mode) - - def load_library(self, libname, mode=None): - """Load and return the library with the given libname. This - method passes the specified 'libname' directly to the - platform's library loading function (dlopen, or LoadLibrary). - - 'mode' allows to override the default flags specified in the - constructor, it is ignored on Windows. - """ - if mode is None: - mode = self._mode - return self._dlltype(libname, mode) - - # alias name for backwards compatiblity - LoadLibrary = load_library - - # Helpers for load and load_version - assembles a filename from name and filename - if os.name in ("nt", "ce"): - # Windows (XXX what about cygwin?) - def _plat_load_version(self, name, version, mode): - # not sure if this makes sense - if version is not None: - return self.load(name + version, mode) - return self.load(name, mode) - - _load = load_library - - elif os.name == "posix" and sys.platform == "darwin": - # Mac OS X - def _plat_load_version(self, name, version, mode): - if version: - return self.load("lib%s.%s.dylib" % (name, version), mode) - return self.load("lib%s.dylib" % name, mode) - - def _load(self, libname, mode): - # _dyld_find raises ValueError, convert this into OSError - try: - pathname = _dyld_find(libname) - except ValueError: - raise OSError("Library %s could not be found" % libname) - return self.load_library(pathname, mode) - - elif os.name == "posix": - # Posix - def _plat_load_version(self, name, version, mode): - if version: - return self.load("lib%s.so.%s" % (name, version), mode) - return self.load("lib%s.so" % name, mode) - - _load = load_library - - else: - # Others, TBD - def _plat_load_version(self, name, version, mode=None): - return self.load(name, mode) - - _load = load_library - - def load_version(self, name, version=None, mode=None): - """Build a (system dependend) filename from 'name' and - 'version', then load and return it. 'name' is the library - name without any prefix like 'lib' and suffix like '.so' or - '.dylib'. This method should be used if a library is - available on different platforms, using the particular naming - convention of each platform. - - 'mode' allows to override the default flags specified in the - constructor, it is ignored on Windows. - """ - return self._plat_load_version(name, version, mode) - - def find(self, name, mode=None): - """Try to find a library, load and return it. 'name' is the - library name without any prefix like 'lib', suffix like '.so', - '.dylib' or version number (this is the form used for the - posix linker option '-l'). - - 'mode' allows to override the default flags specified in the - constructor, it is ignored on Windows. - - On windows, this method does the same as the 'load' method. - - On other platforms, this function might call other programs - like the compiler to find the library. When using ctypes to - write a shared library wrapping, consider using .load() or - .load_version() instead. - """ - for libname in _findLib(name): - try: - return self.load(libname, mode) - except OSError: - continue - raise OSError("Library %r not found" % name) - - def __getattr__(self, name): - """Load a library via attribute access. Calls - .load_version(). The result is cached.""" - if name.startswith("_"): - raise AttributeError(name) - dll = self.load_version(name) - setattr(self, name, dll) - return dll - -################################################################ -# test code - -class CDLL(object): - def __init__(self, name, mode): - self._handle = dlopen(name, mode) - self._name = name - - def __repr__(self): - return "<%s '%s', handle %x at %x>" % \ - (self.__class__.__name__, self._name, - (self._handle & (sys.maxint*2 + 1)), - id(self)) - -cdll = LibraryLoader(CDLL) - -def test(): - if os.name == "nt": - print cdll.msvcrt - print cdll.load("msvcrt") - # load_version looks more like an artefact: - print cdll.load_version("msvcr", "t") - print cdll.find("msvcrt") - - if os.name == "posix": - # find and load_version - print cdll.find("m") - print cdll.find("c") - print cdll.load_version("crypto", "0.9.7") - - # getattr - print cdll.m - print cdll.bz2 - - # load - if sys.platform == "darwin": - print cdll.load("libm.dylib") - print cdll.load("libcrypto.dylib") - print cdll.load("libSystem.dylib") - print cdll.load("System.framework/System") - else: - print cdll.load("libm.so") - print cdll.load("libcrypt.so") - print cdll.find("crypt") - -if __name__ == "__main__": - test() diff --git a/Lib/ctypes/test/test_bitfields.py b/Lib/ctypes/test/test_bitfields.py index 54ea8398b15..92c4669b2e3 100644 --- a/Lib/ctypes/test/test_bitfields.py +++ b/Lib/ctypes/test/test_bitfields.py @@ -24,7 +24,7 @@ class BITS(Structure): ("R", c_short, 6), ("S", c_short, 7)] -func = cdll.load(_ctypes_test.__file__).unpack_bitfields +func = CDLL(_ctypes_test.__file__).unpack_bitfields func.argtypes = POINTER(BITS), c_char ##for n in "ABCDEFGHIMNOPQRS": diff --git a/Lib/ctypes/test/test_byteswap.py b/Lib/ctypes/test/test_byteswap.py index d0ada40229a..1f6899212f2 100644 --- a/Lib/ctypes/test/test_byteswap.py +++ b/Lib/ctypes/test/test_byteswap.py @@ -15,7 +15,7 @@ def bin(s): class Test(unittest.TestCase): def X_test(self): - print sys.byteorder + print >> sys.stderr, sys.byteorder for i in range(32): bits = BITS() setattr(bits, "i%s" % i, 1) diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py index a6ee150d292..9d96a5445e2 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -115,7 +115,7 @@ class SampleCallbacksTestCase(unittest.TestCase): def test_integrate(self): # Derived from some then non-working code, posted by David Foster - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) # The function prototype called by 'integrate': double func(double); CALLBACK = CFUNCTYPE(c_double, c_double) diff --git a/Lib/ctypes/test/test_cast.py b/Lib/ctypes/test/test_cast.py index 6f25febfde6..821ce3f6d83 100644 --- a/Lib/ctypes/test/test_cast.py +++ b/Lib/ctypes/test/test_cast.py @@ -23,33 +23,24 @@ class Test(unittest.TestCase): def test_address2pointer(self): array = (c_int * 3)(42, 17, 2) - # on AMD64, sizeof(int) == 4 and sizeof(void *) == 8. - # By default, cast would convert a Python int (or long) into - # a C int, which would be too short to represent a pointer - # on this platform. - - # So we have to wrap the address into a c_void_p for this to work. - # - # XXX Better would be to hide the differences in the cast function. address = addressof(array) ptr = cast(c_void_p(address), POINTER(c_int)) self.failUnlessEqual([ptr[i] for i in range(3)], [42, 17, 2]) + ptr = cast(address, POINTER(c_int)) + self.failUnlessEqual([ptr[i] for i in range(3)], [42, 17, 2]) + def test_ptr2array(self): array = (c_int * 3)(42, 17, 2) -## # Hm, already tested above. -## ptr = cast(array, POINTER(c_int)) -## self.failUnlessEqual([ptr[i] for i in range(3)], [42, 17, 2]) + from sys import getrefcount -# print cast(addressof(array), c_int * 3)[:] -## ptr = cast(addressof(ptr) - -## print ptr[0], ptr[1], ptr[2] -## ptr = POINTER(c_int).from_address(addressof(array)) -## # XXX this crashes: -## print ptr[0], ptr[1], ptr[2] + before = getrefcount(array) + ptr = cast(array, POINTER(c_int)) + self.failUnlessEqual(getrefcount(array), before + 1) + del ptr + self.failUnlessEqual(getrefcount(array), before) if __name__ == "__main__": unittest.main() diff --git a/Lib/ctypes/test/test_cfuncs.py b/Lib/ctypes/test/test_cfuncs.py index 6e0798d1f4d..9d8db1f4894 100644 --- a/Lib/ctypes/test/test_cfuncs.py +++ b/Lib/ctypes/test/test_cfuncs.py @@ -7,7 +7,7 @@ from ctypes import * import _ctypes_test class CFunctions(unittest.TestCase): - _dll = cdll.load(_ctypes_test.__file__) + _dll = CDLL(_ctypes_test.__file__) def S(self): return c_longlong.in_dll(self._dll, "last_tf_arg_s").value diff --git a/Lib/ctypes/test/test_checkretval.py b/Lib/ctypes/test/test_checkretval.py index 344d0bc31ef..e055c49a35c 100644 --- a/Lib/ctypes/test/test_checkretval.py +++ b/Lib/ctypes/test/test_checkretval.py @@ -14,7 +14,7 @@ class Test(unittest.TestCase): def test_checkretval(self): import _ctypes_test - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) self.failUnlessEqual(42, dll._testfunc_p_p(42)) dll._testfunc_p_p.restype = CHECKED diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py new file mode 100644 index 00000000000..54c663c7f54 --- /dev/null +++ b/Lib/ctypes/test/test_find.py @@ -0,0 +1,90 @@ +import unittest +import os, sys +from ctypes import * +from ctypes.util import find_library +from ctypes.test import is_resource_enabled + +if sys.platform == "win32": + lib_gl = find_library("OpenGL32") + lib_glu = find_library("Glu32") + lib_glut = find_library("glut32") + lib_gle = None +elif sys.platform == "darwin": + lib_gl = lib_glu = find_library("OpenGL") + lib_glut = find_library("GLUT") + lib_gle = None +else: + lib_gl = find_library("GL") + lib_glu = find_library("GLU") + lib_glut = find_library("glut") + lib_gle = find_library("gle") + +## print, for debugging +if is_resource_enabled("printing"): + if lib_gl or lib_glu or lib_glut or lib_gle: + print "OpenGL libraries:" + for item in (("GL", lib_gl), + ("GLU", lib_glu), + ("glut", lib_glut), + ("gle", lib_gle)): + print "\t", item + + +# On some systems, loading the OpenGL libraries needs the RTLD_GLOBAL mode. +class Test_OpenGL_libs(unittest.TestCase): + def setUp(self): + self.gl = self.glu = self.gle = self.glut = None + if lib_gl: + self.gl = CDLL(lib_gl, mode=RTLD_GLOBAL) + if lib_glu: + self.glu = CDLL(lib_glu, RTLD_GLOBAL) + if lib_glut: + self.glut = CDLL(lib_glut) + if lib_gle: + self.gle = CDLL(lib_gle) + + if lib_gl: + def test_gl(self): + if self.gl: + self.gl.glClearIndex + + if lib_glu: + def test_glu(self): + if self.glu: + self.glu.gluBeginCurve + + if lib_glut: + def test_glut(self): + if self.glut: + self.glut.glutWireTetrahedron + + if lib_gle: + def test_gle(self): + if self.gle: + self.gle.gleGetJoinStyle + +##if os.name == "posix" and sys.platform != "darwin": + +## # On platforms where the default shared library suffix is '.so', +## # at least some libraries can be loaded as attributes of the cdll +## # object, since ctypes now tries loading the lib again +## # with '.so' appended of the first try fails. +## # +## # Won't work for libc, unfortunately. OTOH, it isn't +## # needed for libc since this is already mapped into the current +## # process (?) +## # +## # On MAC OSX, it won't work either, because dlopen() needs a full path, +## # and the default suffix is either none or '.dylib'. + +## class LoadLibs(unittest.TestCase): +## def test_libm(self): +## import math +## libm = cdll.libm +## sqrt = libm.sqrt +## sqrt.argtypes = (c_double,) +## sqrt.restype = c_double +## self.failUnlessEqual(sqrt(2), math.sqrt(2)) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/ctypes/test/test_funcptr.py b/Lib/ctypes/test/test_funcptr.py index 89b93c44050..7ea873f0a4d 100644 --- a/Lib/ctypes/test/test_funcptr.py +++ b/Lib/ctypes/test/test_funcptr.py @@ -8,7 +8,7 @@ except NameError: WINFUNCTYPE = CFUNCTYPE import _ctypes_test -lib = cdll.load(_ctypes_test.__file__) +lib = CDLL(_ctypes_test.__file__) class CFuncPtrTestCase(unittest.TestCase): def test_basic(self): diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py index ada9def280d..bfa0cad911b 100644 --- a/Lib/ctypes/test/test_functions.py +++ b/Lib/ctypes/test/test_functions.py @@ -15,9 +15,9 @@ except NameError: WINFUNCTYPE = CFUNCTYPE import _ctypes_test -dll = cdll.load(_ctypes_test.__file__) +dll = CDLL(_ctypes_test.__file__) if sys.platform == "win32": - windll = windll.load(_ctypes_test.__file__) + windll = WinDLL(_ctypes_test.__file__) class POINT(Structure): _fields_ = [("x", c_int), ("y", c_int)] diff --git a/Lib/ctypes/test/test_libc.py b/Lib/ctypes/test/test_libc.py index 8fd27890f39..c39f3507c0c 100644 --- a/Lib/ctypes/test/test_libc.py +++ b/Lib/ctypes/test/test_libc.py @@ -4,7 +4,7 @@ import unittest from ctypes import * import _ctypes_test -lib = cdll.load(_ctypes_test.__file__) +lib = CDLL(_ctypes_test.__file__) class LibTest(unittest.TestCase): def test_sqrt(self): diff --git a/Lib/ctypes/test/test_loading.py b/Lib/ctypes/test/test_loading.py index 45584175186..45585ae54a5 100644 --- a/Lib/ctypes/test/test_loading.py +++ b/Lib/ctypes/test/test_loading.py @@ -1,6 +1,8 @@ from ctypes import * import sys, unittest import os, StringIO +from ctypes.util import find_library +from ctypes.test import is_resource_enabled libc_name = None if os.name == "nt": @@ -18,39 +20,49 @@ else: libc_name = line.split()[4] else: libc_name = line.split()[2] -## print "libc_name is", libc_name break +if is_resource_enabled("printing"): + print "libc_name is", libc_name + class LoaderTest(unittest.TestCase): unknowndll = "xxrandomnamexx" if libc_name is not None: def test_load(self): - cdll.load(libc_name) - cdll.load(os.path.basename(libc_name)) - self.assertRaises(OSError, cdll.load, self.unknowndll) + CDLL(libc_name) + CDLL(os.path.basename(libc_name)) + self.assertRaises(OSError, CDLL, self.unknowndll) if libc_name is not None and os.path.basename(libc_name) == "libc.so.6": def test_load_version(self): - cdll.load_version("c", "6") + cdll.LoadLibrary("libc.so.6") # linux uses version, libc 9 should not exist - self.assertRaises(OSError, cdll.load_version, "c", "9") - self.assertRaises(OSError, cdll.load_version, self.unknowndll, "") + self.assertRaises(OSError, cdll.LoadLibrary, "libc.so.9") + self.assertRaises(OSError, cdll.LoadLibrary, self.unknowndll) - def test_find(self): - name = "c" - cdll.find(name) - self.assertRaises(OSError, cdll.find, self.unknowndll) + def test_find(self): + for name in ("c", "m"): + lib = find_library(name) + if lib: + cdll.LoadLibrary(lib) + CDLL(lib) if os.name in ("nt", "ce"): def test_load_library(self): + if is_resource_enabled("printing"): + print find_library("kernel32") + print find_library("user32") + if os.name == "nt": - windll.load_library("kernel32").GetModuleHandleW + windll.kernel32.GetModuleHandleW + windll["kernel32"].GetModuleHandleW windll.LoadLibrary("kernel32").GetModuleHandleW WinDLL("kernel32").GetModuleHandleW elif os.name == "ce": - windll.load_library("coredll").GetModuleHandleW + windll.coredll.GetModuleHandleW + windll["coredll"].GetModuleHandleW windll.LoadLibrary("coredll").GetModuleHandleW WinDLL("coredll").GetModuleHandleW diff --git a/Lib/ctypes/test/test_pointers.py b/Lib/ctypes/test/test_pointers.py index 3a324a67c34..c81c6c90592 100644 --- a/Lib/ctypes/test/test_pointers.py +++ b/Lib/ctypes/test/test_pointers.py @@ -20,7 +20,7 @@ class PointersTestCase(unittest.TestCase): self.failUnlessRaises(TypeError, A, c_ulong(33)) def test_pass_pointers(self): - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) func = dll._testfunc_p_p func.restype = c_long @@ -35,7 +35,7 @@ class PointersTestCase(unittest.TestCase): self.failUnlessEqual(res[0], 12345678) def test_change_pointers(self): - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) func = dll._testfunc_p_p i = c_int(87654) @@ -70,7 +70,7 @@ class PointersTestCase(unittest.TestCase): return 0 callback = PROTOTYPE(func) - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) # This function expects a function pointer, # and calls this with an integer pointer as parameter. # The int pointer points to a table containing the numbers 1..10 @@ -156,7 +156,7 @@ class PointersTestCase(unittest.TestCase): def test_charpp( self ): """Test that a character pointer-to-pointer is correctly passed""" - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) func = dll._testfunc_c_p_p func.restype = c_char_p argv = (c_char_p * 2)() diff --git a/Lib/ctypes/test/test_posix.py b/Lib/ctypes/test/test_posix.py deleted file mode 100644 index fe0a40a1aea..00000000000 --- a/Lib/ctypes/test/test_posix.py +++ /dev/null @@ -1,40 +0,0 @@ -import unittest, os, sys -from ctypes import * - -if os.name == "posix" and sys.platform == "linux2": - # I don't really know on which platforms this works, - # later it should use the find_library stuff to avoid - # hardcoding the names. - - class TestRTLD_GLOBAL(unittest.TestCase): - def test_GL(self): - if os.path.exists('/usr/lib/libGL.so'): - cdll.load('libGL.so', mode=RTLD_GLOBAL) - if os.path.exists('/usr/lib/libGLU.so'): - cdll.load('libGLU.so') - -##if os.name == "posix" and sys.platform != "darwin": - -## # On platforms where the default shared library suffix is '.so', -## # at least some libraries can be loaded as attributes of the cdll -## # object, since ctypes now tries loading the lib again -## # with '.so' appended of the first try fails. -## # -## # Won't work for libc, unfortunately. OTOH, it isn't -## # needed for libc since this is already mapped into the current -## # process (?) -## # -## # On MAC OSX, it won't work either, because dlopen() needs a full path, -## # and the default suffix is either none or '.dylib'. - -## class LoadLibs(unittest.TestCase): -## def test_libm(self): -## import math -## libm = cdll.libm -## sqrt = libm.sqrt -## sqrt.argtypes = (c_double,) -## sqrt.restype = c_double -## self.failUnlessEqual(sqrt(2), math.sqrt(2)) - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/ctypes/test/test_prototypes.py b/Lib/ctypes/test/test_prototypes.py index 47f5da1091b..aaaa47a34c4 100644 --- a/Lib/ctypes/test/test_prototypes.py +++ b/Lib/ctypes/test/test_prototypes.py @@ -22,7 +22,7 @@ import unittest # In this case, there would have to be an additional reference to the argument... import _ctypes_test -testdll = cdll.load(_ctypes_test.__file__) +testdll = CDLL(_ctypes_test.__file__) # Return machine address `a` as a (possibly long) non-negative integer. # Starting with Python 2.5, id(anything) is always non-negative, and diff --git a/Lib/ctypes/test/test_refcounts.py b/Lib/ctypes/test/test_refcounts.py index 0c62bf2daf6..448f292cd4f 100644 --- a/Lib/ctypes/test/test_refcounts.py +++ b/Lib/ctypes/test/test_refcounts.py @@ -6,7 +6,7 @@ MyCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int) OtherCallback = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_ulonglong) import _ctypes_test -dll = ctypes.cdll.load(_ctypes_test.__file__) +dll = ctypes.CDLL(_ctypes_test.__file__) class RefcountTestCase(unittest.TestCase): diff --git a/Lib/ctypes/test/test_returnfuncptrs.py b/Lib/ctypes/test/test_returnfuncptrs.py index ef1f6fd1506..88dccf2ce1c 100644 --- a/Lib/ctypes/test/test_returnfuncptrs.py +++ b/Lib/ctypes/test/test_returnfuncptrs.py @@ -8,7 +8,7 @@ class ReturnFuncPtrTestCase(unittest.TestCase): def test_with_prototype(self): # The _ctypes_test shared lib/dll exports quite some functions for testing. # The get_strchr function returns a *pointer* to the C strchr function. - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) get_strchr = dll.get_strchr get_strchr.restype = CFUNCTYPE(c_char_p, c_char_p, c_char) strchr = get_strchr() @@ -18,7 +18,7 @@ class ReturnFuncPtrTestCase(unittest.TestCase): self.assertRaises(TypeError, strchr, "abcdef") def test_without_prototype(self): - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) get_strchr = dll.get_strchr # the default 'c_int' would not work on systems where sizeof(int) != sizeof(void *) get_strchr.restype = c_void_p diff --git a/Lib/ctypes/test/test_slicing.py b/Lib/ctypes/test/test_slicing.py index 306c585966c..44d0b116646 100644 --- a/Lib/ctypes/test/test_slicing.py +++ b/Lib/ctypes/test/test_slicing.py @@ -37,7 +37,7 @@ class SlicesTestCase(unittest.TestCase): def test_char_ptr(self): s = "abcdefghijklmnopqrstuvwxyz\0" - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) dll.my_strdup.restype = POINTER(c_char) res = dll.my_strdup(s) self.failUnlessEqual(res[:len(s)], s) @@ -65,7 +65,7 @@ class SlicesTestCase(unittest.TestCase): def test_wchar_ptr(self): s = u"abcdefghijklmnopqrstuvwxyz\0" - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) dll.my_wcsdup.restype = POINTER(c_wchar) dll.my_wcsdup.argtypes = POINTER(c_wchar), res = dll.my_wcsdup(s) diff --git a/Lib/ctypes/test/test_stringptr.py b/Lib/ctypes/test/test_stringptr.py index 183a60c28a2..6ee6ae04466 100644 --- a/Lib/ctypes/test/test_stringptr.py +++ b/Lib/ctypes/test/test_stringptr.py @@ -3,7 +3,7 @@ from ctypes import * import _ctypes_test -lib = cdll.load(_ctypes_test.__file__) +lib = CDLL(_ctypes_test.__file__) class StringPtrTestCase(unittest.TestCase): diff --git a/Lib/ctypes/test/test_unicode.py b/Lib/ctypes/test/test_unicode.py index bb39746031f..78c5cf87c86 100644 --- a/Lib/ctypes/test/test_unicode.py +++ b/Lib/ctypes/test/test_unicode.py @@ -8,7 +8,7 @@ except AttributeError: pass else: import _ctypes_test - dll = ctypes.cdll.load(_ctypes_test.__file__) + dll = ctypes.CDLL(_ctypes_test.__file__) wcslen = dll.my_wcslen wcslen.argtypes = [ctypes.c_wchar_p] @@ -66,7 +66,7 @@ else: self.failUnlessEqual(buf[:], u"ab\0\0\0\0") import _ctypes_test - func = ctypes.cdll.load(_ctypes_test.__file__)._testfunc_p_p + func = ctypes.CDLL(_ctypes_test.__file__)._testfunc_p_p class StringTestCase(UnicodeTestCase): def setUp(self): diff --git a/Lib/ctypes/test/test_values.py b/Lib/ctypes/test/test_values.py index 1f25f9b2608..7ba3e21efa9 100644 --- a/Lib/ctypes/test/test_values.py +++ b/Lib/ctypes/test/test_values.py @@ -10,7 +10,7 @@ import _ctypes_test class ValuesTestCase(unittest.TestCase): def test_an_integer(self): - ctdll = cdll.load(_ctypes_test.__file__) + ctdll = CDLL(_ctypes_test.__file__) an_integer = c_int.in_dll(ctdll, "an_integer") x = an_integer.value self.failUnlessEqual(x, ctdll.get_an_integer()) @@ -18,7 +18,7 @@ class ValuesTestCase(unittest.TestCase): self.failUnlessEqual(x*2, ctdll.get_an_integer()) def test_undefined(self): - ctdll = cdll.load(_ctypes_test.__file__) + ctdll = CDLL(_ctypes_test.__file__) self.assertRaises(ValueError, c_int.in_dll, ctdll, "Undefined_Symbol") class Win_ValuesTestCase(unittest.TestCase): diff --git a/Lib/ctypes/test/test_win32.py b/Lib/ctypes/test/test_win32.py index 3d0b8255eb4..8247d370d39 100644 --- a/Lib/ctypes/test/test_win32.py +++ b/Lib/ctypes/test/test_win32.py @@ -54,7 +54,7 @@ class Structures(unittest.TestCase): ("right", c_long), ("bottom", c_long)] - dll = cdll.load(_ctypes_test.__file__) + dll = CDLL(_ctypes_test.__file__) pt = POINT(10, 10) rect = RECT(0, 0, 20, 20) diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py new file mode 100644 index 00000000000..4b8057ee626 --- /dev/null +++ b/Lib/ctypes/util.py @@ -0,0 +1,122 @@ +import sys, os +import ctypes + +# 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 + + def _findLib_gcc(name): + expr = '[^\(\)\s]*lib%s\.[^\(\)\s]*' % name + cmd = 'if type gcc &>/dev/null; then CC=gcc; else CC=cc; fi;' \ + '$CC -Wl,-t -o /dev/null 2>&1 -l' + name + try: + fdout, outfile = tempfile.mkstemp() + fd = os.popen(cmd) + trace = fd.read() + err = fd.close() + finally: + try: + os.unlink(outfile) + except OSError, e: + if e.errno != errno.ENOENT: + raise + res = re.search(expr, trace) + if not res: + return None + return res.group(0) + + def _findLib_ld(name): + expr = '/[^\(\)\s]*lib%s\.[^\(\)\s]*' % 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 _get_soname(f): + 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) + + def find_library(name): + lib = _findLib_ld(name) or _findLib_gcc(name) + if not lib: + return None + return _get_soname(lib) + +################################################################ +# 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()