Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

cx_Oracle e Tratamento de Exceções - Boas práticas?


No entanto, se não puder se conectar, db não existirá mais abaixo - e é por isso que defino db = None acima de. No entanto, isso é uma boa prática?

Não, configurando db = None não é a melhor prática. Existem duas possibilidades, conectar-se ao banco de dados funcionará ou não.

  • A conexão com o banco de dados não funciona:

    Como a exceção levantada foi capturada e não re-aumentada, você continua até chegar a cursor = db.Cursor() .

    db == None , portanto, uma exceção que se assemelha a TypeError: 'NoneType' object has no attribute 'Cursor' será levantada. Como a exceção gerada quando a conexão com o banco de dados falhou já foi capturada, o motivo da falha é disfarçado.

    Pessoalmente, eu sempre levantaria uma exceção de conexão, a menos que você tente novamente em breve. Como você vai pegá-lo depende de você; se o erro persistir, mando um e-mail para dizer "vá e verifique o banco de dados".

  • A conexão com o banco de dados funciona:

    A variável db é atribuído em seu try:... except quadra. Se o connect método funciona então db é substituído pelo objeto de conexão.

De qualquer forma, o valor inicial de db nunca é usado.

No entanto, ouvi dizer que usar tratamento de exceção para controle de fluxo como isso é uma prática ruim.

Ao contrário de outras linguagens, o Python faz use tratamento de exceção para controle de fluxo. No final da minha resposta, vinculei várias perguntas sobre o Stack Overflow e os programadores que fazem uma pergunta semelhante. Em todos os exemplos, você verá as palavras "mas em Python".

Isso não quer dizer que você deva exagerar, mas o Python geralmente usa o mantra EAFP, "É mais fácil pedir perdão do que permissão." Os três principais exemplos votados em Como verifico se uma variável existe? são bons exemplos de como você pode usar o controle de fluxo ou não.

Aninhar exceções é uma boa ideia? Ou existe uma maneira melhor de lidar com exceções dependentes/em cascata como essa?

Não há nada de errado com exceções aninhadas, mais uma vez, desde que você faça isso de forma sensata. Considere o seu código. Você pode remover todas as exceções e envolver tudo em um try:... except quadra. Se uma exceção for levantada, você saberá o que foi, mas é um pouco mais difícil rastrear exatamente o que deu errado.

O que acontece se você quiser enviar um e-mail sobre a falha do cursor.execute ? Você deve ter uma exceção em torno de cursor.execute para realizar esta tarefa. Você então re-aumenta a exceção para que ela seja capturada em seu try:... externo . Não re-aumentar resultaria em seu código continuando como se nada tivesse acontecido e qualquer lógica que você tivesse colocado em seu try:... externo lidar com uma exceção seria ignorado.

Em última análise, todas as exceções são herdadas de BaseException .

Além disso, existem algumas partes (por exemplo, falhas de conexão) em que eu gostaria que o script terminasse - daí a chamada sys.exit() comentada.

Eu adicionei uma classe simples e como chamá-la, que é mais ou menos como eu faria o que você está tentando fazer. Se isso for executado em segundo plano, a impressão dos erros não vale a pena - as pessoas não ficarão sentadas manualmente procurando erros. Eles devem ser registrados em qualquer que seja sua maneira padrão e as pessoas apropriadas notificadas. Eu removi a impressão por esse motivo e a substituí por um lembrete para registrar.

Como eu dividi a classe em várias funções quando o connect O método falha e uma exceção é gerada a execute chamada não será executada e o script será concluído, após tentar desconectar.
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()

Então chame-o:
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()

Leitura adicional:

cx_Oracle documentação

Por que não usar exceções como fluxo regular de controle?
O tratamento de exceções python é mais eficiente que PHP e/ou outras linguagens?
Argumentos a favor ou contra o uso de try catch como operadores lógicos