Probleme mit Pluging-Framwork und globalen import

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
av_jui
User
Beiträge: 25
Registriert: Samstag 4. Mai 2013, 12:45

Hallo

Ich bin noch total unerfahren was Python angeht, jedoch wollte ich mich jetzt mal ein Projekt wagen, dass auch ein Pluginsystem bietet.
Leider stoße ich momentan an eine Grenze die ich alleine wohl nicht hin bekomme.
Und zwar geht es darum diese Plugins zu laden und die Module zu importieren. Ich habe jetzt schon einiges durchprobiert aber irgendwie komm ich nicht weiter.
Ich hab hier mal die https://github.com/avjui/Homepy/blob/pl ... Manager.py.
Ich hab auch mal zum testen ein

Code: Alles auswählen

sys.modules.keys()
eingebaut um zu sehen welche Module geladen werden.

Hier sehe ich dann auch das was importiert wurde (webcams,homematic,sonos) aber wenn ich das dann aufrufe bekomme ich immer
folgenden Fehler.

Code: Alles auswählen

Traceback (most recent call last):
  File "Homepy.py", line 128, in <module>
    main()
  File "Homepy.py", line 113, in main
    homematic.client().get_name()
NameError: global name 'homematic' is not defined
Kann mir bitte jemand helfen?

P.S. Python ist 2.7 falls das wichtig ist
BlackJack

@av_jui: Es gibt keinen globalen Import. Du hast den Namen `homematic` an nichts gebunden, also ist der auch nicht bekannt. Die `get_plugins()`-Methode gibt die Plugins zurück, Du machst mit dem Rückgabewert aber überhaupt nichts.

Edit: Namen mit einem führenden Unterstrich bedeuten, dass das Implementierungsdetails sind, die man von aussen nicht verwenden sollte. Das betrifft `_load_all()`, aber insbesondere `os._exit()`. Davon solltest Du auf jeden Fall die Finger lassen! Was gefällt Dir an dem öffentlichen `sys.exit()` nicht?
Sirius3
User
Beiträge: 18253
Registriert: Sonntag 21. Oktober 2012, 17:20

@av_jui: ich sehe Du hast im Vergleich zum Original in Deinem »PluginManager« einiges verschlimmbessert:
- Wenn »x« schon mit .py endet dann kann es nie mit .pyc enden (Ein Generatorausdruck wäre hier passender als extra eine neue Liste zu erzeugen).
- Da bei »mod« die Endung .py entfernt wurde, wird der Vergleich mit '__init__.py' immer fehlschlagen.
- sys.path.insert und del sollten zusammenpassen.

Ich will jetzt nichts falsches sagen, aber ein mit _ beginnender Klassenname außerhalb des Moduls benutzen zu müssen, ist für mich seltsam. Wahrscheinlich soll der _ andeutet, dass es sich um eine abstrakte Klasse handelt, ich persönlich würde das eher mit »AbstractPlugin« kennzeichnen.
av_jui
User
Beiträge: 25
Registriert: Samstag 4. Mai 2013, 12:45

BlackJack hat geschrieben:@av_jui: Es gibt keinen globalen Import. Du hast den Namen `homematic` an nichts gebunden, also ist der auch nicht bekannt. Die `get_plugins()`-Methode gibt die Plugins zurück, Du machst mit dem Rückgabewert aber überhaupt nichts.
Und was heißt das jetzt? Beim get_plugins wird ja auch _load_all aufgerufen und da wird ja das Modul importiert. Oder versteh ich das Falsch? Wie kann ich diese Funktionen den nutzen.
BlackJack hat geschrieben:Edit: Namen mit einem führenden Unterstrich bedeuten, dass das Implementierungsdetails sind, die man von aussen nicht verwenden sollte. Das betrifft `_load_all()`, aber insbesondere `os._exit()`. Davon solltest Du auf jeden Fall die Finger lassen! Was gefällt Dir an dem öffentlichen `sys.exit()` nicht?
Ist mir klar war auch nur zum Testen. danach nehme ich get_plugins(). Zum sys.exit hast natürlich recht ist auch ein Überbleibsel von einem Versuch. Ich sollte mal den Code aufräumen :roll:
Sirius3 hat geschrieben: - Da bei »mod« die Endung .py entfernt wurde, wird der Vergleich mit '__init__.py' immer fehlschlagen.
- sys.path.insert und del sollten zusammenpassen.
Ich weiß habe ich hier schon geändert kam vom spielen aus Verzweiflung.
BlackJack

@av_jui: Das heisst jetzt das die Module von der Methode *zurückgegeben* werden. Schau Dir doch einfach mal an was Du dort bekommst.
av_jui
User
Beiträge: 25
Registriert: Samstag 4. Mai 2013, 12:45

BlackJack hat geschrieben:@av_jui: Das heisst jetzt das die Module von der Methode *zurückgegeben* werden. Schau Dir doch einfach mal an was Du dort bekommst.
Zurück liefert es

Code: Alles auswählen

{'Cams': <class 'webcams.cam'>, 'Test': <class 'sonos.Sonos'>, 'Homematic': <class 'homematic.client'>}
Und was heißt das für mich, wenn ich jetzt das homematic.client nutzen möchte?
BlackJack

@av_jui: Ist die Frage ernst gemeint? Das sieht sehr nach einem Wörterbuch (`dict()`) aus. Und Zeichenketten als Schlüsseln. Und zu dem Schlüssel 'Homematic' gibt es einen Wert der augenscheinlich die Klasse `homematic.client` ist. Hol den Wert da raus und mach damit was Du damit machen willst.
av_jui
User
Beiträge: 25
Registriert: Samstag 4. Mai 2013, 12:45

BlackJack hat geschrieben:@av_jui: Ist die Frage ernst gemeint? Das sieht sehr nach einem Wörterbuch (`dict()`) aus. Und Zeichenketten als Schlüsseln. Und zu dem Schlüssel 'Homematic' gibt es einen Wert der augenscheinlich die Klasse `homematic.client` ist. Hol den Wert da raus und mach damit was Du damit machen willst.
Das das ein dict ist ist schon klar, aber mir ist nicht klar wie ich jetzt das homematic.client verwenden kann. Muß ich das hier nochmal importiern (__import__)? was bringt dann das __import__ in der _load_all Funktion? Ich kapiere es einfach nicht.

Edit: Gibt es auch ein fertiges python Plugin Framework was ich nehmen könnte
BlackJack

@av_jui: Nochmal: Du holst das Objekt da einfach heraus und *verwendest* es.

Code: Alles auswählen

plugins = pluginmgr.get_plugins()
client_class = plugins['Homematic']
client = client_class()
print client.get_name()
# 
# oder:
# 
print pluginmgr.get_plugins()['Homematic']().get_name()
Oder halt irgend etwas sinnvolles zwischen diesen beiden Extremen.
av_jui
User
Beiträge: 25
Registriert: Samstag 4. Mai 2013, 12:45

BlackJack hat geschrieben:@av_jui: Nochmal: Du holst das Objekt da einfach heraus und *verwendest* es.

Code: Alles auswählen

plugins = pluginmgr.get_plugins()
client_class = plugins['Homematic']
client = client_class()
print client.get_name()
# 
# oder:
# 
print pluginmgr.get_plugins()['Homematic']().get_name()
Oder halt irgend etwas sinnvolles zwischen diesen beiden Extremen.
Das heißt das sind immer subclasses von pluginmgr.get_plugins()? Verstehe ich das jetzt richtig?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nein, das verstehst du falsch.

`get_plugin()` liefert ein Dictionary mit Strings als Schluesseln und Klassen als Werten. Unterklassen oder Vererbung im Allgemeinen hat damit gar nichts zu tun.

Code: Alles auswählen

client = pluginmgr.get_plugins()['Homematic']
ist hier dann aequivalent zu

Code: Alles auswählen

from homematic import client
wenn `homematic` ein normales Modul ist.
av_jui
User
Beiträge: 25
Registriert: Samstag 4. Mai 2013, 12:45

Danke für die Antworten. Ich denke ich kapier es jetzt einigermaßen.
av_jui
User
Beiträge: 25
Registriert: Samstag 4. Mai 2013, 12:45

Hallo

Ich mal wieder. Nach langer Zeit habe ich mal wieder weitergemacht.
Und siehe da, ich hab wieder mal ein Problem mit meinem Plugin-Framework.
Ich hätte gerne das nur nach Plugins in Verzeichnissen mit einer Tiefe von 1 gesucht wird.
Hier mal die relevante Stelle im Code.

Code: Alles auswählen

    def __init__(self):

        self.plugin_dirs.update({
                                 'plugins/homeautomation/' : False,
                                 'plugins/multimedia/' : False,
                                 'plugins/notification' : False,
                                 'plugins/web/' : False,
                                 })

    def _load_all(self):
        for (pdir, loaded) in self.plugin_dirs.iteritems():
            if loaded: continue

            for root,dirs,mod in os.walk(pdir):
                sys.path.insert(0, root)            
                for mod in [x[:-3] for x in os.listdir(root) if x.endswith('.py')]:
                    if mod and mod != '__init__':
                        if mod in sys.modules:
                            log('Module %s already exists, skip' % mod, 'info')
                        else:
                            try:
                                pymod = __import__(mod)
                                splitted = mod.split('.')
                                self.plugin_dirs[pdir] = True
                                log("Plugin Found [Name] %s	[Path] %s"% (mod, pymod.__file__), 'info')
                                self.plugins = DBFunction().GetList('plugins')
                                for p in self.plugins:
                                     if p[1] == mod.upper():
                                        self.adding = False
                                        break
                                     else:
                                       self.adding = True

                                if self.adding:
                                     DBFunction().AddPlugin(mod.upper(), pymod.__file__.split('/')[1])
                            except ImportError, e:
                                log ('Loading failed, skip plugin %s/%s' % (os.path.basename(root), mod), 'error')

                del(sys.path[0])
Bei Zeile 14 starte ich ein os.walk.
z.b für 'plugins/homeautomation/' gibt dirs folgendes aus.

Code: Alles auswählen

['dmx', 'loewe']
Hier möchte ich nur um 1 höher in die Verzeichnisse. Und dann sollte er mit dem nächsten "Typ" plugin weitermachen.
Hintergrund ist das so das Plugin selber Module mitbringen kann ohne das diese dann auch als Plugin angesehen werden.
Ein Codevorschlag wäre echt toll.

Danke im Voraus
av_jui
User
Beiträge: 25
Registriert: Samstag 4. Mai 2013, 12:45

Niemand ne Idee?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Wenn du nur eine Suchtiefe von 1 haben willst, dann solltest du besser nicht ``os.walk`` verwenden. Benutze besser ``os.listdir``, dann gibt es auch keine Sonderfälle mehr mit Modulen in Plugins.
Das Leben ist wie ein Tennisball.
av_jui
User
Beiträge: 25
Registriert: Samstag 4. Mai 2013, 12:45

Danke werde ich mal versuchen. Wobei ich mittlerweile eine Lösung gefunden habe.
Antworten