Per-scenario timeout¶
Behave provides a global --timeout flag but no way to set a different
timeout per scenario. behave-kit fills that gap with tag-based overrides
and platform-aware handlers.
Overview¶
setup_timeoutconfigures a default timeout on the context.Tags
@timeout:Noverride the timeout per scenario or feature.On expiry the scenario fails with
TimeoutError.
Platform notes¶
Unix (Linux, macOS): uses
signal.SIGALRMfor immediate interruption of the main thread.Windows:
signal.SIGALRMis unavailable, so athreading.Timerfallback is used. This cannot interrupt CPU-bound code — the timeout is detected after the current step finishes. I/O-bound code (time.sleep, socket reads, etc.) is interrupted promptly.
Usage¶
Wire the timeout hooks in your environment.py:
from behave_kit import setup_timeout
from behave_kit.timeout import timeout_before_scenario, timeout_after_scenario
def before_all(context):
setup_timeout(context, default_timeout=30)
def before_scenario(context, scenario):
timeout_before_scenario(context, scenario)
def after_scenario(context, scenario):
timeout_after_scenario(context, scenario)
Environment variable¶
You can set the default timeout with the BEHAVE_SCENARIO_TIMEOUT
environment variable. This is useful for CI runners or behave-runner
integrations that control timeout from the command line:
BEHAVE_SCENARIO_TIMEOUT=30 behave
If setup_timeout(context) is called without default_timeout,
the value is read from the environment. If the variable is not set,
the default is 0 (no timeout).
Tag-based overrides¶
Override the default timeout per scenario or feature using @timeout:N
tags:
@timeout:10
Scenario: Fast scenario with tag override
When I do something quick
@timeout:0
Scenario: Disable timeout for this scenario
When I do something slow
@timeout:60
Feature: Feature-level timeout inherits to all scenarios
Precedence rules:
Scenario tags take precedence over feature tags.
@timeout:0disables the timeout for that scenario.If no tag is present, the default timeout from
setup_timeoutis used.If no default is configured (
default_timeout=0), no timeout is applied.
Custom tag name¶
Use a custom tag name instead of the default timeout:
setup_timeout(context, default_timeout=30, timeout_tag="limit")
Tags would then be @limit:10, @limit:0, etc.
API reference¶
Per-scenario timeout with tag-based overrides.
Behave provides a global --timeout flag but no way to set a different
timeout per scenario. This module fills that gap:
setup_timeoutconfigures a default timeout on the context.Tags
@timeout:Noverride the timeout per scenario or feature.On expiry the scenario fails with
TimeoutError.
Platform notes¶
Unix (Linux, macOS): uses
signal.SIGALRMfor immediate interruption of the main thread.Windows:
signal.SIGALRMis unavailable, so athreading.Timerfallback is used. This cannot interrupt CPU-bound code — the timeout is detected after the current step finishes. I/O-bound code (time.sleep, socket reads, etc.) is interrupted promptly because the timer callback sets a flag that__exit__checks.
Usage in environment.py:
from behave_kit import setup_timeout
from behave_kit.timeout import timeout_before_scenario, timeout_after_scenario
def before_all(context):
setup_timeout(context, default_timeout=30)
def before_scenario(context, scenario):
timeout_before_scenario(context, scenario)
def after_scenario(context, scenario):
timeout_after_scenario(context, scenario)
- class behave_kit.timeout.SignalTimeoutHandler(timeout: float)[source]¶
Bases:
objectUnix timeout handler using
signal.SIGALRM.Interrupts the main thread immediately when the deadline is reached. Only works on the main thread of the main interpreter.
- class behave_kit.timeout.ThreadTimeoutHandler(timeout: float)[source]¶
Bases:
objectWindows fallback timeout handler using
threading.Timer.Cannot interrupt CPU-bound code. The timeout is detected in
__exit__after the wrapped block finishes. I/O-bound code that checks for interrupts (e.g.time.sleep) may be interrupted sooner.
- class behave_kit.timeout.TimeoutHandler(*args, **kwargs)[source]¶
Bases:
ProtocolProtocol for platform-specific timeout handlers.
- behave_kit.timeout.setup_timeout(context: Context, default_timeout: float | None = None, *, timeout_tag: str = 'timeout') None[source]¶
Configure per-scenario timeout.
- Parameters:
context – The Behave context object.
default_timeout – Timeout in seconds for all scenarios.
0disables the per-scenario timeout (Behave’s native--timeoutstill applies independently). IfNone, the value is read from theBEHAVE_SCENARIO_TIMEOUTenvironment variable (default0).timeout_tag – Name of the tag used for per-scenario overrides. The format is
@<timeout_tag>:NwhereNis seconds. Default:"timeout"(i.e.@timeout:10).
- behave_kit.timeout.timeout_after_scenario(context: Context, scenario: BehaveScenario) None[source]¶
Cancel the timeout timer for
scenario.Call this from
after_scenario. RaisesTimeoutErrorif the scenario exceeded its timeout (Windows fallback only; on Unix the error is raised immediately during step execution).If the scenario already failed with an exception, that exception is passed to the handler so it doesn’t mask the original failure.