pytest plugin to abort tests after a timeout
This is a plugin which will terminate tests after a certain timeout. When doing so you will get a stack dump of all threads running at the time. This is useful when e.g. running tests under a continuous integration (CI) server.
Install is as simple as e.g.:
pip install pytest-timeout
By default the plugin will not time out any tests, to enable timeouts you need to set a timeout using either the command line option --timeout=N or using the likewise named configuration option in e.g. pytest.ini (see py.test docs for other files considered as configuration files):
[pytest] timeout = N
In both cases N is an integer number indicating the number of seconds before a timeout will occur. Using a timeout of 0 will result in no timeout, but will enable the plugin so you can use the marker (see below).
Furthermore it is possible to change the default timeout method used by the plugin using timeout_method as either a long option (prefixed with --) or in the configuration file. Valid values are “signal” and “thread”, details of their behaviour and implications can be found below in the “How it Works” section. Normally there is no need to worry about this however as the default will be fine.
When the plugin is enabled by the command line option or configuration file item you can control it’s behaviour on a per-test level using the timeout marker, e.g.:
@pytest.mark.timeout(5) def test_foo(): pass
will set the timeout to 5 seconds for the test. The marker can also specify the method used for a test using the method keyword:
@pytest.mark.timeout(method='thread') def test_foo(): pass
This can be useful if you have a particular test who’s code does conflict with the use of SIGALRM by this plugin. You can of course modify both the timeout and marker at the same time:
@pytest.mark.timeout(5, method='signal') def test_foo(): pass
How It Works
This plugin works in one of two ways. If the system supports the SIGALRM signal an alarm will be scheduled when a test starts and cancelled when it finishes. If the alarm expires during the test the signal handler will use pytest.fail() to interrupt the test after having dumped the stack of any other threads running to stderr.
If the system does not support SIGALRM or the “thread” timeout method was selected then a timer thread will be used instead. Once more, if this timer is not cancelled before it expires it will dump the stack of all threads to stderr before terminating the entire py.test process using os._exit(1).
The downside of the SIGALRM method is that the signal is used by the test framework. If this signal is used by the code under test you will need to use the “thread” timeout method. The limitation of the timer thread however is the extra overhead of creating a thread for each executed test and the fact that after one timeout the entire process is stopped and no further tests are executed.
- Add a marker to modify the timeout delay using a @pytest.timeout(N) syntax, thanks to Laurant Brack for the initial code.
- Allow the timeout marker to select the timeout method using the method keyword argument.
- Rename the –nosigalrm option to –method=thread to future proof support for eventlet and gevent. Thanks to Ronny Pfannschmidt for the hint.
- Add timeout and timeout_method items to the configuration file so you can enable and configure the plugin using the ini file. Thanks to Holger Krekel and Ronny Pfannschmidt for the hints.
- Tested (and fixed) for python 2.6, 2.7 and 3.2.