Subproceso Python mata con tiempo de espera

Estoy ejecutando algunos scripts de shell con el módulo de subproceso en python. Si los scripts de shell se están ejecutando durante mucho tiempo, me gusta matar el subproceso. Pensé que sería suficiente si estoy pasando el timeout=30 a mi sentencia de run(..) .

Aquí está el código:

 try: result=run(['utilities/shell_scripts/{0} {1} {2}'.format( self.language_conf[key][1], self.proc_dir, config.main_file)], shell=True, check=True, stdout=PIPE, stderr=PIPE, universal_newlines=True, timeout=30, bufsize=100) except TimeoutExpired as timeout: 

He probado esta llamada con algunos scripts de shell que ejecutan 120s. Esperaba que el subproceso se eliminara después de los 30, pero de hecho, el proceso está terminando la secuencia de comandos de los 120 y eso provoca la Excepción de tiempo de espera. Ahora la pregunta ¿Cómo puedo matar el subproceso por tiempo de espera?

La documentación establece explícitamente que el proceso debe ser eliminado:

de los documentos para subprocess.run :

“El argumento de tiempo de espera se pasa a Popen.communicate (). Si el tiempo de espera se agota, el proceso secundario se cancelará y se esperará. La excepción TimeoutExpired se volverá a elevar después de que el proceso secundario haya finalizado”.

Pero en su caso, está usando shell=True , y he visto problemas como ese antes, porque el proceso de locking es un elemento secundario del proceso de shell.

No creo que necesites shell=True si descompones tus argumentos correctamente y tus scripts tienen el shebang apropiado. Podrías probar esto:

 result=run( [os.path.join('utilities/shell_scripts',self.language_conf[key][1]), self.proc_dir, config.main_file], # don't compose argument line yourself shell=False, # no shell wrapper check=True, stdout=PIPE, stderr=PIPE, universal_newlines=True, timeout=30, bufsize=100) 

tenga en cuenta que puedo reproducir este problema muy fácilmente en Windows (usando Popen , pero es lo mismo):

 import subprocess,time p=subprocess.Popen("notepad",shell=True) time.sleep(1) p.kill() 

=> el bloc de notas permanece abierto, probablemente porque logra separarse del proceso de shell principal.

 import subprocess,time p=subprocess.Popen("notepad",shell=False) time.sleep(1) p.kill() 

=> Bloc de notas se cierra después de 1 segundo

Curiosamente, si elimina time.sleep() , kill() funciona incluso con shell=True probablemente porque elimina con éxito la shell que está lanzando el notepad .

No estoy diciendo que tenga exactamente el mismo problema, solo estoy demostrando que shell=True es malo por muchas razones, y el hecho de no poder detener / detener el proceso es una razón más.

Sin embargo, si necesita shell=True por alguna razón, puede usar psutil para matar a todos los niños al final. En ese caso, es mejor usar Popen para obtener la identificación del proceso directamente:

 import subprocess,time,psutil parent=subprocess.Popen("notepad",shell=True) for _ in range(30): # 30 seconds if parent.poll() is not None: # process just ended break time.sleep(1) else: # the for loop ended without break: timeout parent = psutil.Process(parent.pid) for child in parent.children(recursive=True): # or parent.children() for recursive=False child.kill() parent.kill() 

(Fuente: ¿Cómo eliminar procesos y procesos secundarios de Python? )

ese ejemplo mata a la instancia del bloc de notas también.