问题
From https://docs.python.org/3/library/ctypes.html#loading-shared-libraries
Shared libraries can also be loaded by using one of the prefabricated objects, which are instances of the LibraryLoader class, either by calling the LoadLibrary() method, or by retrieving the library as attribute of the loader instance.
I found an example for the first way Free the opened ctypes library in Python
I was wondering how to use the second way? In particular, how is the attribute of the loader instance cdll
created? My question comes from Why does loading the libc shared library have "'LibraryLoader' object is not callable" error?
The whole point of the LibraryLoader is that it creates the library for you when you access it. And
cdll.LoadLibrary("foo")
doesn't createcdll.foo
.
Is there something wrong with my experiment? Why does cdll.libc
never exist?
>>> from ctypes import *
>>> cdll.libc
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
dll = self._dlltype(name)
File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle 7f6afe03a000 at 0x7f6afc1afac8>
>>> cdll.libc
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
dll = self._dlltype(name)
File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory
>>> libc=cdll.LoadLibrary("libc.so.6")
>>> cdll.libc
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
dll = self._dlltype(name)
File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory
>>> CDLL("libc.so.6")
<CDLL 'libc.so.6', handle 7f6afe03a000 at 0x7f6afc1af978>
>>> cdll.libc
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
dll = self._dlltype(name)
File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory
>>> libc=CDLL("libc.so.6")
>>> cdll.libc
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.6/ctypes/__init__.py", line 418, in __getattr__
dll = self._dlltype(name)
File "/usr/lib/python3.6/ctypes/__init__.py", line 348, in __init__
self._handle = _dlopen(self._name, mode)
OSError: libc: cannot open shared object file: No such file or directory
>>> cdll.__dict__
{'_dlltype': <class 'ctypes.CDLL'>}
回答1:
Example (what happens):
>>> import sys >>> import ctypes >>> print("Python {:s} on {:s}".format(sys.version, sys.platform)) Python 3.5.4 (v3.5.4:3f56838, Aug 8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32 >>> >>> [item for item in dir(ctypes.windll) if "__" not in item] ['LoadLibrary', '_dlltype', 'kernel32'] >>> >>> user32_dll = ctypes.windll.LoadLibrary("user32") >>> user32_dll <WinDLL 'user32', handle 7ff882810000 at 0x2434399b4e0> >>> [item for item in dir(ctypes.windll) if "__" not in item] ['LoadLibrary', '_dlltype', 'kernel32'] >>> >>> user32_dll = ctypes.WinDLL("user32") >>> user32_dll <WinDLL 'user32', handle 7ff882810000 at 0x2434399b4a8> >>> [item for item in dir(ctypes.windll) if "__" not in item] ['LoadLibrary', '_dlltype', 'kernel32'] >>> >>> user32_dll = ctypes.windll.user32 >>> user32_dll <WinDLL 'user32', handle 7ff882810000 at 0x24343984d68> >>> [item for item in dir(ctypes.windll) if "__" not in item] ['LoadLibrary', '_dlltype', 'kernel32', 'user32'] >>> >>> ctypes.windll.user32 <WinDLL 'user32', handle 7ff882810000 at 0x24343984d68> >>> >>> ctypes.windll.user321 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "c:\install\x64\python\python\3.5\Lib\ctypes\__init__.py", line 421, in __getattr__ dll = self._dlltype(name) File "c:\install\x64\python\python\3.5\Lib\ctypes\__init__.py", line 351, in __init__ self._handle = _dlopen(self._name, mode) OSError: [WinError 126] The specified module could not be found >>> >>> dir(ctypes.windll) ['LoadLibrary', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_dlltype', 'kernel32', 'user32']
How it happens - it's all in "${PYTHON_SRC_DIR}/Lib/ctypes/__init__.py"
([GitHub]: python/cpython - (master) cpython/Lib/ctypes/__init__.py). I'm pasting code for cdll
since windll
(that I used in the example above) is just a wrapper over it (and would require a bit more code):
# ...
class CDLL(object):
# ...
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) # @TODO - cfati: This is the key for always returning the same instance.
return dll
# ...
cdll = LibraryLoader(CDLL)
# ...
@EDIT0:
Hmm, on Ux (Lnx at least) things aren't so nice:
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> ls libcapi.so [cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> LD_LIBRARY_PATH=$(pwd) python3 -c "import ctypes; ctypes.cdll.LoadLibrary('libcapi.so')" [cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> LD_LIBRARY_PATH=$(pwd) python3 -c "import ctypes; ctypes.cdll.LoadLibrary('libcapi')" Traceback (most recent call last): File "<string>", line 1, in <module> File "/usr/lib/python3.5/ctypes/__init__.py", line 425, in LoadLibrary return self._dlltype(name) File "/usr/lib/python3.5/ctypes/__init__.py", line 347, in __init__ self._handle = _dlopen(self._name, mode) OSError: libcapi: cannot open shared object file: No such file or directory
That is because apparently (as opposed to [MSDN]: LoadLibrary function), [man7]: DLOPEN(3) doesn't append the (default) library extension (.so) to the file name (if it doesn't contain it already).
code.c:
#include <stdio.h>
#include <dlfcn.h>
int main(int argc, char *argv[]) {
if (argc == 1) {
printf("Dll name required\n");
return 1;
}
void *handle = dlopen(argv[1], RTLD_NOW);
if (handle == NULL) {
printf("Could not load [%s]\n", argv[1]);
return 2;
} else {
printf("Successfully loaded [%s]\n", argv[1]);
dlclose(handle);
return 0;
}
}
Output:
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> gcc code.c -Wl,-ldl [cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> ./a.out "libcapi.so" Could not load [libcapi.so] [cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> LD_LIBRARY_PATH=$(pwd) ./a.out "libcapi.so" Successfully loaded [libcapi.so] [cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050838633]> LD_LIBRARY_PATH=$(pwd) ./a.out "libcapi" Could not load [libcapi]
So, the Win behavior can't be replicated on Ux (unfortunately, .
(dot) can't be part of an attribute name, in order to overcome this). Or, maybe the linker could be configured to search for .so files implicitly? But that would only solve the problem partially as many libraries look like libc.so.6 (or AFAIK, on OSX both .so and .dylib are OK).
来源:https://stackoverflow.com/questions/50838633/how-is-the-attribute-of-the-loader-instance-cdll-created