Как загрузить dylib-файл как расширение CPython?

Об этом уже спрашивали (например, здесь), но данное решение (т.е. переименование файла в *.so) неприемлемо. У меня есть расширение CPython под названием name.dylib, которое нельзя импортировать. Если имя файла изменено на использование name.so, оно импортируется правильно. Изменение имени файла не является вариантом** и не должно быть необходимым.

В Python есть много хуков для поиска модулей, поэтому должен быть способ заставить его распознавать dylib-файл. Может кто-нибудь показать, как это сделать? Использование импорта низкого уровня, в котором указывается полное имя файла, не очень приятно, но является приемлемым решением.

** потому что код сборки заставляет dylib и другие контексты, которые я предполагаю. Модуль расширения имеет двойное назначение, его можно использовать как в качестве обычной разделяемой библиотеки, так и в качестве расширения Python. Использование символической ссылки действительно работает, но является последним средством, поскольку требует ручного вмешательства в автоматизированный процесс.


person Yttrill    schedule 30.01.2020    source источник


Ответы (1)


Вы можете изменить sys.path_hooks и заменить FileFinder-hook с тем, который будет принимать .dylib-расширения. Но см. также более простую, но менее удобную альтернативу, которая будет импортировать, учитывая полное имя файла расширения.

Дополнительную информацию о том, как импортируются файлы .so, .py и .pyc, можно найти, например, в этом мом ответе.

Эта манипуляция может выглядеть следующим образом:

import sys
import importlib
from importlib.machinery import FileFinder, ExtensionFileLoader

# pick right loader for .dylib-files:
dylib_extension = ExtensionFileLoader, ['.dylib']

# add dylib-support to file-extension supported per default
all_supported_loaders = [dylib_extension]+ importlib._bootstrap_external._get_supported_file_loaders()


# replace the last hook (i.e. FileFinder) with one recognizing `.dylib` as well:
sys.path_hooks.pop()
sys.path_hooks.append(FileFinder.path_hook(*all_supported_loaders))

#and now import name.dylib via
import name

Этот код должен выполняться первым, когда запускается python-скрипт. Другие модули могут не ожидать манипуляций с sys.path_hooks во время выполнения программы, поэтому могут возникнуть некоторые проблемы с другими модулями (такими как pdb, traceback и так далее). Например:

import pdb

#above code

import name

потерпит неудачу, пока

#above code

import pdb

import name

будет работать, так как pdb, кажется, манипулирует механизмом импорта.


Обычно FileFinder-hook является последним в sys.path_hooks, потому что это последнее средство, один раз -L1492" rel="nofollow noreferrer">path_hook_for_FileFinder вызывается для пути, возвращается искатель (ImportError должен быть поднят, если PathFinder должен смотреть на дальнейшие хуки):

def path_hook_for_FileFinder(path):
        """Path hook for importlib.machinery.FileFinder."""
        if not _path_isdir(path):
            raise ImportError('only directories are supported', path=path)
        return cls(path, *loader_details) # HERE Finder is returned!

Тем не менее, можно убедиться и проверить, что действительно заменен правый крючок.


Более простой альтернативой было бы использование imp.load_dynamic (пренебрегая на данный момент тем, что imp устарело):

import imp
imp.load_dynamic('name', 'name.dylib') # or what ever path is used

Это может быть более надежным, чем первое решение (например, нет проблем с pdb), но менее удобным для больших проектов.

person ead    schedule 30.01.2020