2009-12-24 12 views
14

Je voudrais enregistrer toute la sortie d'un script Python. J'ai essayé:Comment rediriger stderr en Python?

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, disons "print" quelque chose # ", il ne sera pas enregistré - il ira dans la console à la place.

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

je vis une solution ici:

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

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

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

+0

Peut-être que writer doit également implémenter writelines(). Ne pas oublier sys.stderr.flush() –

Répondre

5

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

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

+0

Vous avez raison, bien sûr, il ne peut pas le faire. Désolé pour la question stupide. Une autre solution serait de "exec" le script dans un autre script. – EcirH

+0

Tout le code n'est pas compilé à la fois. Les instructions d'importation sont un exemple. –

+1

Un autre cas d'utilisation (mais pas celui d'EcirH): capturer stderr à partir d'une bibliothèque C appelée à partir de python. –

6

Je ne peux pas penser à un moyen facile. L'erreur standard du processus python est inférieure à celle d'un objet Python (C vs. Python).

Vous pouvez placer le script python dans un second script python et utiliser subprocess.Popen. Il est également possible que vous pourriez tirer un peu de magie comme ça 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 puis utilisez select.poll() pour vérifier cat.stdout régulièrement pour trouver la sortie.

Oui, cela semble fonctionner.

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

--------- Modifier

D'une certaine façon je manqué la fonction os.pipe().

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

Puis lire r

18

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

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

il est certainement possible.

Je crois que votre problème est que vous créez deux instances d'écrivain.

Peut-être quelque chose comme:

import sys 

class writer(object): 
    log = [] 

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

logger = writer() 
sys.stdout = logger 
sys.stderr = logger 
+0

J'allais dire quelque chose à propos de la variable de journal ... –

+1

Que faire si un autre code fait sys.stdout.flush()? – Moberg

+1

Dans ce cas, ajoutez simplement une méthode flush factice à votre classe d'auteur: 'def flush (self): pass' – KingRadical

0
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

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

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

Questions connexes