sql >> Base de Datos >  >> RDS >> Oracle

cx_Oracle y manejo de excepciones:¿buenas prácticas?

Sin embargo, si no se puede conectar, entonces db no existirá más abajo, razón por la cual configuré db = None sobre. Sin embargo, ¿es esa una buena práctica?

No, configurando db = None no es la mejor práctica. Hay dos posibilidades, la conexión a la base de datos funcionará o no.

  • La conexión a la base de datos no funciona:

    Como la excepción generada se detectó y no se volvió a generar, continúe hasta llegar a cursor = db.Cursor() .

    db == None , entonces, una excepción que se parece a TypeError: 'NoneType' object has no attribute 'Cursor' será elevado. Como la excepción generada cuando falló la conexión a la base de datos ya se detectó, el motivo del fallo está oculto.

    Personalmente, siempre generaría una excepción de conexión a menos que vuelva a intentarlo en breve. Cómo lo atrapes depende de ti; si el error persiste, envío un correo electrónico para decir "ir y verificar la base de datos".

  • La conexión a la base de datos funciona:

    La variable db está asignado en su try:... except cuadra. Si el connect el método funciona entonces db se reemplaza con el objeto de conexión.

De cualquier manera, el valor inicial de db nunca se usa.

Sin embargo, he oído que usar el manejo de excepciones para el control de flujo como este es una mala práctica.

A diferencia de otros lenguajes, Python lo hace utilice el manejo de excepciones para el control de flujo. Al final de mi respuesta, he vinculado a varias preguntas sobre Desbordamiento de pila y Programadores que hacen una pregunta similar. En cada ejemplo, verá las palabras "pero en Python".

Eso no quiere decir que deba exagerar, pero Python generalmente usa el mantra EAFP, "Es más fácil pedir perdón que permiso". Los tres ejemplos más votados en ¿Cómo verifico si existe una variable? son buenos ejemplos de cómo puede usar el control de flujo o no.

¿Es una buena idea anidar excepciones? ¿O hay una mejor manera de lidiar con excepciones dependientes/en cascada como esta?

No hay nada de malo con las excepciones anidadas, una vez más, siempre y cuando lo hagas con cordura. Considere su código. Puede eliminar todas las excepciones y envolver todo en un try:... except cuadra. Si se genera una excepción, sabrá cuál fue, pero es un poco más difícil rastrear exactamente qué salió mal.

Entonces, ¿qué sucede si desea enviar un correo electrónico a usted mismo sobre la falla de cursor.execute? ? Debería tener una excepción alrededor de cursor.execute para realizar esta única tarea. Luego, vuelve a generar la excepción para que quede atrapada en su try:... . No volver a subir daría como resultado que su código continuara como si nada hubiera pasado y cualquier lógica que haya puesto en su try:... tratar con una excepción sería ignorado.

En última instancia, todas las excepciones se heredan de BaseException .

Además, hay algunas partes (por ejemplo, fallas de conexión) en las que me gustaría que la secuencia de comandos terminara, de ahí la llamada sys.exit() comentada.

Agregué una clase simple y cómo llamarla, que es más o menos cómo haría lo que está tratando de hacer. Si esto se va a ejecutar en segundo plano, la impresión de los errores no vale la pena:las personas no estarán sentadas allí buscando errores manualmente. Deben iniciar sesión sea cual sea su forma estándar y notificar a las personas adecuadas. Quité la impresión por este motivo y la reemplacé con un recordatorio para iniciar sesión.

Como he dividido la clase en múltiples funciones cuando connect el método falla y se genera una excepción execute la llamada no se ejecutará y el script finalizará después de intentar desconectarse.

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

Entonces llámalo:

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

Lectura adicional:

cx_Oracle documentación

¿Por qué no usar excepciones como flujo regular de control?
¿Es Python el manejo de excepciones más eficiente que PHP y/u otros lenguajes?
Argumentos a favor o en contra del uso de try catch como operadores lógicos