Comment faire taire "sys.excepthook est manquant erreur"?
NB: Je n'ai pas essayé de reproduire le problème décrit ci-dessous sous Windows, ou avec des versions de Python autres que 2.7.3.
la façon la plus fiable d'obtenir le problème en question Est de pipe la sortie du script de test suivant par :
(bash
):
try:
for n in range(20):
print n
except:
pass
I. e.:
% python testscript.py | :
close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr
Ma question est:
comment modifier le script de test ci-dessus pour éviter le message d'erreur lorsque le script est exécuté comme indiqué (sous Unix/
bash
)?
(comme le montre le script de test, l'erreur ne peut pas être piégée avec un try-except
.)
l'exemple ci-dessus est, il est vrai, très artificiel, mais je rencontre le même problème parfois lorsque la sortie d'un de mes scripts est acheminée par un logiciel tiers.
Le message d'erreur est certainement inoffensif, mais il est déconcertant pour les utilisateurs finaux, donc je voudrais le silence il.
EDIT: le script suivant, qui diffère de l'original ci-dessus seulement en ce qu'il redéfinit sys.excepthook, se comporte exactement comme celui donné ci-dessus.
import sys
STDERR = sys.stderr
def excepthook(*args):
print >> STDERR, 'caught'
print >> STDERR, args
sys.excepthook = excepthook
try:
for n in range(20):
print n
except:
pass
4 réponses
Comment puis-je modifier le script de test ci-dessus pour éviter le message d'erreur lorsque le script est exécuté comme indiqué (sous Unix/
bash
)?
vous devez empêcher le script d'écrire quoi que ce soit à la sortie standard. Cela signifie supprimer tout print
déclarations et toute utilisation de sys.stdout.write
, ainsi que tout code qui appelle.
la raison pour laquelle cela se produit est que vous acheminez une quantité non nulle de sortie de votre script Python vers quelque chose qui ne se lit jamais à partir de l'entrée standard. Ce n'est pas unique à l' :
commande; vous pouvez obtenir le même résultat en raccordant à n'importe quelle commande qui ne lit pas l'entrée standard, comme
python testscript.py | cd .
Ou pour un exemple simple, considérons un script printer.py
contenant rien de plus que
print 'abcde'
python printer.py | python printer.py
produira la même erreur.
Lorsque vous rediriger la sortie d'un programme vers un autre, la sortie produite par l'écriture le programme est sauvegardé dans un buffer, et attend le programme de lecture pour demander que les données du buffer. Tant que le tampon n'est pas vide, toute tentative de fermer l'objet writing file est censée échouer avec une erreur. C'est la cause profonde des messages que vous voyez.
le code spécifique qui déclenche l'erreur est dans L'implémentation du langage C de Python, ce qui explique pourquoi vous ne pouvez pas l'attraper avec un try
/except
bloc: il court après le contenu de votre script a terminé le traitement. Fondamentalement, alors que Python se ferme, il tente de fermer stdout
, mais cela échoue parce qu'il y a encore une sortie tamponnée qui attend d'être lue. Donc Python essaie de signaler cette erreur comme il le ferait normalement, mais sys.excepthook
a déjà été supprimé dans le cadre de la procédure de finalisation, donc cela échoue. Python essaie d'imprimer un message à sys.stderr
, mais cela a déjà été déallocé donc encore une fois, il échoue. La raison pour laquelle vous voyez les messages à l'écran est que Le code Python contient une contingence fprintf
pour écrire une sortie sur le pointeur de fichier directement, même si L'objet de sortie de Python n'existe pas.
détails Techniques
pour ceux qui s'intéressent aux détails de cette procédure, regardons la séquence d'arrêt de L'interpréteur Python, qui est implémentée dans le Py_Finalize
functionpythonrun.c
.
- après avoir invoqué les crochets de sortie et coupé les fils, le finalisation du code des appels
PyImport_Cleanup
pour finaliser et désallouer tous les modules importés. La dernière tâche exécutée par cette fonction est supprimersys
module, qui consiste principalement à appeler_PyModule_Clear
pour effacer toutes les entrées dans le dictionnaire du module-y compris, en particulier, les objets standard stream (les objets Python) tels questdout
etstderr
. - Lorsqu'une valeur est supprimée d'un dictionnaire ou remplacée par une nouvelle valeur, son compte de référence est décrémenté en utilisant
Py_DECREF
macro. Les objets dont le nombre de référence atteint zéro deviennent éligibles à la désallocation. Depuis l'sys
module contient les dernières références aux objets standard stream, lorsque ces références sont désactivées par_PyModule_Clear
, ils sont alors prêts à être libéré.1 la désallocation D'un objet de fichier Python est accomplie par
file_dealloc
la fonctionfileobject.c
. Cette première invoque l'objet du fichier Pythonclose
méthode en utilisant leclose_the_file
function:ret = close_the_file(f);
Pour un fichier standard de l'objet,
close_the_file(f)
délégués au Cfclose
function, qui définit une condition d'erreur s'il reste des données à écrire dans le pointeur de fichier.file_dealloc
vérifie que la condition d'erreur et imprime le premier message que vous voir:if (!ret) { PySys_WriteStderr("close failed in file object destructor:\n"); PyErr_Print(); } else { Py_DECREF(ret); }
après avoir imprimé ce message, Python tente alors d'afficher l'exception en utilisant
PyErr_Print
. Que les délégués àPyErr_PrintEx
, et dans le cadre de ses fonctionnalités,PyErr_PrintEx
tente d'accéder à L'imprimante d'exception Python à partir desys.excepthook
.hook = PySys_GetObject("excepthook");
ce serait bien si cela était fait dans le cours normal D'un programme Python, mais dans cette situation,
sys.excepthook
a déjà été effacée. 2 Python vérifie cette condition d'erreur et affiche le second message comme notification.if (hook && hook != Py_None) { ... } else { PySys_WriteStderr("sys.excepthook is missing\n"); PyErr_Display(exception, v, tb); }
après nous avoir informés des disparus
excepthook
, Python revient ensuite à imprimer les informations d'exception en utilisantPyErr_Display
, qui est la méthode par défaut pour afficher une trace de pile. La première chose que cette fonction n'est d'essayer d'accèssys.stderr
.PyObject *f = PySys_GetObject("stderr");
Dans ce cas, cela ne fonctionne pas car
sys.stderr
a déjà été dégagé et inaccessible. 3 alors le code invoquefprintf
directement pour envoyer le troisième message au flux d'erreurs standard C.if (f == NULL || f == Py_None) fprintf(stderr, "lost sys.stderr\n");
fait intéressant, le comportement est un peu différent en Python 3.4+ car la procédure de finalisation est maintenant élimine explicitement les flux de sortie et d'erreur standard avant que les modules ne soient effacés. De cette façon, si vous avez des données qui attendent d'être écrites, vous obtenez une erreur qui indique explicitement cette condition, plutôt qu'une défaillance" accidentelle " dans la procédure normale de finalisation. Aussi, si vous exécutez
python printer.py | python printer.py
utilisant Python 3.4 (après avoir mis les parenthèses sur le print
déclaration bien sûr), vous n'avez aucune erreur. Je suppose que la deuxième invocation de Python peut consommer des entrées standard pour une raison ou une autre, mais c'est un tout autre problème.
1en Fait, c'est un mensonge. Le mécanisme d'importation de Python cache une copie du dictionnaire de chaque module importé, qui n'est libéré que _PyImport_Fini
fonctionne, plus tard dans la mise en oeuvre de Py_Finalize
et c'est lorsque les dernières références aux objets standard stream disparaissent. Une fois que le nombre de référence atteint zéro, Py_DECREF
libère les objets . Mais tout ce qui importe pour la réponse principale est que les références sont retirées de la sys
le dictionnaire du module, puis désallocé plus tard.
2Encore une fois, c'est parce que l' sys
le dictionnaire du module est complètement effacé avant que tout soit vraiment désalloué, grâce au mécanisme de mise en cache des attributs. Vous pouvez lancer Python avec le -vv
option pour voir tous les attributs du module être désactivé avant que vous obtenez le message d'erreur sur la fermeture du pointeur de fichier.
3ce comportement particulier est le seul partie qui n'a pas de sens à moins que vous ne connaissiez le mécanisme de mise en cache des attributs mentionné dans les notes précédentes.
--- a/testscript.py
+++ b/testscript.py
@@ -9,5 +9,6 @@ sys.excepthook = excepthook
try:
for n in range(20):
print n
+ sys.stdout.flush()
except:
pass
puis avec ce script rien ne se passe, car l'exception (IOError: [Errno 32] broken pipe) est supprimée par l'essai...sauf.
$ python testscript.py | :
$
dans votre programme lance une exception qui ne peut pas être attrapée en utilisant try/except block. Pour l'attraper, remplacer la fonction sys.excepthook
:
import sys
sys.excepthook = lambda *args: None
sys.exceptithook (type, valeur, traceback)
Lorsqu'une exception est soulevée et non saisie, l'interprète appelle sys.exceptihook avec trois arguments, la classe exception, exception exemple, et un objet traceback. Dans une session interactive ce se produit juste avant que le contrôle soit retourné à l'invite; dans un Python programme cela se produit juste avant la fin du programme. Le traitement de ces exceptions de haut niveau peuvent être personnalisées en assignant une autre fonction à trois arguments de sys.excepthook.
exemple:
import sys
import logging
def log_uncaught_exceptions(exception_type, exception, tb):
logging.critical(''.join(traceback.format_tb(tb)))
logging.critical('{0}: {1}'.format(exception_type, exception))
sys.excepthook = log_uncaught_exceptions
je me rends compte que c'est une vieille question, mais je l'ai trouvé dans une recherche Google pour l'erreur. Dans mon cas, c'était une erreur de codage. L'une de mes dernières déclarations a été:
print "Good Bye"
la solution était simplement de fixer la syntaxe à:
print ("Good Bye")
[Raspberry Pi Zéro, Python 2.7.9]