Oracle
 sql >> Database >  >> RDS >> Oracle

cx_Oracle e gestione delle eccezioni - Buone pratiche?

Tuttavia, se non riesce a connettersi, db non esisterà più in basso, motivo per cui ho impostato db = None sopra. Tuttavia, è una buona pratica?

No, impostando db = None non è la migliore pratica. Ci sono due possibilità, la connessione al database funzionerà o no.

  • La connessione al database non funziona:

    Poiché l'eccezione sollevata è stata catturata e non rilanciata, continui fino a raggiungere cursor = db.Cursor() .

    db == None , quindi, un'eccezione simile a TypeError: 'NoneType' object has no attribute 'Cursor' sarà sollevato. Poiché l'eccezione generata quando la connessione al database non è riuscita è già stata rilevata, il motivo dell'errore viene mascherato.

    Personalmente, solleverò sempre un'eccezione di connessione a meno che tu non voglia riprovare a breve. Come lo prendi dipende da te; se l'errore persiste scrivo un'e-mail per dire "vai a controllare il database".

  • La connessione al database funziona:

    La variabile db è assegnato nel tuo try:... except bloccare. Se il connect il metodo funziona quindi db viene sostituito con l'oggetto connessione.

In entrambi i casi il valore iniziale di db non viene mai utilizzato.

Tuttavia, ho sentito dire che l'utilizzo della gestione delle eccezioni per il controllo del flusso in questo modo è una cattiva pratica.

A differenza di altri linguaggi, Python fa utilizzare la gestione delle eccezioni per il controllo del flusso. Alla fine della mia risposta ho collegato diverse domande su Stack Overflow e Programmers che fanno una domanda simile. In ogni esempio vedrai le parole "ma in Python".

Questo non vuol dire che dovresti esagerare, ma Python generalmente usa il mantra EAFP, "È più facile chiedere perdono che permesso." I primi tre esempi votati in Come posso verificare se esiste una variabile? sono buoni esempi di come sia possibile utilizzare il controllo del flusso o meno.

È una buona idea nidificare le eccezioni? O c'è un modo migliore per gestire le eccezioni dipendenti/a cascata come questo?

Non c'è niente di sbagliato nelle eccezioni nidificate, ancora una volta, purché tu lo faccia in modo sano. Considera il tuo codice. Puoi rimuovere tutte le eccezioni e racchiudere l'intera cosa in un try:... except bloccare. Se viene sollevata un'eccezione, allora sai di cosa si tratta, ma è un po' più difficile rintracciare esattamente cosa è andato storto.

Cosa succede quindi se vuoi dire a te stesso un'e-mail sull'errore di cursor.execute ? Dovresti avere un'eccezione intorno a cursor.execute per svolgere questo compito. Quindi solleva nuovamente l'eccezione in modo che venga catturata nel tuo try:... esterno . Non rilanciare comporterebbe la continuazione del codice come se nulla fosse accaduto e qualunque logica avessi inserito nel tuo try:... esterno gestire un'eccezione verrebbe ignorato.

In definitiva tutte le eccezioni vengono ereditate da BaseException .

Inoltre, ci sono alcune parti (ad esempio errori di connessione) in cui vorrei che lo script terminasse, da qui la chiamata sys.exit() commentata.

Ho aggiunto una classe semplice e come chiamarla, che è più o meno come farei quello che stai cercando di fare. Se questo verrà eseguito in background, la stampa degli errori non vale la pena:le persone non staranno lì a cercare manualmente gli errori. Dovrebbero essere registrati in qualsiasi modo sia il tuo modo standard e le persone appropriate devono essere avvisate. Per questo motivo ho rimosso la stampa e l'ho sostituita con un promemoria per accedere.

Dato che ho diviso la classe in più funzioni quando connect il metodo fallisce e viene sollevata un'eccezione execute la chiamata non verrà eseguita e lo script terminerà, dopo aver tentato di disconnettersi.

import cx_Oracle

class Oracle(object):

    def connect(self, username, password, hostname, port, servicename):
        """ Connect to the database. """

        try:
            self.db = cx_Oracle.connect(username, password
                                , hostname + ':' + port + '/' + servicename)
        except cx_Oracle.DatabaseError as e:
            # Log error as appropriate
            raise

        # If the database connection succeeded create the cursor
        # we-re going to use.
        self.cursor = self.db.cursor()

    def disconnect(self):
        """
        Disconnect from the database. If this fails, for instance
        if the connection instance doesn't exist, ignore the exception.
        """

        try:
            self.cursor.close()
            self.db.close()
        except cx_Oracle.DatabaseError:
            pass

    def execute(self, sql, bindvars=None, commit=False):
        """
        Execute whatever SQL statements are passed to the method;
        commit if specified. Do not specify fetchall() in here as
        the SQL statement may not be a select.
        bindvars is a dictionary of variables you pass to execute.
        """

        try:
            self.cursor.execute(sql, bindvars)
        except cx_Oracle.DatabaseError as e:
            # Log error as appropriate
            raise

        # Only commit if it-s necessary.
        if commit:
            self.db.commit()

Quindi chiamalo:

if __name__ == "__main__":

    oracle = Oracle.connect('username', 'password', 'hostname'
                           , 'port', 'servicename')

    try:
        # No commit as you don-t need to commit DDL.
        oracle.execute('ddl_statements')

    # Ensure that we always disconnect from the database to avoid
    # ORA-00018: Maximum number of sessions exceeded. 
    finally:
        oracle.disconnect()

Ulteriori letture:

cx_Oracle documentazione

Perché non utilizzare le eccezioni come flusso di controllo regolare?
La gestione delle eccezioni Python è più efficiente di PHP e/o di altri linguaggi?
Argomenti a favore o contro l'utilizzo di try catch come operatori logici