7

I am trying to get subprocess output (on Windows) at the time the TimeoutExpired exception is raised. Any ideas?

try:
    proc = subprocess.run(cmd,timeout=3)
except subprocess.TimeoutExpired:
    print(???)
Ali_G
  • 71
  • 1
  • 3
  • Since Python 3.5, the [`TimeoutExpired` exception](https://docs.python.org/3/library/subprocess.html#subprocess.TimeoutExpired) seems to have `stdout` and `stderr` attributes for that purpose (assuming you set `capture_output=True` in you call to `run`). However, it doesn't seem to work for me currently. – ingomueller.net Dec 14 '20 at 11:30

3 Answers3

8

You need to use Popen and subprocess.PIPE in order to catch the process output when timeout expires. In particular Popen.communicate is what you need. Here is an example

proc = subprocess.Popen(["ping", "192.168.1.1"],
                        stdout=subprocess.PIPE)

try:
    output, error = proc.communicate(timeout=2)
except subprocess.TimeoutExpired:
    proc.kill()
    output, error = proc.communicate()
    print(output)
    print(error)

This will print the process output up to the time out expiration.

lch
  • 1,892
  • 2
  • 23
  • 41
  • Thnx for the answer @Leonardo. I tried it but it just prints the command and not the output of the process: "Command '[..., ...]' timed out after 4 seconds" (just like the 2nd output line in your example). I would like to print the subprocess output at the time it terminates. – Ali_G Apr 20 '17 at 14:22
  • @Ali_G I've found a solution, please let me know. – lch Apr 21 '17 at 08:45
  • That doesn't work for me neither... I think the problem is that mentioned by @J.F. Sebastian in some other posts (i.e [link](http://stackoverflow.com/questions/33886406/how-to-avoid-the-deadlock-in-a-subprocess-without-using-communicate/33886970#33886970)). Thnx anyway! – Ali_G Apr 21 '17 at 14:42
2

If you cannot use timeout for whatever reason (one being a too old python version), here's my solution, which works with whatever python version:

  • create a thread that first waits then kills the subprocess object
  • in the main thread, read the lines in a loop.

I'm using a python subprocess, running with the -u (unbuffered) option:

transmitter.py: (test program which prints "hello xx" every 1/10th second)

import time

i=0
while True:
    print("hello {}".format(i))
    i += 1
    time.sleep(0.1)

the program itself (timeout set to 1.5 second):

import subprocess,threading,time

def timeout(p,timeout):
    time.sleep(timeout)
    p.kill()

p = subprocess.Popen(["python","-u","transmitter.py"],stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
t = threading.Thread(target=timeout,args=(p,1.5))
t.start()
output = []
for line in p.stdout:
    output.append(line.decode())

t.join()
print("".join(output))

In the end, after timeout, the program prints:

hello 0
hello 1
hello 2
hello 3
hello 4
hello 5
hello 6
hello 7
hello 8
hello 9
hello 10
hello 11
hello 12
hello 13
hello 14
Jean-François Fabre
  • 131,796
  • 23
  • 122
  • 195
0

Here is the way for capturing stdout for multiprocessing.Process

import app
import sys
import io
from multiprocessing import Process


def run_app(some_param):
    sys.stdout = io.TextIOWrapper(open(sys.stdout.fileno(), 'wb', 0), write_through=True)
    app.run()

app_process = Process(target=run_app, args=('some_param',))
app_process.start()
# Use app_process.termninate() for python <= 3.7.
app_process.kill() 
Andriy Ivaneyko
  • 18,421
  • 4
  • 52
  • 73