Pregunta

Hay varias utilidades - todos con diferentes procedimientos, limitaciones y sistemas operativos de destino - para conseguir un paquete de Python y todas sus dependencias y convertirlos en un solo programa binario que es fácil de barco para los clientes:

Mi situación da un paso más: los desarrolladores de terceras partes serán querer escribir plug-ins, extensiones o complementos para mi aplicación. Es, por supuesto, una cuestión de enormes proporciones que hacen los usuarios en plataformas como Windows instalarían más fácilmente plugins o complementos de tal manera que mi aplicación puede descubrir fácilmente que se han instalado. Pero más allá de esa cuestión fundamental es otra: ¿cómo puede un desarrollador de terceros paquete de su extensión con lo que las bibliotecas de la extensión en sí necesita (que podría ser módulos binarios, como lxml) de tal manera que las dependencias del plugin estén disponibles para su importación al mismo el tiempo que el plugin esté disponible.

¿Cómo puede esto ser abordado? ¿Mi aplicación necesita su propio plug-in de área en el disco y su propio plug-in de registro para hacer de este manejable? O existen mecanismos generales, que podría evitar la escritura de mí mismo, que permitiría una aplicación que se distribuye como un único ejecutable para mirar alrededor y encontrar plugins que también se instalan como archivos individuales?

¿Fue útil?

Solución

debe ser capaz de tener un directorio de plugins que su aplicación escanea en tiempo de ejecución (o posterior) para importar el código en cuestión. He aquí un ejemplo que debería funcionar con .py regular o código .pyc que funciona incluso con plugins almacenan archivos zip en el interior (por lo que los usuarios sólo podrían caer someplugin.zip en el directorio 'plugins' y hacer que mágicamente trabajo):

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])

Así que vamos a decir que tengo un plugin llamado "foo.py" en un directorio llamado 'plugins' (que se encuentra en el directorio de la base de mi aplicación) que agregará una nueva capacidad a mi solicitud. El contenido puede tener este aspecto:

from plugin_stuff import Plugin

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

Yo podría inicializar mis plugins cuando inicio mi aplicación, así:

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

Eso debería funcionar siempre y cuando el complemento es o bien un archivo .py o .pyc (suponiendo que es el byte-compilado para la plataforma en cuestión). Puede ser independiente o archivo dentro de un directorio con un init .py o en el interior de un archivo zip con las mismas reglas.

¿Cómo sé que esto funciona? Es la forma en que he implementado plugins en PyCI . PyCI es una aplicación web, pero no hay razón por la cual este método no funcionaría para la interfaz gráfica de usuario de un ol regular'. Para el ejemplo anterior he elegido utilizar un add_menu_entries imaginarios () en conjunción con una variable de objeto Plugin que podría ser utilizado para añadir los métodos de un plugin para los menús del GUI.

Esperamos que esta respuesta le ayudará a construir su propio sistema de plugins. Si desea ver exactamente cómo se implementa Yo recomiendo descargar el código fuente y PyCI vistazo a plugin_utils.py y el plugin de ejemplo en el directorio plugins_enabled.

Otros consejos

Aquí es otro ejemplo de una aplicación de Python que utiliza plugins: OpenSTV . En este caso, los complementos sólo pueden ser módulos de Python.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top