2011-07-29 12 views
17

tengo el siguiente trozo de código Python (v2.7 corriendo) que resulta en MemoryError excepciones que son lanzadas cuando trabajo con grandes (varios GB) archivos:Alternativas a Python Popen.communicate() limitaciones de memoria?

myProcess = Popen(myCmd, shell=True, stdout=PIPE, stderr=PIPE) 
myStdout, myStderr = myProcess.communicate() 
sys.stdout.write(myStdout) 
if myStderr: 
    sys.stderr.write(myStderr) 

Al leer la documentation to Popen.communicate(), no parece haber algunos de amortiguación pasando:

Nota los datos leídos se tampona en la memoria, así que no use este método si el tamaño de los datos es grande o ilimitado.

¿Hay alguna manera de desactivar este almacenamiento en búfer, o forzar que el caché se borre periódicamente mientras se ejecuta el proceso?

¿Qué enfoque alternativo debo usar en Python para ejecutar un comando que transmite gigabytes de datos a stdout?

Debo señalar que necesito gestionar los flujos de salida y error.

+1

Necesito poder transmitir la salida y el error. –

Respuesta

5

creo que he encontrado una solución:

myProcess = Popen(myCmd, shell=True, stdout=PIPE, stderr=PIPE) 
for ln in myProcess.stdout: 
    sys.stdout.write(ln) 
for ln in myProcess.stderr: 
    sys.stderr.write(ln) 

Esto parece que mi uso de la memoria lo suficiente como para obtener a través de la tarea.

actualización

Recientemente he encontrado una manera más flexible de los flujos de datos de entrega en Python, utilizando threads. ¡Es interesante que Python es tan pobre en algo que los scripts de shell pueden hacer tan fácilmente!

+0

Eso se ve interesante. Voy a probar eso también. –

+4

Esto ignora la 'Advertencia: utilice comunicar() en lugar de .stdin.write, .stdout.read o .stderr.read para evitar interbloqueos debido a que cualquiera de los demás búferes de canal del SO se llenan y bloquean el proceso secundario. la documentación. Probablemente funcione en general, pero existe un riesgo potencial de un punto muerto en 'for ln in myProcess.stdout:' if 'myProcess.stderr' alguna vez se llena. Vine aquí buscando una solución para esto yo mismo. – antak

+1

Por cierto, usar izip_longest() solo ayudará si stdout y stderr son más o menos del mismo tamaño. Si uno se agota antes que el otro, bloqueará y el otro se amortiguará en su totalidad hasta que el proceso finalice. En este caso, el uso de la memoria no se reducirá y en realidad puede ser peor que con '.communicate()', ya que bloqueará si el buffer interno de la izquierda se llena. (Este búfer suele ser mucho más pequeño que lo que '.communicate()' puede asignar.) Al menos con la solución de @ Alex, se da prioridad a '.stdout', que probablemente contenga más datos. – antak

3

Lo que probablemente haría en su lugar, si tuviera que leer el stdout para algo tan grande, es enviarlo a un archivo en la creación del proceso.

with open(my_large_output_path, 'w') as fo: 
    with open(my_large_error_path, 'w') as fe: 
     myProcess = Popen(myCmd, shell=True, stdout=fo, stderr=fe) 

Editar: Si tiene que transmitir, podría intentar hacer un objeto de fichero y pasándolo a stdout y stderr. (No he probado esto, sin embargo). Luego, podría leer (consultar) del objeto tal como está siendo escrito.