0

What is the best way to timeout while loop in python

say:

while not buff.endswith('/abc #'):

After 10 secs, if it does not match, break the loop.

Thanks

voidpro
  • 1,652
  • 13
  • 27
kitty
  • 105
  • 1
  • 3
  • 16
  • 3
    Add more clarity to the question please. Do you want to break the `while` loop after 10 seconds? – voidpro Jun 07 '17 at 05:22
  • I'm amazed no one suggested some kind of `sleep` to prevent the code from blocking other threads. Is `buf` populated asynchronously? – jpmc26 Jun 07 '17 at 06:07

3 Answers3

3

You can record the time before the loop, then inside the while loop you can compare the current time, and if it's > 10 seconds, you can break out of the while loop.

Something like:

from datetime import datetime

start_time = datetime.now()
print(start_time)

while not buff.endswith('/abc #'):
    print('waiting')
    time_delta = datetime.now() - start_time
    print(time_delta)
    if time_delta.total_seconds() >= 10:
        break
Lindsay Ward
  • 469
  • 4
  • 11
  • Shouldn't that be `time_delta.total_seconds()` instead of `time_delta.seconds`? See: https://stackoverflow.com/a/17191361/5815327 – Deantwo Feb 13 '19 at 13:47
  • @Deantwo - both will work the same here. The answer you are referring to is in a different context, where `seconds` is the seconds value of a time that could have many minutes and hours. Here, both `total_seconds` and `seconds` are for a time that starts from `0` and so return the same value (no minutes or hours are possible since we break after 10 seconds). – Lindsay Ward Feb 14 '19 at 22:56
  • 2
    If you believe you can reliably say that your code will never ever be used to make a timeout that is longer than 59 seconds, sure it doesn't matter. Just seems like an issue waiting to happen. – Deantwo Feb 15 '19 at 08:16
  • That makes sense @Deantwo - if the person maintaining the code ever wanted to change `10` to something > 59, then this would indeed be an issue. `seconds` is valid in this context (< 60) only, whereas `total_seconds()` would be valid for any timeout value. – Lindsay Ward Feb 16 '19 at 10:55
1

If your only concern is to end the loop after 10 seconds, try the below code.

from datetime import datetime 
t1 = datetime.now()
while (datetime.now()-t1).seconds <= 10:
  #do something
  print(datetime.now())

Else check for the time difference inside the loop and break it. Like,

t1 = datetime.now()  
while not buff.endswith('/abc #'):
  if (datetime.now()-t1).seconds > 10:
    break
voidpro
  • 1,652
  • 13
  • 27
  • Shouldn't that be `(datetime.now()-t1).total_seconds()` instead of `.seconds`? See: https://stackoverflow.com/a/17191361/5815327 – Deantwo Feb 13 '19 at 11:34
1

You can use interrupting cow package and put you code inside a with management statement.

import interruptingcow
TIME_WAIT=20 #time in seconds

class TimeOutError(Exception):
    """InterruptingCow exceptions cause by timeout"""
    pass

with interruptingcow.timeout(TIME_WAIT, exception=TimeOutError):
    while not buff.endswith('/abc #'):
        pass #do something or just pass

As long you don't use interruptingcow with another systems that implements SIGALARM eg. stopit, python-rq. It will work

Jorge Mendes
  • 388
  • 3
  • 14
  • **Doesn't work for Windows OS**. See [this SO](https://stackoverflow.com/a/52779986/8965861) and [this issue](https://bitbucket.org/evzijst/interruptingcow/issues/6) – LukeSavefrogs Oct 20 '22 at 14:01