Oracle
 sql >> Base de données >  >> RDS >> Oracle

cx_Oracle et gestion des exceptions - Bonnes pratiques ?

Cependant, s'il ne peut pas se connecter, alors db n'existera pas plus bas - c'est pourquoi j'ai défini db = None au dessus de. Cependant, est-ce une bonne pratique ?

Non, réglage db = None n'est pas la meilleure pratique. Il y a deux possibilités, soit la connexion à la base de données fonctionnera, soit elle ne fonctionnera pas.

  • La connexion à la base de données ne fonctionne pas :

    Comme l'exception déclenchée a été interceptée et non relancée, vous continuez jusqu'à ce que vous atteigniez cursor = db.Cursor() .

    db == None , donc, une exception qui ressemble à TypeError: 'NoneType' object has no attribute 'Cursor' sera relevé. Comme l'exception générée lors de l'échec de la connexion à la base de données a déjà été interceptée, la raison de l'échec est déguisée.

    Personnellement, je déclencherais toujours une exception de connexion à moins que vous ne réessayiez sous peu. Comment vous l'attrapez dépend de vous; si l'erreur persiste, j'envoie un e-mail pour dire "allez vérifier la base de données".

  • La connexion à la base de données fonctionne :

    La variable db est assigné dans votre try:... except bloc. Si le connect la méthode fonctionne alors db est remplacé par l'objet de connexion.

Dans tous les cas, la valeur initiale de db n'est jamais utilisé.

Cependant, j'ai entendu dire que l'utilisation de la gestion des exceptions pour le contrôle de flux comme ceci est une mauvaise pratique.

Contrairement à d'autres langages, Python fait utiliser la gestion des exceptions pour le contrôle de flux. À la fin de ma réponse, j'ai lié plusieurs questions sur Stack Overflow et les programmeurs qui posent une question similaire. Dans chaque exemple, vous verrez les mots "mais en Python".

Cela ne veut pas dire que vous devriez aller trop loin, mais Python utilise généralement le mantra EAFP, "Il est plus facile de demander pardon que la permission." Les trois exemples les plus votés dans Comment vérifier si une variable existe ? sont de bons exemples de la façon dont vous pouvez utiliser ou non le contrôle de flux.

L'imbrication des exceptions est-elle une bonne idée ? Ou existe-t-il un meilleur moyen de gérer les exceptions dépendantes/en cascade comme celle-ci ?

Il n'y a rien de mal avec les exceptions imbriquées, encore une fois tant que vous le faites sainement. Considérez votre code. Vous pouvez supprimer toutes les exceptions et envelopper le tout dans un try:... except bloc. Si une exception est déclenchée, vous savez de quoi il s'agit, mais il est un peu plus difficile de déterminer exactement ce qui n'a pas fonctionné.

Que se passe-t-il alors si vous voulez dire vous-même un e-mail en cas d'échec de cursor.execute ? Vous devriez avoir une exception autour de cursor.execute afin d'accomplir cette seule tâche. Vous relancez ensuite l'exception pour qu'elle soit prise dans votre try:... . Ne pas relancer entraînerait la poursuite de votre code comme si de rien n'était et quelle que soit la logique que vous aviez mise dans votre try:... externe traiter une exception serait ignoré.

En fin de compte, toutes les exceptions sont héritées de BaseException .

De plus, il y a certaines parties (par exemple, les échecs de connexion) où j'aimerais que le script se termine simplement - d'où l'appel sys.exit() commenté.

J'ai ajouté une classe simple et comment l'appeler, ce qui correspond à peu près à la façon dont je ferais ce que vous essayez de faire. Si cela doit être exécuté en arrière-plan, l'impression des erreurs n'en vaut pas la peine - les gens ne seront pas assis là à rechercher manuellement les erreurs. Ils doivent être connectés quelle que soit votre méthode habituelle et les personnes appropriées doivent être informées. J'ai supprimé l'impression pour cette raison et l'ai remplacée par un rappel de connexion.

Comme j'ai divisé la classe en plusieurs fonctions lorsque le connect la méthode échoue et une exception est levée le execute l'appel ne sera pas exécuté et le script se terminera après avoir tenté de se déconnecter.

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()

Alors appelez-le :

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()

Lectures complémentaires :

cx_Oracle documents

Pourquoi ne pas utiliser les exceptions comme flux de contrôle régulier ?
La gestion des exceptions en python est-elle plus efficace que PHP et/ou d'autres langages ?
Arguments pour ou contre l'utilisation de try catch comme opérateurs logiques