在py2exe编译代码中动态加载未编译的python插件
我的Python应用程序是这样构建的:一些功能可以作为插件使用。现在的插件架构非常简单:我有一个插件文件夹,里面放着一些Python模块。我是这样加载相关的插件的:
plugin_name = blablabla
try:
module = __import__(plugin_name, fromlist='do_something')
except ImportError:
#some error handling ...
然后执行:
try:
loans = module.do_something(id_t, pin_t)
except xxx:
# error handling
我使用py2exe把应用程序编译成Windows的可执行文件。这工作得很好,但问题是所有插件都必须包含在这个可执行文件里。这就不太方便,因为每次添加新插件时,我都得重新编译并发布一个新版本的应用程序。如果能把新的插件(也就是Python文件)直接复制到应用程序的插件文件夹里,然后我的应用程序能即时读取并执行这个文件里的代码,那就好了。
那么,最好的方法是什么呢?
(我想过逐行读取选定的插件文件,然后用一个exec
语句来执行它。但可能还有更好的方法……)
5 个回答
我建议你使用pkg_resources的entry_points功能(来自setuptools/distribute)来实现插件的发现和实例化。首先,这是一种标准的做法;其次,按照我所知,它不会遇到你提到的问题。你只需要把一些插件打包成一个egg文件,并在里面声明一些入口点(一个egg可以声明多个插件),然后当你把这个egg安装到你的Python环境中时,你的应用程序就能自动发现它声明的所有插件。你也可以把你的应用程序和“工厂”插件打包到同一个egg里,这样会很方便。
PyInstaller 还可以让你导入外部文件。如果你用它来打包你的应用程序,它不会把那些文件放进可执行文件里。这样的话,你就需要确保文件路径是正确的(也就是说,你的应用程序能在正确的文件夹里找到这些模块),这样一切就能正常运行了。
如果你不介意插件会以 .py 文件的形式发布,你可以这样做。把你所有的插件放在一个叫“plugin”的子文件夹里,然后创建一个空的 "__init__.py" 文件。在运行时,它会把这个文件夹里的所有模块一起导入。想了解更多,可以查看 Dive In Python 的解释……不过我最后用的就是这个方法。
def load_plugin(path):
import imp
"""Load plugin from directory and return list of modules"""
files = os.listdir( path )
test = re.compile(".py$", re.IGNORECASE)
files = filter(test.search, files)
filenameToModuleName = lambda f: os.path.splitext(f)[0]
moduleNames = sorted(map(filenameToModuleName, files))
f, filename, desc = imp.find_module('plugin')
plugin = imp.load_module('plugin', f, filename, desc)
modules = []
#print moduleNames
for m in moduleNames:
# skip any files starting with '__', such as __init__.py
if m.startswith('__'):
continue
try:
f, filename, desc = imp.find_module(m, plugin.__path__)
modules.append( imp.load_module(m, f, filename, desc))
except ImportError:
continue
return modules