Was ist besser clean_eval() oder modifiziertes read()?

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
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Das Problem bei eval oder exec ist, dass Definitionen dort global sind und nicht lokal.

Um Definitionen wieder zu beseitigen, habe ich zwei Lösungen gefunden. Die eine wäre eine modifiziertes read().
Wenn man von einem File liest, könnte man die Zeilen einzeln lesen und dann jede Zeile um vier Leerzeichen oder einen Tab einrücken.
Und eine vorgeschaltete erste Zeile könnte dann den Beginn einer Funktionsdefinition enthalten, etwa:

Code: Alles auswählen

def function() 
Dumm dabei ist, wenn in dem File """ vorkommt. Die nachfolgenden Zeilen dürfte man dann nicht einrücken.
Das wäre die eine Lösung, damit man nur lokale Defintionen bekommt, außer natürlich "function"

Die andere Lösung wäre, dass man globale Definitionen in Kauf nimmt und sie dann rauswirft. Aber so etwas gefällt hier wohl niemand:

Code: Alles auswählen

def clean_eval(evcode):
    glob_before = globals().keys()
    eval(evcode)
    glob_after = globals().keys()
    for element in glob_after:
        if element not in glob_before: del globals()[element]
Sollte man dann die erste Lösung implementieren?
Auch eine Lösung wäre natürlich, gleich in den Scripts, die man lädt, diese Funktionsdefinition am Anfang zu machen. Also Lösung Nummer 3
BlackJack

@Alfons Mittelmeyer: Am besten und einfachsten man verwendet kein `eval()` oder ``exec`` und wenn man das tut, dann gibt man halt ein Wörterbuch für den globalen Namensraum an statt sich den tatsächlichen Modulnamensraum vollmüllen zu lassen. Dann muss man aus dem hinterher auch nichts entfernen. Vor allem entfernt Dein `clean_eval()` ja nur *neue* Namen, aber was ist wenn der ausgeführte Code vorher vorhandene Namen neu bindet? Wenn er `globals` oder `glob_before` neu bindet, dann fällt `clean_eval()` auch gleich beim Aufräumen auf die Nase.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Am besten und einfachsten man verwendet kein `eval()` oder ``exec`` und wenn man das tut, dann gibt man halt ein Wörterbuch für den globalen Namensraum an statt sich den tatsächlichen Modulnamensraum vollmüllen zu lassen. Dann muss man aus dem hinterher auch nichts entfernen. Vor allem entfernt Dein `clean_eval()` ja nur *neue* Namen, aber was ist wenn der ausgeführte Code vorher vorhandene Namen neu bindet? Wenn er `globals` oder `glob_before` neu bindet, dann fällt `clean_eval()` auch gleich beim Aufräumen auf die Nase.
Das mit dem Wörterbuch habe ich nicht ganz verstanden. Zuerst hatte ich {} probiert. Aber da war dann der Zugriff auf tkinter nicht da. Dann hatte ich in den Scripts "from tkinter import *" eingebunden. Da war dann er Zugriff da. Aber das Rauslöschen danach war keine gute Idee.

Ich habe jetzt eine andere Idee. Ich könnte ja an den Beginn eines jeden Scripts für DynTkInter schreiben:

Code: Alles auswählen

def main(parent):
Für DynTkInter brauche ich zwar keinen Parent. Aber dann könnte man auch tkinter Scripts einbinden. Nach Reinladen ruft man dann für das Script einfach main mit dem jeweiligen Parent auf. Gute Idee?
BlackJack

@Alfons Mittelmeyer: Sowohl bei `eval()` als auch bei ``exec`` kann man optional ein Wörterbuch angeben das dann den Namensraum bildet in dem der Code ausgeführt wird. *Das* sollte man machen damit man sich nicht das Modul zerschiessen kann in dem die Funktion/Anweisung ausgeführt wird die den Code ausführt.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: natürlich muß jedes Modul selbst konsistent sein, das heißt, alle externen Namen müssen auch importiert werden. Und wenn man explizit einen globalen Namensraum angibt, braucht man auch nichts aufräumen. Aber Du solltest Dir die Idee mit dem eval wirklich nochmal überdenken.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: natürlich muß jedes Modul selbst konsistent sein, das heißt, alle externen Namen müssen auch importiert werden. Und wenn man explizit einen globalen Namensraum angibt, braucht man auch nichts aufräumen. Aber Du solltest Dir die Idee mit dem eval wirklich nochmal überdenken.
Ich denke, dass das mit der Funktion main nicht schlecht ist. Dann brauch ich auch nichts aufräumen, weil dann alles loka ist und main benutzt man sowieso immer. Ständig imp.reload zu machen für Scripte die nur die Funktion main haben, kann ich mir ja noch überlegen.

Ob ich das dann mache, Scripte nach main.py kopieren und dann einen imp.reload für main.py? Kann ja darüber nachdenken. Und dann nach dem reload immer main.main(parent) aufrufen. Und dieser Aufruf sorgt dann dafür, dass weitere solche reloads stattfinden.

Müsste man mal testen, was passiert, wenn main.main(parent) noch nicht abgearbeitet ist und unterdessen das modul reloaded wird.
Zuletzt geändert von Alfons Mittelmeyer am Dienstag 25. August 2015, 22:14, insgesamt 3-mal geändert.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Warum können eigentlich diese Skripte, von denen du immer redest, nicht einfach in einem separatem Interpreter-Prozess laufen (``python dein_skript.py``)? Das wurde dir ja (nicht nur von mir) bereits vorgeschlagen. Was spricht gegen diese Lösung?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

snafu hat geschrieben:Warum können eigentlich diese Skripte, von denen du immer redest, nicht einfach in einem separatem Interpreter-Prozess laufen (``python dein_skript.py``)? Das wurde dir ja (nicht nur von mir) bereits vorgeschlagen. Was spricht gegen diese Lösung?
Also wie geht das? Starten kann man mit 'python dein_skript.py'. Und wie lade ich dann das nächste Script nach? Angenommen, das Script baut die oberste Gui Ebene auf und die folgenden Scripte sollen dann Frames mit Inhalten füllen?
BlackJack

``main.main(parent)`` um dann das nächste Modul zu laden und dort `main()` aufzurufen klingt nicht gut, das kracht irgendwann wegen Rekursionslimit. Und es bleibt dann auch immer alles von jeder `main()` aus erreichbare im Speicher solange bis die ganzen Aufrufe dann am Ende hoffentlich mal zum Aufrufer zurückkehren.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:``main.main(parent)`` um dann das nächste Modul zu laden und dort `main()` aufzurufen klingt nicht gut, das kracht irgendwann wegen Rekursionslimit. Und es bleibt dann auch immer alles von jeder `main()` aus erreichbare im Speicher solange bis die ganzen Aufrufe dann am Ende hoffentlich mal zum Aufrufer zurückkehren.
Naja, mehr wie sechs Ebenen dürfte auch ein komplexe GUI nicht haben. Und direkt Rekursion wäre es nicht, weil das ja dann ein anderes main() wäre.

Ich kann es statt compile und eval dann auch mal mit imp.reload versuchen, ob das ebenso funktioniert.

Allerdings umständlich ist es schon. Es gibt auch ein paar Fälle, wo ich files reinlese und modifiziere. Wenn ich, anstatt sie gleich danach zu kompilieren und auszuführen nochmals abspeichern und reloaden soll.
Zuletzt geändert von Alfons Mittelmeyer am Dienstag 25. August 2015, 22:26, insgesamt 1-mal geändert.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Alfons Mittelmeyer hat geschrieben:
snafu hat geschrieben:Warum können eigentlich diese Skripte, von denen du immer redest, nicht einfach in einem separatem Interpreter-Prozess laufen (``python dein_skript.py``)? Das wurde dir ja (nicht nur von mir) bereits vorgeschlagen. Was spricht gegen diese Lösung?
Also wie geht das? Starten kann man mit 'python dein_skript.py'. Und wie lade ich dann das nächste Script nach? Angenommen, das Script baut die oberste Gui Ebene auf und die folgenden Scripte sollen dann Frames mit Inhalten füllen?
Kann es sein dass du ein Pluginsystem suchst?
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Die Frage war ja nach dem "Warum". Mir war bisher nicht klar, wozu diese Skripte überhaupt dienen sollen.

Wenn sie am Aufbau der GUI beteiligt sein sollen, warum können sie dann nicht einfach ein Kontext-Objekt von deinem Framework abfragen, auf dem sie irgendwelche Dinge anstellen? Sie müssten dafür nur dein Hilfsmodul importieren und dann `get_context()` aufrufen.

Auf diesem Objekt könnten dann so eine Art Session für jedes Skript gestartet werden mit eigenem Namensraum, der nur für die Dauer der Session besteht. Am Ende des Skriptes werden die Modifizierungen mittels `commit()` an dein Framework übergeben und dieses "überträgt" es in TKinter-Aufrufe. Irgendwie sowas.

Ein unkontrollierbares `eval()` halte ich jedenfalls für sehr gefährlich und `ast.literal_eval()` ist vermutlich zu restriktiv für dein Vorhaben.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@snafu Einen Namensraum während einer Session gibt es nicht. Es ist einfach ein main() Aufruf und das war es dann. In Bruchteilen von Sekunden steht dann die GUI und existieren tut dann im Namensraum nur mehr eine nicht mehr benötigte Funktion main.

Die GUI existiert schon. Sie besteht aus Widgets und namenlosen Callbacks.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

DasIch hat geschrieben:Kann es sein dass du ein Pluginsystem suchst?
Weiß nicht, ich habe ja was ich brauche. War nur noch die Frage, wie ich unbenötigte Definitionen wegbekomme. Eine Funktion die immer main heißt, ist da wohl die Lösung, und die dann alles übrige lokal macht. Und ob imp.reload auch gut funktioniert anstatt compile und eval, kann ich ja noch testen.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Wir alle rätseln mal wieder, weil du es nicht schaffte dein eigentliches Problem zu schildern :?

Offensichtlich erzeugt du ein Python Modul welches eine tkinter gui ist, generisch. Die soll dann getestet werden können?!?

Also was spricht gegen subprocess?

Merk dir einfach, das eval und exec zu 95% der falsche Ansatz ist. Aber das sagte man dir ja auch schon des öfteren.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten