Load plugins in python

Делаем плагины на Python

Многие программы поддерживают так называемые плагины (дополнение, расширения и т.п.), с помощью которых можно расширять функциональность программы. На Python делать программы, поддерживающие плагины особенно легко и приятно. Потому что с одной стороны в качестве плагина могут выступать полноценные классы, а с другой стороны благодаря кроссплатформенности языка Python плагины так же остаются кроссплатформенными. Давайте посмотрим что нужно сделать, чтобы ваша программа тоже поддерживала плагины. Заодно убедимся как это легко.

Первый пример. Основные функции

Вначале давайте договоримся о структуре тестовых примеров. Модуль основной программы будет называться main.py, а плагины будут лежать в папке plugins, располагающейся рядом с этим файлом. Чтобы Python принял plugins за пакет, в нем должен находиться файл с именем __init__.py (в нашем случае он будет пустым).

Сначала представим, что динамически во время выполнения программы нам не нужно узнавать имя плагина и мы его знаем на этапе программирования. Пусть плагин имеет имя my_plugin.py и располагается в папке plugins. И пусть внутри файла my_plugin.py находится класс pluginClass, который содержит всю функциональность плагина. Вот его код:

class pluginClass ( object ) :
def __init__ ( self ) :
pass

def run ( self ) :
print «Hello, Plug-in!»

В конечном итоге нам необходимо во время выполнения программы добраться до этого класса, создать его экземпляр и выполнить метод run. Для простоты на первое время договоримся, что в этом модуле нет других классов кроме pluginClass. Как бы мы поступили, если бы все имена (и модуля, и класса) были бы доступны во время программирования? Скорее всего вот так:

Читайте также:  Python array remove duplicates

И в результате получили бы сообщение «Hello, Plug-in!». А теперь вернемся к нашей задаче. Нам нужно сделать то же самое, но при этом имя модуля my_plugin и имя класса pluginClass хранится в соответствующих строковых переменных.

Импорт модуля плагина

Аналогом встроенной директивы import является функция __import__ , она позволяет импортировать модули, имена которых на этапе написания программы неизвестны. У функции __import__ пять параметров, но обязательным является только первый. Необязательные параметры в данной статье мы использовать не будем, поэтому про них умолчим. Итак, единственный обязательный параметр — это имя пакета или модуля, который мы хотим импортировать. Если импорт пройдет удачно, функция возвратит экземпляр класса, который хранит все импортированные элементы.

Начнем с импортирования модуля. Директива import нам не поможет. Зато мы можем воспользоваться функцией __import__ . Аналогом первой строки из записанного выше примера будет следующий код:

После этого переменная package_obj станет экземпляром класса загруженного модуля (пакета) plugins. Чтобы убедиться в этом выполним команду

При этом мы получим что-то вроде (путь, разумеется, может быть другой):

Это сообщение мало информативно, поэтому применим к переменной package_obj встроенную функцию dir, которая возвращает имеющиеся в package_obj атрибуты. Итак, выполним следующий код:

В результате на экран выведется такой список:

Обратите внимание на последний элемент списка — это и есть наш плагин. Итак, пакет мы загрузили, но как нам добраться до модуля нашего плагина? Для этого сначала воспользуемся встроенной функцией getattr, которая позволяет получить из модуля или пакета (в нашем случае package_obj) экземпляр класса атрибута (а нашем случае my_plugin). Эта функция принимает два параметра: соответственно экземпляр объекта, атрибут которого надо получить и строковую переменную, которая содержит имя атрибута. Применяя функцию getattr на пакет, в случае успеха мы получим экземпляр загруженного модуля. Выполним следующий код:

Если все прошло удачно, на экране мы увидим примерно такой результат:

Но скорее всего в программе придется загружать не один плагин, а несколько. Как в этом случае поведет себя функция __import__ ? Рассмотрим пример, в котором загружаются два плагина (все они должны находиться в папке plugins.

modulename1 = «my_plugin_1»
modulename2 = «my_plugin_2»
classname = «pluginClass1»

package_obj = __import__ ( «plugins.» + modulename1 )
package_obj = __import__ ( «plugins.» + modulename2 )

В результате на экране вы увидите следующий результат:

В этом примере результат импорта мы каждый раз присваиваем одной и той же переменной. Но в результате после каждой операции импорта в нее добавляется новый импортированный модуль. Скачать этот пример можно по адресу — test1_1.zip.

Получаем доступ к классу

Итак, модуль my_plugin мы загрузили. Осталось добраться до класса, который содержится внутри. Для этого воспользуемся уже знакомой нам функцией dir и убедимся, внутри действительно хранится наш класс:

В результате выполнения этого кода получим:

Источник

Плагины в Python

Сегодня я расскажу, как построить плагиновую архитектуру в python на include’ах.

Наше приложение будет получать команды и раздавать их плагинам в надежде, что какой-нибудь плагин ее да и обработает

Хитрый план

  • При запуске сканируем папку с плагинами
  • Импортируем все найденные файлы
  • При получении команды отсылаем ее каждому плагину

main.py

Здесь содержится только код «внешней» части примера, и комментировать его, надеюсь, не нужно.

  1. import plugin
  2. plugin.LoadPlugins()
  3. s = »
  4. while (s != ‘exit’ ):
  5. print ‘>’ ,
  6. s = raw_input()
  7. a = s.split( ‘ ‘ )
  8. for p in plugin.Plugins:
  9. p.OnCommand(a[ 0 ], a[ 1 :])

Весь код, относящийся к плагинам мы вынемем в plugin.py

plugin.py

  1. import os
  2. import sys
  3. # Экземпляры загруженных плагинов
  4. Plugins = []
  5. # Базовый класс плагина
  6. class Plugin (object):
  7. Name = ‘undefined’
  8. # Методы обратной связи
  9. def OnLoad (self):
  10. pass
  11. def OnCommand (self, cmd, args):
  12. pass
  13. def LoadPlugins ():
  14. ss = os.listdir( ‘plugins’ ) # Получаем список плагинов в /plugins
  15. sys.path.insert( 0 , ‘plugins’ ) # Добавляем папку плагинов в $PATH, чтобы __import__ мог их загрузить
  16. for s in ss:
  17. print ‘Found plugin’ , s
  18. __import__(os.path.splitext(s)[ 0 ], None, None, [ » ]) # Импортируем исходник плагина
  19. for plugin in Plugin .__subclasses__(): # так как Plugin произведен от object, мы используем __subclasses__, чтобы найти все плагины, произведенные от этого класса
  20. p = plugin() # Создаем экземпляр
  21. Plugins.append(p)
  22. p. OnLoad () # Вызываем событие загруки этого плагина
  23. return

plugins/foo.py — пример плагина

  1. from plugin import Plugin
  2. class HelloPlugin (Plugin): # производим наш плагин от родительского класса
  3. Name = ‘HelloPlugin v 1.0 Extreme Edition’
  4. # замещаем нужные методы
  5. def OnLoad (self):
  6. print ‘HelloPlugin 1.0 Extreme VIP Edition Loaded!’
  7. def OnCommand (self, cmd, args):
  8. if (cmd == ‘hello’ and len(args)> 0 ):
  9. print ‘It\’s’ , args[ 0 ], ‘!\nJeez, man, nice to meet you!’
  10. return True
  11. else :
  12. return False

Сейчас уже можно запустить пример:

$ python main.py
Found plugin foo.py
HelloPlugin 1.0 Extreme VIP Edition Loaded!
> hello %username%
It’s %username%!
Jeez, man, nice to meet you!
>

plugins/shell.py — плагин посложнее

Сделаем плагин, который будет транслировать команды шеллу и возвращать вывод:

  1. from plugin import Plugin
  2. import commands
  3. class ShellPlugin (Plugin):
  4. Name = ‘Shell plugin’
  5. def OnLoad (self):
  6. print ‘Shell plugin loaded.’
  7. def OnCommand (self, cmd, args):
  8. if (cmd == ‘run’ ):
  9. print commands.getoutput ( » » .join(args))
  10. return True
  11. else :
  12. return False

$ python main.py
Found plugin shell.py
Found plugin foo.py
Shell plugin loaded.
HelloPlugin 1.0 Extreme VIP Edition Loaded!
> run uname -r
2.6.31-14-generic
> exit

Источник

Оцените статью