Найпростіша plugin-архітектура на python
Так вже вийшло, що мені вдруге довелося писати модульну систему на пітоні. Причому вдруге довелося йти по одних і тих самих граблях, бо приклад минулої реалізації залишився на ноуті, який зараз лежить в сервісному центрі. Шлях нижче не являється оптимальним чи там найкращим, але головне, що він 100% робочий і задовольняє моїм потребам
А потреби полягають в тому, що кожен плагін представляє собою реалізацію класу з певним набором функцій та має засіб для інстанціювання об’єкту цього класу не знаючи його імені. Отож…
Для початку створимо приблизно наступну систему підкаталогів з файлами, а потім почнемо її наповнювати:
+ / plugins
| |
| + / plugin1
| | |
| | - __init__.py
| |
| + / plugin2
| | |
| | - __init__.py
| |
| - __init__.py
|
- pluginmanager.py
Як неважко здогадатись, модуль plugingmanager.py - буде відповідати за завантаження плагінів. Папка plugins міститиме власне плагіни (кожен з яких буде займати окремий каталог) та файл ініціалізації пакету “plugins” (файл __init__.py) - його можна лишити порожнім, оскільки надпакет plugins використовується лише для групування.
Сподіваюсь, коментарі зрозумілі (хоч і англомовні
). Отож, ми перебираємо папку з плагінами і для кожого файлу (тобто в нашому випадку каталогу) намагаємось імпортувати його по імені. В пітоні завдяки вищенаведеній структурі будується дерево пакетів. Тобто в нашому випадку у нас є пакет верхнього рівня plugins, який має два підпакети: plugins.plugin1 та plugins.plugin2. Для імпорту використовується вбудована функція __import__(). Вона повертає клас модуля верхнього рівня (в нашому випадку - “plugins”), але оскільки нам потрібно дійти до пакета нижнього рівня ми “занурюємось” далі використовуючи рекурсивно функцію attrib. В нашому випадку, звісно, можна було зробити простіше, оскільки вкладеність тут має лише один рівень і назва пакету будується в цій же функції, але код я трохи ускладнив спеціально, щоб дати більш загальну та гнучку реалізацію завантаження пакетів чи модулів.
Отримавши клас модуля ми викликаємо його глобальну функцію CreateInstance(), що поверне нам екземпляр реалізованого в модулі класу.
Тепер розглянемо реалізацію власне плагінів. Для нескладних випадків ми можемо запихнути її прямо в опис пакету plugin1 - файл __init__.py:
По аналогії можна зробити і реалізацію plugin2. Тепер запустивши програму отримаємо наступне:
Набір відповідних плагінів можна тепер збільшувати просто додаючи по аналогії інші підкаталоги-пакети. Звісно, можна переробити це все з підтримкою модулів, а не пакетів (чи не лише пакетів), але мені все ж така структура подобається більше.



Лучше сделать так.
def LoadPlugins(pluginpath):
# enum plugins directory
for dir in os.listdir(pluginpath + “\\plugins”):
# for each non-hidden file or directory
if (dir[0] != “_”) and (dir.endswith (”.py”)):
# try to load corresponding plugin
LoadModule(dir)
Спасибо. Да, этот вариант явно попроще