Seite 1 von 6
Ich bekomme es ohne eval nicht hin
Verfasst: Mittwoch 12. August 2015, 21:42
von Alfons Mittelmeyer
Hier im Forum hat man gesagt, eval und compile darf man nicht verwenden. Deshalb habe ich mir etwas ausgedacht.
Statt mit eval und compile zu arbeiten, könnte man ja Eingaben auch in Files schreiben und die dann importieren.
Aber beim import muss man dann den Filenamen direkt angeben und eine Variable hilft da nichts.
Kann mir da vielleicht jemand weiterhelfen? Mein Code sieht so aus, aber das mit "filename" funktioniert dann nicht:
Code: Alles auswählen
import threading
class MyThread(threading.Thread):
def run(self):
counter=0
while True:
a = input("> ")
filename="temp"+str(counter)
fi=open(filename+'.py','w')
fi.write(a)
fi.close()
try: import filename
except: print("Error:",a)
counter +=1
mythread = MyThread()
mythread.daemon = True
mythread.start()
Der zweite Versuch war auch nichts:
Code: Alles auswählen
import threading
class MyThread(threading.Thread):
def run(self):
counter=0
while True:
a = input("> ")
filename="temp"+str(counter)
fi=open(filename+'.py','w')
fi.write(a)
fi.close()
filename2="imptemp"+str(counter)
fi=open(filename2+'.py','w')
fi.write("import "+filename)
fi.close()
try: import filename2
except: print("Error:",a)
counter +=1
mythread = MyThread()
mythread.daemon = True
mythread.start()
Aber irgendwie muß es doch gehen.
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Mittwoch 12. August 2015, 22:04
von cofi
Da du den Geist der Empfehlung verletzt, kannst du dann auch direkt `eval` und `compile` benutzen.
Der Vollstaendigkeit halber sei noch die `__import__` (Python2) Funktion und das `imp` Modul erwaehnt.
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Mittwoch 12. August 2015, 22:05
von kbr
Der Grund eval und compile zu vermeiden liegt darin, dass es diese Anweisungen ermöglichen, Code während der Laufzeit von extern einzuschleusen und mit den Rechten des laufenden Prozesses auszuführen. Das ist eine riesige Sicherheitslücke. Wenn Du das wirklich willst, dann nutze besser eval. Aber sei Dir des Risikos bewusst.
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Mittwoch 12. August 2015, 22:33
von Alfons Mittelmeyer
kbr hat geschrieben:Der Grund eval und compile zu vermeiden liegt darin, dass es diese Anweisungen ermöglichen, Code während der Laufzeit von extern einzuschleusen und mit den Rechten des laufenden Prozesses auszuführen. Das ist eine riesige Sicherheitslücke. Wenn Du das wirklich willst, dann nutze besser eval. Aber sei Dir des Risikos bewusst.
Ja danke, jetzt funktioniert es:
Code: Alles auswählen
import threading
class MyThread(threading.Thread):
def run(self):
counter=0
while True:
a = input("> ")
filename="temp"+str(counter)
fi=open(filename+'.py','w')
fi.write("from spazieren import *\n"+a)
fi.close()
filename2="imptemp"+str(counter)
fi=open(filename2+'.py','w')
fi.write("import "+filename)
fi.close()
try: eval(compile("import "+filename2,'<string>', 'exec'))
except: print("Error:",a)
counter +=1
mythread = MyThread()
mythread.daemon = True
mythread.start()
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Mittwoch 12. August 2015, 22:46
von BlackJack
@Alfons Mittelmeyer: Genau so machst Du wieder genau das was man nicht machen sollte, also entgegen der Empfehlungen jetzt auch noch ausführen von generiertem Quelltext in verschiedenen Varianten gemischt, auf die umständlichste aller Arten, in einem vollkommen sinnlosen Thread der das ohnehin sinnlose Programm ohne Grund verkompliziert, und das ganze dann auch noch im falschen Unterforum (oder habe ich den Bezug zu Tkinter nur nicht gesehen?)

Re: Ich bekomme es ohne eval nicht hin
Verfasst: Mittwoch 12. August 2015, 22:52
von Alfons Mittelmeyer
BlackJack hat geschrieben:@Alfons Mittelmeyer: Genau so machst Du wieder genau das was man nicht machen sollte, also entgegen der Empfehlungen jetzt auch noch ausführen von generiertem Quelltext in verschiedenen Varianten gemischt, auf die umständlichste aller Arten, in einem vollkommen sinnlosen Thread der das ohnehin sinnlose Programm ohne Grund verkompliziert, und das ganze dann auch noch im falschen Unterforum (oder habe ich den Bezug zu Tkinter nur nicht gesehen?)

Ja ist schon irgendwie komisch. Da schreibt man imports um eval und compile zu vermeiden. Aber um dann die imports auszuführen, muss man eval und compile verwenden. Bezug? Nö das brauche ich genau für Tkinter.
Und richtig ist es dann auch so:
Code: Alles auswählen
import threading
class MyThread(threading.Thread):
def run(self):
while True:
a = input("> ")
try: eval(compile(a,'<string>', 'exec'))
except: print("Error:",a)
mythread = MyThread()
mythread.daemon = True
mythread.start()
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Mittwoch 12. August 2015, 23:14
von BlackJack
@Alfons Mittelmeyer: Man muss nicht `eval()` und `compile()` verwenden um Module dynamisch zu importieren. Dafür gibt es Funktionen. Die sind aber auch nicht dafür gedacht was *Du* damit anscheinend anstellen willst. Denn wenn Du den bisherigen `eval()`/`compile()`-Unsinn den Du in Deinem Code veranstaltest, nun in Module schreiben lässt um die dann zu importieren, dann machst Du immer noch den gleichen Unsinn, nur eben mit anderen Sprachmitteln. *Und* die Module wirst Du auch aus dem Speicher nicht mehr los. Ist also schon sehr eigenartig das Du diesen Weg gehst.
Das Beispiel/Problem hat rein gar nichts mit Tkinter zu tun, und gehört damit auch nicht ins Tkinter-Unterforum.
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Donnerstag 13. August 2015, 07:48
von Sirius3
@Alfons Mittelmeyer: auch wenn das nur sinnlose Beispiele sind, solltest Du Dir Gedanken über Deine Variablennamen machen. Wir wissen ja schon, dass Du alles im Kopf hast und Dich auch noch Jahre später daran erinnerst, was par[17] in Zeile 1437 bedeutet, aber Namen helfen auch, gedanklich eine Struktur ins Programm zu bekommen. MyThread oder a sind so nichtssagen. Wenn man nur die run-Methode eines Threads überschreibt, kann man auch gleich das target-Argument benutzen. Exceptions sind dazu da, um Fehler zu finden. Du gibst bei einem Fehler nur "Error" aus, was niemandem irgendetwas bringt. Eingerückt wird mit 4 Leerzeichen! Jeder Befehl bekommt seine eigene Zeile. Statt eval-compile kannst Du auch gleich exec benutzen.
Ein input in einem Thread ist etwas seltsames. Mehrere Threads können sich dann um Deinen Input streiten? input sollte nur im Hauptthread benutzt werden.
Ich komm dann bei sowas raus:
Code: Alles auswählen
import logging
def main():
while True:
try:
exec(input(">"))
except:
logging.exception("Error at input")
if __name__ == '__main__':
main()
Die interaktive Pythonshell ist aber mächtiger.
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Donnerstag 13. August 2015, 08:09
von Hyperion
Hinzu kommen noch folgende formale Schwächen:
- Vier Spaces statt eines Tabs als Einrückung benutzen!
- Dateien sollte man immer mittels ``with open(...) as handler`` öffnen
- Strings sollte man mittels ``str.format``-Methode oder dem ``%``-Operator zusammensetzen
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Donnerstag 13. August 2015, 08:15
von sparrow
Der Thread erinnert mich ein bisschen an:
A "Der Test Ihres Programms hat ergeben, dass es abstürzt wenn man als Namen mehr als 100 Zeichen eingibt. z.B. 101 mal eine 9"
Nachtest:
B "Mein Programm fängt jetzt die Eeingabe von 101 mal 9 ab!
A "Der Test Ihres Programms hat ergeben, dass es abstürzt wenn man als Namen mehr als 100 Zeichen eingibt. z.B. 101 mal eine 7"
[...]
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Donnerstag 13. August 2015, 08:45
von jens
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Donnerstag 13. August 2015, 20:10
von Alfons Mittelmeyer
Nein eher probiere ich mal aus, einen .pyc file in eine Variable zu laden und dann mit eval oder exec auszuführen.
Wenn es weder Variablen noch Funktionen zu importieren gibt, ist importieren sinnlos.
Oder kann ich auch direkt py und pyc files ausführen, aber ohne importieren?
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Donnerstag 13. August 2015, 21:04
von jens
Du suchst wieder nach sehr unüblichen wegen...
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Donnerstag 13. August 2015, 22:33
von snafu
Alfons Mittelmeyer hat geschrieben:Oder kann ich auch direkt py und pyc files ausführen, aber ohne importieren?
Aber was soll das bringen?
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Donnerstag 13. August 2015, 22:39
von Sirius3
@snafu: Das bringt eine saubere Speicheraufräumung

. Im Übrigen der normale Weg, wenn man einen GUI-Editor und eine Testumgebung schreibt, die laufen nie im selben Prozess.
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Donnerstag 13. August 2015, 23:04
von Alfons Mittelmeyer
snafu hat geschrieben:Alfons Mittelmeyer hat geschrieben:Oder kann ich auch direkt py und pyc files ausführen, aber ohne importieren?
Aber was soll das bringen?
Unterscheiden wir mal echte Module und Pseudomodule. Echte Module enthalten Definitionen wie Funktionen und Variablen, auf die man zugreifen möchte. Daher brauchen sie einen Namensraum und man importiert sie.
Die Pseudomodule enthalten keine Definitionen von Funktionen und Variablen. Sie sind nur eine Abfolge von Kommandos, vergleichbar mit einem Nacheinander von Printbefehlen. Und wenn man sie aufruft, läuft die Kommandosequenz ab und fertig. Und was will man dann mit einem Namensraum, der dann nichts enthält? Außerdem kann es sein, dass man diese Sequenz auch öfters aufruft. Mit import geht es nur einmal. Auch können beliebige andere Sequenzen aufgerufen werden, Anzahl unbeschränkt und so oft wie man will. Ein Import macht hier überhaupt keinen Sinn mit unbeschränkt Namensräumen für nichts. Und ein Imp.reload sagt eventuell gar, dass ein reload nicht geht, weil ein Modul mit nichts kein Modul wäre.
Die Lösung habe ich jetzt für kompilierten Code gefunden. Mit marshal.dump könnte man kompilierten Code in ein Cache Verzeichnis schreiben. Mit marshal.load könnte man den Code wieder laden und und danach ausführen. Außerdem könnte man auch einen Cash im Speicher einrichten für häufiger benützte solche Pseudomodule. Allerdings ob sich der Aufwand dafür lohnt? Ich denke wohl eher nicht. Gui Applikationen, das heißt Widget Definition und Definition der Callbacks sind auch nicht so besonders groß. Und Nachladen von neuen GUI Teilen geht wesentlich schneller als das Laden von Internetseiten auch wenn man jedesmal neu kompiliert. Ich hatte darüber nachgedacht, ob Cache im Hauptspeicher und Cache Verzeichnis mit kompiliertem Code einen bemerkbaren Laufzeitvorteil haben könnten. Wohl nicht. Die Kompilierzeit dürfte kurz sein im Vergleich zur Zeit für den Aufbau der GUI und die ist auch schon sehr kurz.
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Donnerstag 13. August 2015, 23:15
von DasIch
Die Lösung die du suchst sind Klassen, Methoden und Funktionen. Die bieten dir die Möglichkeit Befehle sequentiell in einem temporären Namensraum auszuführen.
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Donnerstag 13. August 2015, 23:39
von Alfons Mittelmeyer
DasIch hat geschrieben:Die Lösung die du suchst sind Klassen, Methoden und Funktionen. Die bieten dir die Möglichkeit Befehle sequentiell in einem temporären Namensraum auszuführen.
Klassen, Methoden und Funktionen habe ich. Das ist nicht die Lösung die ich suche, weil ich sie habe. Die Frage war nur, ob man import oder eval und compile nehmen soll für solche Sequenzen. Import bringt da überhaupt nichts. Und cachen wäre auch zu kompliziert, weil man sich da die Quelle merken müßte. War das ein File aus diesem oder jenem Verzeichnis. Kam das aus einer Datenbank, wenn ja, dann aus welcher, war das der Name eines eindeutig identifierbaren Datensatzes, oder war das ein Datensatz aus einer Datenbank mit Baumstruktur, wie XML oder kam das über diese oder jene Internetadresse oder woher auch immer. Und das zu cachen und dabei die Quellangaben zu verwalten wäre wohl ein viel zu großer Aufwand.
Also ich habe es einfach mit compile und eval gemacht und so funktioniert es am Besten.
Ach übrigens, eine komplette Pythonanwendung, wäre auch so eine Befehlssequenz, wenn sie so geschrieben ist, dass die Anwendung nur temporär vorhanden ist, nämlich die Hauptfunktion definieren dann ausführen und dann löschen, etwa so:
Davon importierte Module würden allerdings im Speicher bleiben. Aber dieses ist mir egal.
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Freitag 14. August 2015, 04:48
von BlackJack
Die letzte Zeile ist Unsinn und `eval()`/`compile()` sind nicht die Lösung sondern eine Funktione zu schreiben. Und Du weisst das diese Antwort kommt, also könntest Du bitte aufhören zu trollen…
Re: Ich bekomme es ohne eval nicht hin
Verfasst: Freitag 14. August 2015, 08:53
von Alfons Mittelmeyer
BlackJack hat geschrieben:Die letzte Zeile ist Unsinn und `eval()`/`compile()` sind nicht die Lösung sondern eine Funktione zu schreiben. Und Du weisst das diese Antwort kommt, also könntest Du bitte aufhören zu trollen…
Also ich habe jetzt so eine Funktion geschrieben:
Code: Alles auswählen
def doPython(program):
fh = open(program,"rb")
a = fh.read()
fh.close()
eval(compile(a,'<string>', 'exec'))
Jetzt wäre die Frage, ob das jetzt OK wäre, weil man ja eine Funktion geschrieben hat, weil man jetzt eval und compile nicht direkt benützt, oder ob es für Python bereits eine derartige offizielle Funktion gibt, die man dann auch benützen darf. Oder wenn nicht, ob man die Python Core Entwickler veranlassen sollte eine derartige offiziell erlaubte Funktion zu schreiben. Die offiziell erlaubte Funktion muß aber automatisch das del enthalten. Denn wenn eine Anwendung beendet wird, soll nichts übrigbleiben. Das ist bei 'python3 myprogram' ja der Fall. Wenn ich python verlasse, muss auch der belegte Speicher wieder frei gegeben werden.
Nur kann ich 'python3 myprogram' nicht zum dynamischen Nachladen während Python läuft verwenden, weil gewisse Objekte in Modulen erhalten bleiben sollen. Man könnte natürlich, was in den Modulen ist, jedesmal inklusive angelegte Objekte als Sourcecode speichern, wenn dann ein zusätzlicher Code aufgerufen werden soll, und den zusätzlichen Aufruf dann an diesen Sourcecode dazukopieren, dann Python beenden und über Batch Datei dann wieder neu starten mit erweiterter Source. Aber das erscheint mir zu umständlich, nur deshalb, weil ich nicht eval und compile und del nehmen soll. Und ausserdem will ich den zusätzlichen Codeauruf nur temperär haben und nicht an die Source womöglich auch noch dauerhaft dazukopiert.
Ich brauche einen Python Programmaufruf während Python läuft, mit Löschen des Main Speichers und ohne Löschen der Modulspeicher.