I write in Python 3.
I want to add plugins support to my program. I don’t want to use heavy frameworks, so I deiced to write a minimal one by my self.
By the way, the plugins need to run by time. I don’t always unload and load the plugin when re-run — the plugins will lose all data.
My folder structs is here:
interfaces/
├── dummy
├── gmail
│ ├── __init__.py
│ └── __pycache__
│ └── __init__.cpython-33.pyc
└── hello
├── __init__.py
└── __pycache__
└── __init__.cpython-33.pyc
Then, I wrote a piece of code to load and execute the plugins:
#!/usr/bin/python3
import os
import imp
INTERFACES_FOLDER = './interfaces'
MAIN_MODULE = '__init__'
def search_plugins():
plugins = []
plugins_folders = os.listdir(INTERFACES_FOLDER)
for i in plugins_folders:
plugin_folder = os.path.join(INTERFACES_FOLDER, i)
if not os.path.isdir(plugin_folder):
continue
if not MAIN_MODULE + '.py' in os.listdir(plugin_folder):
continue
info = imp.find_module(MAIN_MODULE, [plugin_folder])
plugins.append({'name': i, 'info': info})
return plugins
def load_plugin(plugin):
return imp.load_module(MAIN_MODULE, *plugin["info"])
plugins_list = search_plugins()
plugins = []
for i in plugins_list:
module = load_plugin(i)
print(module)
plugins.append(module)
print(plugins)
Outputs:
# it works!
<module '__init__' from './interfaces/gmail/__init__.py'>
<module '__init__' from './interfaces/hello/__init__.py'>
# what's wrong?
[<module '__init__' from './interfaces/hello/__init__.py'>,
<module '__init__' from './interfaces/hello/__init__.py'>]
As you see, when I loaded a plugin, everything is working correctly. But when I append them to the a list, different modules will became a same one.
What’s wrong?
You import different modules, but with the same name (they are all called “init“). Python will, when you import the second module notice that you have already imported a module called “init” and return that.
The module you should import is rather the “gmail” and “hello” modules. That might work.
However, I’d urge you to reconsider about writing your own plugin system. It really isn’t that easy. A quick search finds several plugin systems, some slightweight, some not so much. Most have been abandoned, which is an indication that this isn’t so easy as you might think.
The plugin system in Python that probably is the most widely used, also the most flexible and amongst the oldest, is the Zope Component Architecture. See also the docs. Note that in Python 3 you use class decorators instead of the class body statements.
Another popular plugin system is the “entry points” of Distribute. That’s quite lightweight, although I don’t know if you can load or unload plugins with that.
Others I have never looked at are Yapsy, one called “Plugins” that seem abandoned, another one called “PyPlugin” also abandoned, and one called “easy_plugins” which seem very new, and isn’t abandoned yet.
And here is example code for a plugin manager.