Skip to content

Instantly share code, notes, and snippets.

@TySkby
Last active March 9, 2023 04:24
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save TySkby/143190ad1b88c6115597c45f996b030c to your computer and use it in GitHub Desktop.
Save TySkby/143190ad1b88c6115597c45f996b030c to your computer and use it in GitHub Desktop.
Timeout decorator/context manager using signals (for Python 3)
#!/usr/bin/env python3
"""Easily put time restrictions on things
Note: Requires Python 3.x
Usage as a context manager:
```
with timeout(10):
something_that_should_not_exceed_ten_seconds()
```
Usage as a decorator:
```
@timeout(10)
def something_that_should_not_exceed_ten_seconds():
do_stuff_with_a_timeout()
```
Handle timeouts:
```
try:
with timeout(10):
something_that_should_not_exceed_ten_seconds()
except TimeoutError:
log('Got a timeout, couldn't finish')
```
Suppress TimeoutError and just die after expiration:
```
with timeout(10, suppress_timeout_errors=True):
something_that_should_not_exceed_ten_seconds()
print('Maybe exceeded 10 seconds, but finished either way')
```
"""
import contextlib
import errno
import os
import signal
DEFAULT_TIMEOUT_MESSAGE = os.strerror(errno.ETIME)
class timeout(contextlib.ContextDecorator):
def __init__(self, seconds, *, timeout_message=DEFAULT_TIMEOUT_MESSAGE, suppress_timeout_errors=False):
self.seconds = int(seconds)
self.timeout_message = timeout_message
self.suppress = bool(suppress_timeout_errors)
def _timeout_handler(self, signum, frame):
raise TimeoutError(self.timeout_message)
def __enter__(self):
signal.signal(signal.SIGALRM, self._timeout_handler)
signal.alarm(self.seconds)
def __exit__(self, exc_type, exc_val, exc_tb):
signal.alarm(0)
if self.suppress and exc_type is TimeoutError:
return True
@samuelhwilliams
Copy link

If you could indicate/include a licence for this it would be great (especially if MIT). 👍

@austospumanto
Copy link

Love it! One note: if your "long running thing" involves something that can be chunked (like fetching a bunch of rows from a database table), then definitely do chunk if you're using this timeout context manager. Otherwise, the SIGALRM won't be able to interrupt the "long running thing".
Disclaimer: This is anecdotal.

@DFilyushin
Copy link

Unfortunately, but the gist works only under Linux.

@djm
Copy link

djm commented Jan 12, 2023

This is correct, it relies on the SIGALRM signal which only works on *nix-based systems. (src)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment