pytest-timeout

repository·main·Indexed 42 days ago

https://github.com/pytest-dev/pytest-timeout

A pytest plugin to catch excessively long test durations, such as deadlocked or hanging tests, by terminating them after a specified timeout. Supports timeout configuration via @pytest.mark.timeout markers, command line flags, environment variables, and configuration files. Offers two interruption methods: 'thread' for portability and 'signal' for allowing the test run to complete normally.

Tokens
1.9K
Snippets
9
Records
11
Agent score
4%

What's inside pytest-timeout

  1. Understand timeout methods: thread vs signal

    main

    The plugin uses different methods to interrupt tests. If a method fails, the thread method is the safest fallback.

    thread

    • How it works: Starts a timer thread that terminates the entire process after the timeout.
    • Pros: Most portable and surest method; always works.
    • Cons: High overhead; terminates the entire process, meaning fixture teardown and JUnit XML output will not complete normally.

    signal

    • How it works: Uses SIGALRM to schedule an alarm. If the alarm expires, it dumps thread stacks to stderr and calls pytest.fail() to interrupt the test.
    • Pros: The pytest process is not terminated, allowing the test run to complete normally.
    • Cons: May interfere with code that uses SIGALRM itself.
  2. Configure pytest-timeout timeouts

    main

    By default, the plugin does not time out any tests. You must specify a timeout in seconds. Timeouts are applied with the following priority (highest to lowest):

    1. Per-test marker: Using @pytest.mark.timeout(seconds).
    2. Command line flag: Using --timeout=seconds.
    3. Environment variable: Using PYTEST_TIMEOUT.
    4. Configuration file: Using the timeout option in pytest.ini or tox.ini.

    Setting a timeout to 0 disables the timeout for that specific test.

    # Per-test marker (Highest priority)
    @pytest.mark.timeout(60)
    def test_foo():
        pass
    # Configuration file
    [pytest]
    timeout = 300
    # Command line
    pytest --timeout=300
    # Environment variable
    export PYTEST_TIMEOUT=300
  3. Configure a session timeout

    main

    While standard timeouts apply to individual test functions, a session timeout limits the total time for the entire test suite.

    Note: Session timeouts are cooperative. pytest-timeout checks the session time at the end of each test function. If a test hangs and does not have its own function-level timeout, the session timeout will not be able to interrupt it. It is highly recommended to combine session timeouts with function timeouts.

    To set a session timeout, use the --session-timeout CLI flag or the session_timeout configuration option in your pytest.ini file.

    # Via CLI (e.g., 10 minutes)
    pytest --session-timeout=600
    
    # Via pytest.ini
    [pytest]
    session_timeout = 600
    
    # Recommended: Combining session and function timeouts
    pytest --timeout=5 --session-timeout=100
  4. Configure timeouts for functions only

    main

    By default, the timeout applies to the entire test process, including fixture setup and teardown. If you want the timeout to apply only to the test function itself and ignore fixture durations, use the timeout_func_only setting.

    • In configuration: Set timeout_func_only = true in your pytest.ini.
    • In a decorator: A decorator overrides the global setting. To keep the func_only behavior on a decorated test, you must specify it explicitly.
    # Explicitly requesting func_only on a decorated test
    @pytest.mark.timeout(60, func_only=True)
    def test_foo():
        pass
  5. Use the @pytest.mark.timeout marker API

    main

    The timeout marker allows you to define specific timeout durations and methods for individual tests or classes.

    Signature: pytest.mark.timeout(timeout=0, method=DEFAULT_METHOD)

    • timeout: Number of seconds. 0 disables the timeout.
    • method: Either "thread" or "signal".
    @pytest.mark.timeout(60, method="signal")
    def test_foo():
        pass
  6. Handle debugger detection in timeout callbacks

    main

    If you are using a custom timeout handler or callback, you should check if a debugger is active to avoid triggering a timeout failure while the user is inspecting code. Use pytest_timeout.is_debugging() to detect this state.

    import pytest
    import pytest_timeout
    
    
    def on_timeout():
        if pytest_timeout.is_debugging():
            return
        pytest.fail("+++ Timeout +++")
  7. Extend pytest-timeout with custom hooks

    main

    You can extend pytest-timeout by implementing two specific pytest hooks. These hooks allow plugins to manage how the timeout timer is set and cancelled, which is useful for providing better tracebacks (e.g., for asyncio loops) or alternative implementation strategies.

    • pytest_timeout_set_timer(item, settings): Called during timeout setup. item is the pytest node, and settings is a Settings namedtuple.
    • pytest_timeout_cancel_timer(item): Called during timeout teardown.
    @pytest.hookspec(firstresult=True)
    def pytest_timeout_set_timer(item, settings):
        # Custom setup logic
        pass
    
    @pytest.hookspec(firstresult=True)
    def pytest_timeout_cancel_timer(item):
        # Custom teardown logic
        pass
  8. Disable debugger detection

    main

    The plugin attempts to detect active debuggers (like pdb) to avoid triggering timeouts during interactive debugging. You can disable this detection using:

    • The --disable-debugger-detection CLI flag.
    • The timeout_disable_debugger_detection configuration option in pytest.ini.
    • The timeout_disable_debugger_detection environment variable.
  9. Specify the timeout method

    main

    You can explicitly choose between thread and signal using the timeout_method option via the configuration file, the --timeout_method command line parameter, or the timeout marker.

    # Using the marker to specify method
    @pytest.mark.timeout(method="thread")
    def test_foo():
        pass
  10. Reference the Settings namedtuple in pytest_timeout_set_timer

    main

    When implementing pytest_timeout_set_timer, the settings argument is a Settings namedtuple containing the following fields:

    AttributeIndexValue
    timeout0timeout in seconds or None for no timeout
    method1Method mechanism ('signal' or 'thread')
    func_only2If True, applies timeout to the test function only; otherwise wraps the function and its fixtures.
    | 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 |