Python-Shell in subprocess.Popen

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
Üpsilon
User
Beiträge: 225
Registriert: Samstag 15. September 2012, 19:23

Hallo.
Angenommen, ich möchte eine graphische Python-Shell programmieren - ähnlich wie die in der IDLE. Allerdings so, dass prinzipiell auch andere Shells darin laufen können, wie meinetwegen die Bash oder eine Julia-Shell oder was auch immer. Im ersten Erkundungsversuch habe ich festgestellt, dass für die Bash folgendes funktioniert:

Code: Alles auswählen

import subprocess
p = subprocess.Popen("bash", shell=True, text=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.stdin.write("pwd\n")
p.stdin.flush()
print(p.stdout.readline())
Ersetze ich "bash" jedoch durch "python3" und in der folgenden Zeile das "pwd\n" durch "print('hallo')\n", dann funktioniert das nicht, d.h. das readline hängt sich ergebnislos auf. Habe schon einiges versucht: dem Python3 die Option -u mitgeben, an bufsize rumspielen, aber nichts hat geholfen. Vielleicht liegt es doch nicht an der Buffering-Thematik. Wie kann man das ans Laufen kriegen?

Popen.communicate zu benutzen ist erstmal keine Option, weil das ja den Prozess gleich beenden will.

Betriebssystem ist GNU/Linux Mint.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich vermute mal dazu musst du dann eine vollständige Terminalemulation anbieten. Unter Linux oder mac ist das relativ gut beschrieben in allen möglichen Werken, oder man schaut sich zb mal das pexpect Modul an. Wie das unter Windows läuft kann ich nicht sagen. Kann man aber bestimmt mal suchen.
karolus
User
Beiträge: 143
Registriert: Samstag 22. August 2009, 22:34

Vielleicht möchtest du dir anschauen wie xonsh das macht?
Üpsilon
User
Beiträge: 225
Registriert: Samstag 15. September 2012, 19:23

__deets__ hat geschrieben: Sonntag 6. November 2022, 18:17 Ich vermute mal dazu musst du dann eine vollständige Terminalemulation anbieten. Unter Linux oder mac ist das relativ gut beschrieben in allen möglichen Werken, oder man schaut sich zb mal das pexpect Modul an. Wie das unter Windows läuft kann ich nicht sagen. Kann man aber bestimmt mal suchen.
Hm. Sicher, dass es da nicht eine "einfache" Lösung gibt?
karolus hat geschrieben: Sonntag 6. November 2022, 19:19 Vielleicht möchtest du dir anschauen wie xonsh das macht?
Das scheint ja eine eigene Python-ähnliche Sprache zu sein. Ich nehme mal an, die haben die Ausführung der Sprache selbst in ihrem Hauptprogramm drin und haben nicht einen Subprozess, von dem sie immer abwechselnd lesen und schreiben. Auf so was in der Art will ich ja nicht hinaus.
PS: Die angebotene Summe ist beachtlich.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Üpsilon hat geschrieben: Sonntag 6. November 2022, 21:47 Hm. Sicher, dass es da nicht eine "einfache" Lösung gibt?
Ziemlich. So sicher zumindest, dass es mein Ansatz wäre. Du darfst natürlich auch andere suchen.
Üpsilon
User
Beiträge: 225
Registriert: Samstag 15. September 2012, 19:23

__deets__ hat geschrieben: Sonntag 6. November 2022, 21:52
Üpsilon hat geschrieben: Sonntag 6. November 2022, 21:47 Hm. Sicher, dass es da nicht eine "einfache" Lösung gibt?
Ziemlich. So sicher zumindest, dass es mein Ansatz wäre. Du darfst natürlich auch andere suchen.
Das hab ich schon versucht :mrgreen: :mrgreen:
PS: Die angebotene Summe ist beachtlich.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1121
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

So gehts:

Code: Alles auswählen

import subprocess
import sys

CODE = """
print("Hello World")
"""

stdout1, stderr1 = subprocess.Popen(
    sys.executable, stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding="utf8"
).communicate(CODE)


stdout2 = subprocess.run(
    sys.executable, input=CODE, capture_output=True, encoding="utf8"
).stdout

Normalerweise importiert man die Module und führt dann die Funktion in einem Thread oder einem Prozess aus, wenn es nicht blockieren soll.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

So gehts nicht. Run ist wie communicate. Man muss schon ein Terminal emulierten, damit schreiben nicht gebuffert wird.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1121
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

__deets__ hat geschrieben: Montag 7. November 2022, 14:19 So gehts nicht. Run ist wie communicate. Man muss schon ein Terminal emulierten, damit schreiben nicht gebuffert wird.
Der Code funktioniert so mit Python 3.11. Ich bekomme von beiden Varianten die Ausgabe, wenn ich noch print mit einfüge.

Es gibt für Python den Optionsschalter -u, mit dem man die ungepufferte Ein-/Ausgabe aktivieren kann. Praktisch ist auch -c, mit dem man den Quellcode einfach übergeben kann.

Weiteres Beispiel:

Code: Alles auswählen

                                               
import subprocess
import sys


CODE = """
import time

print("Hello World")
print("Line 2")
time.sleep(5)
"""

proc = subprocess.Popen(
    [sys.executable, "-u", "-c", CODE], stdout=subprocess.PIPE, encoding="utf8"
)

# Programm läuft solange in der Schleife, bis es beendet ist
# oder terminiert wird
while proc.poll() is None:
    print(proc.stdout.readline(), end="")
Sobald etwas ausgegeben wird, aber ein Zeilenende fehlt, geht es erst mal nicht weiter, bis zum nächsten Zeilenende oder flush.

Python-Code importiert man normalerweise und macht nicht einen solchen Blödsinn.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Natürlich bekommst du etwas wieder, wenn das Programm fertig ist. Da kommt der letzte Buffer natürlich raus, egal wie voll. Darum ging’s aber ja nicht. Sondern eine interaktive Shell. Und laut TE klappt das mit dem -u auch nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13533
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DeaD_EyE: Der Code ”funktioniert”, der macht aber nicht was gewollt ist. Es geht darum eine Python-Shell *dauerhaft* laufen zu lassen und damit zu kommunizieren. Das macht Dein Code nicht. *Und* es soll auch was anderes als Python möglich sein, also nützt es nicht wirklich was wenn man das Puffern bei Python abschaltet, weil das keine Lösung für andere Programme/Shells ist.

Code: Alles auswählen

- (void)countSheep {
    unsigned int sheep = 0;
    while ( ! [self isAsleep]) { ++sheep; }
}
Benutzeravatar
bi3mw
User
Beiträge: 11
Registriert: Mittwoch 19. Oktober 2022, 23:14

Üpsilon hat geschrieben: Sonntag 6. November 2022, 18:10 Hallo.
Angenommen, ich möchte eine graphische Python-Shell programmieren - ähnlich wie die in der IDLE. Allerdings so, dass prinzipiell auch andere Shells darin laufen können, wie meinetwegen die Bash oder eine Julia-Shell oder was auch immer. ....
Ich habe einen recht einfach gestrickten Terminal in Python gefunden, bei dem man die Shell auch austauschen kann. Das wäre vielleicht eine passende Vorlage.

https://github.com/smstong/pyterm/blob/main/pyterm.py
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Auch wenn das ein schlechtes Projekt ist (siehe das eigene TODO) - der Hinweis war gut, weil er auf https://docs.python.org/3/library/pty.html geführt hat. Kannte ich nicht. Das ist zumindest unter Unixoiden ein Weg.
Üpsilon
User
Beiträge: 225
Registriert: Samstag 15. September 2012, 19:23

__blackjack__ hat geschrieben: Montag 7. November 2022, 18:41 Es geht darum eine Python-Shell *dauerhaft* laufen zu lassen und damit zu kommunizieren.
Genau.
Habe folgende vorläufige Lösung gefunden: In Zeile 2 des Codes aus dem ersten Beitrag "python3 -i" verwenden. Dann kann ich die Ausgaben mit readline bekommen.

Was übrigens auch funktioniert: Ein Popen mit "bash -i" starten, und dann innerhalb der Bash mit "python3" eine Python-Shell starten. Bei nur "bash" ohne "-i" funktioniert das hingegen nicht.

Ist alles verrückt.
PS: Die angebotene Summe ist beachtlich.
Antworten