Exceptions

Laufzeit Fehler

Nachdem eine Logik erfolgreich geladen wurde, können bei der Ausführung der Logik Laufzeit Fehler auftreten. Falls ein Laufzeitfehler nicht innerhalb der Logik abgefangen wird, führt er zur Beendigung des aktuellen Laufes der Logik und es wird ein entsprechender Fehler geloggt. Die Logik bleibt jedoch geladen und wird, wenn sie getriggert wird, erneut ausgeführt.

Beispiel: Folgende Test Logik

#!/usr/bin/env python3
# test_logic.py

logger.warning("Start der Logik")

var = int('abc')

logger.warning("Ende der Logik")

erzeugt zur Laufzeit folgenden Logeintrag:

2022-01-24  13:55:14 ERROR    logics.test_logic    In der Logik ist ein Fehler aufgetreten:
   Logik 'test_logic', Datei '/usr/local/shng_dev/logics/test_logic.py', Zeile 6
   Hauptroutine der Logik, Exception: invalid literal for int() with base 10: 'abc'

Exception LeaveLogic

Normalerweise wird der aktuelle Lauf einer Logik dadurch beendet, dass das Ende des Codes der Hauptroutine erreicht wird.

In einigen Fällen kann es jedoch sinnvoll sein, die Logik mitten aus dem Code heraus oder as einer Funktion heraus zu beenden/zu verlassen.

Dafür kann eine spezielle Exception ausgelöst werden. Das geschieht mit dem Befehl raise LeaveLogic():

...

if logik_beenden:
   raise LeaveLogic()

...

Optional kann der Exception ein Grund als Parameter mitgegeben werden.

...

if logik_beenden:
   raise LeaveLogic('Die Logik sollte beendet werden')

...

Falls ein Grund angegeben wird, erfolgt ein Logeintrag mit dem Level INFO, der das Verlassen und diesen Grund loggt.

Exception TaskCancelled

Wird der Abbruch einer laufenden Logik angefordert, über die Threads-Seite der Admin GUI oder automatisch über den Parameter runtime_cancel, löst SmartHomeNG in der Logik die Exception TaskCancelled aus. Sie beendet den aktuellen Lauf der Logik. Die Logik bleibt geladen und wird beim nächsten Trigger erneut ausgeführt.

TaskCancelled ist von BaseException abgeleitet, nicht von Exception. Ein except Exception: in der Logik fängt sie deshalb nicht ab. Ein except: ohne Exception-Typ fängt sie dagegen ab und verhindert so den Abbruch.

Muss eine Logik beim Abbruch aufräumen (z.B. eine Verbindung schließen oder ein Item zurücksetzen), geht das mit try/finally. Der finally-Block läuft beim Abbruch genauso wie beim normalen Ende:

verbindung = oeffne_verbindung()
try:
    while not fertig():
        arbeite(verbindung)
finally:
    verbindung.close()

Soll die Logik speziell auf den Abbruch reagieren, kann sie TaskCancelled abfangen. Danach sollte sie die Exception erneut auslösen, damit der Lauf auch wirklich endet:

try:
    ...
except TaskCancelled:
    sh.mein.status('abgebrochen')
    raise

Die Exception wird ausgelöst, sobald die Logik wieder Python-Code ausführt: in einer Rechenschleife sofort, in einem blockierenden Aufruf (time.sleep(), Event.wait(), Netzwerkzugriff, subprocess) erst, wenn dieser zurückkehrt.

Ein abgebrochener Lauf wird mit der Stelle, an der er unterbrochen wurde, als Warnung in das Log der Logik geschrieben. Der Zeitpunkt des letzten Laufs (logic.last_run()) wird für einen abgebrochenen Lauf nicht aktualisiert.