Timeout is very useful when you want to limit the max time for calling a function or running a command. Here are my two Python implementations. (I’m using Python 3.6.5).
Use multiprocessing to timeout a Python function
Use one process to keep time/check timeout and another process to call this Python function.
import time
from itertools import count
from multiprocessing import Processdef inc_forever():
print('Starting function inc_forever()...')
while True:
time.sleep(1)
print(next(counter))def return_zero():
print('Starting function return_zero()...')
return 0if __name__ == '__main__':
# counter is an infinite iterator
counter = count(0) p1 = Process(target=inc_forever, name='Process_inc_forever')
p2 = Process(target=return_zero, name='Process_return_zero')
p1.start()
p2.start()
p1.join(timeout=5)
p2.join(timeout=5)
p1.terminate()
p2.terminate()if p1.exitcode is None:
print(f'Oops, {p1} timeouts!')if p2.exitcode == 0:
print(f'{p2} is luck and finishes in 5 seconds!')
The output is as follows. The exit code tells whether the function is a timeout (exitcode=None) or finished.
Starting function inc_forever()...
Starting function return_zero()...
0
1
2
3
4
Oops, <Process(Process_inc_forever, started)> timeouts!
<Process(Process_return_zero, stopped)> is luck and finishes in 5 seconds!
Use subprocess to timeout an external command
Since Python 3.5, there’s a handy and recommendedrun()
API in subprocess module, which has built-in timeout support.
import subprocessr = subprocess.run(['echo', 'hello timeout'], timeout=5)
print(
f'''type(r)={type(r)},
r.args={r.args},
r.returncode={r.returncode},
r.stdout={r.stdout},
r.stderr={r.stderr}'''
)try:
r = subprocess.run(['ping', 'www.google.com'], timeout=5)
except subprocess.TimeoutExpired as e:
print(e)
When timeout, it raises a TimeoutExpired
exception.
hello timeout
type(r)=<class 'subprocess.CompletedProcess'>,
r.args=['echo', 'hello timeout'],
r.returncode=0,
r.stdout=None,
r.stderr=None
PING www.google.com (216.58.194.164) 56(84) bytes of data.
64 bytes from ...: icmp_seq=1 ttl=54 time=10.4 ms
64 bytes from ...: icmp_seq=2 ttl=54 time=5.90 ms
64 bytes from ...: icmp_seq=3 ttl=54 time=6.19 ms
64 bytes from ...: icmp_seq=4 ttl=54 time=9.04 ms
64 bytes from ...: icmp_seq=5 ttl=54 time=16.7 ms
Command '['ping', 'www.google.com']' timed out after 5 seconds
For UNIX, there’s another way to use the signal module to signal a handler to raise an exception after 5 seconds, but I think it’s a little low-level and not straightforward.