There are exactly four types of dynamic library loaders available in ctypes and two conventions to use them. The classes that represent dynamic and shared libraries are ctypes.CDLL, ctypes.PyDLL, ctypes.OleDLL, and ctypes.WinDLL. The last two are available only on Windows, so we won't discuss them here in detail. The differences between CDLL and PyDLL are as follows:
- ctypes.CDLL: This class represents loaded shared libraries. The functions in these libraries use the standard calling convention and are assumed to return int. GIL is released during the call.
- ctypes.PyDLL: This class works like CDLL, but GIL is not released during the call. After execution, the Python error flag is checked and an exception is raised if it is set. It is only useful when the loaded library is directly calling functions from Python/C API or uses callback functions that may be a Python code.
To load the library, you can either instantiate one of the preceding classes with proper arguments or call the LoadLibrary() function from submodule associated with a specific class:
- ctypes.cdll.LoadLibrary() for ctypes.CDLL
- ctypes.pydll.LoadLibrary() for ctypes.PyDLL
- ctypes.windll.LoadLibrary() for ctypes.WinDLL
- ctypes.oledll.LoadLibrary() for ctypes.OleDLL
The main challenge when loading shared libraries is how to find them in a portable way. Different systems use different suffixes for shared libraries (.dll on Windows, .dylib on macOS, .so on Linux) and search for them in different places. The main offender in this area is Windows, which does not have a predefined naming scheme for libraries. Because of that, we won't discuss details of loading libraries with ctypes on this system and will concentrate mainly on Linux and macOS which deal with this problem in a consistent and similar way. If you are interested in the Windows platform, refer to the official ctypes documentation which has plenty of information about supporting that system (refer to https://docs.python.org/3.5/library/ctypes.html).
Both library loading conventions (the LoadLibrary() function and specific library-type classes) require you to use the full library name. This means all the predefined library prefixes and suffixes need to be included. For example, to load the C standard library on Linux, you need to write the following:
>>> import ctypes >>> ctypes.cdll.LoadLibrary('libc.so.6') <CDLL 'libc.so.6', handle 7f0603e5f000 at 7f0603d4cbd0>
Here, for macOS X, this would be the following:
>>> import ctypes >>> ctypes.cdll.LoadLibrary('libc.dylib')
Fortunately, the ctypes.util submodule provides a find_library() function that allows you to load a library using its name without any prefixes or suffixes and will work on any system that has a predefined scheme for naming shared libraries:
>>> import ctypes >>> from ctypes.util import find_library >>> ctypes.cdll.LoadLibrary(find_library('c')) <CDLL '/usr/lib/libc.dylib', handle 7fff69b97c98 at 0x101b73ac8> >>> ctypes.cdll.LoadLibrary(find_library('bz2')) <CDLL '/usr/lib/libbz2.dylib', handle 10042d170 at 0x101b6ee80> >>> ctypes.cdll.LoadLibrary(find_library('AGL')) <CDLL '/System/Library/Frameworks/AGL.framework/AGL', handle 101811610 at 0x101b73a58>
So, if you are writing a ctypes package that is supposed to work both under macOS and Linux, always use ctypes.util.find_library().
Calling C functions using ctypes is explained in the next section.