Rekursiv oder einfache Schleife?

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
Sempervivum
User
Beiträge: 10
Registriert: Montag 22. November 2021, 21:57

Hallo Community,
ich habe dieses kurze Skript geschrieben:

Code: Alles auswählen

import sys
import socket
import os
import time
import subprocess

try:
    jitsiPath = r'C:\Program Files\Sonstiges\Jitsi\Jitsi.exe'
    sipUrl = r'sip.provider.de'

    def is_connected():
        try:
            # try to connect the sip host:
            sock = socket.create_connection((sipUrl, 5060))
            sock.close()
            # host is online, start jitsi now:
            subprocess.call([jitsiPath])
            return True
        except:
            # wait 10 seconds:
            time.sleep(10)
            # test the connection again:
            is_connected()
        return False
    is_connected()
except:
    print("Error at line", sys.exc_info()[2].tb_lineno, sys.exc_info()[0])
Es prüft zyklisch ob die Internetverbindung steht und startet das Program Jitsi wenn das der Fall ist.
Das funktioniert einwandfrei. Das Exceptionhandling ist sicher noch verbesserungsfähig aber die Kernfrage ist: Es arbeitet rekursiv - wenn es mal über längere Zeit läuft, kann es dann zum Stackoverflow kommen?
Sollte ich besser eine einfache Schleife verwenden?
Beste Grüße - Ulrich
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sempervivum: Ja, kann es. Nie Rekursion wenn es auch eine einfache Schleife tut in Programmiersprachen die keine „tail call optimization“ garantieren. Zudem ist die Funktion auch fehlerhaft, denn die kann ja auch `False` liefern wenn eine Verbindung besteht. Gut das der Rückgabewert nirgends verwendet wird. Oder schlecht, denn dann wäre das vielleicht mal aufgefallen. 🤓

Die Ausnahmebehandlung ist falsch. Das Äussere ``try``/``except`` kann so wie der Code jetzt strukturiert ist, im Grunde nie zum Zuge kommen, weil das alles vom inneren ``try``/``except`` verschluckt wird. Und weil das wirklich *alles* durch einfach ignorieren und noch mal versuchen abbügelt, hat man bei unerwarteten Ausnahmen, beispielsweise weil man sich bei einem Namen vertippt hat, ein Programm was einfach lautlos aber endlos immer weiter versucht etwas zu machen was vielleicht nie gehen wird, weil man einen Programmierfehler gemacht hat. Nackte ``except:`` ohne konkrete Ausnahme die man da behandeln will, sind keine gute Idee. Die einzigen sinnvollen Behandlungen für *alles* sind im Grunde ausführlich den Grund irgendwo zur Verfügung stellen, beispielsweise Ausnahme samt Traceback protokollieren, oder wenn man etwas aufräumt und dann die Ausnahme wieder auslöst, damit das an anderer Stelle sinnvoll behandelt werden kann.

Was auch zu einer Endlosschleife führen wird, ist wenn das externe Programm nicht gestartet werden kann. Warum auch immer. Neben der Umstellung von `subprocess.call()` auf `subprocess.run()` (siehe Dokumentation), sollte man `run()` dann mit ``check=True`` aufrufen, damit einem da keine Probleme lautlos durchrutschen.

Namen von Konstanten werden KOMPLETT_GROSS geschrieben. In `camelCase` wird in Python gar nix benannt. Nicht zu verwechseln mit PascalCase → Klassennamen.

`os` wird importiert, aber nirgends verwendet.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import socket
import subprocess
import time

JITSI_PATH = R"C:\Program Files\Sonstiges\Jitsi\Jitsi.exe"
SIP_URL = R"sip.provider.de"


def main():
    while True:
        try:
            with socket.create_connection((SIP_URL, 5060)):
                pass
        except OSError:
            time.sleep(10)
        else:
            subprocess.run([JITSI_PATH], check=True)


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sempervivum
User
Beiträge: 10
Registriert: Montag 22. November 2021, 21:57

Hallo blackjack und danke für die schnelle Antwort.
Ich habe dein Skript getestet und muss mich vor allem mit dieser Konstruktion try-except-else noch vertraut machen.
Leider lief es nicht auf Anhieb sondern ich bin in eine Endlosschleife geraten: Immer wieder der Aufruf von subprocess.run. Als erste Maßnahme habe ich ein Variable terminate eingeführt, um die Schleife zu beenden. Das hat auch funktioniert, aber dann wurde Jitsi sofort wieder beendet wenn das Skript fertig war. Das war der Fall sowohl bei run als auch bei call. So funktioniert es:

Code: Alles auswählen

import socket
import subprocess
import time

JITSI_PATH = R"C:\Program Files\Sonstiges\Jitsi\Jitsi.exe"
SIP_URL = R"sip.provide.de"


def main():
    terminate = False
    while not terminate:
        try:
            with socket.create_connection((SIP_URL, 5060)):
                pass
        except OSError:
            time.sleep(10)
        else:
            subprocess.run([JITSI_PATH])
            terminate = True


if __name__ == "__main__":
    main()

input()
Aber mit dem input() erzwingen, dass das Skript sich nicht beendet, ist nicht so das Wahre. Es gibt doch sicher auch eine Möglichkeit, dass Programm im Hintergrund zu starten?

Edit: Mit Popen funktioniert es:

Code: Alles auswählen

            subprocess.Popen(["rm", "-r", JITSI_PATH])
Zuletzt geändert von Sempervivum am Montag 29. November 2021, 11:01, insgesamt 1-mal geändert.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Das ist eine wichtige Information, dass Jitsi im Hintergrund läuft.
Diese ganze Flag-Geschichte ist überflüssig, da man hier einfach mit `break` arbeiten kann und das input auch, denn wenn das Programm fertig ist, dann ist es fertig.

Code: Alles auswählen

import socket
import subprocess
import time

JITSI_PATH = "C:/Program Files/Sonstiges/Jitsi/Jitsi.exe"
SIP_URL = "sip.provide.de"


def main():
    while True:
        try:
            with socket.create_connection((SIP_URL, 5060)):
                pass
        except OSError:
            time.sleep(10)
        else:
            break
    subprocess.run([JITSI_PATH])

if __name__ == "__main__":
    main()
Sempervivum
User
Beiträge: 10
Registriert: Montag 22. November 2021, 21:57

Danke, so habe ich immer noch das Problem, dass sich der Prozess gleich wieder beendet wenn das Skript sich beendet. Mit Popen funktioniert es dagegen, siehe mein "Edit" oben.
Antworten