Domanda

Ci sono diverse utility - tutti con diverse procedure, limitazioni, e sistemi operativi di destinazione - per ottenere un pacchetto di Python e tutte le sue dipendenze e trasformarli in un unico programma binario che è facile da spedire ai clienti:

La mia situazione va un passo oltre: sviluppatori di terze parti saranno voler scrivere plug-in, estensioni o add-on per la mia applicazione. E ', ovviamente, una domanda scoraggiante come gli utenti su piattaforme come Windows sarebbero più facilmente installare plugin o addon in tal modo un che la mia applicazione può facilmente scoprire che essi sono stati installati. Ma al di là di quella domanda fondamentale è un'altra: come può uno sviluppatore di terze parti fascio la loro estensione con qualunque biblioteche l'estensione in sé ha bisogno (che potrebbe essere moduli binari, come lxml) in modo tale che le dipendenze del plugin saranno disponibili per l'importazione allo stesso tempo che il plugin diventa disponibile.

Come può essere affrontato? La mia applicazione bisogno proprio plug-in area sul disco e il proprio plug-in Registro di sistema per rendere questo trattabili? O ci sono meccanismi generali, che ho potuto evitare di scrivere io stesso, che permetterebbe un app che viene distribuito come un unico file eseguibile di guardarsi intorno e trovare i plugin che vengono installati anche come singoli file?

È stato utile?

Soluzione

Si dovrebbe essere in grado di avere una directory dei plugin che l'applicazione esegue la scansione in fase di esecuzione (o successivo) per importare il codice in questione. Ecco un esempio che dovrebbe funzionare con .py regolare o codice .pyc che funziona anche con i plugin memorizzati all'interno dei file zip (così gli utenti possono solo cadere someplugin.zip nella directory 'plugins' e lo hanno magicamente lavoro):

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

Quindi, consente di dire che ho un plugin chiamato "foo.py" in una directory chiamata 'plugins' (che si trova nella directory di base della mia app) che aggiungerà una nuova funzionalità per la mia applicazione. Il contenuto potrebbe essere simile a questo:

from plugin_stuff import Plugin

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

ho potuto inizializzare miei plugin quando lancio il mio app in questo modo:

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

Che dovrebbe funzionare fintanto che il plugin è o un .py o un file .pyc (ammesso che è byte-compilato per la piattaforma in questione). Può essere file autonomo o all'interno di una directory con un init .py o all'interno di un file zip con le stesse regole.

Come faccio a sapere funziona questo? E 'così che ho implementato plugin in PyCI . PyCI è un'applicazione web, ma non c'è alcun motivo per cui questo metodo non avrebbe funzionato per un GUI ol normale'. Per l'esempio di cui sopra ho scelto di utilizzare un add_menu_entries immaginaria () in combinazione con una variabile oggetto plugin che potrebbe essere utilizzato per aggiungere i metodi di un plugin ai menu del GUI.

Speriamo che questa risposta vi aiuterà a costruire il proprio sistema di plugin. Se volete vedere esattamente come viene implementato vi consiglio di scaricare il codice sorgente e PyCI sguardo plugin_utils.py e il plugin di esempio nella directory plugins_enabled.

Altri suggerimenti

Ecco un altro esempio di un'applicazione Python che utilizza i plugin: OpenSTV . Qui, i plugin possono essere solo moduli Python.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top