python - loader 实例 `cdll` 的属性是如何创建的?
问题描述
来自https://docs.python.org/3/library/ctypes.html#loading-shared-libraries
共享库也可以通过使用预制对象之一来加载,这些对象是 LibraryLoader 类的实例,或者通过调用 LoadLibrary() 方法,或者通过检索库作为加载器实例的属性。
我找到了第一种方式的示例Free the open ctypes library in Python
我想知道如何使用第二种方式?特别是loader实例的属性是如何cdll
创建的?我的问题来自为什么加载 libc 共享库有“'LibraryLoader' 对象不可调用”错误?
LibraryLoader 的全部意义在于它会在您访问它时为您创建库。并且
cdll.LoadLibrary("foo")
不创建cdll.foo
.
我的实验有问题吗?为什么cdll.libc
永远不存在?
>>> 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'>}
解决方案
示例(发生了什么):
>>> 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']
它是如何发生的——都在“${PYTHON_SRC_DIR}/Lib/ctypes/__init__.py”
([GitHub]:python/cpython -(master)cpython/Lib/ctypes/__init__.py)中。我正在粘贴代码,cdll
因为windll
(我在上面的示例中使用)只是它的一个包装器(并且需要更多代码):
# ...
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:
嗯,在Ux(至少是Lnx )上,事情并不是那么好:
[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
那是因为显然(相对于[MSDN]: LoadLibrary function),[man7]: DLOPEN(3)不会将(默认)库扩展名(.so)附加到文件名(如果它不包含它)已经)。
代码.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;
}
}
输出:
[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]
因此,无法在Ux上复制Win行为(不幸的是,(dot)不能成为属性名称的一部分,以克服这一点)。或者,也许链接器可以配置为隐式搜索.so文件?但这只能部分解决问题,因为许多库看起来像libc.so.6(或AFAIK,在OSX上.so和.dylib都可以)。.
推荐阅读
- python - 装饰器返回函数对象而不是包装函数的输出
- java - Spring Data Elasticsearch 可以很好地保存数据,但使用 findAll 返回空数组
- javascript - Mochajs 测试函数“It”在 Promise.then 函数中调用时永远不会运行
- android - 我的应用程序在使用泄漏金丝雀时冻结 - 为什么?
- python - 如何从字典列表中删除重复的字典对象
- python - 在 python 中将多个特定文本文件转换为 CSV
- reactjs - React、Enzyme 异步生命周期函数
- javascript - html2canvas is not reading inline background-color style
- delphi - 在内联汇编中将常量读入 SSE/AVX 寄存器
- sql - Subtract all oracle tables with prefix USER from tables with prefix ALL in single statement