Entrada y tubería muy grandes usando subproceso. Abrir

Tengo un problema bastante simple. Tengo un archivo grande que pasa por tres pasos, un paso de deencoding usando un progtwig externo, algo de procesamiento en Python, y luego la reencoding usando otro progtwig externo. He estado usando subprocess.Popen () para tratar de hacer esto en Python en lugar de formar tuberías de Unix. Sin embargo, todos los datos se almacenan en la memoria intermedia. ¿Hay alguna manera en que Pythonic haga esta tarea, o es mejor volver a un simple script en python que lee desde stdin y escribe en stdout con canales UNIX en cada lado?

import os, sys, subprocess def main(infile,reflist): print infile,reflist samtoolsin = subprocess.Popen(["samtools","view",infile], stdout=subprocess.PIPE,bufsize=1) samtoolsout = subprocess.Popen(["samtools","import",reflist,"-", infile+".tmp"],stdin=subprocess.PIPE,bufsize=1) for line in samtoolsin.stdout.read(): if(line.startswith("@")): samtoolsout.stdin.write(line) else: linesplit = line.split("\t") if(linesplit[10]=="*"): linesplit[9]="*" samtoolsout.stdin.write("\t".join(linesplit)) 

Trate de hacer este pequeño cambio, ver si la eficiencia es mejor.

  for line in samtoolsin.stdout: if(line.startswith("@")): samtoolsout.stdin.write(line) else: linesplit = line.split("\t") if(linesplit[10]=="*"): linesplit[9]="*" samtoolsout.stdin.write("\t".join(linesplit)) 

Popen tiene un parámetro bufsize que limitará el tamaño del búfer en la memoria. Si no desea que los archivos estén en la memoria, puede pasar los objetos de archivo como parámetros stdin y stdin . De los documentos de subproceso :

bufsize, si se proporciona, tiene el mismo significado que el argumento correspondiente a la función open () incorporada: 0 significa sin búfer, 1 significa línea con búfer, cualquier otro valor positivo significa usar un búfer de (aproximadamente) ese tamaño. Un bufsize negativo significa usar el valor predeterminado del sistema, que generalmente significa que está completamente almacenado en búfer. El valor predeterminado para bufsize es 0 (sin búfer).

Sin embargo, todos los datos se almacenan en la memoria intermedia …

¿Está utilizando subprocess.Popen.communicate() ? Por diseño, esta función esperará a que finalice el proceso, mientras acumula los datos en un búfer, y luego se los devolverá. Como ha señalado, esto es problemático si se trata de archivos muy grandes.

Si desea procesar los datos mientras se generan, deberá escribir un bucle utilizando los métodos poll() y .stdout.read() , luego escribir esa salida en otro socket / archivo / etc.

Asegúrese de observar las advertencias en la documentación para no hacer esto, ya que es fácil generar un punto muerto (el proceso principal espera que el proceso secundario genere datos, quien a su vez espera que el proceso principal vacíe el búfer de la tubería) .

Estaba usando el método .read () en la secuencia stdout. En su lugar, simplemente necesitaba leer directamente de la secuencia en el bucle for anterior. El código corregido hace lo que esperaba.

  #! / usr / bin / env python
 importación OS
 sistemas de importación
 subproceso de importación

 def principal (infile, reflist):
     Imprimir infile, reflist
     samtoolsin = subprocess.Popen (["samtools", "view", infile],
                                   stdout = subprocess.PIPE, bufsize = 1)
     samtoolsout = subprocess.Popen (["samtools", "import", reflist, "-",
                                     infile + ". tmp"], stdin = subprocess.PIPE, bufsize = 1)
     para la línea en samtoolsin.stdout:
         if (line.startswith ("@")):
             samtoolsout.stdin.write (línea)
         más:
             linesplit = line.split ("\ t")
             if (linesplit [10] == "*"):
                 linesplit [9] = "*"
             samtoolsout.stdin.write ("\ t" .join (división de líneas))

Tratando de hacer algunas tuberías básicas de shell con una entrada muy grande en python:

 svnadmin load /var/repo < r0-100.dump 

Encontré que la forma más sencilla de hacer que esto funcionara, incluso con archivos grandes (2-5GB) era:

 subprocess.check_output('svnadmin load %s < %s' % (repo, fname), shell=True) 

Me gusta este método porque es simple y se puede hacer una redirección de shell estándar.

Intenté ir a la ruta de Popen para ejecutar una redirección:

 cmd = 'svnadmin load %s' % repo p = Popen(cmd, stdin=PIPE, stdout=PIPE, shell=True) with open(fname) as inline: for line in inline: p.communicate(input=line) 

Pero eso se rompió con archivos grandes. Utilizando:

 p.stdin.write() 

También se rompió con archivos muy grandes.