web-dev-qa-db-fra.com

Existe-t-il un moyen d'empêcher une exception SystemExit déclenchée par sys.exit () d'être interceptée?

Les documents indiquent que l'appel à sys.exit () déclenche une exception SystemExit qui peut être interceptée dans les niveaux externes. J'ai une situation dans laquelle je veux sortir définitivement et incontestablement de l'intérieur d'un cas de test, mais le module le plus unitaire intercepte SystemExit et empêche la sortie. C'est normalement génial, mais la situation spécifique que j'essaie de gérer est celle où notre framework de test a détecté qu'il est configuré pour pointer vers une base de données non test. Dans ce cas, je souhaite quitter et empêcher l'exécution de tests supplémentaires. Bien sûr, puisque le plus unitaire piège le SystemExit et continue joyeusement sur son chemin, cela me contrarie.

La seule option à laquelle j'ai pensé jusqu'à présent est d'utiliser des ctypes ou quelque chose de similaire pour appeler directement exit (3), mais cela semble être un hack assez rapide pour quelque chose qui devrait être vraiment simple.

51
John

Vous pouvez appeler os._exit() pour quitter directement, sans lever d'exception:

import os
os._exit(1)

Cela contourne toute la logique d'arrêt python, comme le module atexit, et ne passera pas par la logique de gestion des exceptions que vous essayez d'éviter dans cette situation. Le L'argument est le code de sortie qui sera retourné par le processus.

75
Jerub

Comme l'a dit Jerub, os._exit(1) est votre réponse. Mais, considérant qu'il contourne toutes les procédures de nettoyage , y compris finally: les blocs, la fermeture de fichiers, etc., et devrait vraiment être évité à tout prix, puis-je présenter une manière "plus sûre" de l'utiliser?

Si votre problème est que SystemExit est attrapé aux niveaux externes (c'est-à-dire unittest), alors soyez le niveau externe vous-même! Enveloppez votre code principal dans un bloc try/except, attrapez SystemExit et appelez os._exit là, et seulement là! De cette façon, vous pouvez appeler sys.exit normalement n'importe où dans le code, laissez-le bouillonner au niveau supérieur, en fermant gracieusement tous les fichiers et en exécutant tous les nettoyages, et puis en appelant os._exit .

Vous pouvez même choisir les sorties "d'urgence". Le code ci-dessous est un exemple d'une telle approche:

import sys, os

EMERGENCY = 255  # can be any number actually

try:
    # wrap your whole code here ...
    # ... some code
    if x: sys.exit()
    # ... some more code
    if y: sys.exit(EMERGENCY)  # use only for emergency exits
    # ...
except SystemExit as e:
    if e.code != EMERGENCY:
        raise  # normal exit, let unittest catch it
    else:
        os._exit(EMERGENCY)  # try to stop *that*, sucker!
47
MestreLion