Subir (lanzar) manualmente una excepción en Python


¿Cómo puedo plantear una excepción en Python para que pueda ser capturada más tarde a través de un bloque except?

Author: DavidRR, 2010-01-13

5 answers

¿Cómo puedo lanzar/subir manualmente una excepción en Python?

Utilice el constructor de excepción más específico que semánticamente se ajuste a su problema .

Sea específico en su mensaje, por ejemplo:

raise ValueError('A very specific bad thing happened.')

No levante excepciones genéricas

Evite plantear una excepción genérica. Para atraparlo, tendrás que atrapar todas las demás excepciones más específicas que la subclase.

Problema 1: Ocultar errores

raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.

Para ejemplo:

def demo_bad_catch():
    try:
        raise ValueError('Represents a hidden bug, do not catch this')
        raise Exception('This is the exception you expect to handle')
    except Exception as error:
        print('Caught this error: ' + repr(error))

>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)

Problema 2: No atrapará

Y capturas más específicas no atraparán la excepción general:

def demo_no_catch():
    try:
        raise Exception('general exceptions not caught by specific handling')
    except ValueError as e:
        print('we will not catch exception: Exception')


>>> demo_no_catch()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling

Mejores prácticas: raise declaración

En su lugar, use el constructor de excepción más específico que semánticamente se ajuste a su problema.

raise ValueError('A very specific bad thing happened')

Que también permite fácilmente pasar un número arbitrario de argumentos al constructor:

raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz') 

Estos argumentos son accedidos por el atributo args en la Excepción objeto. Por ejemplo:

try:
    some_code_that_may_raise_our_value_error()
except ValueError as err:
    print(err.args)

Impresiones

('message', 'foo', 'bar', 'baz')    

En Python 2.5, se agregó un atributo message real a BaseException en favor de alentar a los usuarios a subclase Excepciones y dejar de usar args, pero la introducción de message y la obsolescencia original de args se ha retraído.

Mejores Prácticas: except cláusula

Cuando está dentro de una cláusula except, es posible que desee, por ejemplo, registrar que ocurrió un tipo específico de error y luego volver a subir. El la mejor manera de hacer esto mientras se preserva el rastro de pila es utilizar una declaración bare raise. Por ejemplo:

logger = logging.getLogger(__name__)

try:
    do_something_in_app_that_breaks_easily()
except AppError as error:
    logger.error(error)
    raise                 # just this!
    # raise AppError      # Don't do this, you'll lose the stack trace!

No modifique sus errores... pero si insistes.

Puede conservar la traza apilada (y el valor de error) con sys.exc_info(), pero esto es mucho más propenso a errores y tiene problemas de compatibilidad entre Python 2 y 3, prefiere usar un raise desnudo para volver a subir.

Para explicar - el sys.exc_info() devuelve el tipo, el valor y el seguimiento.

type, value, traceback = sys.exc_info()

Esta es la sintaxis en Python 2-nota esto no es compatible con Python 3:

    raise AppError, error, sys.exc_info()[2] # avoid this.
    # Equivalently, as error *is* the second object:
    raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

Si lo desea, puede modificar lo que sucede con su nueva subida, por ejemplo, estableciendo nuevos argumentos para la instancia:

def error():
    raise ValueError('oops!')

def catch_error_modify_message():
    try:
        error()
    except ValueError:
        error_type, error_instance, traceback = sys.exc_info()
        error_instance.args = (error_instance.args[0] + ' <modification>',)
        raise error_type, error_instance, traceback

Y hemos conservado todo el rastreo mientras modificamos los args. Tenga en cuenta que esto es no es una buena práctica y es sintaxis inválida en Python 3 (haciendo que mantener la compatibilidad sea mucho más difícil de evitar).

>>> catch_error_modify_message()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in catch_error_modify_message
  File "<stdin>", line 2, in error
ValueError: oops! <modification>

En Python 3:

    raise error.with_traceback(sys.exc_info()[2])

De nuevo: evite manipular manualmente los rastreos. Es menos eficiente y más propenso a errores. Y si estás usando threading y sys.exc_info incluso puedes obtener el rastreo incorrecto (especialmente si estás usando el manejo de excepciones para el flujo de control, lo que personalmente tiendo a evitar.)

Python 3, Encadenamiento de excepciones

En Python 3, puede encadenar excepciones, que conservan rastreos:

    raise RuntimeError('specific message') from error

Be aware:

  • esto hace permite cambiar el tipo de error planteado, y
  • esto es no compatible con Python 2.

Métodos obsoletos:

Estos pueden ocultarse fácilmente e incluso entrar en el código de producción. Usted quiere levantar una excepción, y al hacerlas se levantará una excepción, ¡pero no la prevista!

Válido en Python 2, pero no en Python 3 es el siguiente:

raise ValueError, 'message' # Don't do this, it's deprecated!

Únicamente válido en versiones mucho más antiguas de Python (2.4 y inferiores), es posible que todavía vea a personas elevando cadenas:

raise 'message' # really really wrong. don't do this.

En todas las versiones modernas, esto realmente levantará un TypeError, porque no estás levantando un tipo BaseException. Si no está comprobando la excepción correcta y no tiene un revisor que esté al tanto del problema, podría entrar en producción.

Ejemplo de uso

Presento excepciones para advertir a los consumidores de mi API si la están usando incorrectamente:

def api_func(foo):
    '''foo should be either 'baz' or 'bar'. returns something very useful.'''
    if foo not in _ALLOWED_ARGS:
        raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))

Cree sus propios tipos de error cuando corresponda

"Quiero cometer un error a propósito, para que entrara en la excepción"

Puede crear sus propios tipos de error, si desea indicar que algo específico está mal con su aplicación, solo subclase el punto apropiado en la jerarquía de excepciones:

class MyAppLookupError(LookupError):
    '''raise this when there's a lookup error for my app'''

Y uso:

if important_key not in resource_dict and not ok_to_be_missing:
    raise MyAppLookupError('resource is missing, and that is not ok.')
 2012
Author: Aaron Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-28 20:07:54

NO HAGAS ESTO . Levantar un desnudo Exception es absolutamente no lo correcto; ver la excelente respuesta de Aaron Hall en su lugar.

No se puede conseguir mucho más pitónico que esto:

raise Exception("I know python!")

Consulte la instrucción raise docs para python si desea más información.

 536
Author: Gabriel Hurley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:39

Para el caso común en el que necesita lanzar una excepción en respuesta a algunas condiciones inesperadas, y que nunca tiene la intención de atrapar, sino simplemente fallar rápidamente para permitirle depurar desde allí si alguna vez sucede, la más lógica parece ser AssertionError:

if 0 < distance <= RADIUS:
    #Do something.
elif RADIUS < distance:
    #Do something.
else:
    raise AssertionError("Unexpected value of 'distance'!", distance)
 30
Author: Evgeni Sergeev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-19 04:55:14

En Python3 hay 4 sintaxis diferentes para las excepciones de rasing:

1. raise exception 
2. raise exception (args) 
3. raise
4. raise exception (args) from original_exception

1. elevar excepción vs 2. levantar excepción (args)

Si usa raise exception (args) para generar una excepción, entonces el args se imprimirá cuando imprima el objeto de excepción, como se muestra en el ejemplo siguiente.

  #raise exception (args)
    try:
        raise ValueError("I have raised an Exception")
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error I have raised an Exception 



  #raise execption 
    try:
        raise ValueError
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error 

3.raise

raise la declaración sin ningún argumento vuelve a plantear la última excepción. Esto es útil si lo necesita realice algunas acciones después de capturar la excepción y luego quiera volver a elevarla. Pero si antes no había excepción, raise la declaración plantea TypeError Excepción.

def somefunction():
    print("some cleaning")

a=10
b=0 
result=None

try:
    result=a/b
    print(result)

except Exception:            #Output ->
    somefunction()           #some cleaning
    raise                    #Traceback (most recent call last):
                             #File "python", line 8, in <module>
                             #ZeroDivisionError: division by zero

4. elevar excepción (args) desde original_exception

Esta instrucción se usa para crear cadenas de excepciones en las que una excepción que se genera en respuesta a otra excepción puede contener los detalles de la excepción original, como se muestra en el ejemplo debajo.

class MyCustomException(Exception):
pass

a=10
b=0 
reuslt=None
try:
    try:
        result=a/b

    except ZeroDivisionError as exp:
        print("ZeroDivisionError -- ",exp)
        raise MyCustomException("Zero Division ") from exp

except MyCustomException as exp:
        print("MyException",exp)
        print(exp.__cause__)

Salida:

ZeroDivisionError --  division by zero
MyException Zero Division 
division by zero
 26
Author: N Randhawa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-26 10:00:38

Lea las respuestas existentes primero, esto es solo un anexo.

Observe que puede plantear excepciones con o sin argumentos.

Ejemplo:

raise SystemExit

Sale del programa, pero es posible que desee saber qué happened.So puedes usar esto.

raise SystemExit("program exited")

Esto imprimirá "programa salido" a stderr antes de cerrar el programa.

 6
Author: Anant Prakash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-02 12:59:25