web-dev-qa-db-fra.com

Python: utilisation des différences et suggestions sys.exit ou SystemExit

En lisant en ligne, certains programmeurs utilisent sys.exit, d’autres SystemExit.
Désolé pour la question de base:

  1. Quelle est la différence?
  2. Quand dois-je utiliser SystemExit ou sys.exit dans une fonction?

Exemple

ref = osgeo.ogr.Open(reference)
if ref is None:
    raise SystemExit('Unable to open %s' % reference)

ou

ref = osgeo.ogr.Open(reference)
if ref is None:
    print('Unable to open %s' % reference)
    sys.exit(-1)
35
Gianni Spear

Aucune différence pratique, mais il existe une autre différence dans votre exemple de code - print passe à la sortie standard, mais le texte de l'exception passe à l'erreur standard (ce qui est probablement ce que vous voulez).

19
RichieHindle

sys.exit(s) est simplement un raccourci pour raise SystemExit(s), comme décrit dans le document docstring de l'ancien; essayez help(sys.exit). Donc, au lieu de l’un de vos exemples de programmes, vous pouvez faire

sys.exit('Unable to open %s' % reference)
23
Fred Foo

Il y a 3 fonctions de sortie, en plus d'élever SystemExit.

Le sous-jacent est os._exit, qui requiert un argument de 1 int et se termine immédiatement sans nettoyage. Il est peu probable que vous souhaitiez toucher à celui-ci, mais c'est là.

sys.exit est défini dans sysmodule.c et exécute simplement PyErr_SetObject(PyExc_SystemExit, exit_code);, ce qui revient en fait à relancer directement SystemExit. Dans le détail, relancer SystemExit est probablement plus rapide, puisque sys.exit nécessite un opcall LOAD_ATTR et CALL_FUNCTION vs RAISE_VARARGS. De plus, raise SystemExit produit un code binaire légèrement plus petit (moins de 4 octets), (1 octet supplémentaire si vous utilisez from sys import exit car sys.exit ne renverra aucun, ce qui inclut un POP_TOP supplémentaire). 

La dernière fonction de sortie est définie dans site.py et est associée à exit ou quit dans le REPL. Il s’agit en fait d’une instance de la classe Quitter (elle peut donc avoir un __repr__ personnalisé, elle est donc probablement la plus lente en cours d’exécution. En outre, elle ferme sys.stdin avant de générer SystemExit, elle est donc recommandée uniquement dans REPL. 

Pour ce qui est de la gestion de SystemExit, le VM appelle finalement os._exit, mais avant cela, il effectue un nettoyage. Il exécute également atexit._run_exitfuncs(), qui exécute tous les rappels enregistrés via le module atexit. L'appel de os._exit contourne directement l'étape atexit.

10
Perkins

Ma préférence personnelle est qu’au moins SystemExit soit soulevé (ou mieux encore - une exception personnalisée plus significative et bien documentée) et ensuite capturé aussi près que possible de la fonction "principale", ce qui peut alors avoir une dernière chance de le considérer comme telle. une sortie valide ou non. Les bibliothèques/fonctions profondément intégrées qui ont sys.exit sont tout simplement désagréables du point de vue de la conception. (En général, la sortie doit être "aussi haute que possible")

6
Jon Clements

SystemExit est une exception, ce qui signifie que votre programme a eu un comportement tel que vous souhaitez l'arrêter et générer une erreur. sys.exit est la fonction que vous pouvez appeler pour quitter votre programme, en donnant éventuellement un code de retour au système.

EDIT: ils sont en effet la même chose, donc la seule différence est dans la logique derrière votre programme. Une exception est une sorte de comportement "indésirable", qu'un appel à une fonction soit, du point de vue du programmeur, davantage une action "standard".

3
Cynical

D'après la documentation, sys.exit(s) fait effectivement raise SystemExit(s), c'est donc à peu près la même chose.

2
piokuc

Bien que de nombreuses réponses aient répondu à la différence, https://mail.python.org/pipermail/python-list/2016-April/707470.html fait valoir un point intéressant:

TL; DR: Il est préférable de simplement lever une exception "normale" et d'utiliser SystemExit ou sys.exit uniquement aux niveaux supérieurs d'un script.

Je suis sur Python 2.7 et Linux, j'ai un code simple besoin de suggestion si je Je pourrais remplacer sys.exit (1) par raise SystemExit.

== Code actuel ==

def main():    
    try:
       create_logdir()
       create_dataset()
       unittest.main()    
     except Exception as e:
       logging.exception(e)
       sys.exit(EXIT_STATUS_ERROR)

if __== '__main__':    main()

== Code modifié ==

def main():    
    try:
       create_logdir()
       create_dataset()
       unittest.main()    
    except Exception as e:
       logging.exception(e)
       raise SystemExit

if __== '__main__':    
    main()

Je suis personnellement contre les deux. Mon modèle préféré est comme ce:

  def main(argv):
    try:
      ...
    except Exception as e:
      logging.exception(e)
      return 1

  if __== '__main__':
    sys.exit(main(sys.argv))

Notez que main () est redevenu une fonction normale avec résultats.

En outre, la plupart d’entre nous éviteraient le «sauf exception» et laisseraient un sommet level except bulle: de cette façon, vous obtenez une trace de pile pour débogage. Je suis d'accord, il empêche l'enregistrement de l'exception et fait pour sortie de la console plus laid, mais je pense que c'est une victoire. Et si vous voulez voulez pour enregistrer l'exception il y a toujours ceci:

essayer: ... sauf Exception comme e: logging.exception (e) élever

réciter l'exception dans le journal tout en la laissant échapper normalement.

Le problème avec le modèle "sauf exception" est qu'il capture et cachechaque exception, et pas seulement le jeu restreint d'exceptions spécifiques que vous comprenez.

Enfin, il est mal vu de créer une classe d'exception nue. Dans python 3 Je crois que c'est en fait interdit, donc c'est non portable. en tous cas. Mais même en Python, il est préférable de fournir une exception exemple, pas la classe:

soulève SystemExit (1)

  1. Toutes les fonctions du bloc try ont une exception bouillonnée à l'aide de la levée

    Exemple pour create_logdir (), voici la définition de la fonction

def create_logdir ():

essayer: os.makedirs (LOG_DIR) sauf OSError en tant que e: sys.stderr.write ("Echec de la création du répertoire de journalisation ... Exit !!!") élever print "fichier journal:" + corrupt_log retourne True

def main (): essayer: create_logdir () sauf exception comme e: logging.exception (e) soulève SystemExit

(a) En cas d'échec de create_logdir (), l'erreur ci-dessous sera générée, à savoir cette amende ou dois-je améliorer ce code.

Échec de la création du répertoire de journal ... Sortie !!! ERREUR: root: [Errno 17] Fichier existe: '/ var/log/dummy'

Traceback (dernier appel passé): Fichier "corrupt_test.py", ligne 245, dans le fichier principal create_logdir () Fichier "corrupt_test.py", ligne 53, dans create_logdir os.makedirs (LOG_DIR) Fichier "/usr/local/lib/python2.7/os.py", ligne 157, dans makedirs OSError: [Errno 17] Le fichier existe: '/ var/log/dummy'

Je préfère l’approche par bulles, peut-être avec un journal ou un avertissement messages comme vous l'avez fait, par exemple:

logging.exception ("create_logdir a échoué: makedirs (% r):% s"% (LOG_DIR, e)) augmenter

(En outre, le message de journal n'enregistre pas davantage de contexte: le contexte est très utile Utile pour les problèmes de débogage.)

Pour de très petits scripts, sys.stderr.write est correct, mais en général, aucun vos fonctions qui se sont révélées être généralement utiles pourraient migrer dans une bibliothèque pour être réutilisé; considérez que stderr n'est pas toujours la place pour les messages; lire à la place pour le module de journalisation avec error () ou wanr () ou exception () selon le cas. Il y a plus possibilité de configurer l'emplacement de la sortie sans câblage cela dans vos fonctions intérieures.

  1. Puis-je avoir juste soulever, au lieu de SystemExit ou sys.exit (1). Ce me semble faux

    def main ():

    essayer: create_logdir () sauf exception comme e logging.exception (e) élever

C'est ce que je ferais moi-même.

Pensez: l'exception a-t-elle été "gérée", c'est-à-dire que la situation est été traité parce que c'était prévu? Sinon, laissez l'exception dégager une bulle pour que l'utilisateur sache que quelque chose pas est compris par le programme a eu lieu.

Enfin, il est généralement mauvais pour SystemExit ou sys.exit () de l’intérieur autre chose que la fonction principale () la plus externe. Et je résiste même là; la fonction principale, si elle est bien écrite, peut souvent s'appeler utile ailleurs, ce qui en fait effectivement une bibliothèque fonction (il a été réutilisé). Une telle fonction ne devrait pas abandonner unilatéralement le programme. Si vulgaire! Au lieu de cela, laissez l'exception bubble out: peut-être que le caller de main () l'attend et peut gérer il. En abandonnant et non en "augmentant", vous avez privé l'appelant de la chance de faire quelque chose de approprié, même si vous-même (c'est-à-dire "main") ne connaissent pas assez de contexte pour gérer l'exception.

Donc, je suis pour "élever" moi-même. Et ensuite uniquement parce que vous souhaitez enregistrer le fichier Erreur. Si vous ne voulez pas vous connecter à l'exception, vous pouvez éviter le essayez/sauf entièrement et utilisez un code plus simple: laissez l’appelant s’inquiéter sur les exceptions non gérées!

0
serv-inc