behave-comments

July 16, 2026 · View on GitHub

Parse doc strings, extract metadata from comments, and declare lifecycle hooks in Behave .feature files.

Features

Text Block Parsing

Detect content types in doc strings and parse them automatically:

Given a JSON document
  """json
  {"name": "Alice", "age": 30}
  """
from behave_comments import with_parsed_text

@given("a JSON document")
@with_parsed_text()
def step_given_json(context, text_block):
    data = text_block.parsed  # {"name": "Alice", "age": 30}

Supported content types: json, yaml, xml, csv, form-urlencoded, graphql, text/plain.

Metadata Annotations

Extract structured metadata from # @key value comments:

# @jira TICKET-42
# @owner team-alpha
Feature: Login

  # @id SC-001
  Scenario: Successful login
    Given the user is on the login page
from behave_comments import inject_metadata

def before_feature(context, feature):
    inject_metadata(context, feature)
    # context.metadata == {
    #   "feature": [{"key": "jira", "value": "TICKET-42"}, ...],
    #   "scenario": [{"key": "id", "value": "SC-001"}, ...],
    # }

Lifecycle Hooks

Declare setup/teardown steps directly in comments:

# @before-feature: Given a clean database
Feature: User Tests

  # @after-scenario: Then clear the cache
  Scenario: User login
    Given a registered user
from behave_comments import setup_lifecycle_hooks, run_before_feature, run_after_scenario

def before_feature(context, feature):
    setup_lifecycle_hooks(context, feature)
    run_before_feature(context, feature)

def after_scenario(context, scenario):
    run_after_scenario(context, scenario)

Installation

pip install behave-comments

YAML support is included — pyyaml is a required dependency.

Usage

Text Blocks

from behave_comments import with_parsed_text, extract_text_block

# Option 1: Decorator
@given("a JSON document")
@with_parsed_text()
def step(context, text_block):
    data = text_block.parsed

# Option 2: Manual extraction
def step(context):
    text_block = extract_text_block(context.step)
    data = text_block.parsed

Annotations

from behave_comments import inject_metadata, extract_annotations, annotations_to_tags

def before_feature(context, feature):
    inject_metadata(context, feature)

# Or extract directly
annotations = extract_annotations("features/login.feature")
tags = annotations_to_tags(annotations)

Lifecycle Hooks Usage

from behave_comments import (
    setup_lifecycle_hooks,
    setup_lifecycle_hooks_from_path,
    run_before_all,
    run_after_all,
    run_before_feature,
    run_after_feature,
    run_before_scenario,
    run_after_scenario,
    run_before_step,
    run_after_step,
)

def before_all(context):
    setup_lifecycle_hooks_from_path(context, "features/")
    run_before_all(context)

def after_all(context):
    run_after_all(context)

def before_feature(context, feature):
    setup_lifecycle_hooks(context, feature)
    run_before_feature(context, feature)

def after_feature(context, feature):
    run_after_feature(context, feature)

def before_scenario(context, scenario):
    run_before_scenario(context, scenario)

def after_scenario(context, scenario):
    run_after_scenario(context, scenario)

def before_step(context, step):
    run_before_step(context, step)

def after_step(context, step):
    run_after_step(context, step)

API Reference

Errors

  • BehaveCommentsError — Base exception
  • ContentTypeError — Unsupported content type
  • ParseError — Parsing failed
  • AnnotationParseError — Invalid annotation syntax
  • LifecycleStepError — Lifecycle step execution failed

Models

  • TextBlock(content, content_type, line, parsed) — Parsed doc string
  • Annotation(key, value, line, scope, scope_name) — Extracted annotation
  • LifecycleHook(hook_type, step_text, line) — Lifecycle hook declaration

Development

pip install -e ".[dev]"
pytest
ruff check behave_comments tests
mypy behave_comments/

License

MIT