在py2exe编译代码中动态加载未编译的python插件

6 投票
5 回答
2935 浏览
提问于 2025-04-16 01:20

我的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 个回答

0

我建议你使用pkg_resources的entry_points功能(来自setuptools/distribute)来实现插件的发现和实例化。首先,这是一种标准的做法;其次,按照我所知,它不会遇到你提到的问题。你只需要把一些插件打包成一个egg文件,并在里面声明一些入口点(一个egg可以声明多个插件),然后当你把这个egg安装到你的Python环境中时,你的应用程序就能自动发现它声明的所有插件。你也可以把你的应用程序和“工厂”插件打包到同一个egg里,这样会很方便。

1

PyInstaller 还可以让你导入外部文件。如果你用它来打包你的应用程序,它不会把那些文件放进可执行文件里。这样的话,你就需要确保文件路径是正确的(也就是说,你的应用程序能在正确的文件夹里找到这些模块),这样一切就能正常运行了。

3

如果你不介意插件会以 .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

撰写回答