Eine .py im Unterordner aufrufen und starten

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.
Benutzeravatar
Batt0sa1
User
Beiträge: 15
Registriert: Montag 21. April 2008, 23:15

Eine .py im Unterordner aufrufen und starten

Beitragvon Batt0sa1 » Donnerstag 14. August 2008, 14:56

Hi @ll...

ich versuche gerade in eine Menüleiste, abhängig von den vorhandenen Ordnern im Ordner "plugins", Menüeinträge zu erzeugen. Wenn man dann einen Eintrag anklickt, soll das ensprechende Plugin starten (jeder Ordner enthält eine start.py)....

Die Unterordner kann ich mittlerweile auslesen. Nur wie kann ich in die "start.py" wechseln und die Startfunktion starten?
Bisher bin ich so weit:

Code: Alles auswählen

def Plugins():
    #in den Ordner 'plugins' wechseln
    list = []
    path = os.path.abspath(os.path.join(os.path.curdir, u"plugins"))   
    #iterieren
    for foldername in os.listdir(path):
            start = open(foldername+'/start.py', 'r')
            list.append([foldername, 'Beschreibung', start.start, '', '',True])
    print list


Momentan meckert Python (sicher berechtigterweise ^^):
No such file or directory: u'.svn/start

Hab ich ein Verständnisproblem, oder geht das doch irgendwie?
(muss das evtl. über einen Import geschehen?)

Ich bin über jegliche Hilfe froh :D

[Edit]
PS.: die Menüleiste existiert schon und erwartet eine Liste mit:
[Name, Beschreibung und startfunktion]
Benutzeravatar
Hyperion
Moderator
Beiträge: 7472
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Beitragvon Hyperion » Donnerstag 14. August 2008, 15:08

1.) Für das Zusammensetzen von Pfadnamen gibt es join()
2.) Die Fehlermeldung ist doch eindeutig! Es gibt in dem Ordner eben keine start.py Datei! Also fange diese Exception ab.

Edit: Also ich verstehe noch nicht genau, was Du eigentlich machen willst! Wenn es um Funktionalität geht, kann ein open natürlich nicht funktionieren. Damit öffnest Du ja nur eine Datei zum Lesen. Generell ist import die Lösung, wenn es um das Einbinden von Python Modulen geht.

Vielleicht versuchst Du doch noch einmal genauer zu beschreiben, was Du erreichen willst! (Stichworte: Runtime vs. Desgintime!)
Benutzeravatar
Batt0sa1
User
Beiträge: 15
Registriert: Montag 21. April 2008, 23:15

Beitragvon Batt0sa1 » Donnerstag 14. August 2008, 15:29

Dann versuche ich es nochmal zu erklären:

1. es existiert ein Framework
2. in dieses Framework soll ein Plugin importiert werden
3. dieses Plugin befindet sich im Ordner "plugins', besitzt eine Datei namens 'start.py', welche wiederum die Funktion 'start' hat.

In die Menüleiste des Frameworks sollen nun alle Unterordner (von 'plugins') mit ihrem Namen angezeigt werden und sobald eins ausgewählt wurde, in den entsprechenden Unterordner gegangen und die startfunktion ausgeführt werden.

Das Open nicht so der wirklich gute Ansatz ist, konnte ich mir schon fast denken... Also geht so etwas nur über import?
Wie wäre denn dann der richtige Ansatz?
Ich meine mitten in der Laufzeit kann ich doch nicht mehr importieren oder?

Ansonsten:
deinen 1. Punkt verstehe ich nicht wirklich, was ist denn an meiner Lösung falsch?
Und zu dem 2. mag ja sein, dass Python die Datei nicht findet, ich hab sie aber definitiv angelegt...

Bisher hab ich mit Pfadangaben wenig gearbeitet und kenne mich da überhaupt nicht aus. Im Großen und Ganzen bin ich auch eher Anfänger...
Deshalb stelle ich ja die Fragen :)

PS.: Runtime vs. Designtime sagt mir ehrlich gesagt noch viel weniger...
Benutzeravatar
Hyperion
Moderator
Beiträge: 7472
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Beitragvon Hyperion » Donnerstag 14. August 2008, 15:41

Batt0sa1 hat geschrieben:Dann versuche ich es nochmal zu erklären:

1. es existiert ein Framework

ok. Ist das gegeben und Du musst damit klar kommen, oder hast Du das so gesignt?
2. in dieses Framework soll ein Plugin importiert werden
3. dieses Plugin befindet sich im Ordner "plugins', besitzt eine Datei namens 'start.py', welche wiederum die Funktion 'start' hat.

ok
In die Menüleiste des Frameworks sollen nun alle Unterordner (von 'plugins') mit ihrem Namen angezeigt werden und sobald eins ausgewählt wurde, in den entsprechenden Unterordner gegangen und die startfunktion ausgeführt werden.

ok
Das Open nicht so der wirklich gute Ansatz ist, konnte ich mir schon fast denken... Also geht so etwas nur über import?

Also imho ist das der beste Ansatz - aber die Experten im Forum hier sind andere ;-) Außer natürlich das es sich um quasi abgeschottete eigene Programme handelt. Dann wäre wohl ein Aufruf mit subprocess ideal.
Wie wäre denn dann der richtige Ansatz?
Ich meine mitten in der Laufzeit kann ich doch nicht mehr importieren oder?

Wieso nicht? Hast Du es mal probiert?
Ansonsten:
deinen 1. Punkt verstehe ich nicht wirklich, was ist denn an meiner Lösung falsch?

das "+" ;-)
Und zu dem 2. mag ja sein, dass Python die Datei nicht findet, ich hab sie aber definitiv angelegt...

Ich glaube kaum, dass Du in einem .svn-Ordner danach suchen willst ;-) (Oder sie da wirklich existiert!)
PS.: Runtime vs. Designtime sagt mir ehrlich gesagt noch viel weniger...

ok, ist jetzt nicht mehr wichtig, da ich nun weiß, was Du tun willst. Generell geht es darum, ob vor Programmstart klar ist, was vorhanden ist, oder es während das Programm läuft zu zusätzlichen Funktionalitäten kommen soll.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7472
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Beitragvon Hyperion » Donnerstag 14. August 2008, 15:57

Ok, ich hab ein wenig Mist erzählt: import funzt auch mitten im Code, aber nicht per String, also folgendes geht eben so nicht:

Code: Alles auswählen

mod = "random"
import mod

Allerdings steht dazu im wiki einiges interessantes:
[wiki]Import[/wiki]
Benutzeravatar
Batt0sa1
User
Beiträge: 15
Registriert: Montag 21. April 2008, 23:15

Beitragvon Batt0sa1 » Donnerstag 14. August 2008, 16:01

Das Framework ist gegeben und ich habe dafür ein Plugin entwickelt.
Da die Pluginschnittstelle aber noch nicht steht (momentan ist sie hart gecodet), wollte ich eine einfügen.

Das man nicht während der Laufzeit importiert, habe ich einfach mal inerpretiert, da alle Programme und Scripte die ich bisher in Python gesehen habe, dies prinzipiell am Anfang der Datei erledigt haben....
Ist mir aber wenn beides (import bzw. open) funzt relativ egal.
Hauptsache die Funktionalität ist bei allen Beriebssystemen gleich :)
Prinzip "Function over Design" *g*

Generell geht es darum, ob vor Programmstart klar ist, was vorhanden ist, oder es während das Programm läuft zu zusätzlichen Funktionalitäten kommen soll.


Es sind vor Programmstart alle Funktionalitäten bekannt.

[edit]
ich bin deinem Link mal gefolgt und da steht relativ weit unten ein Beispiel...
werde mich mal dran versuchen und wenn ich eine Lsg. hab schreib ich sie hier rein (oder frag nochmal ^^)
DasIch
User
Beiträge: 2423
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Beitragvon DasIch » Donnerstag 14. August 2008, 16:17

Hyperion hat geschrieben:Ok, ich hab ein wenig Mist erzählt: import funzt auch mitten im Code, aber nicht per String[...]

Falsch. Schau dir mal __import__ an. ;)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7472
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Beitragvon Hyperion » Donnerstag 14. August 2008, 16:23

DasIch hat geschrieben:
Hyperion hat geschrieben:Ok, ich hab ein wenig Mist erzählt: import funzt auch mitten im Code, aber nicht per String[...]

Falsch. Schau dir mal __import__ an. ;)

"__import__" ist was anderes als "import" ;-)
DasIch
User
Beiträge: 2423
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Beitragvon DasIch » Donnerstag 14. August 2008, 17:00

Ja schon, aber man kann das gleiche mit machen ;)
Benutzeravatar
Batt0sa1
User
Beiträge: 15
Registriert: Montag 21. April 2008, 23:15

Meine (vorläufige) Lösung

Beitragvon Batt0sa1 » Donnerstag 14. August 2008, 17:57

Das hab ich jetzt gezaubert :)
Importieren geht, wenn man eine __init__.py (kann leer sein) in die entsprechenden Unerordner hineinfügt :idea:

Code: Alles auswählen

def Plugins():
    #in den Ordner 'plugins' wechseln
    list = []
    path = os.path.abspath(os.path.join(os.path.curdir, u"plugins"))   
    #iterieren
    for foldername in os.listdir(path):
        #versteckte und py - Dateien verhindern
        if foldername.startswith(".") or (foldername.endswith("py")
                                      or foldername.endswith("pyc")
                                      or foldername.endswith("pyo")):
            continue
        try:
            #in jedem Ordner die Readme auslesen und die Datei start.py importieren
            #
            f_path = os.path.join(path, foldername, 'Readme.txt')
            file = open(f_path,"r")
            Beschreibung = file.readlines(1)
            import start
        except:
            pass
        try:
            list.append([str(foldername), str(Beschreibung), start.startmenu, '', '',True])
        except:
            pass
    return list


Als Liste kriege ich dann soetwas hier:
['Plugin', "['hier steht eine Beschreibung\\n']", <function startmenu at 0x8d35374>, '', '', True]


Soweit eigentlich fast alles schick :)
Die Menüeinträge sind in jedem Fall da.
Nur 2 Sachen die noch stören...

1.: der (rote) Beschreibungstext sieht sehr komisch aus... wie krieg ich da die eckigen Klammern und das "\n" weg? Liegt glaube ich an "readlines()"
und das zweite wohl schwierigere Problem:
wie kann ich Übergabewerte mitgeben? Momentan kann mein Plugin nicht aufgerufen werden, da meinem (und allen zukünftigen) Plugin(s) ja keine Übergabewerte mitgegeben werden. Mein Lösungsansatz wäre gewesen, die benötigten Objekte in die Readme zu schreiben, aber das hat bisher noch nicht gefruchtet.

PS.: hab grad mitgekriegt, dass das doch nicht so ganz funzt wie es sollte... bei "import start", importiert er die falsche. D.h. er springt nicht in den Ordner "plugins" :evil:
Benutzeravatar
Batt0sa1
User
Beiträge: 15
Registriert: Montag 21. April 2008, 23:15

Beitragvon Batt0sa1 » Freitag 15. August 2008, 22:09

Bin nun noch einen kleinen Schritt weiter. Wie man am Code sehen kann hab ich mir die Module zusammengesetzt und importiere sie nun dynamisch :)
In der Menüleiste wird auch soweit alles angezeigt.

Der Fehler gestern war wohl ein Verständnisproblem denke ich mal...
Es wird wohl doch nicht automatisch aus dem aktuellem Ordner importiert :D
Jedenfalls habe ich, indem ich in jedem Ordner eine __init__.py angelegt habe, mir diese aufrufbar gestaltet.

Der Code:

Code: Alles auswählen

def Plugins(parent):
    '''ToDo: die benötigten Übergabewerte der Pugin-Startfunktion irgendwie auslesen
            '''
    #in den Ordner 'plugins' wechseln
    list = []
    m_path = os.path.abspath(os.path.join(os.path.curdir))
    path = os.path.abspath(os.path.join(os.path.curdir, u"plugins"))   
    #iterieren
    for foldername in os.listdir(path):
        #versteckte und py - Dateien ausblenden
        if foldername.startswith(".") or (foldername.endswith("py")
                                      or foldername.endswith("pyc")):
            continue
        try:
            #in jedem Ordner die Readme auslesen und die Datei start.py importieren
            f_path = os.path.join(path, foldername, 'Readme.txt')
            file = open(f_path,"r")
            Beschreibung = file.readlines(1)

        except:
            logging.debug(u"keine Readme.txt gefunden in "+ f_path + " gefunden")
        #import plugins
        try:
            pluginort =  "plugins."+foldername+".start"
            #__import__ akzeptiert Strings als Übergabewert
            plugin = __import__(pluginort)
        except:
            logging.debug(u"konnte kein Startmodul in "+ pluginort + " finden")
           
        try:
            list.append([str(foldername), str(Beschreibung), plugin.__init__, '', '',True])
        except:
            logging.debug(u"konnte Modul "+ foldername + " nicht laden")

    #print list
    return list


Hab aber immer noch ein relativ elementares Problem:
Wenn ich nun den Menüeintrag auswähle (klicke) kriege ich die folgende Fehlermeldung:
TypeError: module.__init__() argument 1 must be string, not CommandEvent

Wie kann ich es hier hinkriegen das Plugin zu initialisieren und Werte (evtl. dynamisch) zu übergeben?
Regelt das die __init__ im Ordner oder sollte ich mir eine in das Modul schreiben. Beide Ansaätze haben bisher nichts gebracht, aber das heißt bei mir ja nichts *g*
Ich krieg bestenfalls die obige Fehlermeldung :(

Wäre toll wenn jemand noch einen Tip hätte :wink:
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Beitragvon Trundle » Freitag 15. August 2008, 22:36

`__import__` gibt bei leerem ``fromlist``-Argument das Package zurück, und nicht das Modul. Wenn `__init__` also eine Funktion im ``start``-Modul ist, müsste dort also ``plugin.start.__init__`` stehen und nicht ``plugin.__init__``. Ansonsten ist, zumindest für mich, nicht ersichtlich, wie die Plugins jetzt aufgebaut sind oder woher der besagte Fehler überhaupt kommt, aus dem geposteten Code jedenfalls nicht.

Außerdem ist das [mod]imp[/mod]-Modul noch interessant, bei dem man auch Pfade übergeben kann.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Benutzeravatar
Batt0sa1
User
Beiträge: 15
Registriert: Montag 21. April 2008, 23:15

Beitragvon Batt0sa1 » Samstag 16. August 2008, 19:06

Hab zwar deinen Tip mit "fromlist" nicht verstanden, aber nach ein wenig goggeln bin ich auf eine Mailingliste gestossen wo es nochmal erklaert wurde...
Damit hab ich dann das Hauptproblem loesen koennen :D

Aber nochmal zu deiner Frage, die Struktur soll prinzipiell so aussehen:

/Programmordner
+enthaelt Hauptdateien
-------/Gesamt_Pluginordner
-------/__init__.py (ohne "richtigen" Code, nur Disclaimer und Sachen wie __author__)
-----------/Pluginordner
-----------/ __init__.py Inhalt wie oben)
-----------/ start.py initialisiert plugin (enthaelt eine Funktion "start")

Initialisieren und importieren krieg ich jetzt aber hin :)
Was mir jetzt noch weiterhelfen wuerde, waere wenn mir jetzt noch jemand sagen kann, wie ich da einen Uebergabewert mitgeben kann...

Der Code sieht jetzt so aus:

Code: Alles auswählen

def Plugins(parent):
    '''ToDo: die benoetigten Uebergabewerte der Startfunktion irgendwie auslesen
            '''
    #in den Ordner 'plugins' wechseln
    list = []
    path = os.path.abspath(os.path.join(os.path.curdir, u"plugins"))   
    #iterieren
    for foldername in os.listdir(path):
        #versteckte und py - Dateien ausblenden
        if foldername.startswith(".") or (foldername.endswith("py")
                                      or foldername.endswith("pyc")):
            continue
        try:
            #in jedem Ordner die Readme auslesen und die Datei start.py importieren
            f_path = os.path.join(path, foldername, 'Readme.txt')
            file = open(f_path,"r")
            Beschreibung = file.readlines(1)

        except:
            logging.debug(u"keine Readme.txt gefunden in "+ f_path + " gefunden")
        #import plugins
        try:#start.py
            pluginort =  "plugins"#."+foldername#+".start"
            #print pluginort
            #__import__ akzeptiert Strings als Uebergabewert
            plugin = __import__("plugins."+foldername+".start", globals(), locals(), ['start'])
            #print plugin.__author__
        except:
            logging.debug(u"konnte kein Startmodul in "+ pluginort + " finden")
           
        try:
            '''hier wuerde ich gerne soetwas wie plugin.Start(parent) machen ;)'''

            list.append([str(foldername), str(Beschreibung), plugin.Start, '', '',True])
        except:
            logging.debug(u"konnte Modul "+ foldername + " nicht laden")

    #print list
    return list


Der 3. Wert in der "list" - Liste wird fuer das Menue gebraucht.
in der Funktion die das Menue erstellt sieht das grob gesagt so aus:

Code: Alles auswählen

menu = wx.Menu().Append(wx.NewId(),list[0],list[1],list[3])
self.Bind(wx.EVT_MENU,list[2],menu)


Tipps und Hilfestellungen sind weiterhin willkommen :D

[Edit]
Kann das Problem jetzt noch ein wenig weiter eingrenzen...
Wenn ich einen Übergabewert mitgebe (parent), sperrt sich mein Plugin!
Da ich im Plugin mit dem Frame und einer Liste arbeite, beides bei der Menüinitialisierung aber anscheinend noch nicht vorhanden, meckert Python.

[Doppeledit]
Nachdem ich nun die Menüinitialisierung nach hinten versetzt habe, scheint erstmal fast alles zu stimmen...
Aber anscheinend wird mit dem Menüeintrag auch das Plugin gestartet...
Wie kann man das verhindern?
Hab es über:

Code: Alles auswählen

 if __name__ == __main__:
 main()

probiert, dass hat aber (bisher) nichts bewirkt
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Beitragvon Trundle » Samstag 16. August 2008, 23:21

Batt0sa1 hat geschrieben:-----------/ start.py initialisiert plugin (enthaelt eine Funktion "start")

Das deckt sich aber doch wieder nicht mit dem Code, im Code schreibst du ``plugin.Start``.

Batt0sa1 hat geschrieben:Nachdem ich nun die Menüinitialisierung nach hinten versetzt habe, scheint erstmal fast alles zu stimmen...
Aber anscheinend wird mit dem Menüeintrag auch das Plugin gestartet...
Wie kann man das verhindern?
Hab es über:

Code: Alles auswählen

 if __name__ == __main__:
 main()


probiert, dass hat aber (bisher) nichts bewirkt

Das hat nichts bewirkt? Das müsste, wenn man nicht gerade etwas wie ``import __main__`` oder so gemacht hat, zumindest einen `NameError` werfen, und wenn man den dann behoben hat, einen `IndentationError`. Und was heißt, das Plugin wird gestartet? Bei einem import wird eben alles auf Modulebene ausgeführt. Wenn man das nicht will, muss man den Code eben in eine Art init-Funktion packen, die der Plugin-Loader dann aufruft.

Ansonsten wäre vllt ein minimales, ausführbares Codebeispiel des Problems nicht schlecht, weil mit "sperrt sich mein Plugin" oder "meckert Python" kann man reichlich wenig anfangen.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Benutzeravatar
Batt0sa1
User
Beiträge: 15
Registriert: Montag 21. April 2008, 23:15

Beitragvon Batt0sa1 » Samstag 16. August 2008, 23:44

Hab jetzt gerade eine Lösung gefunden.
Scheint soweit erstmal zu funktionieren...

Code: Alles auswählen

def Plugins(parent):
    '''ToDo: die benötigten Übergabewerte der Startfunktion irgendwie auslesen
            '''
    #in den Ordner 'plugins' wechseln
    list = []
    path = os.path.abspath(os.path.join(os.path.curdir, u"plugins"))   
    #iterieren
    def test(event):
        plugin.Start(parent)
        event.Skip()
    for foldername in os.listdir(path):
        #versteckte und py - Dateien ausblenden
        if foldername.startswith(".") or (foldername.endswith("py")
                                      or foldername.endswith("pyc")):
            continue
        try:
            #in jedem Ordner die Readme auslesen
            f_path = os.path.join(path, foldername, 'Readme.txt')
            file = open(f_path,"r")
            Beschreibung = file.readlines(1)
        except:
            logging.debug(u"keine Readme.txt gefunden in "+ f_path + " gefunden")
       
        #import plugins
        try:
            #die Datei start.py aus /plugins/Plugin-name importieren
            pluginort =  "plugins."+foldername
            #__import__ akzeptiert Strings als Übergabewert
            plugin = __import__("plugins."+foldername+".start", globals(), locals(), ['start'])
            #print plugin.__author__, plugin.__date__
        except:
            logging.debug(u"konnte kein Startmodul in "+ pluginort + " finden")
           
        #try:
        #Listeneintrag für das PluginMenu hinzufügen
        list.append([str(foldername), str(Beschreibung), test, '', '', True])
        #except:
        #    logging.debug(u"konnte Modul "+ foldername + " nicht laden")
           

    return list


Wobei mein Plugin start.py so aussieht:

Code: Alles auswählen

def Start(self):
    shapes = self.canvas.shapes
    malkasten = self.frame
    ...(Code)


Woran aber nun das Problem lag, dass das Plugin aufgerufen wurde und "python meckerte" ^^ kann ich ehrlich gesagt nicht sagen...
Ich tippe darauf, dass ich versucht habe, zwanghaft ein Attribut mit dem Event zu übergeben. Da ich mich aber an eine Hilfe von Gerold erinnert habe (http://www.python-forum.de/topic-14651.html)
bin ich auf die Lösung gekommen...


Inwieweit das jetzt Stylekonform (o.ä.) ist, kann ich nicht sagen...
Ich hoffe mal, dass das eine "normale" Lösung ist :?
Mit dem Thread hier bin ich dann auf die Lösung gekommen *GG*
-->http://www.python-forum.de/topic-15733.html

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder