Разблокируйте приложение Python в качестве одного файла для поддержки надстройки или расширений?

StackOverflow https://stackoverflow.com/questions/2876967

Вопрос

Есть несколько коммунальных услуг - все с разными процедурами, ограничениями и целевыми операционными системами - для получения пакета Python и всех его зависимостей и превращение их в одну двоичную программу, которая легко отправить клиентам:

Моя ситуация идет на шаг дальше: сторонние разработчики будут хотят написать плагины, расширения или дополнения для моего приложения. Это, конечно, непростой вопрос о том, как пользователи на платформах, таких как Windows, наиболее легко устанавливают плагины или дополнения таким образом, что мое приложение может легко обнаружить, что они были установлены. Но помимо того, что основной вопрос - это еще один: как сторонняя разработчик заблокирует их расширение с любыми библиотеками, которые нуждаются в себе расширение (которые могут быть бинарными модулями, такие как LXML) таким образом, что зависимости плагина становятся доступными для импорта в то же время Время, когда плагин становится доступным.

Как это можно подойти? Нужно ли мое приложение собственное плагин на диске и его собственный реестр плагинов, чтобы сделать эту тягусь? Или есть ли общие механизмы, чтобы я мог избежать писать сам, что позволит приложению, которое распространяется как один исполняемый файл, чтобы осмотреться и найти плагины, которые также устанавливаются как отдельные файлы?

Это было полезно?

Решение

Вы должны иметь возможность иметь каталог плагинов, который сканирует ваше приложение во время выполнения (или позже), чтобы импортировать рассматриваемый код. Вот пример, который должен работать с регулярным .py или .pyc, который даже работает с плагинами, хранящимися внутри Zip-файлов (чтобы пользователи могли просто бросить some

import re, os, sys
class Plugin(object):
    """
    The base class from which all plugins are derived.  It is used by the
    plugin loading functions to find all the installed plugins.
    """
    def __init__(self, foo):
        self.foo = foo
    # Any useful base plugin methods would go in here.

def get_plugins(plugin_dir):
    """Adds plugins to sys.path and returns them as a list"""

    registered_plugins = []

    #check to see if a plugins directory exists and add any found plugins
    # (even if they're zipped)
    if os.path.exists(plugin_dir):
        plugins = os.listdir(plugin_dir)
        pattern = ".py$"
        for plugin in plugins:
            plugin_path = os.path.join(plugin_dir, plugin)
            if os.path.splitext(plugin)[1] == ".zip":
                sys.path.append(plugin_path)
                (plugin, ext) = os.path.splitext(plugin) # Get rid of the .zip extension
                registered_plugins.append(plugin)
            elif plugin != "__init__.py":
                if re.search(pattern, plugin):
                    (shortname, ext) = os.path.splitext(plugin)
                    registered_plugins.append(shortname)
            if os.path.isdir(plugin_path):
                plugins = os.listdir(plugin_path)
                for plugin in plugins:
                    if plugin != "__init__.py":
                        if re.search(pattern, plugin):
                            (shortname, ext) = os.path.splitext(plugin)
                            sys.path.append(plugin_path)
                            registered_plugins.append(shortname)
    return registered_plugins

def init_plugin_system(cfg):
    """
    Initializes the plugin system by appending all plugins into sys.path and
    then using load_plugins() to import them.

        cfg - A dictionary with two keys:
        plugin_path - path to the plugin directory (e.g. 'plugins')
        plugins - List of plugin names to import (e.g. ['foo', 'bar'])
    """
    if not cfg['plugin_path'] in sys.path:
        sys.path.insert(0, cfg['plugin_path'])
    load_plugins(cfg['plugins'])

def load_plugins(plugins):
    """
    Imports all plugins given a list.
    Note:  Assumes they're all in sys.path.
    """
    for plugin in plugins:
        __import__(plugin, None, None, [''])
        if plugin not in Plugin.__subclasses__():
            # This takes care of importing zipped plugins:
            __import__(plugin, None, None, [plugin])

Итак, давайте скажем, у меня есть плагин с именем «foo.py» в каталоге, называемом «плагинами» (то есть в базе DIR моего приложения), который добавит новую возможность моим приложению. Содержимое может выглядеть так:

from plugin_stuff import Plugin

class Foo(Plugin):
    """An example plugin."""
    self.menu_entry = {'Tools': {'Foo': self.bar}}
    def bar(self):
        return "foo plugin!"

Я мог инициализировать мои плагины, когда я запускаю мое приложение, как так:

plugin_dir = "%s/plugins" % os.getcwd()
plugin_list = get_plugins(plugin_dir)
init_plugin_system({'plugin_path': plugin_dir, 'plugins': plugin_list})
plugins = find_plugins()
plugin_menu_entries = []
for plugin in plugins:
    print "Enabling plugin: %s" % plugin.__name__
    plugin_menu_entries.append(plugin.menu_entry))
add_menu_entries(plugin_menu_entries) # This is an imaginary function

Это должно работать до тех пор, пока плагин - это файл .py или .pyc (при условии, что он имеет байт-компиляцию для рассматриваемой платформы). Это может быть автономным файлом или внутри каталога с помощью в этом.py или Внутри ZIP-файла с одинаковыми правилами.

Откуда я знаю, что это работает? Это как я реализовал плагины в Pyci.. Отказ PYCI - это веб-приложение, но нет причин, почему этот метод не будет работать на регулярный OL 'GUI. Для приведенного выше примера я решил использовать функцию воображаемого ADD_MENU_ENTRIES () в сочетании с переменной объекта плагина, которая может быть использована для добавления методов плагина в меню GUI.

Надеюсь, этот ответ поможет вам создать собственную систему плагинов. Если вы хотите увидеть именно, как это реализуется, я рекомендую вам загрузить исходный код PYCI и посмотрите на Plugin_utils.py и пример плагина в каталоге Plugins_Enabled.

Другие советы

Вот еще один пример приложения Python, который использует плагины: OpenStv.. Отказ Здесь плагины могут быть только модулями Python.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top