Seite 1 von 1

Flexibles Plugin-System mit entry-points

Verfasst: Dienstag 13. April 2010, 21:24
von Phenex
Ich bin auf der Suche nach einer eleganten Möglichkeit, wie ich es Plugins ermöglichen kann sich an definierten Punkten in den Programmfluss einhaken zu können und diesen zu beinflussen. Dies wäre sehr praktisch für Webaplikationen aber ich brauche es momentan auch für eine andere Sache, daher poste ich ins allgemeine Forum. Die Idee habe ich von dem Boardsystem PunBB und dessen sehr coolem Plugin-System das folgendermaßen funktioniert.
Ich habe meinen Code und darin definiere ich sogenannte Hooks, das sieht dann so aus:

Code: Alles auswählen

[Code...]
($hook = get_hook('li_login_form_submitted')) ? eval($hook) : null;
[Code...] 
An dieser Stelle können dann Plugins ihren zusätlichen Code einhängen. Unschön ist natürlich das eval - das kommt daher, dass die Codergänzungen in einer XML-Datei stehen, das will ich natürlich nicht so machen.
Am liebsten wäre es mir daher, wenn ich an dieser Stelle Funktionen einhängen kann, die dann aufgerufen werden, mal grob ungefär so:

Code: Alles auswählen

a = 12
set_hook('some_hook_name')
b = a + 2
Und im Plugin irgendwie sowas:

Code: Alles auswählen

def myFunction():
  ...
hooks['some_hook_name'].append(myFunction)
Nur kann ich nicht so einfach auf Variablen in dem Namensraum außerhalb des Funktionsaufrufes zugreifen. global geht hier ja auch nicht, weil die Funktion an einer anderen Stelle definiert wird und das globals-array erlaubt laut Doku keinen Schreibzugriff.

Nun ist meine Frage ob jemand eine Idee hat dies elegant zu lösen.
Freue mich schon auf Vorschläge.

Verfasst: Dienstag 13. April 2010, 21:55
von BlackJack
@Phenex: Man könnte der Hook-Funktion ja Argumente übergeben.

Verfasst: Mittwoch 14. April 2010, 06:58
von Phenex
Das wäre irgendwie umständlich, außerdem hätte man dann auf die Attribute nur Lesezugriff.

Re: Flexibles Plugin-System mit entry-points

Verfasst: Mittwoch 14. April 2010, 07:19
von tordmor
Phenex hat geschrieben: Nur kann ich nicht so einfach auf Variablen in dem Namensraum außerhalb des Funktionsaufrufes zugreifen.
Warum nicht? Das nennt sich dann import.

Module hooks.py:

Code: Alles auswählen

from collections import defaultdict

_hooks = defaultdict(lambda: [])

class Hook():
    def __init__(self, hook_name, function):
        self.hook_name = hook_name
        self.function = function
        _hooks[self.hook_name].append(self)
        
    def __call__(self, *args, **kwargs):
        self.function(*args, **kwargs)
        
def call(hook_name, *args, **kwargs):
    for hook in _hooks[hook_name]:
        hook(*args, **kwargs)
Definieren mit

Code: Alles auswählen

import hooks
def my_func():
    pass

hooks.Hook('blubb', my_func)
Aufrufen mit

Code: Alles auswählen

import hooks

a = 5
hooks.call('blubb')
b = a + 3
Oder hab ich was nicht verstanden?

Verfasst: Mittwoch 14. April 2010, 07:19
von Pekh
Phenex hat geschrieben:Das wäre irgendwie umständlich, außerdem hätte man dann auf die Attribute nur Lesezugriff.
Jein. Wenn du z.B. eine Liste übergibst, kannst du diese manipulieren und die Änderungen werden an allen Stellen sichtbar, an denen dieses Objekt (keine Kopie!) verwendet wird. Normalerweise versucht man solche Effekte gerade zu vermeiden, aber gehen tut es. Das mindeste wäre aber, diese Seiteneffekte an prominenter Stelle so zu dokumentieren, daß sie einem Leser sofort ins Auge stechen. Sonst kann man nämlich ewig nach Fehlern suchen.

Re: Flexibles Plugin-System mit entry-points

Verfasst: Mittwoch 14. April 2010, 10:15
von Phenex
tordmor hat geschrieben:
Phenex hat geschrieben: Oder hab ich was nicht verstanden?
Naja, nur könnte ich jedoch den Wert a in myFunction nicht überschreiben, so dass dies die Wertzuweisung von b beeinflusst.

Seiteneffekt, so eine dämliche Übersetztung^^
Schön wäre es, wenn ich eben solche Dinge nicht tun müsste, sindern es eine Möglichkeit gäbe sozusagen auf einen fremden Scope zuzugreifen. Wenn ich dem hook das localdict übergebe, kann ich zwar Variablen aus dem Scope auslesen, in dem der Hook gesetzt ist - jedoch kann ich diese nicht verändern.

Re: Flexibles Plugin-System mit entry-points

Verfasst: Mittwoch 14. April 2010, 10:21
von cofi
Phenex hat geschrieben:Seiteneffekt, so eine dämliche Übersetztung^^
Sie ist ja auch falsch. Nebeneffekt ist die richtige Uebersetzung.
Phenex hat geschrieben:Wenn ich dem hook das localdict übergebe, kann ich zwar Variablen aus dem Scope auslesen, in dem der Hook gesetzt ist - jedoch kann ich diese nicht verändern.
Testen! Das geht durchaus, sofern du das Dictionary uebergibst. Am saubersten waere aber ein eigenes Dictionary, in dem du die Umgebung aufbaust, die du mit den Plugins teilen moechtest.

Verfasst: Mittwoch 14. April 2010, 10:30
von ms4py
Als Ergänzung zu Pekhs Beitrag:
Phenex hat geschrieben:Das wäre irgendwie umständlich, außerdem hätte man dann auf die Attribute nur Lesezugriff.
Nein, in Python werden immer Referenzen übergeben. Wenn du eine Referenz auf einen Datentyp übergibst, kannst du dessen Attribute sozusagen global verändern. Dabei musst du halt beachten, dass du bei immutable Datentypen wie int, str die Attribute selbst nicht verändern kannst, sondern nur neue Instanzen anlegen.

Code: Alles auswählen

>>> i = 5
>>> id(i)
37420776
>>> i += 1
>>> id(i)
37420764
# vs.
>>> l = list()
>>> id(l)
37714000
>>> l += [5]
>>> id(l)
37714000
Als Lösung könntest du einen eigenen Datentyp anlegen (als Klasse).

Alternativ gibt es ja auch noch die Möglichkeit, aus einer Funktion Werte zurückzugeben...

Edit: Ja, wie cofi gesagt hat, ein Environment als dict oder eigener Datentyp wäre die sauberste Lösung.

Re: Flexibles Plugin-System mit entry-points

Verfasst: Mittwoch 14. April 2010, 11:40
von Phenex
cofi hat geschrieben:
Phenex hat geschrieben:Seiteneffekt, so eine dämliche Übersetztung^^
Sie ist ja auch falsch. Nebeneffekt ist die richtige Uebersetzung.
Jep ich weiß, wer kommt bloß auf so einen Quatsch...
cofi hat geschrieben:
Phenex hat geschrieben:Testen! Das geht durchaus, sofern du das Dictionary uebergibst. Am saubersten waere aber ein eigenes Dictionary, in dem du die Umgebung aufbaust, die du mit den Plugins teilen moechtest.
Laut Python-Dokumentation sollte man aber nicht schreibend darauf zugreifen, hat sicher auch einen Sinn (http://docs.python.org/library/functions.html#locals).

Danke erstmal für die Antworten. Klar könnte ich ein Dictionary übergeben aber ich suche ja gerade eine Möglichkeit das machen zu können ohne da groß was an extra Anpassungen für jeden Hook zu machen.