Source

python-utils / utils / dll.py

"""DLL wrapper"""
import os
import sys
import warnings
from ctypes import CDLL
from ctypes.util import find_library

__all__ = ["DLL"]

def _findlib(libnames, path=None):
    """."""
    platform = sys.platform
    if platform in ("win32", "cli"):
        suffix = ".dll"
    elif platform == "darwin":
        suffix = ".dylib"
    else:
        suffix = ".so"

    searchfor = libnames
    if type(libnames) is dict:
        # different library names for the platforms
        if platform == "cli" and platform not in libnames:
            # if not explicitly specified, use the Win32 libs for IronPython
            platform = "win32"
        if platform not in libnames:
            platform = "DEFAULT"
        searchfor = libnames[platform]
    results = []
    if path:
        for libname in searchfor:
            dllfile = os.path.join(path, "%s%s" % (libname, suffix))
            if os.path.exists(dllfile):
                results.append(dllfile)
    for libname in searchfor:
        dllfile = find_library(libname)
        if dllfile:
            results.append(dllfile)
    return results


class DLL(object):
    """A simple wrapper class for loading shared libraries through ctypes.

    The libinfo argument is a descriptive name of the library, that is
    recommended to be platform neutral, since it is shown to the user on
    errors. libnames can be a list of shared library names or a dictionary
    consisting of platform->library name mappings. path is the explicit library
    path to be used, if any.  path acts as the first location to be used for
    loading the library, before the standard mechanisms of ctypes will be used.
    """
    def __init__(self, libinfo, libnames, path=None):
        self._dll = None
        foundlibs = _findlib(libnames, path)
        if len(foundlibs) == 0:
            raise RuntimeError("could not find any library for %s" % libinfo)
        for libfile in foundlibs:
            try:
                self._dll = CDLL(libfile)
                self._libfile = libfile
                break
            except Exception as exc:
                # Could not load it, silently ignore that issue and move
                # to the next one.
                warnings.warn(exc, ImportWarning)
        if self._dll is None:
            raise RuntimeError("could not load any library for %s" % libinfo)
        if path is not None and sys.platform in ("win32", "cli") and \
            path in self._libfile:
            os.environ["PATH"] += ";%s" % path

    def bind_function(self, funcname, args=None, returns=None, optfunc=None):
        """Binds the passed argument and return value types to the specified
        function."""
        func = getattr(self._dll, funcname, None)
        if not func:
            if optfunc:
                warnings.warn\
                    ("function '%s' not found in %r, using replacement" %
                     (funcname, self._dll))
                func = optfunc
            else:
                raise ValueError("could not find function '%s' in %r" %
                                 (funcname, self._dll))
        func.argtypes = args
        func.restype = returns
        return func

    @property
    def libfile(self):
        """Gets the filename of the loaded library."""
        return self._libfile


# Example:
#
# dll = DLL("OpenAL", {"win32": ["OpenAL", "OpenAL32", "soft_oal"],
#                      "darwin": ["OpenAL"],
#                      "DEFAULT": ["openal"]}, os.getenv("OPENAL_DLL_PATH"))
#
# alGetError = dll.bind_function("alGetError", None, ctypes.c_int)
# ...