pythonpython-3.xsubprocesswindows-process

Why is this subprocess command not timing-out properly?


So I have this simple POC

import subprocess

proc = subprocess.run('cmd', timeout=1, shell=True, capture_output=True)

it is not raising a TimeoutError after the given interval (which is 1 second in this case)

and it seems like the timeout flag in the subprocess.run() is working just fine in Linux but seems to produce bugs in windows

For example

import subprocess

proc = subprocess.run('ping -n 10 127.0.0.1', shell=True, capture_output=True, timeout=1)

Seems to not raise the timeout error untill after the command completes

Edit of what I tried and the output:

C:\Users\zaid2\Documents>type t.py
import subprocess
import time

s = time.time()
try:
    roc = subprocess.run('ping -n 10 127.0.0.1', shell=True, capture_output=True, timeout=1)
finally:
    print(f'\nFinished in {round(time.time() - s)} seconds.\n\n')
C:\Users\zaid2\Documents>python t.py

Finished in 9 seconds.


Traceback (most recent call last):
  File "C:\Users\zaid2\Documents\t.py", line 6, in <module>
    roc = subprocess.run('ping -n 10 127.0.0.1', shell=True, capture_output=True, timeout=1)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\zaid2\AppData\Local\Programs\Python\Python311\Lib\subprocess.py", line 550, in run
    stdout, stderr = process.communicate(input, timeout=timeout)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\zaid2\AppData\Local\Programs\Python\Python311\Lib\subprocess.py", line 1209, in communicate
    stdout, stderr = self._communicate(input, endtime, timeout)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\zaid2\AppData\Local\Programs\Python\Python311\Lib\subprocess.py", line 1628, in _communicate
    raise TimeoutExpired(self.args, orig_timeout)
subprocess.TimeoutExpired: Command 'ping -n 10 127.0.0.1' timed out after 1 seconds

C:\Users\zaid2\Documents>python --version
Python 3.11.8

Solution

  • made my own timeout implementation using Popen

    import subprocess
    import time
    
    
    def run_command_with_timeout(cmd, timeout):
        start_time = time.time()
        process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
    
        while time.time() - start_time < timeout:
            if process.poll() is not None:
                return process.communicate()
            time.sleep(0.1)
    
        else:
            process.kill()
            raise TimeoutError
    
    
    
    
    if __name__ == "__main__":
        stdout, stderr = run_command_with_timeout('ping -n 10 google.com', timeout=1)
        if stdout:
            print(stdout.decode())
        if stderr:
            print(stderr.decode())
        print("Process finished")