Как создается атрибут экземпляра загрузчика `cdll`? - PullRequest
0 голосов
/ 13 июня 2018

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

Совместно используемые библиотеки также можно загрузить с помощью одного из готовых объектов, которые являются экземплярами класса LibraryLoader, либо , вызвав метод LoadLibrary () или путем извлечения библиотеки как атрибута экземпляра загрузчика .

Я нашел пример для первого способа Освободить открытую библиотеку ctypes в Python

Мне было интересно, как использовать второй способ?В частности, как создается атрибут экземпляра загрузчика 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'>}

1 Ответ

0 голосов
/ 13 июня 2018

Пример ( что происходит ):

>>> 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 - (основной) 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 ), [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 (к сожалению, . ( точка ) не может быть частью имени атрибута, чтобы преодолеть это).Или, может быть, компоновщик можно настроить для неявного поиска файлов .so ?Но это только частично решит проблему, так как многие библиотеки выглядят как libc.so.6 (или AFAIK , на OSX и .so и .dylib OK ).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...