213 Stimmen

Aufbau einer minimalen Plugin-Architektur in Python

Ich habe eine in Python geschriebene Anwendung, die von einer eher technischen Zielgruppe (Wissenschaftler) genutzt wird.

Ich suche nach einer guten Möglichkeit, die Anwendung durch die Benutzer erweiterbar zu machen, d.h. eine Skripting/Plugin-Architektur.

Ich bin auf der Suche nach etwas extrem leicht . Die meisten Skripte oder Plugins werden nicht von einem Drittanbieter entwickelt, vertrieben und installiert, sondern sind etwas, das ein Benutzer in wenigen Minuten erstellt, um eine sich wiederholende Aufgabe zu automatisieren, ein Dateiformat zu unterstützen usw. Plugins sollten also ein absolutes Minimum an Boilerplate-Code haben und keine andere "Installation" erfordern als das Kopieren in einen Ordner (so etwas wie setuptools-Einstiegspunkte oder die Zope-Plugin-Architektur scheint zu viel zu sein).

Gibt es bereits solche Systeme oder Projekte, die ein ähnliches Schema implementieren, die ich mir ansehen sollte, um Ideen und Anregungen zu erhalten?

160voto

TJG Punkte 1951

Meines ist im Grunde ein Verzeichnis namens "Plugins", das die Hauptanwendung abfragen und dann verwenden kann imp.load_module um Dateien abzuholen, suchen Sie nach einem bekannten Einstiegspunkt, möglicherweise mit Konfigurationsparametern auf Modulebene, und gehen Sie von dort aus. Ich verwende Datei-Überwachung Zeug für ein gewisses Maß an Dynamik, in denen Plugins aktiv sind, aber das ist ein nice-to-have.

Natürlich besteht bei jeder Anforderung, bei der es heißt: "Ich brauche [große, komplizierte Sache] X nicht; ich will nur etwas Leichtgewichtiges", die Gefahr, dass X eine entdeckte Anforderung nach der anderen neu implementiert wird. Aber das soll nicht heißen, dass man nicht trotzdem Spaß dabei haben kann :)

66voto

dbr Punkte 158949

module_example.py :

def plugin_main(*args, **kwargs):
    print args, kwargs

loader.py :

def load_plugin(name):
    mod = __import__("module_%s" % name)
    return mod

def call_plugin(name, *args, **kwargs):
    plugin = load_plugin(name)
    plugin.plugin_main(*args, **kwargs)

call_plugin("example", 1234)

Es ist sicherlich "minimal", es hat absolut keine Fehlerprüfung, wahrscheinlich unzählige Sicherheitsprobleme, es ist nicht sehr flexibel - aber es sollte Ihnen zeigen, wie einfach ein Plugin-System in Python sein kann.

Wahrscheinlich sollten Sie sich mit dem imp Modul, obwohl man auch mit einem einfachen Modul viel erreichen kann. __import__ , os.listdir und einige String-Manipulationen.

33voto

PhilS Punkte 1239

Werfen Sie einen Blick auf bei dieser Übersicht über bestehende Plugin-Frameworks / Bibliotheken ist dies ein guter Ausgangspunkt. Ich mag sehr yapsy aber das hängt von Ihrem Anwendungsfall ab.

25voto

Nicolas Dumazet Punkte 7080

Diese Frage ist zwar sehr interessant, aber ohne weitere Details wohl kaum zu beantworten. Um welche Art von Anwendung handelt es sich? Hat sie eine grafische Benutzeroberfläche? Ist es ein Befehlszeilentool? Eine Reihe von Skripten? Ein Programm mit einem eindeutigen Einstiegspunkt, usw...

Angesichts der wenigen Informationen, die mir vorliegen, werde ich sehr allgemein antworten.

Welche Mittel müssen Sie einsetzen, um Plugins hinzuzufügen?

  • Wahrscheinlich müssen Sie eine Konfigurationsdatei hinzufügen, in der die zu ladenden Pfade/Verzeichnisse aufgeführt sind.
  • Eine andere Möglichkeit wäre, zu sagen "alle Dateien in diesem Plugin/Verzeichnis werden geladen", aber das hat den Nachteil, dass die Benutzer Dateien verschieben müssen.
  • Eine letzte Zwischenlösung wäre, dass alle Plugins im gleichen Plugin/Ordner liegen müssen und dann über relative Pfade in einer Konfigurationsdatei aktiviert/deaktiviert werden.

In der reinen Code-/Design-Praxis müssen Sie eindeutig festlegen, welches Verhalten bzw. welche spezifischen Aktionen Ihre Benutzer ausführen sollen. Bestimmen Sie den gemeinsamen Einstiegspunkt bzw. eine Reihe von Funktionen, die immer überschrieben werden sollen, und legen Sie Gruppen innerhalb dieser Aktionen fest. Sobald dies geschehen ist, sollte es einfach sein, Ihre Anwendung zu erweitern,

Beispiel mit Häkchen Die Sprache ist von MediaWiki inspiriert (PHP, aber ist die Sprache wirklich wichtig?):

import hooks

# In your core code, on key points, you allow user to run actions:
def compute(...):
    try:
        hooks.runHook(hooks.registered.beforeCompute)
    except hooks.hookException:
        print('Error while executing plugin')

    # [compute main code] ...

    try:
        hooks.runHook(hooks.registered.afterCompute)
    except hooks.hookException:
        print('Error while executing plugin')

# The idea is to insert possibilities for users to extend the behavior 
# where it matters.
# If you need to, pass context parameters to runHook. Remember that
# runHook can be defined as a runHook(*args, **kwargs) function, not
# requiring you to define a common interface for *all* hooks. Quite flexible :)

# --------------------

# And in the plugin code:
# [...] plugin magic
def doStuff():
    # ....
# and register the functionalities in hooks

# doStuff will be called at the end of each core.compute() call
hooks.registered.afterCompute.append(doStuff)

Ein weiteres Beispiel, inspiriert von mercurial. Hier fügen die Erweiterungen nur Befehle zum hg Kommandozeilenprogramm, das das Verhalten erweitert.

def doStuff(ui, repo, *args, **kwargs):
    # when called, a extension function always receives:
    # * an ui object (user interface, prints, warnings, etc)
    # * a repository object (main object from which most operations are doable)
    # * command-line arguments that were not used by the core program

    doMoreMagicStuff()
    obj = maybeCreateSomeObjects()

# each extension defines a commands dictionary in the main extension file
commands = { 'newcommand': doStuff }

Für beide Ansätze benötigen Sie möglicherweise gemeinsame initialisieren y finalisieren für Ihre Erweiterung. Sie können entweder eine gemeinsame Schnittstelle verwenden, die alle Ihre Erweiterungen implementieren müssen (passt besser zum zweiten Ansatz; Mercurial verwendet ein reposetup(ui, repo), das für alle Erweiterungen aufgerufen wird), oder einen hakenartigen Ansatz mit einem hooks.setup-Haken verwenden.

Aber auch hier gilt: Wenn du brauchbare Antworten willst, musst du deine Frage eingrenzen ;)

11voto

edomaur Punkte 1389

Marty Allchin's einfaches Plugin-Framework ist die Basis, die ich für meinen eigenen Bedarf verwende. Ich empfehle wirklich, einen Blick darauf zu werfen, ich denke, es ist wirklich ein guter Anfang, wenn man etwas einfaches und leicht hackbares will. Sie können es auch finden als Django-Schnipsel .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X