07 settembre, 2019

Gestire condizioni di errore


Forse è solo una cosa per noi vecchi, i giovani sono abituati a un ambiente integrato, guidato, dove questi errori non possono capitare. A meno che... Ecco parlo (solo un cenno) di quando un'elaborazione è composta di più programmi concatenati da eseguire in sequenza. E (giustamente) si automatizza.

Tento di farla semplice, la realtà può essere molto più articolata, i dati possono arrivare dal web e certe valutazioni temporizzate o quando si supera una soglia predefinita o altre condizioni ancora. Tutte cose che salto, mi concentro solo su un argomento semplicissimo: l'exit-code che ogni programma ritorna (o dovrebbe tornare).

Un esempio --il minimo che sono riuscito a pensare-- è eec.py, legge dati e qui può insorgere un errore e li elabora dove non tutto può essere come previsto:

#!/usr/bin/python3

from sys import argv

def leggi_input():
    global n1, n2
    try:
        n1 = int(argv[1])
        n2 = int(argv[2])
    except:
        print('Errore di input')
        exit(1)

def rapporto():
    global n1, n2
    print(n1, n2)
    try:
        return n1 / n2
    except:
        print('Errore nel rapporto')
        exit(1)

# main
leggi_input()
r = rapporto()
print(n1, n2, r)


Provandolo si ha:

$ py3 eec.py 10 3
10 3
10 3 3.3333333333333335
$ py3 eec.py 10
Errore di input
$ py3 eec.py 10 0
10 0
Errore nel rapporto

OK, in caso di errore exit interrompe l'esecuzione e setta l'exit code a 1. Se tutto va come previsto il codice ritornato è 0, a essere davvero puntiglioso occorrerebbe inserire come ultima istruzione exit(0), come si fa con il C, ma Python lo fa automaticamente:

Help on built-in function exit in module sys:

exit(...)
    exit([status])

    Exit the interpreter by raising SystemExit(status).
    If the status is omitted or None, it defaults to zero (i.e., success).
    If the status is an integer, it will be used as the system exit status.
    If it is another kind of object, it will be printed and the system
    exit status will be one (i.e., failure).


Una cosa che non sono riuscito a trovare è la differenza tra le due diverse funzioni exit() di Python: c'è quella predefinita, usata nell'esempio, un'altra nel modulo sys. L'help riportato sopra è quello ottenuto con help(sys.exit) ma vale anche per l'exit() predefinito.

Nota: se status è una stringa il codice di uscita è sempre 1, (file ecs.py):

#!/bin/bash

from sys import argv

ec = argv[1]
print('Esco con:', ec)
exit(ec)


ottengo:

$ py3 ecs.py 0
Esco con: 0
0
$ echo $?
1
$ py3 ecs.py 10
Esco con: 10
10
$ echo $?
1
$ py3 ecs.py orrore!
Esco con: orrore!
orrore!
$ echo $?
1

OK, resta da vedere il lato bash (o altro equivalente). La versione naïf non funziona (tec.sh):

#!/bin/bash

python3 eec.py $1
echo "exit code =" $?
echo "e adesso continuo..."


$ bash tec.sh 10 3
10 3
10 3 3.3333333333333335
exit code = 0
e adesso continuo...
$ bash tec.sh 10
Errore di input
exit code = 1
e adesso continuo...
$ bash tec.sh 10 0
10 0
Errore nel rapporto
exit code = 1
e adesso continuo...

La correzione richiesta è semplice (tecs.sh):

#!/bin/bash

set -e

python3 eec.py $1 $2
echo "exit code =" $?
echo "e adesso continuo..."


$ bash tecs.sh 10 3
10 3
10 3 3.3333333333333335
exit code = 0
e adesso continuo...
$ bash tecs.sh 10
Errore di input
$ bash tecs.sh 10 0
10 0
Errore nel rapporto

OK 🤩👌

C'è ancora un altro caso che finora non abbiamo risolto: la pipe, vero che c'è pipefail e PIPESTATUS ma, nonostante le googlate, niente finora 👿
🔴

Nessun commento:

Posta un commento