Context Utilities

Tools for managing the Behave context: automatic dumps on failure and scoped attribute cleanup.

Context dump on failure

When a scenario fails, dump_context() writes every JSON-serializable context attribute to a file for post-mortem debugging.

behave_kit.context.dump.dump_context(context: Context, path: str | Path = 'debug/') Path[source]

Write every JSON-serializable attribute of context to a file.

Non-serializable attributes are skipped with a logging.warning.

Returns:

The path of the written file.

behave_kit.context.dump.dump_context_on_failure(context: Context, scenario: object, path: str | Path = 'debug/') Path | None[source]

Call dump_context only when scenario.status is "failed".

Examples

Automatic (via `setup()`):

from behave_kit import setup

def before_all(context):
    setup(context, env="staging")

# Context dump on failure is wired automatically.
# Failed scenarios produce a JSON file in debug/.

Manual:

from behave_kit import dump_context_on_failure

def after_scenario(context, scenario):
    dump_context_on_failure(context, scenario, path="debug/")

Always dump (not just on failure):

from behave_kit import dump_context

def after_scenario(context, scenario):
    dump_context(context, path="debug/")

Output format

The dump file is named after the scenario and contains all serializable attributes:

{
  "base_url": "https://example.com",
  "status_code": 500,
  "user": {"name": "Alice", "age": 30},
  "tags": ["browser", "api"]
}

Non-serializable attributes (e.g. WebDriver instances, file handles) are skipped with a warning.

Scoped attributes

The @scoped decorator tracks context attributes and automatically deletes them after the scenario, preventing leaks between scenarios.

behave_kit.context.scoped.scoped(name: str, scope: Scope = Scope.SCENARIO) Callable[[Callable[[Concatenate[Context, P]], R]], Callable[[Concatenate[Context, P]], R]][source]

Decorator: track context.<name> so it is cleaned up automatically.

After the wrapped step function runs, name is registered for cleanup at the given scope (see cleanup_scoped).

behave_kit.context.scoped.cleanup_scoped(context: Context, scope: Scope = Scope.SCENARIO) None[source]

Delete every attribute tracked with @scoped at the given scope.

Examples

Basic usage:

from behave_kit import scoped

@scoped("driver")
@when("I start the driver")
def step(context):
    context.driver = start_driver()
# context.driver is automatically deleted after the scenario

Multiple scoped attributes:

@scoped("browser")
@scoped("session")
@when("I start a browser session")
def step(context):
    context.browser = start_browser()
    context.session = create_session()

Feature-scoped attributes:

from behave_kit import scoped, Scope

@scoped("database", scope=Scope.FEATURE)
@given("I have a database connection")
def step(context):
    context.database = connect_to_database()
# context.database is cleaned up after all scenarios in the feature

Manual cleanup:

from behave_kit import cleanup_scoped

def after_scenario(context, scenario):
    cleanup_scoped(context)  # delete all scenario-scoped attributes

Why scoped attributes matter

Without @scoped, attributes set in one scenario survive into the next:

# Scenario 1
@when("I create a temporary file")
def step(context):
    context.temp_file = create_temp_file()

# Scenario 2 (temp_file still exists from Scenario 1!)
@then("no temp file should exist")
def step(context):
    assert not hasattr(context, "temp_file")  # FAILS without @scoped

With @scoped:

@scoped("temp_file")
@when("I create a temporary file")
def step(context):
    context.temp_file = create_temp_file()
# temp_file is deleted after Scenario 1, so Scenario 2 passes