加载程序实例`cdll`的属性是如何创建的? [英] How is the attribute of the loader instance `cdll` created?

查看:118
本文介绍了加载程序实例`cdll`的属性是如何创建的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

来自 https://docs.python.org/3/library/ctypes.html#loading-shared-libraries

共享库也可以通过使用预制模块之一来加载 对象,它们是LibraryLoader类的实例,由 调用LoadLibrary()方法,或通过检索库为 加载程序实例的属性.

我找到了第一种方法的示例在Python中释放打开的ctypes库

我想知道如何使用第二种方式?特别是,如何创建加载程序实例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"
(

那是因为显然(与 [man7]:DLOPEN(3)不会在文件名后附加(默认)库扩展名( .so ). >

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;
    }
}

输出:

[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]

因此, Win 行为无法在 Ux 上复制(不幸的是,.( dot )不能成为一部分为了克服这个问题).或者,也许可以将链接器配置为隐式搜索 .so 文件?但这只能部分解决该问题,因为许多库看起来像 libc.so.6 (或 OSX 上的 AFAIK )一样,都是.因此 .dylib OK ).

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 create cdll.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'>}

解决方案

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).

这篇关于加载程序实例`cdll`的属性是如何创建的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆