In limbajul Python, erorile sunt impartite in 2 categorii principale:

- erori de sintaxa
- erori de executie denumite si exceptii.

Erorile de sintaxa sunt cauzate de nerespectarea sintaxei limbajului Python si atunci cand apar, trebuie corectate de catre programator.

Erorile de executie (exceptiile) sunt cauzate de modul in care este scris codul Python, reprezentand erori de programare. Unele dintre aceste tipuri de erori trebuie tratate automat de catre program. In cazul in care programul nu este capabil sa trateze erorile de executie, executia programului respectiv se intrerupe si un mesaj de eroare, care indica tipul erorii, este lansat.

In [1]:

    # Exemplul 1: generarea unei exceptii = eroare de executie la impartirea cu 0
    x = 10
    y = x/0    # impartirea la 0 genereaza o eroare de executie
               # de tipul ZeroDivisionError

    print(y)

    ---------------------------------------------------------------------------
    ZeroDivisionError                         Traceback (most recent call last)
    <ipython-input-1-e885390d4e9a> in <module>
          1 # Exemplul 1: generarea unei exceptii = eroare de executie la impartirea cu 0
          2 x = 10
    ----> 3 y = x/0    # impartirea la 0 genereaza o eroare de executie
          4            # de tipul ZeroDivisionError
          5

    ZeroDivisionError: division by zero

In [2]:

    # Exemplul 2: generarea unei exceptii = eroare de executie la conversia unei valori nepermise intr-un tip specificat
    x = 'zece'
    y = int(x)    # fortarea conversiei unei valori nepermise
                  # genereaza o eroare de executie de tipul ValueError
    print(y)

    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-2-adc732b8f1ff> in <module>
          1 # Exemplul 2: generarea unei exceptii = eroare de executie la conversia unei valori nepermise intr-un tip specificat
          2 x = 'zece'
    ----> 3 y = int(x)    # fortarea conversiei unei valori nepermise
          4               # genereaza o eroare de executie de tipul ValueError
          5 print(y)

    ValueError: invalid literal for int() with base 10: 'zece'

# 1. Clauza `try - except`¶

Pentru tratarea exceptiilor, in Python este definita clauza `try-except`.
Sintaxa:

    try:
        segment_cod
    except tip_exceptie:
        segment_cod_tratare_exceptie

Modul in care este executat codul intr-o clauza `try-except` este urmatorul:

- in cazul in care codul inclus in segmentul de cod subordonat clauzei `try` nu genereaza exceptii, atunci acesta este executat, dupa care se trece la prima instructiune plasata in codul sursa dupa clauza `try-except`;
- in cazul in care codul inclus in segmentul de cod subordonat clauzei `try` genereaza exceptii, atunci acesta nu este executat; in locul acestuia este executat segmentul de cod care descrie modul in care este tratata exceptia depistata, dupa care se trece la prima instructiune plasata in codul sursa dupa clauza `try-except`;

Utilizand clauza `try-except`, fluxul de executie al codului nu este intrerupt, chiar daca o exceptie este generata.

In [3]:

    # Exemplul 3: exemplu de tratare a unei exceptii de tipul ZeroDivisionError

    x = 10
    y = 0

    try:
        y = x/0
    except ZeroDivisionError:
        print('Illegal operation: division by 0.')

    print(y)

    Illegal operation: division by 0.
    0

#### Tratarea tuturor exceptiilor¶

In sintaxa prezentata mai sus, informatia din campul tip_exceptie este optionala.

Daca informatia din campul tip_exceptie este omisa, atunci oricare exceptie lansata de catre segmentul de cod subordonat clauzei `try` va fi tratata, dar, in acest caz, toate exceptiile generate vor fi tratate in mod identic.

In [4]:

    # Exemplul 4: exemplu de tratare a tuturor exceptiilor

    x = 10
    y = 0

    try:
        d = int(input('Enter your number: '))    # genereaza o eroare ValueError daca d = doi, de exemplu
        y = x/d
    except:
        print('An unexpected error has occured. Exit from your program.')
                                                # prin omiterea tipului de exceptie,
                                                # toate exceptiile lansate se vor trata;
                                                # modul de tratare al exceptiilor este identic
    print(y)

    Enter your number: doi
    An unexpected error has occured. Exit from your program.
    0

#### Tratarea exceptiilor multiple¶

Atunci cand in campul tip_exceptie este specificat un anumit tip de exceptie, segmentul de cod care descrie modul de tratare al execeptiei este executat numai in cazul in care, exceptia generata de catre codul subordonat clauzei `try` genereaza o exceptie de tipul specificat in campul tip_exceptie.
In cazul in care, codul inclus in segmentul de cod subordonat clauzei `try` genereaza o exceptie diferita de tipul specificat in campul tip_exceptie, atunci segmentul de cod care descrie modul in care este tratata exceptia nu este executat (exceptia respectiva nu este tratata), iar programul este intrerupt.

In [5]:

    # Exemplul 5: exemplu de tratare a unei exceptii de tipul ZeroDivisionError, dar nu si ValueError

    x = 10
    y = 0

    try:
        d = int(input('Enter your number:'))    # genereaza o eroare ValueError daca d = doi, de exemplu
        y = x/d
    except ZeroDivisionError:
        print('Illegal operation: division by 0. Try again.')

    print(y)

    Enter your number:trei

    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-5-37a41eb5d41a> in <module>
          5
          6 try:
    ----> 7     d = int(input('Enter your number:'))    # genereaza o eroare ValueError daca d = doi, de exemplu
          8     y = x/d
          9 except ZeroDivisionError:

    ValueError: invalid literal for int() with base 10: 'trei'

In cazul in care un cod poate genera mai multe tipuri de exceptii si fiecare dintre acestea necesita un tratament diferit, acestea se pot trata intr-o clauza `try-except` care are sintaxa urmatoare:

    try:
        segment_cod
    except tip_exceptie_1:
        segment_cod_tratare_exceptie_1
    except tip_exceptie_2:
        segment_cod_tratare_exceptie_2
    #...
    except tip_exceptie_n:
        segment_cod_tratare_exceptie_n

In varianta de mai sus a clauzei `try-except`, tratarea exceptiei specificate in campul tip_exceptie_1 are prioritate maxima. Acest lucru inseamna ca, in cazul in care segmentul de cod subordonat clauzei `try` lanseaza mai multe exceptii printre care si cea specificata in campul tip_exceptie_1, exceptia care va fi tratata va fi cea specificata in campul tip_exceptie_1.
In continuare, tratarea exceptiei specificate in campul tip_exceptie_2 are prioritate imediat urmatoare dupa cea specificata in campul tip_exceptie_1. Acest lucru inseamna ca, in cazul in care segmentul de cod subordonat clauzei `try` lanseaza mai multe exceptii printre care si cea specificata in campul tip_exceptie_2, dar nu si cea specificata in campul tip_exceptie_1, exceptia care va fi tratata va fi cea specificata in campul tip_exceptie_2.
Aceasta abordare in tratarea exceptiilor se continua pentru toate exceptiile specificate in campurile tipexceptie corespunzatoare. In final, tratarea exceptiei specificate in campul tip_exceptie_n are prioritatea cea mai mica.

Tipurile de exceptii definite in limbajul Python respecta o anumita ierarhie, care au ca lider tipul cel mai general de exceptie, denumita `Exception`, din care sunt apoi derivate tipuri de exceptii mai specifice, etc. In precizarea ordinii prioritatilor exceptiilor tratate in clauza `try-except`, prioritatea maxima trebuie atribuita exceptiilor celor mai specifice, iar prioritatea minima exceptiilor celor mai generale. Se recomanda astfel ca secventa de exceptii tratate in clauza `try-except` sa se finalizeze mereu cu tipul `Exception`, deoarece acest tip este cel mai general.

In [6]:

    # Exemplul 6: exemplu de tratare a unor exceptii multiple
    import sys

    x = 10
    y = 0

    try:
        d = int(input('Enter your number: '))    # genereaza o eroare ValueError daca d = doi, de exemplu
        y = x/d
    except ValueError:
        print('Invalid input data. Try again.')
    except ZeroDivisionError:
        print('Illegal operation: division by 0. Exit from your program.')
        sys.exit()
    except Exception:
        print('An unexpected error has occured. Exit from your program.')
        sys.exit()

    print(y)

    Enter your number: patru
    Invalid input data. Try again.
    0

# 2. Clauza `finally`¶

Clauza `try` poate contine in componenta sa si clauza `finally`, care este executata automat, la iesirea din clauza `try`, indiferent daca segmentul de cod subordonat clauzei `try` lanseaza sau nu o exceptie. Clauza `finally` este de obicei utilizata pentru atunci cand este necesara o rutina care gestioneaza eliberarea resurselor sistemului, atunci cand la executia segmentului de cod subordonat clauzei `try`, acestea au fost utilizate.

    try:
        segment_cod
    except tip_exceptie_1:
        segment_cod_tratare_exceptie_1
    #...
    except tip_exceptie_n:
        segment_cod_tratare_exceptie_n
    finally:
        segment_cod_iesire_try

In [7]:

    # Exemplul 7: exemplu de utilizare a clauzei finally
    import sys

    x = 10
    y = 0

    try:
        d = int(input('Enter your number: '))    # genereaza o eroare ValueError daca d = doi, de exemplu
        y = x/d
    except Exception:
        print('An unexpected error has occured. Exit from your program.')
        sys.exit()
    finally:
        print('Cleaning-up the system resources.')

    print(y)

    Enter your number: cinci

    ERROR:root:Internal Python error in the inspect module.
    Below is the traceback from this internal error.

    An unexpected error has occured. Exit from your program.
    Cleaning-up the system resources.
    Traceback (most recent call last):
      File "<ipython-input-7-286c0b7a9dd4>", line 8, in <module>
        d = int(input('Enter your number: '))    # genereaza o eroare ValueError daca d = doi, de exemplu
    ValueError: invalid literal for int() with base 10: 'cinci'

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "C:\Users\user\anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 3331, in run_code
        exec(code_obj, self.user_global_ns, self.user_ns)
      File "<ipython-input-7-286c0b7a9dd4>", line 12, in <module>
        sys.exit()
    SystemExit

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "C:\Users\user\anaconda3\lib\site-packages\IPython\core\ultratb.py", line 1151, in get_records
        return _fixed_getinnerframes(etb, number_of_lines_of_context, tb_offset)
      File "C:\Users\user\anaconda3\lib\site-packages\IPython\core\ultratb.py", line 319, in wrapped
        return f(*args, **kwargs)
      File "C:\Users\user\anaconda3\lib\site-packages\IPython\core\ultratb.py", line 353, in _fixed_getinnerframes
        records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
      File "C:\Users\user\anaconda3\lib\inspect.py", line 1502, in getinnerframes
        frameinfo = (tb.tb_frame,) + getframeinfo(tb, context)
    AttributeError: 'tuple' object has no attribute 'tb_frame'

    An exception has occurred, use %tb to see the full traceback.

    ---------------------------------------------------------------------------

    During handling of the above exception, another exception occurred:

    SystemExit

    C:\Users\user\anaconda3\lib\site-packages\IPython\core\interactiveshell.py:3339: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.
      warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)

In versiunile recente ale limbajului Python, cum ar fi Python 3.x, utilizarea clauzei `finally` poate fi evitata, daca, in segmentul de cod subordonat clauzei `try`, resursele sistemului sunt accesate prin intermediul specificatiei `with`, care, dupa terminarea executie clauzei `try` elibereaza automat resursele sistemului care au fost utilizate.

# 3. Lansarea unei exceptii¶

Uneori, este necesar ca la identificarea unor situatii care pot genera erori, programul sa lanseze exceptii. De exemplu, in cazul in care la apelul unei functii acesteia ii sunt furnizate argumente care nu satisfac anumite criterii de siguranta, este util ca functia respectiva sa lanseze exceptii prin care atentioneaza programul apelant ca este necesar sa trateze exceptia respectiva.

Sintaxa pentru lansarea unei exeptii este:

    raise nume_exceptie ('mesaj de eroare')

Specificatia `raise` creeaza un obiect de tipul exceptie, care poate fi in continuare tratat de catre program. In cazul in care exceptia respectiva nu este tratata, programul isi opreste executia.

In [8]:

    # Exemplul 8: exemplu de functie care lanseaza o exceptie

    def my_division(a, b):
        if b == 0:
            raise ZeroDivisionError ('ERROR: Illegal operation = divide by zero.')
        else:
            return a/b

    x = 10
    y = 0

    try:
        d = int(input('Enter your number: '))    # genereaza o eroare ValueError daca d = doi, de exemplu
        y = my_division(x,d)
    except ValueError:
        print('Invalid input data. Try again.')
    except ZeroDivisionError:
        print('Illegal operation: division by 0. Try again.')
    except Exception:
        print('An unexpected error has occured. Exit from your program.')
        sys.exit()

    print(y)

    Enter your number: 0
    Illegal operation: division by 0. Try again.
    0