Aus einer TRY in einer While "ausbrechen".

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
Holzkopp
User
Beiträge: 12
Registriert: Samstag 24. Juli 2021, 20:49

Hey Leute,

ich habe eine While-Schleife, die wiederum eine Try-Funktion startet. Bei einer ganz bestimmten except soll die While-Schleife beendet werden.

Code: Alles auswählen

while True:
        Aufruf Funktion
        print("wait 10 sec...")
        sleep(10)
Dann erledige eine andere Aufgabe
  
  
        
def Funktion:
 	try:
 		erledige eine Aufgabe
 	except1
 		print("Fehler1")
 	except2
 		print("ein anderer Fehler")
 		break       
Ich kann wohl mit break nur die innerste Schleife beenden. Aber wie bekomme ich dann die While-Schleife auf False?
Die Möglichkeit dies mit except KeyboardInterrupt zu erledigen ist für mich keine Option, da so der Befehl "Dann starte eine andere Aufgabe" nicht ausgeführt wird.
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Innerhalb einer Funktion kann man diese per "return" verlassen und dabei sogar Werte zurück geben. ;-)
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@forum@mong.de: zuerst einmal solltest Du gültige Pythonsyntax schreiben. Eingerückt wird auch nur mit 4 Leerzeichen pro Ebene, 8 sind etwas viel. Und Tabs benutzt man gar nicht, vor allem mischt man nicht.
Das `break` in `Funktion` funktioniert nicht, weil `Funktion` gar keine Schleife hat.
Wenn ich das Codefragment interpretieren müßte, hast Du eine Funktion, die so lange wiederholt werden soll, bis sie erfolgreich war. Und dieses "Erfolgreich" mußt Du natürlich der Aufrufenden Codestelle mitteilen, per Rückgabewert.

Code: Alles auswählen

def some_function():
    try:
        do_something()
    except SomeException:
        print("Fehler1")
    except OtherException:
        print("ein anderer Fehler")
        return True
    return False

while True:
    success = some_function()
    if success:
        break
    print("wait 10 sec...")
    sleep(10)
Holzkopp
User
Beiträge: 12
Registriert: Samstag 24. Juli 2021, 20:49

Sirius3 hat geschrieben: Sonntag 25. Juli 2021, 08:36 Das `break` in `Funktion` funktioniert nicht, weil `Funktion` gar keine Schleife hat.
Wenn ich das Codefragment interpretieren müßte, hast Du eine Funktion, die so lange wiederholt werden soll, bis sie erfolgreich war. Und dieses "Erfolgreich" mußt Du natürlich der Aufrufenden Codestelle mitteilen, per Rückgabewert.
Das mit den 4 Leerzeichen ist mir bewusst. Wieso sind Tabulatoren eigentlich so schlecht?

Das Skript überprüft in der While-Schleife alle 10 Sekunden, ob auf meinem Raspberry Pi noch der Samba-Server läuft und signalisert mit LED das jeweilige Ergebnis.
Dafür habe ich eine Exception definiert, in deren Fall der Server neu gestartet wird.
Eine weitere Exception prüft, ob ein Zugriff auf den Inhalt des Servers funktioniert (Festplatte angeschlossen) und gibt eine Warnmeldung aus.
Noch eine weitere Exception prüft, ob überhaupt ein Samba-Server installiert wurde. In diesem Fall kann das Skript komplett abgebrochen werden, weil eine weitere Prüfung (nach 10 Sekunden) nutzlos ist.

Ich hatte das Skript extra nur als Pseudo-Code gepostet, da es arg umfangreich ist.

Dein Skript mit meiner Logik angepasst müsste doch dann folgendermaßen lauten, oder?

Code: Alles auswählen

def some_function(Led-Variable):
    try:
        do_something()
    except Server angehalten:
        starte Server neu
    except kein Server installiert:
        print("Abbruch")
        return True
    return False

while True:
    success = some_function(Led-Variable)
    if success:
        break
    print("wait 10 sec...")
    sleep(10)
So scheint es zu funktionieren!
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Dann fängst Du die Exception an der falschen Stelle ab. Das `except kein Server installiert` gehört auf oberster Ebene um die while-Schleife. Es macht ja keinen Sinn, eine Exception in einen Wahrheitswert umzuwandeln um den dann am einer anderen Stelle auszuwerten.
Holzkopp
User
Beiträge: 12
Registriert: Samstag 24. Juli 2021, 20:49

Das Skript soll ja endlos weiterlaufen, es sei denn, es wurde noch gar kein Samba-Server installiert (bzw. es wurde ein Fehler festgestellt, der nicht behoben werden kann). Nur dann soll ein Abbruch erfolgen und eine Nachricht per Telegram versendet werden.

Sirius3 hat geschrieben: Sonntag 25. Juli 2021, 12:52 Dann fängst Du die Exception an der falschen Stelle ab. Das `except kein Server installiert` gehört auf oberster Ebene um die while-Schleife. Es macht ja keinen Sinn, eine Exception in einen Wahrheitswert umzuwandeln um den dann am einer anderen Stelle auszuwerten.
Das verstehe ich nicht. Wo deiner Meinung nach müsste das Skript abgeändert werden?
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Fehler werden dort behandelt, wo es sinnvoll ist, und es ist nicht sinnvoll einen Fehler innerhalb der Schleife zu behandeln, wenn das nur dafür sorgt, dass die Schleife händisch abgebrochen wird.

Code: Alles auswählen

def main():
    try:
        while True:
            try:
                do_something(Led-Variable)
            except Server angehalten:
                starte Server neu
            print("wait 10 sec...")
            sleep(10)
    except kein Server installiert:
        print("Abbruch")

if __name__ == "__main__":
    main()
Holzkopp
User
Beiträge: 12
Registriert: Samstag 24. Juli 2021, 20:49

Vielleicht reden wir an einander vorbei oder ich checks nicht.
Ich bin der Meinung, dass dein Vorschlag nicht funktionieren kann, weil except kein Server installiert in der "falschen Schleife" aufgeführt wird.

Also weg vom Pseudo-Code. Hier ist das Skript komplett, damit mein Plan deutlicher wird:

Code: Alles auswählen

#!/usr/bin/env python3
# coding=utf-8

from smb.SMBConnection import SMBConnection
from smb import smb_structs
import tempfile
from socket import gethostbyname
from time import sleep, strftime
from gpiozero import LED
from subprocess import run


USER = "pi"
PASSWORD = "123456789"
SERVER = "Raspi"           
Freigabe = 'Toshiba'        
Testdatei = '/samba.txt'    

LED_PIN = 18   


def connection_status(status_led):
    try:
        server_ip = gethostbyname(SERVER)
        conn = SMBConnection(USER, PASSWORD, "name", SERVER, use_ntlm_v2 = True)
        conn.connect(server_ip, 445)
        file_obj = tempfile.NamedTemporaryFile() 
        conn.retrieveFile(Freigabe, Testdatei, file_obj)
        file_obj.close()
        conn.close()
        print("Samba-Server läuft...")
        status_led.on()
    except ConnectionRefusedError:
        print("Keine Verbindung zum Samba-Server.")
        status_led.off()
        run(['sudo', 'service', 'smbd', 'restart'])
        run(['sudo', 'service', 'nmbd', 'restart'])
        print("Samba-Server wird neu gestartet...")
    except ConnectionResetError:
        print("Es ist kein Samba-Server installiert.")
        status_led.off()
        return True
    except smb_structs.OperationFailure:
        print("Testdatei nicht gefunden")
        status_led.blink()
    return False


def main():
    status_led = LED(LED_PIN)
    while True:
        success = connection_status(status_led)
        if success:
            print("Abbruch")
            break
        print("wait 10 sec...")
        sleep(10)


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("    Bye!")
Das Skript klappt wunderbar, bis auf die Tatsache, dass bei except ConnectionResetError das Skript munter weiter läuft (stets aufs neuen testet), ich es bei diesem Fehler aber gerne abbrechen würde.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@forum@mong.de: Das sollte aber nicht passieren. Wobei anzumerken wäre, dass `success` entweder der komplett falsche Name ist, oder Du `True` und `False` austauschen und die Bedingung negieren solltest.

Beziehungsweise wie Sirius3 bereits vorgeschlagen hat die Ausnahme nicht auf der Ebene behandeln solltest wo sie jetzt in einen Wahrheitswert ”umgewandelt” wird. Ausnahmen wurde erfunden um solche Fehlerrückgabewerte loszuwerden, nun führst Du das da wieder ein.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Mein Code passt doch exakt zu dem, was Dein Code machen sollte:

Code: Alles auswählen

#!/usr/bin/env python3
from smb.SMBConnection import SMBConnection
from smb import smb_structs
from socket import gethostbyname
from time import sleep, strftime
from gpiozero import LED
from subprocess import run

USER = "pi"
PASSWORD = "123456789"
SERVER = "Raspi"           
FREIGABE = 'Toshiba'        
TESTDATEI = '/samba.txt'    

LED_PIN = 18   


def connection_status(status_led):
    try:
        server_ip = gethostbyname(SERVER)
        connection = SMBConnection(USER, PASSWORD, "name", SERVER, use_ntlm_v2=True)
        connection.connect(server_ip, 445)
        with open('/dev/null') as file:
            connection.retrieveFile(FREIGABE, TESTDATEI, file)
        connection.close()
        print("Samba-Server läuft...")
        status_led.on()
    except ConnectionRefusedError:
        print("Keine Verbindung zum Samba-Server.")
        status_led.off()
        run(['sudo', 'service', 'smbd', 'restart'])
        run(['sudo', 'service', 'nmbd', 'restart'])
        print("Samba-Server wird neu gestartet...")
    except smb_structs.OperationFailure:
        print("Testdatei nicht gefunden")
        status_led.blink()

def main():
    status_led = LED(LED_PIN)
    try:
        while True:
            connection_status(status_led)
            print("wait 10 sec...")
            sleep(10)
    except ConnectionResetError:
        print("Es ist kein Samba-Server installiert.")
        status_led.off()
    except KeyboardInterrupt:
        print("    Bye!")

if __name__ == "__main__":
    main()
Holzkopp
User
Beiträge: 12
Registriert: Samstag 24. Juli 2021, 20:49

Ah, jetzt verstehe ich was du meinst!

Zudem scheine ich es mir mit meinem Code unnötig kompliziert gemacht zu haben.


Wenn ich deinen Code exakt übernehme, bekomme ich folgende Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "samba_check.py", line 52, in <module>
    main()
  File "samba_check.py", line 42, in main
    connection_status(status_led)
  File "samba_check.py", line 24, in connection_status
    connection.retrieveFile(FREIGABE, TESTDATEI, file)
  File "/home/pi/.local/lib/python3.7/site-packages/smb/SMBConnection.py", line 321, in retrieveFile
    return self.retrieveFileFromOffset(service_name, path, file_obj, 0, -1, timeout)
  File "/home/pi/.local/lib/python3.7/site-packages/smb/SMBConnection.py", line 353, in retrieveFileFromOffset
    self._pollForNetBIOSPacket(timeout)
  File "/home/pi/.local/lib/python3.7/site-packages/smb/SMBConnection.py", line 642, in _pollForNetBIOSPacket
    self.feedData(data)
  File "/home/pi/.local/lib/python3.7/site-packages/nmb/base.py", line 54, in feedData
    self._processNMBSessionPacket(self.data_nmb)
  File "/home/pi/.local/lib/python3.7/site-packages/nmb/base.py", line 75, in _processNMBSessionPacket
    self.onNMBSessionMessage(packet.flags, packet.data)
  File "/home/pi/.local/lib/python3.7/site-packages/smb/base.py", line 144, in onNMBSessionMessage
    if self._updateState(self.smb_message):
  File "/home/pi/.local/lib/python3.7/site-packages/smb/base.py", line 338, in _updateState_SMB2
    req.callback(message, **req.kwargs)
  File "/home/pi/.local/lib/python3.7/site-packages/smb/base.py", line 987, in readCB
    file_obj.write(read_message.payload.data)
TypeError: write() argument must be str, not bytes
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Da muß man die Datei binär öffnen: `open('/dev/null', mode="wb")`
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Statt "/dev/null" könnte man auch die Konstante `os.devnull` verwenden.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Holzkopp
User
Beiträge: 12
Registriert: Samstag 24. Juli 2021, 20:49

Jawoll, so klappts.

Ich habe zum einfacheren Testen einmal die
except smb_structs.OperationFailure
in die main-funktion verschoben:

Code: Alles auswählen

def main():
    status_led = LED(LED_PIN)
    try:
        while True:
            connection_status(status_led)
            print("Wait 10 sec...")
            sleep(10)
    except smb_structs.OperationFailure:
        print("Testdatei nicht gefunden")
        status_led.blink()
    except ConnectionResetError:
        print("Es ist kein Samba-Server installiert.")
        status_led.off()
    except KeyboardInterrupt:
        print("    Bye!")
Lösche ich TESTDATEI auf der Festplatte, wird die Fehlermeldung ausgeben, dass "Testdatei nicht gefunden" werden kann. Das Skripte sollte jetzt eigentlich abbrechen. Stattdessen läuft es aber immer noch weiter. Wenn ich nämlich jetzt eine gleichnamige Datei neu erstelle, wird mir nun bestätigt, das alles einwandfrei funktioniert.

Wäre das Skript tatsächlich abgebrochen worden, gäbe es keine weitere Prüfung und entsprechend auch nicht diese Rückmeldung. Ich bin verwirrt.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie sieht denn nun das komplette Programm aus?
Holzkopp
User
Beiträge: 12
Registriert: Samstag 24. Juli 2021, 20:49

Jetzt hab ich es doch endlich hinbekommen!
Ich sollte beim Schreiben besser aufpassen, dann funktionierts auch.

Vielen Dank für eure Hilfe!
Antworten