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

10

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
  • 2,028
  • 2
  • 25
  • 46
  • 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
  • 137,073
  • 23
  • 153
  • 219
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
  • 20,639
  • 6
  • 60
  • 82