2009-04-22 13 views
7

Este código genera "AttributeError: objeto 'Popen' no tiene atributo 'fileno'" cuando se ejecuta con Python 2.5.1Python subproceso "objeto no tiene atributo 'fileno'" error

Código:

def get_blame(filename): 
    proc = [] 
    proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE)) 
    proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE) 
    proc.append(Popen(['tr', r"'\040'", r"';'"], stdin=proc[-1]), stdout=PIPE) 
    proc.append(Popen(['cut', r"-d", r"\;", '-f', '3'], stdin=proc[-1]), stdout=PIPE) 
    return proc[-1].stdout.read() 

Pila:

function walk_folder in blame.py at line 55 
print_file(os.path.join(os.getcwd(), filename), path) 

function print_file in blame.py at line 34 
users = get_blame(filename) 

function get_blame in blame.py at line 20 
proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE) 

function __init__ in subprocess.py at line 533 
(p2cread, p2cwrite, 

function _get_handles in subprocess.py at line 830 
p2cread = stdin.fileno() 

Este código debe trabajar la pitón documentos describen this usage.

+4

Cierre esto como "demasiado localizada" es ridícula como * I * pareció útil, años más tarde. Esta es la naturaleza de los problemas de programación: siempre son problemas de nicho aplicables a una base de usuarios limitada. Pero SON ÚTILES ... ASÍ ES LO QUE ME ENGAÑA EN ESTE DÍA, y es por eso que ya no lo uso a menudo. – Dan

Respuesta

10

Tres cosas

primer lugar, su() 's son erróneas.

En segundo lugar, el resultado de subprocess.Popen() es un objeto de proceso, no un archivo.

proc = [] 
proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE)) 
proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE) 

El valor de proc[-1] no es el archivo, que es el proceso que contiene el archivo.

proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1].stdout, stdout=PIPE)) 

tercer lugar, no hacer todo lo que tr y cut basura en la concha, algunas cosas podrían ser más lento. Escriba el procesamiento tr y cut en Python, es más rápido y simple.

+0

¿Era el "Árbol" del humor, o quiso decir "Tres"? –

+0

Gracias por el comentario. Está arreglado. –

+0

El script solo se usará en Linux. Es más una herramienta única que cualquier otra cosa. Usar herramientas de shell fue más fácil que tratar de descubrir el equivalente de Python. Solo colocar un comando de shell en funcionamiento es más fácil que depurar regex. – epochwolf

-1

parece un error de sintaxis. excepto que primero se agrega el resto son erróneos (corchetes de revisión).

+0

No es en realidad. – epochwolf

+0

no es qué? ¡cada respuesta incluía el hecho de que tienes un problema con tus corchetes! es un problema de sintaxis, además de la tubería, por supuesto. – SilentGhost

+0

Es un problema de sintaxis pero no es un error de sintaxis por extraño que parezca. El código se pega directamente desde el script que tenía. El error fue el error que se generó. Un error de sintaxis habría matado al script en el medio de la definición de la función. La función en realidad se llama. Creo que es porque list.append() acepta múltiples argumentos. – epochwolf

1

desea que el stdout del proceso, por lo que reemplazar su stdin=proc[-1] con stdin=proc[-1].stdout

Además, es necesario mover los paren, debe venir después del argumento stdout.

proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE) 

debería ser:

proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1].stdout, stdout=PIPE)) 

arreglar sus otros append llamadas de la misma manera.

3

Hay algunas cosas extrañas en el guión,

  • ¿Por qué estás almacenando cada proceso en una lista? ¿No sería mucho más fácil usar variables? La eliminación de toda la .append()s revela un error de sintaxis, varias veces que haya pasado la salida estándar = tubo a la append argumentos, en lugar de Popen:

    proc.append(Popen(...), stdout=PIPE) 
    

    Así que una reescritura recta (aún con errores voy a mencionar en un segundo) se convertiría en ..

    def get_blame(filename): 
        blame = Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE) 
        tr1 = Popen(['tr', '-s', r"'\040'"], stdin=blame, stdout=PIPE) 
        tr2 = Popen(['tr', r"'\040'", r"';'"], stdin=tr1), stdout=PIPE) 
        cut = Popen(['cut', r"-d", r"\;", '-f', '3'], stdin=tr2, stdout=PIPE) 
        return cut.stdout.read() 
    
  • En cada comando posterior, que haya pasado el objeto Popen, no que procesa stdout. Desde la sección "Replacing shell pipeline" de los documentos de subproceso, lo haces ..

    p1 = Popen(["dmesg"], stdout=PIPE) 
    p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) 
    

    ..mientras que estabas haciendo el equivalente de stdin=p1.

    El tr1 = (en el código reescrito arriba) se convertiría en línea ..

    tr1 = Popen(['tr', '-s', r"'\040'"], stdin=blame.stdout, stdout=PIPE) 
    
  • No es necesario para escapar de comandos/discusiones con subproceso, como subproceso no se ejecuta el comando en cualquier shell (a menos usted especifica shell=True). Consulte la sección Security de los documentos del subproceso.

    .. En lugar de

    proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE)) 
    

    .you puede hacerlo con seguridad ..

    Popen(['svn', 'blame', filename], stdout=PIPE) 
    
  • Como sugirió S. Lott, no utilice el subproceso de hacer más fácil la manipulación de texto a cabo en Python (los comandos tr/cut). Por un lado, tr/cut, etc. no son muy portátiles (las diferentes versiones tienen argumentos diferentes), también son bastante difíciles de leer (no tengo idea de lo que están haciendo tr y cut)

    Si tuviera que reescribir el comando, yo probablemente haría algo así ..

    def get_blame(filename): 
        blame = Popen(['svn', 'blame', filename], stdout=PIPE) 
        output = blame.communicate()[0] # preferred to blame.stdout.read() 
        # process commands output: 
        ret = [] 
        for line in output.split("\n"): 
         split_line = line.strip().split(" ") 
         if len(split_line) > 2: 
          rev = split_line[0] 
          author = split_line[1] 
          line = " ".join(split_line[2:]) 
    
          ret.append({'rev':rev, 'author':author, 'line':line}) 
    
        return ret 
    
-2

como dijo S. Lott, procesando el texto en Python es mejor.

Pero si desea utilizar las utilidades de línea de órdenes, se puede mantener legible mediante el uso de shell=True:

cmdline = r"svn blame %s | tr -s '\040' | tr '\040' ';' | cut -d \; -f 3" % shellquote(filename) 
return Popen(cmdline, shell=True, stdout=PIPE).communicate()[0] 
+1

Existe un enorme riesgo de seguridad al usar shell = True, así como una posibilidad real de que el proceso pueda colgar entre los datos de las tuberías de un comando al siguiente sin proporcionar ningún control de error real. – SummerEla

+0

@SummerEla es suficiente, aunque es una compensación: las personas lo hacen en scripts de shell todo el tiempo y lo aceptan allí. Es posible hacer la misma compensación al crear scripts en Python también. La legibilidad y la simplicidad tienen su valor también – orip

Cuestiones relacionadas