"""DLL wrapper""" import os import sys import warnings from ctypes import CDLL from ctypes.util import find_library __all__ = ["DLL", "nullfunc"] def _findlib(libnames, path=None): """.""" platform = sys.platform if platform in ("win32",): pattern = "%s.dll" elif platform == "darwin": pattern = "lib%s.dylib" else: pattern = "lib%s.so" searchfor = libnames if type(libnames) is dict: # different library names for the platforms if platform not in libnames: platform = "DEFAULT" searchfor = libnames[platform] results = [] if path: for libname in searchfor: for subpath in str.split(path, os.pathsep): dllfile = os.path.join(subpath, pattern % libname) if os.path.exists(dllfile): results.append(dllfile) for libname in searchfor: dllfile = find_library(libname) if dllfile: results.append(dllfile) return results class DLLWarning(Warning): pass class DLL(object): """Function wrapper around the different DLL functions. Do not use or instantiate this one directly from your user code. """ def __init__(self, libinfo, libnames, path=None): self._dll = None foundlibs = _findlib(libnames, path) dllmsg = "PYSDL2_DLL_PATH: %s" % (os.getenv("PYSDL2_DLL_PATH") or "unset") if len(foundlibs) == 0: raise RuntimeError("could not find any library for %s (%s)" % (libinfo, dllmsg)) for libfile in foundlibs: try: self._dll = CDLL(libfile) self._libfile = libfile break except Exception as exc: # Could not load the DLL, move to the next, but inform the user # about something weird going on - this may become noisy, but # is better than confusing the users with the RuntimeError below warnings.warn(repr(exc), DLLWarning) if self._dll is None: raise RuntimeError("found %s, but it's not usable for the library %s" % (foundlibs, libinfo)) if path is not None and sys.platform in ("win32",) and \ path in self._libfile: os.environ["PATH"] = "%s;%s" % (path, os.environ["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) warnings.warn\ ("function '%s' not found in %r, using replacement" % (funcname, self._dll), ImportWarning) if not func: if optfunc: warnings.warn\ ("function '%s' not found in %r, using replacement" % (funcname, self._dll), ImportWarning) func = _nonexistent(funcname, 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 def _nonexistent(funcname, func): """A simple wrapper to mark functions and methods as nonexistent.""" def wrapper(*fargs, **kw): warnings.warn("%s does not exist" % funcname, category=RuntimeWarning, stacklevel=2) return func(*fargs, **kw) wrapper.__name__ = func.__name__ return wrapper def nullfunc(*args): """A simple no-op function to be used as dll replacement.""" return try: dll = DLL("SDL2", ["SDL2", "SDL2-2.0"], os.getenv("PYSDL2_DLL_PATH")) except RuntimeError as exc: raise ImportError(exc) def get_dll_file(): """Gets the file name of the loaded SDL2 library.""" return dll.libfile _bind = dll.bind_function