Metadata-Version: 2.4
Name: behave-model
Version: 1.0.0
Summary: Canonical object model for Behave projects
Project-URL: Homepage, https://github.com/MathiasPaulenko/behave-model
Project-URL: Repository, https://github.com/MathiasPaulenko/behave-model
Project-URL: Issues, https://github.com/MathiasPaulenko/behave-model/issues
Project-URL: Documentation, https://mathiaspaulenko.github.io/behave-model/
Project-URL: Changelog, https://github.com/MathiasPaulenko/behave-model/blob/main/CHANGELOG.md
Author: Mathias Paulenko
License-Expression: MIT
License-File: LICENSE
Keywords: bdd,behave,cucumber,domain,gherkin,model,testing
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.11
Requires-Dist: behave>=1.2.6
Provides-Extra: dev
Requires-Dist: mkdocs-material>=9.5; extra == 'dev'
Requires-Dist: mkdocs>=1.6; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Description-Content-Type: text/markdown

<div align="center">

# behave-model

**The canonical object model for [Behave](https://github.com/behave/behave) projects.**

[![CI](https://github.com/MathiasPaulenko/behave-model/actions/workflows/ci.yml/badge.svg)](https://github.com/MathiasPaulenko/behave-model/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/behave-model.svg)](https://pypi.org/project/behave-model/)
[![Python](https://img.shields.io/pypi/pyversions/behave-model.svg)](https://pypi.org/project/behave-model/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Coverage](https://img.shields.io/badge/coverage-95%25-brightgreen)](https://github.com/MathiasPaulenko/behave-model)

[Documentation](https://mathiaspaulenko.github.io/behave-model/) · [Quick Start](https://mathiaspaulenko.github.io/behave-model/quick_start/) · [API Reference](https://mathiaspaulenko.github.io/behave-model/api_reference/)

</div>

---

`behave-model` provides a clean, stable, and extensible Python API that represents every element of a Behave project — features, rules, scenarios, steps, tags, tables, docstrings, and more. It is the foundation for an entire ecosystem of tools: formatters, linters, analyzers, report generators, and anything else that needs to understand `.feature` files.

## Why?

Every Behave tooling project currently parses `.feature` files independently. `behave-model` provides a single, well-tested domain model so that future tools can depend on it instead of reinventing the parser.

- **Compatible with Behave 1.3.x** — Tag Expression v2 and Gherkin v6 (including `Rule` blocks)
- **Clean domain model** — Pure dataclasses, no external runtime dependencies beyond Behave
- **Visitor pattern** — Traverse the entire tree with custom visitors
- **Query API** — Find features, scenarios, steps, and tags by name, tag, or keyword
- **Serializers** — Dict, JSON, and pretty-printed Gherkin output
- **Transformations** — Safe in-place modifications (rename tags, sort, normalize)
- **Validation framework** — Pluggable rules with built-in checks
- **95% test coverage** — Comprehensive unit, integration, and golden file tests

## Installation

```bash
pip install behave-model
```

For development:

```bash
pip install -e ".[dev]"
```

## Quick Start

```python
from behave_model import load_project

# Load all .feature files from a directory
project = load_project("features/")

# Access features, rules, and scenarios
print(len(project.features))          # number of features
print(project.features[0].name)       # first feature name
print(len(project.features[0].rules)) # rules (Gherkin v6)

# Query the model
for scenario in project.find_scenarios(tag="@smoke"):
    print(scenario.name)

# Statistics
stats = project.statistics()
print(f"{stats['features']} features, {stats['scenarios']} scenarios, {stats['steps']} steps")

# Traverse the tree
for node in project.walk():
    print(type(node).__name__)
```

## Domain Model

```text
Project
├── Metadata
├── Feature
│   ├── Tag
│   ├── Background
│   │   └── Step
│   ├── Scenario
│   │   ├── Tag
│   │   └── Step
│   ├── ScenarioOutline
│   │   ├── Tag
│   │   ├── Step
│   │   └── Examples
│   │       └── Table
│   └── Rule (Gherkin v6)
│       ├── Tag
│       ├── Background
│       │   └── Step
│       ├── Scenario
│       └── ScenarioOutline
└── ...
```

Every node has a `Location` (filename, line, column) for precise source mapping.

## Features at a Glance

### Loading

```python
from behave_model import load_project, load_feature

project = load_project("features/")               # all .feature files
feature = load_feature("features/login.feature")  # single file
```

### Visitor Pattern

```python
from behave_model import Visitor

class StepCounter(Visitor):
    def __init__(self):
        self.count = 0

    def visit_step(self, step):
        self.count += 1

visitor = StepCounter()
project.accept(visitor)
print(f"Total steps: {visitor.count}")
```

### Query API

```python
project.find_feature("Login")
project.find_tag("@smoke")
project.find_scenarios(tag="@api")
project.find_scenarios(name_contains="login")
project.find_steps(keyword="Given")
project.find_steps(text_contains="user")
```

### Serialization

```python
from behave_model import JsonSerializer, DictSerializer, PrettyPrinter

# JSON
json_str = JsonSerializer().serialize_project(project)

# Dictionary
data = DictSerializer().serialize_project(project)

# Pretty-printed Gherkin
text = PrettyPrinter().print_feature(feature)
```

### Transformations

```python
from behave_model import rename_tag, sort_tags, normalize_whitespace

rename_tag(project, "@smoke", "@critical")
sort_tags(project)
normalize_whitespace(project)
```

### Validation

```python
from behave_model import Validator

validator = Validator()
issues = validator.validate(project)
for issue in issues:
    print(f"[{issue.severity}] {issue.rule_name}: {issue.message}")
```

## Architecture

```text
Feature File → Parser Adapter → Domain Model → Visitors → Queries → Transformations → Serializers
```

The Domain Model never depends on report generation or formatting. Each layer has a single responsibility and can be used independently.

| Layer | Package | Responsibility |
|-------|---------|----------------|
| Parser Adapter | `behave_model.parser` | Wraps Behave's parser, adapts to domain model |
| Domain Model | `behave_model.model` | Pure dataclasses for every Gherkin element |
| Visitors | `behave_model.visitors` | Generic traversal pattern |
| Queries | `behave_model.queries` | High-level filtering API |
| Transformations | `behave_model.transformations` | Safe in-place modifications |
| Serializers | `behave_model.serializers` | Dict, JSON, Gherkin output |
| Validation | `behave_model.validation` | Pluggable rule framework |

## Compatibility

| Feature | Supported |
|---------|-----------|
| Behave 1.3.x | ✅ |
| Tag Expression v1 | ✅ |
| Tag Expression v2 | ✅ |
| Gherkin v6 (Rules) | ✅ |
| Scenario Outlines | ✅ |
| Data Tables | ✅ |
| DocStrings | ✅ |
| Background | ✅ |
| Multi-language features | ✅ |

## Development

```bash
# Clone and install
git clone https://github.com/MathiasPaulenko/behave-model.git
cd behave-model
pip install -e ".[dev]"

# Run tests
make test

# Run tests with coverage
make coverage

# Lint
make lint

# Format
make format

# Build
make build
```

## Documentation

Full documentation is available at [mathiaspaulenko.github.io/behave-model](https://mathiaspaulenko.github.io/behave-model/).

## Contributing

Contributions are welcome! See [CONTRIBUTING.md](docs/contributing.md) for guidelines.

## License

MIT
