Plugin System

Code-Stücke können hier veröffentlicht werden.
Antworten
2bock
User
Beiträge: 94
Registriert: Freitag 12. September 2003, 07:58
Wohnort: 50.9333300 / 6.3666700

Hallo alle zusammen.

Habe aus der Not heraus ein kleines Plugin System geschrieben, aber dann am Ende festgestellt, dass ich es eigenlich gar nicht benötige. Also wer so etwas nutzen zu müssen, der sollte nochmal ganz genau darüber nachdenken, ob dies wirklich so ist. Ich erstelle am ende meiner Entwicklungen mit py2exe immer ein Setup. Wer sich dann mal damit auseinander setzt, wird feststellen, das die *.pyo Dateien gezippt in einer Datei hinterlegt sind. Man bräuchte eigentlich nur die entsprechende *.pyo Datei neu erzeugen und dann auf das Zielsystem kopieren. So und jetzt für alle, die immer noch so was haben wollen der Sourcecode.

Dieser Sourcecode kommt in die Anwendung. Er liest alle konforme Dateien aus einem Ordner ein.

Code: Alles auswählen

import os, os.path, sys, string, imp, traceback
ini_plugin_format = '_plug.py'

class Plugin:
    pluginlist = []

    def __init__(self):
        sys.path.append(os.path.abspath('./plugins'))
        self._importPlugins()
        
    def _importPlugins(self):
        path = os.path.abspath('./plugins')    
        
        for f in os.listdir(path):
            self.pluginlist.append(__import__(f.split('.')[0],globals(),locals(),[]))
            #~ print " ".join(globals().keys())
            #~ print self.pluginlist
    
    def _getPlugins(self):
        for modul in self.pluginlist:
            instance = modul.Test_plug()
            print instance.Function()
            print instance.pluginProp ['name']


if __name__ == '__main__':
    
    plg = Plugin()
    plg._getPlugins()

Und dieser Code kommt in die Plugindatei.

Code: Alles auswählen

class Test_plug:# Muss wie die Datei heissen und die Datei muss auf 
    def __init__(self):
        
        pluginProp = {}
        pluginProp ['name'] = "Beispiel Plugin"
        pluginProp ['version'] = "0.1"
        pluginProp ['format'] = "*.txt"
        pluginProp ['description'] = """laber rababer"""
        pluginProp ['author'] = "Author"
        pluginProp ['author_email'] = "nn@nn.de"
        self.pluginProp = pluginProp 

    def Function(self):
        return 'Dies ist eine Testeingabe'


if __name__ == '__main__':
    
    plg = Test_plug()
    print plg.pluginProp ['name']
    
Viel Spaß mit dem Code.

Greetz from 2bock :wink:
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Mache mir gerade auch Gedanken über ein Plugin/Modul System. Deswegen belebe ich mal diesen Thread...

Gibt es da eigentlich irgendeine offizielle Lösung für???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

jens hat geschrieben:Gibt es da eigentlich irgendeine offizielle Lösung für???
Nein, nicht dass ich wüsste.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Schade, dann bastel ich mir da mal was... Als Anregung nehme ich mal den Code von 2bock...

Hier mal was:
./module/__init__.py

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# by jensdiemer.de (steht unter GPL-License)

import sys, os, glob

class module_manager:
    def __init__( self ):
        self.modul_data = {}

    def parse_modules( self ):
        for filename in glob.glob( "*.py" ):
            if filename == "__init__.py":
                # Sich selber auslassen ;)
                continue

            module_name = os.path.splitext( filename )[0]

            try:
                # Infromation's Dict aus Modul holen
                self.modul_data[module_name] = __import__( module_name ).modul_info
            except:
                pass

    ######################################################

    def __getitem__( self, key ):
        return self.modul_data[key]

    def iteritems( self ):
        return self.modul_data.iteritems()

    def __setitem__( self, key, value ):
        self.modul_data[key] = value

    def has_key( self, key ):
        return self.modul_data.has_key( key )

    def __str__( self ):
        return str( self.modul_data )

    ######################################################

    def debug( self ):
        print "Content-type: text/html\n"
        print "<h1>Modul Manager Debug:</h1>"
        print "<pre>"
        for modul_name,modul_info in self.modul_data.iteritems():
            print modul_name
            for section, value in modul_info.iteritems():
                print "\t%s - %s" % (section,value)
            print "-"*80
        print "</pre>"



if __name__ == "__main__":
    mm = module_manager()
    mm.parse_modules()
    mm.debug()
./module/log.py

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# by jensdiemer.de (steht unter GPL-License)


modul_info = {
    "description"   : "Wertet die Log-Daten aus der DB aus",
    "admin_needed"  : True, # Benötigt Administrationsrechte
    "url_parameter" : "log_admin"
}

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

In dem Zusammenhang hab ich ein altes Posting von Dookie *schnief* gefunden:
http://www.python-forum.de/viewtopic.php?p=12533#12533

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

In meiner Variante suche ich ja nach dem "PACKAGE CONTENTS" also den Python-Skripten im eigenen Verzeichnis mit glob.glob( "*.py" )
Aber das muß doch auch anders gehen!

Bsp.:

./index.py
./PyLucid_modules/__init__.py - Ist leer
./PyLucid_modules/ein_modul.py

./index.py

Code: Alles auswählen

import PyLucid_modules
help(PyLucid_modules)
In dem Hilfetext sehe ich das unter "PACKAGE CONTENTS" alle Module im Verzeichnis ./PyLucid_modules/ also das ein_modul
Somit müßte es doch auch für __init__ schon irgendwie sichtbar sein, oder?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Vortec
User
Beiträge: 52
Registriert: Dienstag 10. Dezember 2002, 11:54

Wer sehen will wie ich Dookie's Code eingebaut habe:

http://svn.tectic.de/viewcvs/trunk/lib/ ... iew=markup
| [url=http://www.sourceforge.net/projects/propolice/]propolice[/url] | [url=http://del.icio.us/vortec/]bookmarks[/url] | [url=http://www.BlowIRC.net/]irc[/url] | [url=irc://irc.BlowIRC.net/python]#python[/url] |
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab da noch ein Problem.
Ich hole mir ja die Modul-Informationen mit:

Code: Alles auswählen

self.modul_data[module_name] = __import__( module_name ).modul_info
Wenn nun aber ein Modul quasi in __main__ schon Anweisungen hat, dann werden diese Ausgeführt. Das möchte ich aber eigentlich nicht! Ich will nur die Informationen aus der Pseudo-Klasse modul_info(). Wie kann ich die erhalten ohne das das Modul "gestartet" wird???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Deswegen füge in in meine Module den Import-Hook ein:

Code: Alles auswählen

if __name__ == '__main__':
    main()
Dann wird deim Importieren das Modul nicht gestartet.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ich hab mein Plugin-System (Modul-Manager bei PyLucid) nun eh umgebaut.

Früher war es so, das ich erstmal das ganze Modul-Verzeichnis "durchsucht" hab und die nötigen Informatione aus jedem Modul rausgeholt hab. Das hab ich mit __import__() gemacht.
Nun gehe ich anders vor, nun wird es dann nachgesehen ob ein Modul überhaupt existiert, wenn es benötigt wird.

Ich hab nämlich festgestellt, das ein import des Moduls auch die ganzen import's im Modul selber nach sich zieht. Wenn z.B. urllib, xml.dom.minidom usw. importiert werden, dauert es schon etwas Zeit...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
henning
User
Beiträge: 274
Registriert: Dienstag 26. Juli 2005, 18:37

Ansonsten spricht ja auch nichts dagegen, Module aus jweils 2 files bestehen zu kassen oder?
Also ein Info-File (z.B. modul_info.py), das halt nichts importiert sondern nur ein paar Strings definiert, die sagen, was das Modul machts, etc...
und dann das eigentliche Modul.

So weißt du vorher schon welche Plugins es gibt mit allen interessanten Einzelheiten und ohne den großen Ladeaufwand.

Wenns unbedingt eine Datei sein soll,
könnte man ja auch überlegen, wichtige Infos in den ersten Zeilen kommentiert unterzubringen, dann nur diese Zeilen einzulesen, Kommentarzeichen entfernen und durch eval() laufen lassen. (Oder direkt selbst parsen)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Wo wir grad dabei sind, man könnte für Metadaten auch gleich SCALE verwenden. Ich habe es jedoch nicht getestet, wie gut es ist.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

@henning: Das wäre auch eine Lösung... Ich finde es allerdings besser, wenn man nur eine Datei editieren muß ;)

Wenn man Metadaten irgendwie parsen muß, egal ob nun mit diesem SCALE oder was eigens gebautes... Es dürfte auch ein wenig Rechenzeit brauchen. Da ist die Frage ob es schneller oder langsamer geht, als ein Modul zu importieren in dem die Daten schon als fertige Daten (Dict o.ä.) vorliegen, es aber nachsich zieht, das diese Modul wieder andere Module importiert, was auch wieder Zeit kostet...

Ich denke ich fahre mit meiner variante schon ganz gut, Module erst dann zu importieren, wenn sie gebraucht werden... Es kann dabei nur passieren, das evtl. der User keine Rechte hat, das Modul auszuführen. In dem Falle war das importieren auch umsonst...
Naja, ich denke das kommt nicht all zu oft vor...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
uphill
User
Beiträge: 22
Registriert: Sonntag 10. Dezember 2006, 20:17

hi,
Bin auch grad dabei sowas zu basteln, aber die da oben funzen ja beide nicht vernünftig. Der erste laded nur die Beschreibung und vorallem muss das Hauptprogramm im sleben verzeihnis ligen wie die Plugins...

deswegen, wie finde ich heraus in was für einem Pfad sich mein aktuelles modul gerade befindet? Und kann ich dann von diesem aus gleich direkt sachen aus dem verzeichnis wo ich bin importeren?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

uphill hat geschrieben:deswegen, wie finde ich heraus in was für einem Pfad sich mein aktuelles modul gerade befindet?
So.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
uphill
User
Beiträge: 22
Registriert: Sonntag 10. Dezember 2006, 20:17

Danke erstmal, hab das ganze weitergesponnen und das ist dabei rausgekommen. Funktioniert leider nicht ganz, weil ich beim importieren irgendwas falsch mache, aber ich denke das das so ein ganz guter Ansatz ist. Vielleicht sieht wer von euch was da nicht funktioniert:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import sys, os, glob
print __name__
this_path = os.path.dirname(__file__)

class PluginManager:
	def __init__(self):
		self.plugin_data = {}

	def load_plugins(self):
		'''
		load all plugins
		'''
		files = glob.glob(os.path.join(this_path,'*.py'))
		files += glob.glob(os.path.join(this_path,'*.pyw'))
		for path in files:
			filename = os.path.basename(path)
			print filename
			if filename.startswith('_'):
				# Module mit underline sind "private"
				continue

			plugin_name = os.path.splitext(filename)[0]
			# Infromation's Dict aus plugin holen
			self.plugin_data[plugin_name] = __import__(__name__,globals(), locals(), [plugin_name,])


	######################################################

	def __getitem__( self, key ):
		return self.plugin_data[key]

	def iteritems( self ):
		return self.plugin_data.iteritems()

	def has_key( self, key ):
		return self.plugin_data.has_key( key )

	def __str__( self ):
		return str( self.plugin_data )

	######################################################

	def debug( self ):
		for plugin_name,plugin in self.plugin_data.iteritems():
			print plugin_name
			print dir(plugin)
			for section, value in plugin.plugin_info.iteritems():
				print "\t%s - %s" % (section,value)
			print "-"*80


if __name__ == "__main__":
	mm = PluginManager()
	mm.load_plugins()
	mm.debug()
uphill
User
Beiträge: 22
Registriert: Sonntag 10. Dezember 2006, 20:17

also die zeile 28:

Code: Alles auswählen

self.plugin_data[plugin_name] = __import__(plugin_name, globals(), locals(), [], -1)
Antworten