Ich bekomme es ohne eval nicht hin

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

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.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

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.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

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

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

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?) :roll:
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()
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.
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@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.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

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"

[...]
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

@Alfons Mittelmeyer: Du suchst wohl: https://docs.python.org/3/library/funct ... l#__import__

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jens hat geschrieben:@Alfons Mittelmeyer: Du suchst wohl: https://docs.python.org/3/library/funct ... l#__import__
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?
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Du suchst wieder nach sehr unüblichen wegen...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Alfons Mittelmeyer hat geschrieben:Oder kann ich auch direkt py und pyc files ausführen, aber ohne importieren?

Code: Alles auswählen

python dein_file.py
Aber was soll das bringen?
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@snafu: Das bringt eine saubere Speicheraufräumung :P. Im Übrigen der normale Weg, wenn man einen GUI-Editor und eine Testumgebung schreibt, die laufen nie im selben Prozess.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

snafu hat geschrieben:
Alfons Mittelmeyer hat geschrieben:Oder kann ich auch direkt py und pyc files ausführen, aber ohne importieren?

Code: Alles auswählen

python dein_file.py
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.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

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

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:

Code: Alles auswählen

def mymain():
   ###
   ###
   ###

mymain()
del mymain
Davon importierte Module würden allerdings im Speicher bleiben. Aber dieses ist mir egal.
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…
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

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.
Gesperrt