web-dev-qa-db-fra.com

Comment rediriger stderr en Python?

J'aimerais enregistrer toute la sortie d'un script Python. J'ai essayé:

import sys

log = []

class writer(object):
    def write(self, data):
        log.append(data)

sys.stdout = writer()
sys.stderr = writer()

Maintenant, si je "imprime" quelque chose "", il est enregistré. Mais si je fais par exemple une erreur de syntaxe, dites "print 'quelque chose #", il ne sera pas enregistré - il ira à la place dans la console.

Comment capturer également les erreurs de l'interpréteur Python?

J'ai vu une solution possible ici:

http://www.velocityreviews.com/forums/showpost.php?p=1868822&postcount=3

mais le deuxième exemple se connecte à/dev/null - ce n'est pas ce que je veux. Je voudrais le connecter dans une liste comme mon exemple ci-dessus ou StringIO ou tel ...

Aussi, de préférence, je ne veux pas créer de sous-processus (et lire ses stdout et stderr dans un thread séparé).

16
EcirH

Vous ne pouvez rien faire dans le code Python qui puisse capturer des erreurs lors de la compilation de ce même code. Comment pourrait-il? Si le compilateur ne peut pas terminer la compilation du code, il ne l'exécutera pas. Votre redirection n'a même pas encore pris effet.

C'est là qu'intervient votre sous-processus (non désiré). Vous pouvez écrire du code Python qui redirige la sortie standard, puis appeler l'interpréteur Python pour compiler un autre morceau de code.

5
Ned Batchelder

J'ai un logiciel que j'ai écrit pour le travail et qui capture stderr dans un fichier comme suit:

import sys
sys.stderr = open('C:\\err.txt', 'w')

donc c'est certainement possible.

Je crois que votre problème est que vous créez deux instances de writer.

Peut-être quelque chose de plus semblable à:

import sys

class writer(object):
    log = []

    def write(self, data):
        self.log.append(data)

logger = writer()
sys.stdout = logger
sys.stderr = logger
19
KingRadical

Je ne peux pas penser à un moyen facile. L'erreur standard du processus python réside à un niveau inférieur à celui d'un objet de fichier python (C vs. python).

Vous pouvez envelopper le script python dans un second script python et utiliser subprocess.Popen. Il est également possible de tirer un sort magique comme celui-ci dans un seul script:

import os
import subprocess
import sys

cat = subprocess.Popen("/bin/cat", stdin=subprocess.PIPE, stdout=subprocess.PIPE)
os.close(sys.stderr.fileno())
os.dup2(cat.stdin.fileno(), sys.stderr.fileno())

Et utilisez ensuite select.poll () pour vérifier régulièrement cat.stdout afin de trouver la sortie.

Oui, cela semble fonctionner.

Le problème que je prévois est que la plupart du temps, quelque chose imprimé sur stderr en python indique qu'il est sur le point de quitter. La manière la plus habituelle de gérer cela serait via des exceptions.

---------Modifier

Quelque part, j'ai raté la fonction os.pipe ().

import os, sys
r, w = os.pipe()
os.close(sys.stderr.fileno())
os.dup2(w, sys.stderr.fileno())

Puis lisez de r

6
user227667

En fait, si vous utilisez linux/mac os, vous pouvez simplement utiliser la redirection de fichier pour le faire. Par exemple, si vous exécutez "a.py" et enregistrez tous les messages qu'il générera dans le fichier "a.out", ce sera simplement

python a.py 2>&1 > a.out

La première partie redirige stderr vers stdout, et la seconde le redirige vers un fichier appelé a.out.

Pour une liste plus longue des opérateurs de redirection sous Linux/Unix, voir https://askubuntu.com/questions/420981/how-do-i-save-terminal-output-to-a-file

4
VitleysingurQ

Python n'exécutera pas votre code en cas d'erreur. Mais vous pouvez importer votre script dans un autre script et capturer des exceptions. Exemple:

Script.py

print 'something#

FinalScript.py

from importlib.machinery import SourceFileLoader

try:
    SourceFileLoader("main", "<SCRIPT PATH>").load_module()
except Exception as e:
    # Handle the exception here
0
ColdGrub1384
import sys
import tkinter

# ********************************************

def mklistenconsswitch(*printf: callable) -> callable:
    def wrapper(*fcs: callable) -> callable:
        def newf(data):
            [prf(data) for prf in fcs]
        return newf
    stdoutw, stderrw = sys.stdout.write, sys.stderr.write
    funcs = [(wrapper(sys.stdout.write, *printf), wrapper(sys.stderr.write, *printf)), (stdoutw, stderrw)]
    def switch():
        sys.stdout.write, sys.stderr.write = dummy = funcs[0]
        funcs[0] = funcs[1]
        funcs[1] = dummy
    return switch

# ********************************************

def datasupplier():
    i = 5.5
    while i > 0:
        yield i
        i -= .5

def testloop():
    print(supplier.__next__())
    svvitch()
    root.after(500, testloop)

root = tkinter.Tk()
cons = tkinter.Text(root)
cons.pack(fill='both', expand=True)
supplier = datasupplier()
svvitch = mklistenconsswitch(lambda text: cons.insert('end', text))
testloop()
root.mainloop()
0
yagami raito

Depuis Python 3.5, vous pouvez utiliser contextlib.redirect_stderr

with open('help.txt', 'w') as f:
    with redirect_stdout(f):
        help(pow)
0
max

Pour ajouter à la réponse de Ned, il est difficile de capturer les erreurs à la volée lors de la compilation.

Vous pouvez écrire plusieurs instructions d'impression dans votre script et vous pouvez stdout dans un fichier, il cessera d'écrire dans le fichier lorsque l'erreur se produit. Pour déboguer le code, vous pouvez vérifier la dernière sortie enregistrée et vérifier votre script après ce moment.


Quelque chose comme ceci:

# Add to the beginning of the script execution(eg: if __== "__main__":).
from datetime import datetime
dt = datetime.now()
script_dir = os.path.dirname(os.path.abspath(__file__))      # gets the path of the script
stdout_file = script_dir+r'\logs\log'+('').join(str(dt.date()).split("-"))+r'.log'
sys.stdout = open(stdout_file, 'w')

Cela créera un fichier journal et transmettra les instructions d'impression dans le fichier.


Remarque: Attention aux caractères d'échappement dans votre chemin de fichier lors de la concaténation avec script_dir dans la deuxième ligne à partir de la dernière dans le code. Vous voudrez peut-être quelque chose de similaire à une chaîne brute. Vous pouvez vérifier ce fil pour cela.

0
rzskhr