2009-12-24 31 views
14

Me gustaría registrar todo el resultado de un script de Python. Intenté:¿Cómo redirigir stderr en Python?

import sys 

log = [] 

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

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

Ahora, si "imprimo" algo, se registra. Pero si hago, por ejemplo, algún error de sintaxis, digamos "imprimir" algo # ", no se registrará, sino que irá a la consola.

¿Cómo capturo también los errores del intérprete de Python?

vi una posible solución a este problema:

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

pero el segundo ejemplo se registra en/dev/null - esto no es lo que quiero. Me gustaría iniciar sesión en una lista como mi ejemplo anterior o StringIO o similar ...

Además, preferiblemente no quiero crear un subproceso (y leer su stdout y stderr en un hilo separado).

+0

Quizás el escritor también debería implementar writelines(). No olvides sys.stderr.flush() –

Respuesta

5

No se puede hacer nada en el código Python que pueda capturar errores durante la compilación de ese mismo código. ¿Cómo podría? Si el compilador no puede terminar de compilar el código, no ejecutará el código, por lo que su redirección ni siquiera ha tenido efecto.

Aquí es donde entra su subproceso (no deseado). Puede escribir el código de Python que redirige el stdout, luego invoca el intérprete de Python para compilar otro código.

+0

Tienes razón, por supuesto que no puede hacerlo. Perdón por la estúpida pregunta. Otra solución sería "ejecutar" el script dentro de otro script. – EcirH

+0

No todo el código se compila a la vez. las declaraciones de importación son un ejemplo. –

+1

Otro caso de uso (aunque no de EcirH): capturar stderr de una biblioteca C llamada desde python. –

6

No puedo pensar en una manera fácil. El error estándar del proceso de python es vivir en un nivel inferior al de un objeto de archivo python (C contra python).

Puede ajustar el script de python en un segundo script de python y usar el subproceso.Popen. También es posible que usted podría tirar un poco de magia como esta en una única secuencia de comandos:

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()) 

y luego usar select.poll() para comprobar cat.stdout regularmente para encontrar la salida.

Sí, parece que funciona.

El problema que preveo es que la mayoría de las veces, algo impreso en stderr por python indica que está a punto de salir. La forma más habitual de manejar esto sería a través de excepciones.

--------- Editar

De alguna manera se perdió la función os.pipe().

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

A continuación, lea desde r

18

tengo una pieza de software que escribí para el trabajo que captura stderr a un archivo de este modo:

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

lo que es definitivamente posible.

Creo que su problema es que está creando dos instancias de escritor.

Tal vez algo más como:

import sys 

class writer(object): 
    log = [] 

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

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

Iba a decir algo sobre la variable de registro ... –

+1

¿Qué pasa si otro código hace sys.stdout.flush()? – Moberg

+1

En ese caso, simplemente agregue un método flush flush a su clase de escritor: 'def flush (self): pase' – 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 realidad, si usted está utilizando sistema operativo Linux/Mac, puede simplemente usar redirección de archivos para hacer eso. Por ejemplo, si va a ejecutar "a.py" y registrar todos los mensajes que generará en el archivo "a.out", simplemente sería

python a.py 2> & 1> a.

Cuestiones relacionadas