summaryrefslogtreecommitdiffstats
path: root/contrib/python/pytest-timeout/py3
diff options
context:
space:
mode:
authorpefavel <[email protected]>2025-09-23 15:06:19 +0300
committerpefavel <[email protected]>2025-09-23 15:24:02 +0300
commit13183af1efa6b08ffa7a942d1282ee35f66094f2 (patch)
tree5d84cb0849019ba34df77c0d55c5180755d41918 /contrib/python/pytest-timeout/py3
parent555a474f62a7f1948772f5375e7f70bb511937aa (diff)
Add test timeout to nemesis
feat: add per test timeout for nemesis commit_hash:0808539c32e3ddf9c1fa99b669fd5d0efc4d767d
Diffstat (limited to 'contrib/python/pytest-timeout/py3')
-rw-r--r--contrib/python/pytest-timeout/py3/.dist-info/METADATA623
-rw-r--r--contrib/python/pytest-timeout/py3/.dist-info/entry_points.txt2
-rw-r--r--contrib/python/pytest-timeout/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/pytest-timeout/py3/LICENSE22
-rw-r--r--contrib/python/pytest-timeout/py3/README.rst588
-rw-r--r--contrib/python/pytest-timeout/py3/pytest_timeout.py558
-rw-r--r--contrib/python/pytest-timeout/py3/ya.make27
7 files changed, 1821 insertions, 0 deletions
diff --git a/contrib/python/pytest-timeout/py3/.dist-info/METADATA b/contrib/python/pytest-timeout/py3/.dist-info/METADATA
new file mode 100644
index 00000000000..90ae6ab7865
--- /dev/null
+++ b/contrib/python/pytest-timeout/py3/.dist-info/METADATA
@@ -0,0 +1,623 @@
+Metadata-Version: 2.4
+Name: pytest-timeout
+Version: 2.4.0
+Summary: pytest plugin to abort hanging tests
+Home-page: https://github.com/pytest-dev/pytest-timeout
+Author: Floris Bruynooghe
+Author-email: [email protected]
+License: MIT
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Environment :: Plugins
+Classifier: Intended Audience :: Developers
+Classifier: License :: DFSG approved
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Topic :: Software Development :: Testing
+Classifier: Framework :: Pytest
+Requires-Python: >=3.7
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Requires-Dist: pytest>=7.0.0
+Dynamic: license-file
+
+==============
+pytest-timeout
+==============
+
+|python| |version| |anaconda| |ci| |pre-commit|
+
+.. |version| image:: https://img.shields.io/pypi/v/pytest-timeout.svg
+ :target: https://pypi.python.org/pypi/pytest-timeout
+
+.. |anaconda| image:: https://img.shields.io/conda/vn/conda-forge/pytest-timeout.svg
+ :target: https://anaconda.org/conda-forge/pytest-timeout
+
+.. |ci| image:: https://github.com/pytest-dev/pytest-timeout/workflows/build/badge.svg
+ :target: https://github.com/pytest-dev/pytest-timeout/actions
+
+.. |python| image:: https://img.shields.io/pypi/pyversions/pytest-timeout.svg
+ :target: https://pypi.python.org/pypi/pytest-timeout/
+
+.. |pre-commit| image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest-timeout/master.svg
+ :target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest-timeout/master
+
+
+.. warning::
+
+ Please read this README carefully and only use this plugin if you
+ understand the consequences. This plugin is designed to catch
+ excessively long test durations like deadlocked or hanging tests,
+ it is not designed for precise timings or performance regressions.
+ Remember your test suite should aim to be **fast**, with timeouts
+ being a last resort, not an expected failure mode.
+
+This plugin will time each test and terminate it when it takes too
+long. Termination may or may not be graceful, please see below, but
+when aborting it will show a stack dump of all thread 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::
+
+ While by default on POSIX systems pytest 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 3.6 and higher,
+including PyPy3. See tox.ini for currently tested versions.
+
+
+Usage
+=====
+
+Install is as simple as e.g.::
+
+ pip install pytest-timeout
+
+Now you can run the test suite while setting a timeout in seconds, any
+individual test which takes longer than the given duration will be
+terminated::
+
+ pytest --timeout=300
+
+Furthermore you can also use a decorator to set the timeout for an
+individual test. If combined with the ``--timeout`` flag this will
+override the timeout for this individual test:
+
+.. code:: python
+
+ @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 a 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 `pytest configuration file`__
+ using the ``timeout`` option. E.g.:
+
+ .. code:: ini
+
+ [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:
+
+ .. code:: python
+
+ @pytest.mark.timeout(300)
+ def test_foo():
+ pass
+
+__ https://docs.pytest.org/en/latest/reference.html#ini-options-ref
+
+.. _marker: https://docs.pytest.org/en/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 pytest features, like e.g. JUnit XML output or
+fixture teardown, will not function normally. The second issue might
+be alleviated by using the ``--forked`` option of the pytest-forked_
+plugin.
+
+.. _pytest-forked: https://pypi.org/project/pytest-forked/
+
+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 the alarm when the test 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 pytest 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 `pytest 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:
+
+.. code:: python
+
+ @pytest.mark.timeout(method="thread")
+ def test_foo():
+ pass
+
+__ https://docs.pytest.org/en/latest/reference.html#ini-options-ref
+
+.. _marker: https://docs.pytest.org/en/latest/mark.html
+
+The ``timeout`` Marker API
+==========================
+
+The full signature of the timeout marker is:
+
+.. code:: python
+
+ 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: https://docs.pytest.org/en/latest/mark.html
+
+.. _examples: https://docs.pytest.org/en/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.
+
+Avoiding timeouts in Fixtures
+=============================
+
+The timeout applies to the entire test including any fixtures which
+may need to be setup or torn down for the test (the exact affected
+fixtures depends on which scope they are and whether other tests will
+still use the same fixture). If the timeouts really are too short to
+include fixture durations, firstly make the timeouts larger ;). If
+this really isn't an option a ``timeout_func_only`` boolean setting
+exists which can be set in the pytest ini configuration file, as
+documented in ``pytest --help``.
+
+For the decorated function, a decorator will override
+``timeout_func_only = true`` in the pytest ini file to the default
+value. If you need to keep this option for a decorated test, you
+must specify the option explicitly again:
+
+.. code:: python
+
+ @pytest.mark.timeout(60, func_only=True)
+ def test_foo():
+ pass
+
+
+Debugger Detection
+==================
+
+This plugin tries to avoid triggering the timeout when a debugger is
+detected. This is mostly a convenience so you do not need to remember
+to disable the timeout when interactively debugging.
+
+The way this plugin detects whether or not a debugging session is
+active is by checking if a trace function is set and if one is, it
+check to see if the module it belongs to is present in a set of known
+debugging frameworks modules OR if pytest itself drops you into a pdb
+session using ``--pdb`` or similar.
+
+This functionality can be disabled with the ``--disable-debugger-detection`` flag
+or the corresponding ``timeout_disable_debugger_detection`` ini setting / environment
+variable.
+
+
+Extending pytest-timeout with plugins
+=====================================
+
+``pytest-timeout`` provides two hooks that can be used for extending the tool. These
+hooks are used for setting the timeout timer and cancelling it if the timeout is not
+reached.
+
+For example, ``pytest-asyncio`` can provide asyncio-specific code that generates better
+traceback and points on timed out ``await`` instead of the running loop iteration.
+
+See `pytest hooks documentation
+<https://docs.pytest.org/en/latest/how-to/writing_hook_functions.html>`_ for more info
+regarding to use custom hooks.
+
+``pytest_timeout_set_timer``
+----------------------------
+
+.. code:: python
+
+ @pytest.hookspec(firstresult=True)
+ def pytest_timeout_set_timer(item, settings):
+ """Called at timeout setup.
+
+ 'item' is a pytest node to setup timeout for.
+
+ 'settings' is Settings namedtuple (described below).
+
+ Can be overridden by plugins for alternative timeout implementation strategies.
+ """
+
+
+``Settings``
+------------
+
+When ``pytest_timeout_set_timer`` is called, ``settings`` argument is passed.
+
+The argument has ``Settings`` namedtuple type with the following fields:
+
++-----------+-------+--------------------------------------------------------+
+|Attribute | Index | Value |
++===========+=======+========================================================+
+| timeout | 0 | timeout in seconds or ``None`` for no timeout |
++-----------+-------+--------------------------------------------------------+
+| method | 1 | Method mechanism, |
+| | | ``'signal'`` and ``'thread'`` are supported by default |
++-----------+-------+--------------------------------------------------------+
+| func_only | 2 | Apply timeout to test function only if ``True``, |
+| | | wrap all test function and its fixtures otherwise |
++-----------+-------+--------------------------------------------------------+
+
+``pytest_timeout_cancel_timer``
+-------------------------------
+
+.. code:: python
+
+ @pytest.hookspec(firstresult=True)
+ def pytest_timeout_cancel_timer(item):
+ """Called at timeout teardown.
+
+ 'item' is a pytest node which was used for timeout setup.
+
+ Can be overridden by plugins for alternative timeout implementation strategies.
+ """
+
+``is_debugging``
+----------------
+
+When the timeout occurs, user can open the debugger session. In this case, the timeout
+should be discarded. A custom hook can check this case by calling ``is_debugging()``
+function:
+
+.. code:: python
+
+ import pytest
+ import pytest_timeout
+
+
+ def on_timeout():
+ if pytest_timeout.is_debugging():
+ return
+ pytest.fail("+++ Timeout +++")
+
+
+
+Session Timeout
+===============
+
+The above mentioned timeouts are all per test function.
+The "per test function" timeouts will stop an individual test
+from taking too long. We may also want to limit the time of the entire
+set of tests running in one session. A session all of the tests
+that will be run with one invokation of pytest.
+
+A session timeout is set with `--session-timeout` and is in seconds.
+
+The following example shows a session timeout of 10 minutes (600 seconds)::
+
+ pytest --session-timeout=600
+
+You can also set the session timeout the pytest configuration file using the ``session_timeout`` option:
+
+ .. code:: ini
+
+ [pytest]
+ session_timeout = 600
+
+Cooperative timeouts
+--------------------
+
+Session timeouts are cooperative timeouts. pytest-timeout checks the
+session time at the end of each test function, and stops further tests
+from running if the session timeout is exceeded. The session will
+results in a test failure if this occurs.
+
+In particular this means if a test does not finish of itself, it will
+only be interrupted if there is also a function timeout set. A
+session timeout is not enough to ensure that a test-suite is
+guaranteed to finish.
+
+Combining session and function timeouts
+---------------------------------------
+
+It works fine to combine both session and function timeouts. In fact
+when using a session timeout it is recommended to also provide a
+function timeout.
+
+For example, to limit test functions to 5 seconds and the full session
+to 100 seconds::
+
+ pytest --timeout=5 --session-timeout=100
+
+
+Changelog
+=========
+
+2.4.0
+-----
+
+- Detect debuggers registered with sys.monitoring. Thanks Rich
+ Chiodo.
+- Make it clear the timeout message comes from pytest-timeout. Thanks
+ Pedro Brochado.
+
+2.3.1
+-----
+
+- Fixup some build errors, mostly README syntax which stopped twine
+ from uploading.
+
+2.3.0
+-----
+
+- Fix debugger detection for recent VSCode, this compiles pydevd using
+ cython which is now correctly detected. Thanks Adrian Gielniewski.
+- Switched to using Pytest's ``TerminalReporter`` instead of writing
+ directly to ``sys.{stdout,stderr}``.
+ This change also switches all output from ``sys.stderr`` to ``sys.stdout``.
+ Thanks Pedro Algarvio.
+- Pytest 7.0.0 is now the minimum supported version. Thanks Pedro Algarvio.
+- Add ``--session-timeout`` option and ``session_timeout`` setting.
+ Thanks Brian Okken.
+
+2.2.0
+-----
+
+- Add ``--timeout-disable-debugger-detection`` flag, thanks
+ Michael Peters
+
+2.1.0
+-----
+
+- Get terminal width from shutil instead of deprecated py, thanks
+ Andrew Svetlov.
+- Add an API for extending ``pytest-timeout`` functionality
+ with third-party plugins, thanks Andrew Svetlov.
+
+2.0.2
+-----
+
+- Fix debugger detection on OSX, thanks Alexander Pacha.
+
+2.0.1
+-----
+
+- Fix Python 2 removal, thanks Nicusor Picatureanu.
+
+2.0.0
+-----
+
+- Increase pytest requirement to >=5.0.0. Thanks Dominic Davis-Foster.
+- Use thread timeout method when plugin is not called from main
+ thread to avoid crash.
+- Fix pycharm debugger detection so timeouts are not triggered during
+ debugger usage.
+- Dropped support for Python 2, minimum pytest version supported is 5.0.0.
+
+1.4.2
+-----
+
+- Fix compatibility when run with pytest pre-releases, thanks
+ Bruno Oliveira,
+- Fix detection of third-party debuggers, thanks Bruno Oliveira.
+
+1.4.1
+-----
+
+- Fix coverage compatibility which was broken by 1.4.0.
+
+1.4.0
+-----
+
+- Better detection of when we are debugging, thanks Mattwmaster58.
+
+1.3.4
+-----
+
+- Give the threads a name to help debugging, thanks Thomas Grainger.
+- Changed location to https://github.com/pytest-dev/pytest-timeout
+ because bitbucket is dropping mercurial support. Thanks Thomas
+ Grainger and Bruno Oliveira.
+
+1.3.3
+-----
+
+- Fix support for pytest >= 3.10.
+
+1.3.2
+-----
+
+- This changelog was omitted for the 1.3.2 release and was added
+ afterwards. Apologies for the confusion.
+- Fix pytest 3.7.3 compatibility. The capture API had changed
+ slightly and this needed fixing. Thanks Bruno Oliveira for the
+ contribution.
+
+1.3.1
+-----
+
+- Fix deprecation warning on Python 3.6. Thanks Mickaël Schoentgen
+- Create a valid tag for the release. Somehow this didn't happen for
+ 1.3.0, that tag points to a non-existing commit.
+
+1.3.0
+-----
+
+- Make it possible to only run the timeout timer on the test function
+ and not the whole fixture setup + test + teardown duration. Thanks
+ Pedro Algarvio for the work!
+- Use the new pytest marker API, Thanks Pedro Algarvio for the work!
+
+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 pytest 2.8, thanks Holger Krekel.
+* No longer test with pexpect on py26 as it is no longer supported
+* Require pytest 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 pytest
+
+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.
diff --git a/contrib/python/pytest-timeout/py3/.dist-info/entry_points.txt b/contrib/python/pytest-timeout/py3/.dist-info/entry_points.txt
new file mode 100644
index 00000000000..f7ac24a8d49
--- /dev/null
+++ b/contrib/python/pytest-timeout/py3/.dist-info/entry_points.txt
@@ -0,0 +1,2 @@
+[pytest11]
+timeout = pytest_timeout
diff --git a/contrib/python/pytest-timeout/py3/.dist-info/top_level.txt b/contrib/python/pytest-timeout/py3/.dist-info/top_level.txt
new file mode 100644
index 00000000000..34eec7a2ee6
--- /dev/null
+++ b/contrib/python/pytest-timeout/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+pytest_timeout
diff --git a/contrib/python/pytest-timeout/py3/LICENSE b/contrib/python/pytest-timeout/py3/LICENSE
new file mode 100644
index 00000000000..e968817fc44
--- /dev/null
+++ b/contrib/python/pytest-timeout/py3/LICENSE
@@ -0,0 +1,22 @@
+The 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.
diff --git a/contrib/python/pytest-timeout/py3/README.rst b/contrib/python/pytest-timeout/py3/README.rst
new file mode 100644
index 00000000000..a426d6f0799
--- /dev/null
+++ b/contrib/python/pytest-timeout/py3/README.rst
@@ -0,0 +1,588 @@
+==============
+pytest-timeout
+==============
+
+|python| |version| |anaconda| |ci| |pre-commit|
+
+.. |version| image:: https://img.shields.io/pypi/v/pytest-timeout.svg
+ :target: https://pypi.python.org/pypi/pytest-timeout
+
+.. |anaconda| image:: https://img.shields.io/conda/vn/conda-forge/pytest-timeout.svg
+ :target: https://anaconda.org/conda-forge/pytest-timeout
+
+.. |ci| image:: https://github.com/pytest-dev/pytest-timeout/workflows/build/badge.svg
+ :target: https://github.com/pytest-dev/pytest-timeout/actions
+
+.. |python| image:: https://img.shields.io/pypi/pyversions/pytest-timeout.svg
+ :target: https://pypi.python.org/pypi/pytest-timeout/
+
+.. |pre-commit| image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest-timeout/master.svg
+ :target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest-timeout/master
+
+
+.. warning::
+
+ Please read this README carefully and only use this plugin if you
+ understand the consequences. This plugin is designed to catch
+ excessively long test durations like deadlocked or hanging tests,
+ it is not designed for precise timings or performance regressions.
+ Remember your test suite should aim to be **fast**, with timeouts
+ being a last resort, not an expected failure mode.
+
+This plugin will time each test and terminate it when it takes too
+long. Termination may or may not be graceful, please see below, but
+when aborting it will show a stack dump of all thread 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::
+
+ While by default on POSIX systems pytest 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 3.6 and higher,
+including PyPy3. See tox.ini for currently tested versions.
+
+
+Usage
+=====
+
+Install is as simple as e.g.::
+
+ pip install pytest-timeout
+
+Now you can run the test suite while setting a timeout in seconds, any
+individual test which takes longer than the given duration will be
+terminated::
+
+ pytest --timeout=300
+
+Furthermore you can also use a decorator to set the timeout for an
+individual test. If combined with the ``--timeout`` flag this will
+override the timeout for this individual test:
+
+.. code:: python
+
+ @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 a 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 `pytest configuration file`__
+ using the ``timeout`` option. E.g.:
+
+ .. code:: ini
+
+ [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:
+
+ .. code:: python
+
+ @pytest.mark.timeout(300)
+ def test_foo():
+ pass
+
+__ https://docs.pytest.org/en/latest/reference.html#ini-options-ref
+
+.. _marker: https://docs.pytest.org/en/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 pytest features, like e.g. JUnit XML output or
+fixture teardown, will not function normally. The second issue might
+be alleviated by using the ``--forked`` option of the pytest-forked_
+plugin.
+
+.. _pytest-forked: https://pypi.org/project/pytest-forked/
+
+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 the alarm when the test 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 pytest 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 `pytest 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:
+
+.. code:: python
+
+ @pytest.mark.timeout(method="thread")
+ def test_foo():
+ pass
+
+__ https://docs.pytest.org/en/latest/reference.html#ini-options-ref
+
+.. _marker: https://docs.pytest.org/en/latest/mark.html
+
+The ``timeout`` Marker API
+==========================
+
+The full signature of the timeout marker is:
+
+.. code:: python
+
+ 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: https://docs.pytest.org/en/latest/mark.html
+
+.. _examples: https://docs.pytest.org/en/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.
+
+Avoiding timeouts in Fixtures
+=============================
+
+The timeout applies to the entire test including any fixtures which
+may need to be setup or torn down for the test (the exact affected
+fixtures depends on which scope they are and whether other tests will
+still use the same fixture). If the timeouts really are too short to
+include fixture durations, firstly make the timeouts larger ;). If
+this really isn't an option a ``timeout_func_only`` boolean setting
+exists which can be set in the pytest ini configuration file, as
+documented in ``pytest --help``.
+
+For the decorated function, a decorator will override
+``timeout_func_only = true`` in the pytest ini file to the default
+value. If you need to keep this option for a decorated test, you
+must specify the option explicitly again:
+
+.. code:: python
+
+ @pytest.mark.timeout(60, func_only=True)
+ def test_foo():
+ pass
+
+
+Debugger Detection
+==================
+
+This plugin tries to avoid triggering the timeout when a debugger is
+detected. This is mostly a convenience so you do not need to remember
+to disable the timeout when interactively debugging.
+
+The way this plugin detects whether or not a debugging session is
+active is by checking if a trace function is set and if one is, it
+check to see if the module it belongs to is present in a set of known
+debugging frameworks modules OR if pytest itself drops you into a pdb
+session using ``--pdb`` or similar.
+
+This functionality can be disabled with the ``--disable-debugger-detection`` flag
+or the corresponding ``timeout_disable_debugger_detection`` ini setting / environment
+variable.
+
+
+Extending pytest-timeout with plugins
+=====================================
+
+``pytest-timeout`` provides two hooks that can be used for extending the tool. These
+hooks are used for setting the timeout timer and cancelling it if the timeout is not
+reached.
+
+For example, ``pytest-asyncio`` can provide asyncio-specific code that generates better
+traceback and points on timed out ``await`` instead of the running loop iteration.
+
+See `pytest hooks documentation
+<https://docs.pytest.org/en/latest/how-to/writing_hook_functions.html>`_ for more info
+regarding to use custom hooks.
+
+``pytest_timeout_set_timer``
+----------------------------
+
+.. code:: python
+
+ @pytest.hookspec(firstresult=True)
+ def pytest_timeout_set_timer(item, settings):
+ """Called at timeout setup.
+
+ 'item' is a pytest node to setup timeout for.
+
+ 'settings' is Settings namedtuple (described below).
+
+ Can be overridden by plugins for alternative timeout implementation strategies.
+ """
+
+
+``Settings``
+------------
+
+When ``pytest_timeout_set_timer`` is called, ``settings`` argument is passed.
+
+The argument has ``Settings`` namedtuple type with the following fields:
+
++-----------+-------+--------------------------------------------------------+
+|Attribute | Index | Value |
++===========+=======+========================================================+
+| timeout | 0 | timeout in seconds or ``None`` for no timeout |
++-----------+-------+--------------------------------------------------------+
+| method | 1 | Method mechanism, |
+| | | ``'signal'`` and ``'thread'`` are supported by default |
++-----------+-------+--------------------------------------------------------+
+| func_only | 2 | Apply timeout to test function only if ``True``, |
+| | | wrap all test function and its fixtures otherwise |
++-----------+-------+--------------------------------------------------------+
+
+``pytest_timeout_cancel_timer``
+-------------------------------
+
+.. code:: python
+
+ @pytest.hookspec(firstresult=True)
+ def pytest_timeout_cancel_timer(item):
+ """Called at timeout teardown.
+
+ 'item' is a pytest node which was used for timeout setup.
+
+ Can be overridden by plugins for alternative timeout implementation strategies.
+ """
+
+``is_debugging``
+----------------
+
+When the timeout occurs, user can open the debugger session. In this case, the timeout
+should be discarded. A custom hook can check this case by calling ``is_debugging()``
+function:
+
+.. code:: python
+
+ import pytest
+ import pytest_timeout
+
+
+ def on_timeout():
+ if pytest_timeout.is_debugging():
+ return
+ pytest.fail("+++ Timeout +++")
+
+
+
+Session Timeout
+===============
+
+The above mentioned timeouts are all per test function.
+The "per test function" timeouts will stop an individual test
+from taking too long. We may also want to limit the time of the entire
+set of tests running in one session. A session all of the tests
+that will be run with one invokation of pytest.
+
+A session timeout is set with `--session-timeout` and is in seconds.
+
+The following example shows a session timeout of 10 minutes (600 seconds)::
+
+ pytest --session-timeout=600
+
+You can also set the session timeout the pytest configuration file using the ``session_timeout`` option:
+
+ .. code:: ini
+
+ [pytest]
+ session_timeout = 600
+
+Cooperative timeouts
+--------------------
+
+Session timeouts are cooperative timeouts. pytest-timeout checks the
+session time at the end of each test function, and stops further tests
+from running if the session timeout is exceeded. The session will
+results in a test failure if this occurs.
+
+In particular this means if a test does not finish of itself, it will
+only be interrupted if there is also a function timeout set. A
+session timeout is not enough to ensure that a test-suite is
+guaranteed to finish.
+
+Combining session and function timeouts
+---------------------------------------
+
+It works fine to combine both session and function timeouts. In fact
+when using a session timeout it is recommended to also provide a
+function timeout.
+
+For example, to limit test functions to 5 seconds and the full session
+to 100 seconds::
+
+ pytest --timeout=5 --session-timeout=100
+
+
+Changelog
+=========
+
+2.4.0
+-----
+
+- Detect debuggers registered with sys.monitoring. Thanks Rich
+ Chiodo.
+- Make it clear the timeout message comes from pytest-timeout. Thanks
+ Pedro Brochado.
+
+2.3.1
+-----
+
+- Fixup some build errors, mostly README syntax which stopped twine
+ from uploading.
+
+2.3.0
+-----
+
+- Fix debugger detection for recent VSCode, this compiles pydevd using
+ cython which is now correctly detected. Thanks Adrian Gielniewski.
+- Switched to using Pytest's ``TerminalReporter`` instead of writing
+ directly to ``sys.{stdout,stderr}``.
+ This change also switches all output from ``sys.stderr`` to ``sys.stdout``.
+ Thanks Pedro Algarvio.
+- Pytest 7.0.0 is now the minimum supported version. Thanks Pedro Algarvio.
+- Add ``--session-timeout`` option and ``session_timeout`` setting.
+ Thanks Brian Okken.
+
+2.2.0
+-----
+
+- Add ``--timeout-disable-debugger-detection`` flag, thanks
+ Michael Peters
+
+2.1.0
+-----
+
+- Get terminal width from shutil instead of deprecated py, thanks
+ Andrew Svetlov.
+- Add an API for extending ``pytest-timeout`` functionality
+ with third-party plugins, thanks Andrew Svetlov.
+
+2.0.2
+-----
+
+- Fix debugger detection on OSX, thanks Alexander Pacha.
+
+2.0.1
+-----
+
+- Fix Python 2 removal, thanks Nicusor Picatureanu.
+
+2.0.0
+-----
+
+- Increase pytest requirement to >=5.0.0. Thanks Dominic Davis-Foster.
+- Use thread timeout method when plugin is not called from main
+ thread to avoid crash.
+- Fix pycharm debugger detection so timeouts are not triggered during
+ debugger usage.
+- Dropped support for Python 2, minimum pytest version supported is 5.0.0.
+
+1.4.2
+-----
+
+- Fix compatibility when run with pytest pre-releases, thanks
+ Bruno Oliveira,
+- Fix detection of third-party debuggers, thanks Bruno Oliveira.
+
+1.4.1
+-----
+
+- Fix coverage compatibility which was broken by 1.4.0.
+
+1.4.0
+-----
+
+- Better detection of when we are debugging, thanks Mattwmaster58.
+
+1.3.4
+-----
+
+- Give the threads a name to help debugging, thanks Thomas Grainger.
+- Changed location to https://github.com/pytest-dev/pytest-timeout
+ because bitbucket is dropping mercurial support. Thanks Thomas
+ Grainger and Bruno Oliveira.
+
+1.3.3
+-----
+
+- Fix support for pytest >= 3.10.
+
+1.3.2
+-----
+
+- This changelog was omitted for the 1.3.2 release and was added
+ afterwards. Apologies for the confusion.
+- Fix pytest 3.7.3 compatibility. The capture API had changed
+ slightly and this needed fixing. Thanks Bruno Oliveira for the
+ contribution.
+
+1.3.1
+-----
+
+- Fix deprecation warning on Python 3.6. Thanks Mickaël Schoentgen
+- Create a valid tag for the release. Somehow this didn't happen for
+ 1.3.0, that tag points to a non-existing commit.
+
+1.3.0
+-----
+
+- Make it possible to only run the timeout timer on the test function
+ and not the whole fixture setup + test + teardown duration. Thanks
+ Pedro Algarvio for the work!
+- Use the new pytest marker API, Thanks Pedro Algarvio for the work!
+
+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 pytest 2.8, thanks Holger Krekel.
+* No longer test with pexpect on py26 as it is no longer supported
+* Require pytest 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 pytest
+
+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.
diff --git a/contrib/python/pytest-timeout/py3/pytest_timeout.py b/contrib/python/pytest-timeout/py3/pytest_timeout.py
new file mode 100644
index 00000000000..7c220699e23
--- /dev/null
+++ b/contrib/python/pytest-timeout/py3/pytest_timeout.py
@@ -0,0 +1,558 @@
+"""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 inspect
+import os
+import signal
+import sys
+import threading
+import time
+import traceback
+from collections import namedtuple
+
+import pytest
+
+
+__all__ = ("is_debugging", "Settings")
+SESSION_TIMEOUT_KEY = pytest.StashKey[float]()
+SESSION_EXPIRE_KEY = pytest.StashKey[float]()
+PYTEST_FAILURE_MESSAGE = "Timeout (>%ss) from pytest-timeout."
+
+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, 'thread' uses a timer
+thread. If unspecified 'signal' is used on platforms which support
+SIGALRM, otherwise 'thread' is used.
+""".strip()
+FUNC_ONLY_DESC = """
+When set to True, defers the timeout evaluation to only the test
+function body, ignoring the time it takes when evaluating any fixtures
+used in the test.
+""".strip()
+DISABLE_DEBUGGER_DETECTION_DESC = """
+When specified, disables debugger detection. breakpoint(), pdb.set_trace(), etc.
+will be interrupted by the timeout.
+""".strip()
+SESSION_TIMEOUT_DESC = """
+Timeout in seconds for entire session. Default is None which
+means no timeout. Timeout is checked between tests, and will not interrupt a test
+in progress.
+""".strip()
+
+# bdb covers pdb, ipdb, and possibly others
+# pydevd covers PyCharm, VSCode, and possibly others
+KNOWN_DEBUGGING_MODULES = {"pydevd", "bdb", "pydevd_frame_evaluator"}
+Settings = namedtuple(
+ "Settings", ["timeout", "method", "func_only", "disable_debugger_detection"]
+)
+
+
+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",
+ action="store",
+ choices=["signal", "thread"],
+ help="Deprecated, use --timeout-method",
+ )
+ group.addoption(
+ "--timeout-method",
+ dest="timeout_method",
+ action="store",
+ choices=["signal", "thread"],
+ help=METHOD_DESC,
+ )
+ group.addoption(
+ "--timeout-disable-debugger-detection",
+ dest="timeout_disable_debugger_detection",
+ action="store_true",
+ help=DISABLE_DEBUGGER_DETECTION_DESC,
+ )
+ group.addoption(
+ "--session-timeout",
+ action="store",
+ dest="session_timeout",
+ default=None,
+ type=float,
+ metavar="SECONDS",
+ help=SESSION_TIMEOUT_DESC,
+ )
+ parser.addini("timeout", TIMEOUT_DESC)
+ parser.addini("timeout_method", METHOD_DESC)
+ parser.addini("timeout_func_only", FUNC_ONLY_DESC, type="bool", default=False)
+ parser.addini(
+ "timeout_disable_debugger_detection",
+ DISABLE_DEBUGGER_DETECTION_DESC,
+ type="bool",
+ default=False,
+ )
+ parser.addini("session_timeout", SESSION_TIMEOUT_DESC)
+
+
+class TimeoutHooks:
+ """Timeout specific hooks."""
+
+ @pytest.hookspec(firstresult=True)
+ def pytest_timeout_set_timer(item, settings):
+ """Called at timeout setup.
+
+ 'item' is a pytest node to setup timeout for.
+
+ Can be overridden by plugins for alternative timeout implementation strategies.
+
+ """
+
+ @pytest.hookspec(firstresult=True)
+ def pytest_timeout_cancel_timer(item):
+ """Called at timeout teardown.
+
+ 'item' is a pytest node which was used for timeout setup.
+
+ Can be overridden by plugins for alternative timeout implementation strategies.
+
+ """
+
+
+def pytest_addhooks(pluginmanager):
+ """Register timeout-specific hooks."""
+ pluginmanager.add_hookspecs(TimeoutHooks)
+
+
+def pytest_configure(config):
+ """Register the marker so it shows up in --markers output."""
+ config.addinivalue_line(
+ "markers",
+ "timeout(timeout, method=None, func_only=False, "
+ "disable_debugger_detection=False): Set a timeout, timeout "
+ "method and func_only evaluation 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. The "
+ "*func_only* keyword, when set to True, defers the timeout evaluation "
+ "to only the test function body, ignoring the time it takes when "
+ "evaluating any fixtures used in the test. The "
+ "*disable_debugger_detection* keyword, when set to True, disables "
+ "debugger detection, allowing breakpoint(), pdb.set_trace(), etc. "
+ "to be interrupted",
+ )
+
+ settings = get_env_settings(config)
+ config._env_timeout = settings.timeout
+ config._env_timeout_method = settings.method
+ config._env_timeout_func_only = settings.func_only
+ config._env_timeout_disable_debugger_detection = settings.disable_debugger_detection
+
+ timeout = config.getoption("session_timeout")
+ if timeout is None:
+ ini = config.getini("session_timeout")
+ if ini:
+ timeout = _validate_timeout(config.getini("session_timeout"), "config file")
+ if timeout is not None:
+ expire_time = time.time() + timeout
+ else:
+ expire_time = 0
+ timeout = 0
+ config.stash[SESSION_TIMEOUT_KEY] = timeout
+ config.stash[SESSION_EXPIRE_KEY] = expire_time
+
+
[email protected](hookwrapper=True)
+def pytest_runtest_protocol(item):
+ """Hook in timeouts to the runtest protocol.
+
+ If the timeout is set on the entire test, including setup and
+ teardown, then this hook installs the timeout. Otherwise
+ pytest_runtest_call is used.
+ """
+ hooks = item.config.pluginmanager.hook
+ settings = _get_item_settings(item)
+ is_timeout = settings.timeout is not None and settings.timeout > 0
+ if is_timeout and settings.func_only is False:
+ hooks.pytest_timeout_set_timer(item=item, settings=settings)
+ yield
+ if is_timeout and settings.func_only is False:
+ hooks.pytest_timeout_cancel_timer(item=item)
+
+ # check session timeout
+ expire_time = item.session.config.stash[SESSION_EXPIRE_KEY]
+ if expire_time and (expire_time < time.time()):
+ timeout = item.session.config.stash[SESSION_TIMEOUT_KEY]
+ item.session.shouldfail = f"session-timeout: {timeout} sec exceeded"
+
+
[email protected](hookwrapper=True)
+def pytest_runtest_call(item):
+ """Hook in timeouts to the test function call only.
+
+ If the timeout is set on only the test function this hook installs
+ the timeout, otherwise pytest_runtest_protocol is used.
+ """
+ hooks = item.config.pluginmanager.hook
+ settings = _get_item_settings(item)
+ is_timeout = settings.timeout is not None and settings.timeout > 0
+ if is_timeout and settings.func_only is True:
+ hooks.pytest_timeout_set_timer(item=item, settings=settings)
+ yield
+ if is_timeout and settings.func_only is True:
+ hooks.pytest_timeout_cancel_timer(item=item)
+
+
[email protected](tryfirst=True)
+def pytest_report_header(config):
+ """Add timeout config to pytest header."""
+ timeout_header = []
+
+ if config._env_timeout:
+ timeout_header.append(
+ "timeout: %ss\ntimeout method: %s\ntimeout func_only: %s"
+ % (
+ config._env_timeout,
+ config._env_timeout_method,
+ config._env_timeout_func_only,
+ )
+ )
+
+ session_timeout = config.getoption("session_timeout")
+ if session_timeout:
+ timeout_header.append("session timeout: %ss" % session_timeout)
+ if timeout_header:
+ return timeout_header
+
+
[email protected](tryfirst=True)
+def pytest_exception_interact(node):
+ """Stop the timeout when pytest enters pdb in post-mortem mode."""
+ hooks = node.config.pluginmanager.hook
+ hooks.pytest_timeout_cancel_timer(item=node)
+
+
+def pytest_enter_pdb():
+ """Stop the timeouts when we entered pdb.
+
+ This stops timeouts from triggering when pytest's builting pdb
+ support notices we entered 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
+
+
+def is_debugging(trace_func=None):
+ """Detect if a debugging session is in progress.
+
+ This looks at both pytest's builtin pdb support as well as
+ externally installed debuggers using some heuristics.
+
+ This is done by checking if either of the following conditions is
+ true:
+
+ 1. Examines the trace function to see if the module it originates
+ from is in KNOWN_DEBUGGING_MODULES.
+ 2. Check is SUPPRESS_TIMEOUT is set to True.
+
+ :param trace_func: the current trace function, if not given will use
+ sys.gettrace(). Used to unit-test this function.
+ """
+ global SUPPRESS_TIMEOUT, KNOWN_DEBUGGING_MODULES
+ if SUPPRESS_TIMEOUT:
+ return True
+ if trace_func is None:
+ trace_func = sys.gettrace()
+ trace_module = None
+ if trace_func:
+ trace_module = inspect.getmodule(trace_func) or inspect.getmodule(
+ trace_func.__class__
+ )
+ if trace_module:
+ parts = trace_module.__name__.split(".")
+ for name in KNOWN_DEBUGGING_MODULES:
+ if any(part.startswith(name) for part in parts):
+ return True
+
+ # For 3.12, sys.monitoring is used for tracing. Check if any debugger has been registered.
+ if hasattr(sys, "monitoring"):
+ return sys.monitoring.get_tool(sys.monitoring.DEBUGGER_ID) != None
+ return False
+
+
+SUPPRESS_TIMEOUT = False
+
+
[email protected](trylast=True)
+def pytest_timeout_set_timer(item, settings):
+ """Setup up a timeout trigger and handler."""
+ timeout_method = settings.method
+ if (
+ timeout_method == "signal"
+ and threading.current_thread() is not threading.main_thread()
+ ):
+ timeout_method = "thread"
+
+ if timeout_method == "signal":
+
+ def handler(signum, frame):
+ __tracebackhide__ = True
+ timeout_sigalrm(item, settings)
+
+ 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, settings.timeout)
+ elif timeout_method == "thread":
+ timer = threading.Timer(settings.timeout, timeout_timer, (item, settings))
+ timer.name = "%s %s" % (__name__, item.nodeid)
+
+ def cancel():
+ timer.cancel()
+ timer.join()
+
+ item.cancel_timeout = cancel
+ timer.start()
+ return True
+
+
[email protected](trylast=True)
+def pytest_timeout_cancel_timer(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()
+ return True
+
+
+def get_env_settings(config):
+ """Return the configured timeout settings.
+
+ This looks up the settings in the environment and config file.
+ """
+ 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
+
+ func_only = config.getini("timeout_func_only")
+
+ disable_debugger_detection = config.getvalue("timeout_disable_debugger_detection")
+ if disable_debugger_detection is None:
+ ini = config.getini("timeout_disable_debugger_detection")
+ if ini:
+ disable_debugger_detection = _validate_disable_debugger_detection(
+ ini, "config file"
+ )
+
+ return Settings(timeout, method, func_only, disable_debugger_detection)
+
+
+def _get_item_settings(item, marker=None):
+ """Return (timeout, method) for an item."""
+ timeout = method = func_only = disable_debugger_detection = None
+ if not marker:
+ marker = item.get_closest_marker("timeout")
+ if marker is not None:
+ settings = _parse_marker(item.get_closest_marker(name="timeout"))
+ timeout = _validate_timeout(settings.timeout, "marker")
+ method = _validate_method(settings.method, "marker")
+ func_only = _validate_func_only(settings.func_only, "marker")
+ disable_debugger_detection = _validate_disable_debugger_detection(
+ settings.disable_debugger_detection, "marker"
+ )
+ if timeout is None:
+ timeout = item.config._env_timeout
+ if method is None:
+ method = item.config._env_timeout_method
+ if func_only is None:
+ func_only = item.config._env_timeout_func_only
+ if disable_debugger_detection is None:
+ disable_debugger_detection = item.config._env_timeout_disable_debugger_detection
+ return Settings(timeout, method, func_only, disable_debugger_detection)
+
+
+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 = func_only = NOTSET = object()
+ for kw, val in marker.kwargs.items():
+ if kw == "timeout":
+ timeout = val
+ elif kw == "method":
+ method = val
+ elif kw == "func_only":
+ func_only = 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
+ if func_only is NOTSET:
+ func_only = None
+ return Settings(timeout, method, func_only, None)
+
+
+def _validate_timeout(timeout, where):
+ 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):
+ if method is None:
+ return None
+ if method not in ["signal", "thread"]:
+ raise ValueError("Invalid method %s from %s" % (method, where))
+ return method
+
+
+def _validate_func_only(func_only, where):
+ if func_only is None:
+ return None
+ if not isinstance(func_only, bool):
+ raise ValueError("Invalid func_only value %s from %s" % (func_only, where))
+ return func_only
+
+
+def _validate_disable_debugger_detection(disable_debugger_detection, where):
+ if disable_debugger_detection is None:
+ return None
+ if not isinstance(disable_debugger_detection, bool):
+ raise ValueError(
+ "Invalid disable_debugger_detection value %s from %s"
+ % (disable_debugger_detection, where)
+ )
+ return disable_debugger_detection
+
+
+def timeout_sigalrm(item, settings):
+ """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 not settings.disable_debugger_detection and is_debugging():
+ return
+ __tracebackhide__ = True
+ nthreads = len(threading.enumerate())
+ terminal = item.config.get_terminal_writer()
+ if nthreads > 1:
+ terminal.sep("+", title="Timeout")
+ dump_stacks(terminal)
+ if nthreads > 1:
+ terminal.sep("+", title="Timeout")
+ pytest.fail(PYTEST_FAILURE_MESSAGE % settings.timeout)
+
+
+def timeout_timer(item, settings):
+ """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 not settings.disable_debugger_detection and is_debugging():
+ return
+ terminal = item.config.get_terminal_writer()
+ try:
+ capman = item.config.pluginmanager.getplugin("capturemanager")
+ if capman:
+ capman.suspend_global_capture(item)
+ stdout, stderr = capman.read_global_capture()
+ else:
+ stdout, stderr = None, None
+ terminal.sep("+", title="Timeout")
+ caplog = item.config.pluginmanager.getplugin("_capturelog")
+ if caplog and hasattr(item, "capturelog_handler"):
+ log = item.capturelog_handler.stream.getvalue()
+ if log:
+ terminal.sep("~", title="Captured log")
+ terminal.write(log)
+ if stdout:
+ terminal.sep("~", title="Captured stdout")
+ terminal.write(stdout)
+ if stderr:
+ terminal.sep("~", title="Captured stderr")
+ terminal.write(stderr)
+ dump_stacks(terminal)
+ terminal.sep("+", title="Timeout")
+ except Exception:
+ traceback.print_exc()
+ finally:
+ terminal.flush()
+ sys.stdout.flush()
+ sys.stderr.flush()
+ os._exit(1)
+
+
+def dump_stacks(terminal):
+ """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 = "<unknown>"
+ terminal.sep("~", title="Stack of %s (%s)" % (thread_name, thread_ident))
+ terminal.write("".join(traceback.format_stack(frame)))
diff --git a/contrib/python/pytest-timeout/py3/ya.make b/contrib/python/pytest-timeout/py3/ya.make
new file mode 100644
index 00000000000..5f98848548d
--- /dev/null
+++ b/contrib/python/pytest-timeout/py3/ya.make
@@ -0,0 +1,27 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(2.4.0)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/python/pytest
+)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ pytest_timeout.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/pytest-timeout/py3/
+ .dist-info/METADATA
+ .dist-info/entry_points.txt
+ .dist-info/top_level.txt
+)
+
+END()