在不使用 imp.load_dynamic 的情况下将 DLL 导入 Python 3
目标
我想为Total Phase Aardvark的Python接口添加Windows支持,目前这个接口只支持Linux。这个接口是一个设备的封装,而这个设备的可用接口只有一个.so(Linux)或.dll(Windows)的闭源二进制文件。不过,它是作为一个Python包制作的(我不确定这个说法是否准确),而不是像你用ctypes加载的标准C接口。
文件结构
在这个项目中,我们有一个ext文件夹,它和执行导入的脚本在同一级别,里面有适用于Linux和Windows的32/64位库(这是我添加的):
pyaardvark.py (file doing imports)
ext
linux32
__init.py__ (empty)
aardvark.so
linux 64
__init.py__ (empty)
aardvark.so
win32
__init.py__ (empty)
aardvark.dll
win64
__init.py__ (empty)
aardvark.dll
导入问题
Linux的实现使用了:
from .ext.linux32 import aardvark as api
我最初在Python 2.7中测试添加Windows导入时,发现相对路径的导入方式无法正常工作。唯一成功的方法是:
import imp
import os
cur_path = os.path.dirname(__file__)
api = imp.load_dynamic('aardvark', os.path.join(cur_path, 'ext', 'win32', 'aardvark.dll'))
这个方法看起来不太好,但在Python 2.7中运行得很好,所有的API都可以使用。
我切换到Python 3.4进行测试,发现imp模块已经被弃用了。而且,从Python 3.2开始,imp模块似乎就没有load_dynamic这个功能了。我在Python 3.4中找不到让这个DLL可用的方法。
尝试的方法
方法1
在Python 2.7和Python 3.4中都失败了。
from .ext.win32 import aardvark as api
方法2
在Python 2.7和Python 3.4中都失败了。
import importlib
import os
cur_path = os.path.dirname(__file__)
api = importlib.import_module('aardvark', os.path.join(cur_path, 'ext', 'win32', 'aardvark.dll'))
方法3
在Python 2.7中有效,但在Python 3.4中失败。
import imp
import os
cur_path = os.path.dirname(__file__)
api = imp.load_dynamic('aardvark', os.path.join(cur_path, 'ext', 'win32', 'aardvark.dll'))
方法4
在Python 2.7和Python 3.4中都没有报错,但也不是我想要的结果。
import os
cur_path = os.path.dirname(__file__)
from ctypes import cdll
api = cdll.LoadLibrary(os.path.join(cur_path, 'ext', 'win32', 'aardvark.dll'))
在Python 2.7中使用有效的(imp.load_dynamic)导入后,dir(api)
给我的结果是:
['__doc__', '__file__', '__name__', '__package__', 'py_aa_async_poll', 'py_aa_close', 'py_aa_configure', 'py_aa_features', 'py_aa_find_devices', 'py_aa_find_devices_ext', 'py_aa_gpio_change', 'py_aa_gpio_direction', 'py_aa_gpio_get', 'py_aa_gpio_pullup', 'py_aa_gpio_set', 'py_aa_i2c_bitrate', 'py_aa_i2c_bus_timeout', 'py_aa_i2c_free_bus', 'py_aa_i2c_monitor_disable', 'py_aa_i2c_monitor_enable', 'py_aa_i2c_monitor_read', 'py_aa_i2c_pullup', 'py_aa_i2c_read', 'py_aa_i2c_read_ext', 'py_aa_i2c_slave_disable', 'py_aa_i2c_slave_enable', 'py_aa_i2c_slave_read', 'py_aa_i2c_slave_read_ext', 'py_aa_i2c_slave_set_response', 'py_aa_i2c_slave_write_stats', 'py_aa_i2c_slave_write_stats_ext', 'py_aa_i2c_write', 'py_aa_i2c_write_ext', 'py_aa_i2c_write_read', 'py_aa_log', 'py_aa_open', 'py_aa_open_ext', 'py_aa_port', 'py_aa_sleep_ms', 'py_aa_spi_bitrate', 'py_aa_spi_configure', 'py_aa_spi_master_ss_polarity', 'py_aa_spi_slave_disable', 'py_aa_spi_slave_enable', 'py_aa_spi_slave_read', 'py_aa_spi_slave_set_response', 'py_aa_spi_write', 'py_aa_status_string', 'py_aa_target_power', 'py_aa_unique_id', 'py_aa_version', 'py_version']
使用ctypes导入后,dir(api)
给我的结果是:
['_FuncPtr', '__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__', '_func_flags_', '_func_restype_', '_handle', '_name']
我不知道接下来该尝试什么。我在Python 2.7中工作没问题,但我真的希望能支持Python 3。
1 个回答
我在使用Python 3.6时,通过importlib
成功导入了一个外部库。官方文档提供了一个直接导入源文件的方法:
import importlib.util
import sys
# For illustrative purposes.
import tokenize
file_path = tokenize.__file__
module_name = tokenize.__name__
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# Optional; only necessary if you want to be able to import the module
# by name later.
sys.modules[module_name] = module
(https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly)
我把这个方法稍微改了一下:
import importlib.util
def load_dynamic(module, path):
spec = importlib.util.spec_from_file_location(module, path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
然后你就可以在Python 3中做类似这样的事情:
load_dynamic('aardvark', os.path.join(cur_path, 'ext', 'win32', 'aardvark.dll'))