python windows stdout multiprocessing stderr

Multiprocesamiento de Python: ¿Cómo puedo CONFIANZA redirigir la salida estándar de un proceso secundario?



windows stdout (3)

Asumo que estoy fuera de lugar y me estoy perdiendo algo, pero para lo que vale la pena aquí es lo que me vino a la mente cuando leí tu pregunta.

Si puede interceptar todos los stdout y stderr (tengo esa impresión de su pregunta), ¿por qué no agregar o envolver esa funcionalidad de captura en cada uno de sus procesos? Luego, envíe lo que se captura a través de una cola a un consumidor que puede hacer lo que quiera con todas las salidas.

NÓTESE BIEN. He visto la salida del registro de multiprocesamiento . Proceso : desafortunadamente, no responde a esta pregunta.

Estoy creando un proceso hijo (en Windows) a través de multiprocesamiento. Quiero que toda la salida stdout y stderr del proceso hijo se redirija a un archivo de registro, en lugar de aparecer en la consola. La única sugerencia que he visto es que el proceso secundario establezca sys.stdout en un archivo. Sin embargo, esto no redirige de manera efectiva toda la salida de la salida estándar, debido al comportamiento de la redirección de la salida estándar en Windows.

Para ilustrar el problema, cree una DLL de Windows con el siguiente código

#include <iostream> extern "C" { __declspec(dllexport) void writeToStdOut() { std::cout << "Writing to STDOUT from test DLL" << std::endl; } }

Luego, cree y ejecute una secuencia de comandos de Python como la siguiente, que importa esta DLL y llama a la función:

from ctypes import * import sys print print "Writing to STDOUT from python, before redirect" print sys.stdout = open("stdout_redirect_log.txt", "w") print "Writing to STDOUT from python, after redirect" testdll = CDLL("Release/stdout_test.dll") testdll.writeToStdOut()

Para ver el mismo comportamiento que yo, es probable que la DLL se cree en un tiempo de ejecución de C diferente al que utiliza Python. En mi caso, Python está creado con Visual Studio 2010, pero mi DLL está creado con VS 2005.

El comportamiento que veo es que la consola muestra:

> stdout_test.py Writing to STDOUT from python, before redirect Writing to STDOUT from test DLL

Mientras que el archivo stdout_redirect_log.txt termina conteniendo:

Writing to STDOUT from python, after redirect

En otras palabras, la configuración de sys.stdout no pudo redirigir la salida de salida estándar generada por la DLL. Esto no es sorprendente dada la naturaleza de las API subyacentes para la redirección de la salida estándar en Windows. He encontrado este problema en el nivel nativo / C ++ antes y nunca encontré una manera de redirigir de manera confiable la salida estándar desde un proceso. Tiene que ser hecho externamente.

Esta es realmente la razón por la que estoy iniciando un proceso secundario: es para poder conectarme externamente a sus tuberías y así garantizar que intercepto toda su salida. Definitivamente puedo hacer esto iniciando el proceso manualmente con pywin32, pero me gustaría mucho poder usar las facilidades de multiprocesamiento, en particular la capacidad de comunicarse con el proceso hijo a través de un objeto Pipe de multiprocesamiento, para avanzar. actualizaciones La pregunta es si hay alguna forma de usar el multiprocesamiento para sus instalaciones de IPC y de redirigir de manera confiable todas las salidas stdout y stderr del niño a un archivo.

ACTUALIZACIÓN: Al observar el código fuente de multiprocessing.Processs, tiene un miembro estático, _Popen, que parece que se puede usar para anular la clase utilizada para crear el proceso. Si se establece en Ninguno (predeterminado), utiliza multiprocessing.forking._Popen, pero parece que al decir

multiprocessing.Process._Popen = MyPopenClass

Podría anular la creación del proceso. Sin embargo, aunque podría derivar de multiprocessing.forking._Popen, parece que tendría que copiar un montón de cosas internas en mi implementación, lo que suena inestable y no muy a prueba de futuro. Si esa es la única opción, creo que probablemente me complacería hacerlo manualmente con pywin32.


La solución que sugiere es buena: cree sus procesos manualmente de modo que tenga acceso explícito a sus controladores de archivos stdout / stderr. Luego puede crear un socket para comunicarse con el subproceso y usar multiprocessing.connection a través de ese socket (multiprocessing.Pipe crea el mismo tipo de objeto de conexión, por lo que esto debería proporcionarle la misma funcionalidad de IPC).

Aquí hay un ejemplo de dos archivos.

master.py:

import multiprocessing.connection import subprocess import socket import sys, os ## Listen for connection from remote process (and find free port number) port = 10000 while True: try: l = multiprocessing.connection.Listener((''localhost'', int(port)), authkey="secret") break except socket.error as ex: if ex.errno != 98: raise port += 1 ## if errno==98, then port is not available. proc = subprocess.Popen((sys.executable, "subproc.py", str(port)), stdout=subprocess.PIPE, stderr=subprocess.PIPE) ## open connection for remote process conn = l.accept() conn.send([1, "asd", None]) print(proc.stdout.readline())

subproc.py:

import multiprocessing.connection import subprocess import sys, os, time port = int(sys.argv[1]) conn = multiprocessing.connection.Client((''localhost'', port), authkey="secret") while True: try: obj = conn.recv() print("received: %s/n" % str(obj)) sys.stdout.flush() except EOFError: ## connection closed break

También es posible que desee ver la primera respuesta a esta pregunta para obtener lecturas sin bloqueo del subproceso.


No creo que tenga una mejor opción que redirigir un subproceso a un archivo como mencionó en su comentario.

La forma en que funcionan las consolas stdin / out / err en Windows es que cada proceso cuando nace tiene sus controladores std definidos. Puedes cambiarlos con SetStdHandle . Cuando modificas sys.stdout de python, solo modificas donde python imprime cosas, no donde otras DLL están imprimiendo cosas. Parte del CRT en su DLL es usar GetStdHandle para averiguar dónde imprimir. Si lo desea, puede hacer cualquier canalización que desee en la API de Windows en su DLL o en su script de python con pywin32. Aunque creo que será más sencillo con el subprocess .