pax_global_header00006660000000000000000000000064132073527660014525gustar00rootroot0000000000000052 comment=e305b26734b1a46a521e0e4c8894327f600af196 .hgignore000066400000000000000000000001051320735276600126700ustar00rootroot00000000000000syntax: glob build dist .tox .ropeproject .cache *~ *.pyc *.egg-info .hgtags000066400000000000000000000004771320735276600123570ustar00rootroot0000000000000036f2c379bdb3f4d24df1670f559bd32e4bb34c7a 0.1 67ad1855fe86bfad269ef2ae12c733cb30cd6ff1 0.2 e54c7a8a55c3b5bd779bd7da3ce56191c17a1709 0.3 1d2ceacdfebe912255aecc712c83ffbc5875a3ac 0.4 d1923e7c95f45fcb571b3b127c496e4e2d3c1e6f 0.5 dc4e05d45303e20d2c347912215aa31513d25f3d 1.0.0 31adcd142d8c560f512252802d4f9b2463e65191 1.2.0 LICENSE000066400000000000000000000020751320735276600121020ustar00rootroot00000000000000The MIT License Copyright (C) 2012, 2014 Floris Bruynooghe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. MANIFEST.in000066400000000000000000000001531320735276600126260ustar00rootroot00000000000000include MANIFEST.in include LICENSE include test_pytest_timeout.py include failure_demo.py include tox.ini README000066400000000000000000000204361320735276600117560ustar00rootroot00000000000000============== pytest-timeout ============== This is a plugin which will terminate tests after a certain timeout. When doing so it will show a stack dump of all threads running at the time. This is useful when running tests under a continuous integration server or simply if you don't know why the test suite hangs. Note that while by default on POSIX systems py.test will continue to execute the tests after a test has timed, out this is not always possible. Often the only sure way to interrupt a hanging test is by terminating the entire process. As this is a hard termination (``os._exit()``) it will result in no teardown, JUnit XML output etc. But the plugin will ensure you will have the debugging output on stderr nevertheless, which is the most important part at this stage. See below for detailed information on the timeout methods and their side-effects. The pytest-timeout plugin has been tested on python 2.7 or higher, including 3.X, pypy and pypy3. See tox.ini for currently tested versions. Usage ===== Install is as simple as e.g.:: pip install pytest-timeout Now you can run tests using a timeout, in seconds, after which they will be terminated:: py.test --timeout=300 Alternatively you can mark individual tests as having a timeout:: @pytest.mark.timeout(60) def test_foo(): pass By default the plugin will not time out any tests, you must specify a valid timeout for the plugin to interrupt long-running tests. A timeout is always specified as an integer number of seconds and can be defined in a number of ways, from low to high priority: 1. You can set a global timeout in the `py.test configuration file`__ using the ``timeout`` option. E.g.:: [pytest] timeout = 300 2. The ``PYTEST_TIMEOUT`` environment variable sets a global timeout overriding a possible value in the configuration file. 3. The ``--timeout`` command line option sets a global timeout overriding both the environment variable and configuration option. 4. Using the ``timeout`` marker_ on test items you can specify timeouts on a per-item basis:: @pytest.mark.timeout(300) def test_foo(): pass __ http://pytest.org/latest/customize.html#how-test-configuration-is-read-from-configuration-ini-files .. _marker: http://pytest.org/latest/mark.html Setting a timeout to 0 seconds disables the timeout, so if you have a global timeout set you can still disable the timeout by using the mark. Timeout Methods =============== Interrupting tests which hang is not always as simple and can be platform dependent. Furthermore some methods of terminating a test might conflict with the code under test itself. The pytest-timeout plugin tries to pick the most suitable method based on your platform, but occasionally you may need to specify a specific timeout method explicitly. If a timeout method does not work your safest bet is to use the *thread* method. thread ------ This is the surest and most portable method. It is also the default on systems not supporting the *signal* method. For each test item the pytest-timeout plugin starts a timer thread which will terminate the whole process after the specified timeout. When a test item finishes this timer thread is cancelled and the test run continues. The downsides of this method are that there is a relatively large overhead for running each test and that test runs are not completed. This means that other py.test features, like e.g. JUnit XML output or fixture teardown, will not function normally. The second issue might be alleviated by using the ``--boxed`` option of the pytest-xdist_ plugin. .. _pytest-xdist: http://pypi.python.org/pypi/pytest-xdist The benefit of this method is that it will always work. Furthermore it will still provide you debugging information by printing the stacks of all the threads in the application to stderr. signal ------ If the system supports the SIGALRM signal the *signal* method will be used by default. This method schedules an alarm when the test item starts and cancels it when it finishes. If the alarm expires during the test the signal handler will dump the stack of any other threads running to stderr and use ``pytest.fail()`` to interrupt the test. The benefit of this method is that the py.test process is not terminated and the test run can complete normally. The main issue to look out for with this method is that it may interfere with the code under test. If the code under test uses SIGALRM itself things will go wrong and you will have to choose the *thread* method. Specifying the Timeout Method ----------------------------- The timeout method can be specified by using the ``timeout_method`` option in the `py.test configuration file`__, the ``--timeout_method`` command line parameter or the ``timeout`` marker_. Simply set their value to the string ``thread`` or ``signal`` to override the default method. On a marker this is done using the ``method`` keyword:: @pytest.mark.timeout(method='thread') def test_foo(): pass __ http://pytest.org/latest/customize.html#how-test-configuration-is-read-from-configuration-ini-files .. _marker: http://pytest.org/latest/mark.html The ``timeout`` Marker API ========================== The full signature of the timeout marker is:: pytest.mark.timeout(timeout=0, method=DEFAULT_METHOD) You can use either positional or keyword arguments for both the timeout and the method. Neither needs to be present. See the marker api documentation_ and examples_ for the various ways markers can be applied to test items. .. _documentation: http://pytest.org/latest/mark.html .. _examples: http://pytest.org/latest/example/markers.html#marking-whole-classes-or-modules Timeouts in Fixture Teardown ============================ The plugin will happily terminate timeouts in the finalisers of fixtures. The timeout specified applies to the entire process of setting up fixtures, running the tests and finalising the fixtures. However when a timeout occurs in a fixture finaliser and the test suite continues, i.e. the signal method is used, it must be realised that subsequent fixtures which need to be finalised might not have been executed, which could result in a broken test-suite anyway. In case of doubt the thread method which terminates the entire process might result in clearer output. Changelog ========= 1.2.1 ----- - Fix for pytest 3.3, thanks Bruno Oliveira. - Update supported python versions: - Add CPython 3.6. - Drop CPyhon 2.6 (as did pytest 3.3) - Drop CPyhon 3.3 - Drop CPyhon 3.4 1.2.0 ----- * Allow using floats as timeout instead of only integers, thanks Tom Myers. 1.1.0 ----- * Report (default) timeout duration in header, thanks Holger Krekel. 1.0.0 ----- * Bump version to 1.0 to commit to semantic versioning. * Fix issue #12: Now compatible with py.test 2.8, thanks Holger Krekel. * No longer test with pexpect on py26 as it is no longer supported * Require py.test 2.8 and use new hookimpl decorator 0.5 --- * Timeouts will no longer be triggered when inside an interactive pdb session started by ``pytest.set_trace()`` / ``pdb.set_trace()``. * Add pypy3 environment to tox.ini. * Transfer repository to pytest-dev team account. 0.4 --- * Support timeouts happening in (session scoped) finalizers. * Change command line option --timeout_method into --timeout-method for consistency with py.test 0.3 --- * Added the PYTEST_TIMEOUT environment variable as a way of specifying the timeout (closes issue #2). * More flexible marker argument parsing: you can now specify the method using a positional argument. * The plugin is now enabled by default. There is no longer a need to specify ``timeout=0`` in the configuration file or on the command line simply so that a marker would work. 0.2 --- * 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. TODO000066400000000000000000000002451320735276600115620ustar00rootroot00000000000000TODO ==== * Consider checking for an existing signal handler If it exists maybe fall back to the threading based solution. * Add support for eventlet and geventfailure_demo.py000066400000000000000000000007101320735276600140740ustar00rootroot00000000000000"""Demonstration of timeout failures using pytest_timeout To use this demo, invoke py.test on it:: py.test failure_demo.py """ import threading import time import pytest def sleep(s): # Separate function to demonstrate nested calls time.sleep(s) @pytest.mark.timeout(1) def test_simple(): sleep(2) def run(): sleep(2) @pytest.mark.timeout(1) def test_thread(): t = threading.Thread(target=run) t.start() sleep(2) pytest_timeout.py000066400000000000000000000237601320735276600145510ustar00rootroot00000000000000"""Timeout for tests to stop hanging testruns This plugin will dump the stack and terminate the test. This can be useful when running tests on a continuous integration server. If the platform supports SIGALRM this is used to raise an exception in the test, otherwise os._exit(1) is used. """ import os import signal import sys import threading import traceback import py import pytest HAVE_SIGALRM = hasattr(signal, 'SIGALRM') if HAVE_SIGALRM: DEFAULT_METHOD = 'signal' else: DEFAULT_METHOD = 'thread' TIMEOUT_DESC = """ Timeout in seconds before dumping the stacks. Default is 0 which means no timeout. """.strip() METHOD_DESC = """ Timeout mechanism to use. 'signal' uses SIGALRM if available, 'thread' uses a timer thread. The default is to use 'signal' and fall back to 'thread'. """.strip() @pytest.hookimpl def pytest_addoption(parser): """Add options to control the timeout plugin""" group = parser.getgroup( 'timeout', 'Interrupt test run and dump stacks of all threads after ' 'a test times out') group.addoption('--timeout', type=float, help=TIMEOUT_DESC) group.addoption('--timeout_method', type='choice', action='store', choices=['signal', 'thread'], help='Depreacted, use --timeout-method') group.addoption('--timeout-method', dest='timeout_method', type='choice', action='store', choices=['signal', 'thread'], help=METHOD_DESC) parser.addini('timeout', TIMEOUT_DESC) parser.addini('timeout_method', METHOD_DESC) @pytest.hookimpl def pytest_configure(config): # Register the marker so it shows up in --markers output. config.addinivalue_line( 'markers', 'timeout(timeout, method=None): Set a timeout and timeout method on ' 'just one test item. The first argument, *timeout*, is the timeout ' 'in seconds while the keyword, *method*, takes the same values as the ' '--timeout_method option.') config._env_timeout, config._env_timeout_method = get_env_timeout_and_method(config) @pytest.hookimpl(hookwrapper=True) def pytest_runtest_protocol(item): timeout_setup(item) outcome = yield timeout_teardown(item) @pytest.hookimpl(tryfirst=True) def pytest_report_header(config): if config._env_timeout: return ["timeout: %ss method: %s" % (config._env_timeout, config._env_timeout_method)] @pytest.hookimpl(tryfirst=True) def pytest_exception_interact(node): timeout_teardown(node) @pytest.hookimpl def pytest_enter_pdb(): # Since pdb.set_trace happens outside of any pytest control, we don't have # any pytest ``item`` here, so we cannot use timeout_teardown. Thus, we # need another way to signify that the timeout should not be performed. global SUPPRESS_TIMEOUT SUPPRESS_TIMEOUT = True SUPPRESS_TIMEOUT = False def timeout_setup(item): """Setup up a timeout trigger and handler""" timeout, method = get_params(item) if timeout is None or timeout <= 0: return if method == 'signal': def handler(signum, frame): __tracebackhide__ = True timeout_sigalrm(item, timeout) def cancel(): signal.setitimer(signal.ITIMER_REAL, 0) signal.signal(signal.SIGALRM, signal.SIG_DFL) item.cancel_timeout = cancel signal.signal(signal.SIGALRM, handler) signal.setitimer(signal.ITIMER_REAL, timeout) elif method == 'thread': timer = threading.Timer(timeout, timeout_timer, (item, timeout)) def cancel(): timer.cancel() timer.join() item.cancel_timeout = cancel timer.start() def timeout_teardown(item): """Cancel the timeout trigger if it was set""" # When skipping is raised from a pytest_runtest_setup function # (as is the case when using the pytest.mark.skipif marker) we # may be called without our setup counterpart having been # called. cancel = getattr(item, 'cancel_timeout', None) if cancel: cancel() def get_env_timeout_and_method(config): timeout = config.getvalue('timeout') if timeout is None: timeout = _validate_timeout( os.environ.get('PYTEST_TIMEOUT'), 'PYTEST_TIMEOUT environment variable') if timeout is None: ini = config.getini('timeout') if ini: timeout = _validate_timeout(ini, 'config file') method = config.getvalue('timeout_method') if method is None: ini = config.getini('timeout_method') if ini: method = _validate_method(ini, 'config file') if method is None: method = DEFAULT_METHOD return timeout, method def get_params(item): """Return (timeout, method) for an item""" timeout = method = None if 'timeout' in item.keywords: timeout, method = _parse_marker(item.keywords['timeout']) timeout = _validate_timeout(timeout, 'marker') method = _validate_method(method, 'marker') if timeout is None: timeout = item.config._env_timeout if method is None: method = item.config._env_timeout_method return timeout, method def _parse_marker(marker): """Return (timeout, method) tuple from marker Either could be None. The values are not interpreted, so could still be bogus and even the wrong type. """ if not marker.args and not marker.kwargs: raise TypeError('Timeout marker must have at least one argument') timeout = method = NOTSET = object() for kw, val in marker.kwargs.items(): if kw == 'timeout': timeout = val elif kw == 'method': method = val else: raise TypeError( 'Invalid keyword argument for timeout marker: %s' % kw) if len(marker.args) >= 1 and timeout is not NOTSET: raise TypeError( 'Multiple values for timeout argument of timeout marker') elif len(marker.args) >= 1: timeout = marker.args[0] if len(marker.args) >= 2 and method is not NOTSET: raise TypeError( 'Multiple values for method argument of timeout marker') elif len(marker.args) >= 2: method = marker.args[1] if len(marker.args) > 2: raise TypeError('Too many arguments for timeout marker') if timeout is NOTSET: timeout = None if method is NOTSET: method = None return timeout, method def _validate_timeout(timeout, where): """Helper for get_params()""" if timeout is None: return None try: return float(timeout) except ValueError: raise ValueError('Invalid timeout %s from %s' % (timeout, where)) def _validate_method(method, where): """Helper for get_params()""" if method is None: return None if method not in ['signal', 'thread']: raise ValueError('Invalid method %s from %s' % (method, where)) return method def timeout_sigalrm(item, timeout): """Dump stack of threads and raise an exception This will output the stacks of any threads other then the current to stderr and then raise an AssertionError, thus terminating the test. """ if SUPPRESS_TIMEOUT: return __tracebackhide__ = True nthreads = len(threading.enumerate()) if nthreads > 1: write_title('Timeout', sep='+') dump_stacks() if nthreads > 1: write_title('Timeout', sep='+') pytest.fail('Timeout >%ss' % timeout) def timeout_timer(item, timeout): """Dump stack of threads and call os._exit() This disables the capturemanager and dumps stdout and stderr. Then the stacks are dumped and os._exit(1) is called. """ if SUPPRESS_TIMEOUT: return try: capman = item.config.pluginmanager.getplugin('capturemanager') if capman: if pytest.__version__ >= '3.3': stdout, stderr = capman.suspend_global_capture(item) else: stdout, stderr = capman.suspendcapture(item) else: stdout, stderr = None, None write_title('Timeout', sep='+') caplog = item.config.pluginmanager.getplugin('_capturelog') if caplog and hasattr(item, 'capturelog_handler'): log = item.capturelog_handler.stream.getvalue() if log: write_title('Captured log') write(log) if stdout: write_title('Captured stdout') write(stdout) if stderr: write_title('Captured stderr') write(stderr) dump_stacks() write_title('Timeout', sep='+') except Exception: traceback.print_exc() finally: sys.stdout.flush() sys.stderr.flush() os._exit(1) def dump_stacks(): """Dump the stacks of all threads except the current thread""" current_ident = threading.current_thread().ident for thread_ident, frame in sys._current_frames().items(): if thread_ident == current_ident: continue for t in threading.enumerate(): if t.ident == thread_ident: thread_name = t.name break else: thread_name = '' write_title('Stack of %s (%s)' % (thread_name, thread_ident)) write(''.join(traceback.format_stack(frame))) def write_title(title, stream=None, sep='~'): """Write a section title If *stream* is None sys.stderr will be used, *sep* is used to draw the line. """ if stream is None: stream = sys.stderr width = py.io.get_terminal_width() fill = int((width - len(title) - 2) / 2) line = ' '.join([sep * fill, title, sep * fill]) if len(line) < width: line += sep * (width - len(line)) stream.write('\n' + line + '\n') def write(text, stream=None): """Write text to stream Pretty stupid really, only here for symetry with .write_title(). """ if stream is None: stream = sys.stderr stream.write(text) setup.cfg000066400000000000000000000000331320735276600127060ustar00rootroot00000000000000[bdist_wheel] universal = 1setup.py000066400000000000000000000017271320735276600126120ustar00rootroot00000000000000from setuptools import setup setup( name='pytest-timeout', description='py.test plugin to abort hanging tests', long_description=open("README").read(), version='1.2.1', author='Floris Bruynooghe', author_email='flub@devork.be', url='http://bitbucket.org/pytest-dev/pytest-timeout/', license='MIT', py_modules=['pytest_timeout'], entry_points={'pytest11': ['timeout = pytest_timeout']}, install_requires=['pytest>=2.8.0'], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Environment :: Plugins', 'Intended Audience :: Developers', 'License :: DFSG approved', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Topic :: Software Development :: Testing', ], ) test_pytest_timeout.py000066400000000000000000000153721320735276600156100ustar00rootroot00000000000000import os import os.path import signal import time import pytest pytest_plugins = 'pytester' have_sigalrm = pytest.mark.skipif(not hasattr(signal, "SIGALRM"), reason='OS does not have SIGALRM') @pytest.fixture def testdir(testdir): if hasattr(testdir, "runpytest_subprocess"): # on pytest-2.8 runpytest runs inline by default # patch the testdir instance to use the subprocess method testdir.runpytest = testdir.runpytest_subprocess return testdir def test_header(testdir): testdir.makepyfile(""" def test_x(): pass """) result = testdir.runpytest('--timeout=1') result.stdout.fnmatch_lines([ 'timeout: 1.0s method:*' ]) @have_sigalrm def test_sigalrm(testdir): testdir.makepyfile(""" import time def test_foo(): time.sleep(2) """) result = testdir.runpytest('--timeout=1.5') result.stdout.fnmatch_lines([ '*Failed: Timeout >1.5s*' ]) def test_thread(testdir): testdir.makepyfile(""" import time def test_foo(): time.sleep(2) """) result = testdir.runpytest('--timeout=1', '--timeout_method=thread') result.stderr.fnmatch_lines([ '*++ Timeout ++*', '*~~ Stack of MainThread* ~~*', '*File *, line *, in *', '*++ Timeout ++*', ]) assert '++ Timeout ++' in result.stderr.lines[-1] def test_timeout_env(testdir, monkeypatch): testdir.makepyfile(""" import time def test_foo(): time.sleep(2) """) monkeypatch.setitem(os.environ, 'PYTEST_TIMEOUT', '1') result = testdir.runpytest() assert result.ret > 0 # @pytest.mark.parametrize('meth', [have_sigalrm('signal'), 'thread']) # def test_func_fix(meth, testdir): # testdir.makepyfile(""" # import time, pytest # @pytest.fixture(scope='function') # def fix(): # time.sleep(2) # def test_foo(fix): # pass # """) # result = testdir.runpytest('--timeout=1', # '--timeout_method={0}'.format(meth)) # assert result.ret > 0 # assert 'Timeout' in result.stdout.str() + result.stderr.str() @pytest.mark.parametrize('meth', [have_sigalrm('signal'), 'thread']) @pytest.mark.parametrize('scope', ['function', 'class', 'module', 'session']) def test_fix_setup(meth, scope, testdir): testdir.makepyfile(""" import time, pytest class TestFoo: @pytest.fixture(scope='{scope}') def fix(self): time.sleep(2) def test_foo(self, fix): pass """.format(scope=scope)) result = testdir.runpytest('--timeout=1', '--timeout_method={0}'.format(meth)) assert result.ret > 0 assert 'Timeout' in result.stdout.str() + result.stderr.str() @pytest.mark.parametrize('meth', [have_sigalrm('signal'), 'thread']) @pytest.mark.parametrize('scope', ['function', 'class', 'module', 'session']) def test_fix_finalizer(meth, scope, testdir): testdir.makepyfile(""" import time, pytest class TestFoo: @pytest.fixture(scope='{scope}') def fix(self, request): print('fix setup') def fin(): print('fix finaliser') time.sleep(2) request.addfinalizer(fin) def test_foo(self, fix): pass """.format(scope=scope)) result = testdir.runpytest('--timeout=1', '-s', '--timeout_method={0}'.format(meth)) assert result.ret > 0 assert 'Timeout' in result.stdout.str() + result.stderr.str() @have_sigalrm def test_timeout_mark_sigalrm(testdir): testdir.makepyfile(""" import time, pytest @pytest.mark.timeout(1) def test_foo(): time.sleep(2) assert False """) result = testdir.runpytest() result.stdout.fnmatch_lines(['*Failed: Timeout >1.0s*']) def test_timeout_mark_timer(testdir): testdir.makepyfile(""" import time, pytest @pytest.mark.timeout(1) def test_foo(): time.sleep(2) """) result = testdir.runpytest('--timeout_method=thread') result.stderr.fnmatch_lines(['*++ Timeout ++*']) def test_timeout_mark_non_int(testdir): testdir.makepyfile(""" import time, pytest @pytest.mark.timeout(0.5) def test_foo(): time.sleep(1) """) result = testdir.runpytest('--timeout_method=thread') result.stderr.fnmatch_lines(['*++ Timeout ++*']) def test_timeout_mark_non_number(testdir): testdir.makepyfile(""" import pytest @pytest.mark.timeout('foo') def test_foo(): pass """) result = testdir.runpytest() result.stdout.fnmatch_lines(['*ValueError*']) def test_timeout_mark_args(testdir): testdir.makepyfile(""" import pytest @pytest.mark.timeout(1, 2) def test_foo(): pass """) result = testdir.runpytest() result.stdout.fnmatch_lines(['*ValueError*']) def test_timeout_mark_method_nokw(testdir): testdir.makepyfile(""" import time, pytest @pytest.mark.timeout(1, 'thread') def test_foo(): time.sleep(2) """) result = testdir.runpytest() result.stderr.fnmatch_lines(['*+ Timeout +*']) def test_timeout_mark_noargs(testdir): testdir.makepyfile(""" import pytest @pytest.mark.timeout def test_foo(): pass """) result = testdir.runpytest() result.stdout.fnmatch_lines(['*TypeError*']) def test_ini_timeout(testdir): testdir.makepyfile(""" import time def test_foo(): time.sleep(2) """) testdir.makeini(""" [pytest] timeout = 1.5 """) result = testdir.runpytest() assert result.ret def test_ini_method(testdir): testdir.makepyfile(""" import time def test_foo(): time.sleep(2) """) testdir.makeini(""" [pytest] timeout = 1 timeout_method = thread """) result = testdir.runpytest() assert '=== 1 failed in ' not in result.outlines[-1] def test_marker_help(testdir): result = testdir.runpytest('--markers') result.stdout.fnmatch_lines(['@pytest.mark.timeout(*']) def test_suppresses_timeout_when_pdb_is_entered(testdir): p1 = testdir.makepyfile(""" import pytest, pdb @pytest.mark.timeout(1) def test_foo(): pdb.set_trace() """) child = testdir.spawn_pytest(str(p1)) child.expect("test_foo") time.sleep(2) child.send('c\n') child.sendeof() result = child.read() if child.isalive(): child.wait() assert b'Timeout >1.0s' not in result tox.ini000066400000000000000000000002251320735276600124030ustar00rootroot00000000000000[pytest] minversion = 2.8 addopts = -ra [tox] envlist = py27,py35,py36,pypy,pypy3 [testenv] deps = pytest pexpect commands = py.test {posargs}