Плагіни в кишені або складаний ножик у програмі


У цій статті мова піде про плагіни — програмних модулях, які можна легко встановити в основний додаток для розширення його функціоналу. Точніше, не про самих плагінах, а про те, як реалізувати у своїй програмі систему взаємодії "Додаток — Плагін".
У мережі можна легко знайти досить складні і, почасти, заплутані алгоритми інтеграції в ваш програмний код такої системи, але, оскільки ми будемо використовувати мову програмування Python, для нас все буде просто і зрозуміло.
Однак перед тим як показати читачеві всі інструменти своєї плагинной системи — свого роду складаний ножик, який на мою думку, повинен бути в кожному поважаючому себе кишені, пардон, програмі — трохи передісторії...
Одного разу для свого проекту мені потрібна, одна єдина, ну дуже важлива функція, яку на етапі розробки я, на жаль, не передбачив. Проект був великий і досить старий, тому мені довелося з тиждень повозитися, розшукуючи в його коді потрібні класи і процедури, щоб повісити в меню програми заповітну кнопочку, що реалізує необхідний функціонал. Та й, зізнатися, мало радості в переписуванні нехай і не все, але якоїсь частини програми.
Після цього я твердо вирішив переглянути архітектуру своїх проектів та, щоб не наступати на одні і ті ж граблі, поставив для себе завдання: розширення можливостей моїх програм повинно проводитися в один клік! Стверджують — зроблено.
Отже, дістаємо із шафи новенький скелет майбутньої програми, який виглядає ось так:
image
І перерахуємо його кісточки:
Папка Libs призначена для бібліотек і модулів майбутнього проекту. На даний момент в ній знаходяться модулі для ініціалізації та підключення плагінів:
loadplugin.py — завантажує плагіни програми з папки Plugins (буде створена автоматично) кореневій директорії проекту;
manifest.py — клас, що описує маніфест завантаження плагіна;
В кореневій директорії проекту:
program.py — основний рограммный код додатка, що знаходиться в класі Program;
main.py — запускає програмний код program.py і підключає плагіни, якщо такі є;
Далі я опишу тільки ключові моменти реалізації плагинной системи, опускаючи створення найпростішого інтерфейсу програми для демонстации роботи плагіна. Невеликий архів з прикладом ви зможете скачати за посиланням у кінці статті.
main.py:
#! /usr/bin/python2.7
# -*- coding: utf-8 -*-

import traceback

__version__ = "0.0.1"

def main():
try:
from Libs.loadplugin import loadplugin # функція завантаження плагінів
from program import Program # імпортуємо основний клас програми

app = Program()
loadplugin(app) # завантажуємо плагіни
app.run() # запуск програми
except as Exception exc:
print traceback.format_exc()
traceback.print_exc(file=open("error.log", "w"))

# Вивід вікна з текстом помилки.

if __name__ "__main__":
main()

Алгоритм модуля main.py:
  1. Створюємо екземпляр app класу Program і передаємо його в функцію завантаження плагінів (даний екземпляр буде доступний всім потрібним плагінів, надаючи їм доступ до змінних і функцій, реалізованих в основному програмному коді класу Program).
  2. Завантажуємо плагіни.
  3. Запускаємо додаток.
  4. Виводимо вікно з текстом помилки, якщо така виникла при запуску програми.
loadplugin.py:
# -*- coding: utf-8 -*-

import os
import traceback

def loadplugin(app):
"""Завантажує плагіни.

:type app: <class 'program.Program'>;
:param app: екземпляр класу Program;

"""

# Директорія плагінів.
plugins_path = \
"{}/Plugins".format(os.path.split(os.path.abspath(sys.argv[0]))[0])
# Список дозволених до підключення плагінів.
plugin_list = \
eval(open("{}/plugins_list.list".format(plugins_path)).read())

for name in os.listdir(plugins_path):
if name.startswith("__init__."):
continue

path = os.path.join(plugins_path, name)
if not os.path.isdir(path):
continue

try:
if name in plugin_list:
execfile(os.path.join(path, '__init__.py'),
{"app": app, "path": path})
except Exception:
raise Exception(traceback.format_exc())

Алгоритм модуля loadplugin.py:
  1. Скануємо директорію (пакет) Plugins на присутність пакетів плагінів (будь python-пакет в папці Plugins буде вважатися плагіном).
  2. Ініціалізуємо список плагінів з файлу plugins_list.list, який знаходиться в директорії Plugins.
  3. Якщо знайдений плагін згаданий в даному списку, підключаємо його, в іншому випадку шукаємо наступний плагін.
Для більшої наочності я прибрав з модуля loadplugin.py верефикацию плагінів і різні перевірки на відсутність директорії Plugins файл plugins_list.list та ін
Як ви помітили, плагін виповнюється функцією execfile:
execfile(os.path.join(path, '__init__.py'),
{"app": app, "path": path})

яка виконує код файлу init.py пакету плагіна і передає в глобальний простір імен init.py примірник app головного класу програми.
Далі, через app, плагін отримує доступ до всіх функцій і змінним програмного коду програми. Досить просто.
Тепер давайте перейдемо від слів до справи і запустимо тестовий прикладmain.py з папки TestPlugin.
Тут варто сказати, що пишу я, використовуючи в основному фреймворк Kivy, так що для запуску тестових прикладів передбачається, що всі необхідні бібліотеки встановлені на вашій робочій станції.
Отже, запустивши приклад, побачимо простенький інтерфейс actionbar внизу екрану. Виберемо кнопку в нижньому правому куті, відкривши випадаючий список з двох пунктів, натискаємо пункт "Плагіни" і читаємо повідомлення "Немає встановлених плагінів".
image
Справді, заглянувши в папку проекту, виявимо нову директорію Plugins.
image
Крім вже відомих нам файлів, директорія для плагінів порожня. Ну що ж! Саме час їх встановити. Давайте розширимо інструментал нашого складаного ножика з допомогою плагіна і додамо actionbar нову кнопку з логотипом Хабра.
В архіві з тестовим прикладом, крім проекту TestPlugins, є папка Plugins. Відкрийте її і скопіюйте плагін HabraButton в проект в директорію Plugins.
image
Тепер знову запускаємо наш тестове додаток і вибираємо пункт "Плагіни".
image
Ура! Наш складаний ножик тільки що обзавівся новеньким інструментом, як і було обещанно, в один клік.
елемент з ім'ям свіжого плагіна горить жовтим кольором, це означає, що програма його впізнала, але не підключило до використання. Виправляємо це, натискаючи на кнопку з знайденим плагіном.
image
Підключаємо плагін, читаємо повідомлення, що "Плагін підключений і буде імпортовано в проект після наступного запуску."
Якщо знову вибрати у випадаючому списку пункт "Плагіни", побачимо, що, дійсно, плагін успішно падключен, про що свідчить кнопка, палаюча вже блакитним кольором.
image
Власне, що сталося, коли ми вибрали пункт "Підключити"? Ім'я плагіна було додано до списку дозволених до використання в файл plugins_list.list в каталог Plugins. Тепер модуль завантаження плагінів loadplugin.py зможе імпортувати цей плагін.
Давайте це перевіримо і запустимо тестовий приклад ще раз.
image
З'явилася нова кнопка з логотипом Хабра actionbar. Натискаємо її і насолоджуємося повідомленням "Привіт, плагін"!
Ось, власне, і все.
Я не розглядаю в даній статті код самого плагіна (він досить простий). Ви можете відкрити файл init.py пакету HabraButton і подивитися, як через примірник app плагін звертається до об'єктів програмного коду класу Program. Також за бортом я залишив верефикацию плагінів. Мені вона потрібна, щоб відстежувати сумісність і візуалізувати інформацію про плагіни. Все це є в тестовому прикладі.
Сподіваюся, зміг бути корисним!
Тестовий приклад — 40.65 Kb.

Джерело: Хабрахабр
  • avatar
  • 0

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.