behave-format 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. behave_format-1.0.0/.github/workflows/ci.yml +47 -0
  2. behave_format-1.0.0/.github/workflows/docs.yml +53 -0
  3. behave_format-1.0.0/.github/workflows/release.yml +86 -0
  4. behave_format-1.0.0/.gitignore +20 -0
  5. behave_format-1.0.0/.pre-commit-config.yaml +7 -0
  6. behave_format-1.0.0/CHANGELOG.md +24 -0
  7. behave_format-1.0.0/LICENSE +21 -0
  8. behave_format-1.0.0/Makefile +37 -0
  9. behave_format-1.0.0/PKG-INFO +280 -0
  10. behave_format-1.0.0/README.md +248 -0
  11. behave_format-1.0.0/behave_format/__init__.py +41 -0
  12. behave_format-1.0.0/behave_format/cli/__init__.py +1 -0
  13. behave_format-1.0.0/behave_format/cli/main.py +175 -0
  14. behave_format-1.0.0/behave_format/config/__init__.py +1 -0
  15. behave_format-1.0.0/behave_format/config/settings.py +81 -0
  16. behave_format-1.0.0/behave_format/pipeline/__init__.py +1 -0
  17. behave_format-1.0.0/behave_format/pipeline/align.py +69 -0
  18. behave_format-1.0.0/behave_format/pipeline/formatter.py +96 -0
  19. behave_format-1.0.0/behave_format/pipeline/normalize.py +124 -0
  20. behave_format-1.0.0/behave_format/pipeline/rules.py +31 -0
  21. behave_format-1.0.0/behave_format/pipeline/sort.py +65 -0
  22. behave_format-1.0.0/behave_format/printer/__init__.py +1 -0
  23. behave_format-1.0.0/behave_format/printer/feature_printer.py +93 -0
  24. behave_format-1.0.0/behave_format/printer/scenario_printer.py +117 -0
  25. behave_format-1.0.0/behave_format/printer/step_printer.py +46 -0
  26. behave_format-1.0.0/behave_format/printer/table_printer.py +47 -0
  27. behave_format-1.0.0/behave_format/printer/tag_printer.py +21 -0
  28. behave_format-1.0.0/conftest.py +12 -0
  29. behave_format-1.0.0/docs/api/formatter.md +3 -0
  30. behave_format-1.0.0/docs/api/overview.md +18 -0
  31. behave_format-1.0.0/docs/api/pipeline.md +17 -0
  32. behave_format-1.0.0/docs/api/printers.md +21 -0
  33. behave_format-1.0.0/docs/api/settings.md +3 -0
  34. behave_format-1.0.0/docs/architecture.md +47 -0
  35. behave_format-1.0.0/docs/changelog.md +3 -0
  36. behave_format-1.0.0/docs/contributing.md +40 -0
  37. behave_format-1.0.0/docs/design_decisions.md +26 -0
  38. behave_format-1.0.0/docs/examples.md +76 -0
  39. behave_format-1.0.0/docs/getting-started/installation.md +20 -0
  40. behave_format-1.0.0/docs/getting-started/quick_start.md +29 -0
  41. behave_format-1.0.0/docs/guides/cli_usage.md +48 -0
  42. behave_format-1.0.0/docs/guides/configuration.md +42 -0
  43. behave_format-1.0.0/docs/guides/formatting_rules.md +54 -0
  44. behave_format-1.0.0/docs/guides/pipeline.md +45 -0
  45. behave_format-1.0.0/docs/guides/python_api.md +63 -0
  46. behave_format-1.0.0/docs/index.md +25 -0
  47. behave_format-1.0.0/examples/data_tables.feature +20 -0
  48. behave_format-1.0.0/examples/login.feature +35 -0
  49. behave_format-1.0.0/examples/rules.feature +36 -0
  50. behave_format-1.0.0/examples/shopping_cart.feature +30 -0
  51. behave_format-1.0.0/mkdocs.yml +80 -0
  52. behave_format-1.0.0/pyproject.toml +81 -0
  53. behave_format-1.0.0/tests/conftest.py +27 -0
  54. behave_format-1.0.0/tests/golden/data_tables_expected.feature +20 -0
  55. behave_format-1.0.0/tests/golden/login_expected.feature +35 -0
  56. behave_format-1.0.0/tests/golden/rules_expected.feature +36 -0
  57. behave_format-1.0.0/tests/golden/shopping_cart_expected.feature +30 -0
  58. behave_format-1.0.0/tests/test_cli.py +113 -0
  59. behave_format-1.0.0/tests/test_formatting.py +89 -0
  60. behave_format-1.0.0/tests/test_golden.py +36 -0
  61. behave_format-1.0.0/tests/test_idempotency.py +73 -0
  62. behave_format-1.0.0/tests/test_performance.py +67 -0
  63. behave_format-1.0.0/tox.ini +22 -0
@@ -0,0 +1,47 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+ branches: [main, master]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.11"
17
+ - run: pip install ruff
18
+ - run: ruff check behave_format/ tests/
19
+ - run: ruff format --check behave_format/ tests/
20
+
21
+ test:
22
+ runs-on: ubuntu-latest
23
+ strategy:
24
+ matrix:
25
+ python-version: ["3.11", "3.12", "3.13"]
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+ - uses: actions/setup-python@v5
29
+ with:
30
+ python-version: ${{ matrix.python-version }}
31
+ - run: pip install -e ".[dev]"
32
+ - run: python -m pytest tests/ --cov=behave_format --cov-report=xml --cov-report=term
33
+ - uses: codecov/codecov-action@v4
34
+ with:
35
+ file: ./coverage.xml
36
+ fail_ci_if_error: false
37
+
38
+ packaging:
39
+ runs-on: ubuntu-latest
40
+ steps:
41
+ - uses: actions/checkout@v4
42
+ - uses: actions/setup-python@v5
43
+ with:
44
+ python-version: "3.11"
45
+ - run: pip install build twine
46
+ - run: python -m build
47
+ - run: python -m twine check dist/*
@@ -0,0 +1,53 @@
1
+ name: Docs
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - "docs/**"
9
+ - "mkdocs.yml"
10
+ - "README.md"
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+ pages: write
16
+ id-token: write
17
+
18
+ concurrency:
19
+ group: "pages"
20
+ cancel-in-progress: false
21
+
22
+ jobs:
23
+ build:
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+
28
+ - uses: actions/setup-python@v5
29
+ with:
30
+ python-version: "3.12"
31
+
32
+ - name: Install MkDocs
33
+ run: pip install mkdocs mkdocs-material
34
+
35
+ - name: Build site
36
+ run: mkdocs build
37
+
38
+ - name: Upload artifact
39
+ uses: actions/upload-pages-artifact@v3
40
+ with:
41
+ path: site
42
+
43
+ deploy:
44
+ needs: build
45
+ runs-on: ubuntu-latest
46
+ environment:
47
+ name: github-pages
48
+ url: ${{ steps.deployment.outputs.page_url }}
49
+ continue-on-error: true
50
+ steps:
51
+ - name: Deploy to GitHub Pages
52
+ id: deployment
53
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,86 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: write
10
+ id-token: write
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ matrix:
17
+ python-version: ["3.11", "3.12", "3.13"]
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+ - run: pip install -e ".[dev]"
24
+ - run: python -m pytest tests/ --cov=behave_format --cov-report=term
25
+
26
+ build:
27
+ needs: test
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+
32
+ - uses: actions/setup-python@v5
33
+ with:
34
+ python-version: "3.12"
35
+
36
+ - name: Install build dependencies
37
+ run: pip install build twine
38
+
39
+ - name: Build distributions
40
+ run: python -m build
41
+
42
+ - name: Check distributions
43
+ run: python -m twine check dist/*
44
+
45
+ - name: Upload distributions
46
+ uses: actions/upload-artifact@v4
47
+ with:
48
+ name: dist
49
+ path: dist/
50
+
51
+ publish-pypi:
52
+ needs: build
53
+ runs-on: ubuntu-latest
54
+ environment: pypi
55
+ permissions:
56
+ id-token: write
57
+ continue-on-error: true
58
+ steps:
59
+ - name: Download distributions
60
+ uses: actions/download-artifact@v4
61
+ with:
62
+ name: dist
63
+ path: dist/
64
+
65
+ - name: Publish to PyPI
66
+ uses: pypa/gh-action-pypi-publish@release/v1
67
+
68
+ github-release:
69
+ needs: build
70
+ runs-on: ubuntu-latest
71
+ permissions:
72
+ contents: write
73
+ steps:
74
+ - uses: actions/checkout@v4
75
+
76
+ - name: Download distributions
77
+ uses: actions/download-artifact@v4
78
+ with:
79
+ name: dist
80
+ path: dist/
81
+
82
+ - name: Create GitHub Release
83
+ uses: softprops/action-gh-release@v2
84
+ with:
85
+ generate_release_notes: true
86
+ files: dist/*
@@ -0,0 +1,20 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ *.egg
6
+ dist/
7
+ build/
8
+ .eggs/
9
+ *.so
10
+ .tox/
11
+ .coverage
12
+ .coverage.*
13
+ htmlcov/
14
+ .pytest_cache/
15
+ .ruff_cache/
16
+ *.whl
17
+ *.tar.gz
18
+ .venv/
19
+ venv/
20
+ env/
@@ -0,0 +1,7 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.5.0
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
@@ -0,0 +1,24 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.0.0] - 2025-07-02
11
+
12
+ ### Added
13
+
14
+ - Initial release of behave-format
15
+ - Opinionated, deterministic formatter for Behave `.feature` files
16
+ - Consumes `behave-model.Project` as input (no direct Gherkin parsing)
17
+ - Formatting pipeline: normalize → sort → align → print
18
+ - Tags sorted alphabetically by default
19
+ - Table column alignment
20
+ - Whitespace normalization (trailing spaces, indentation)
21
+ - CLI with `--check` (CI mode), `--diff`, and write modes
22
+ - Configuration via `pyproject.toml` under `[tool.behave-format]`
23
+ - Golden file tests, idempotency tests, CLI tests, performance tests
24
+ - GitHub Actions CI workflow (lint, test, coverage, packaging)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mathias Paulenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,37 @@
1
+ .PHONY: install dev test test-verbose lint format coverage build clean docs docs-serve
2
+
3
+ install:
4
+ pip install -e .
5
+
6
+ dev:
7
+ pip install -e ".[dev]"
8
+
9
+ test:
10
+ pytest tests/ -q
11
+
12
+ test-verbose:
13
+ pytest tests/ -v --tb=short
14
+
15
+ lint:
16
+ ruff check behave_format/ tests/
17
+ ruff format --check behave_format/ tests/
18
+
19
+ format:
20
+ ruff format behave_format/ tests/
21
+ ruff check --fix behave_format/ tests/
22
+
23
+ coverage:
24
+ pytest tests/ --cov=behave_format --cov-report=term-missing --cov-report=html
25
+
26
+ build:
27
+ python -m build
28
+
29
+ docs:
30
+ mkdocs build
31
+
32
+ docs-serve:
33
+ mkdocs serve
34
+
35
+ clean:
36
+ rm -rf build/ dist/ *.egg-info/ .coverage .pytest_cache/ htmlcov/ .tox/ .ruff_cache/
37
+ find . -type d -name __pycache__ -exec rm -rf {} +
@@ -0,0 +1,280 @@
1
+ Metadata-Version: 2.4
2
+ Name: behave-format
3
+ Version: 1.0.0
4
+ Summary: The opinionated formatter for Behave .feature files
5
+ Project-URL: Homepage, https://github.com/MathiasPaulenko/behave-format
6
+ Project-URL: Repository, https://github.com/MathiasPaulenko/behave-format
7
+ Project-URL: Issues, https://github.com/MathiasPaulenko/behave-format/issues
8
+ Project-URL: Changelog, https://github.com/MathiasPaulenko/behave-format/blob/main/CHANGELOG.md
9
+ Author: Mathias Paulenko
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: bdd,behave,cucumber,format,formatter,gherkin,testing
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Classifier: Topic :: Software Development :: Testing
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: behave-model>=0.1.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
26
+ Requires-Dist: pytest>=8.0; extra == 'dev'
27
+ Requires-Dist: ruff>=0.5; extra == 'dev'
28
+ Provides-Extra: docs
29
+ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
30
+ Requires-Dist: mkdocs>=1.6; extra == 'docs'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # behave-format
34
+
35
+ > The opinionated formatter for Behave `.feature` files.
36
+
37
+ [Black](https://github.com/psf/black) is for Python. [gofmt](https://go.dev/blog/gofmt) is for Go. **behave-format** is for Gherkin.
38
+
39
+ ---
40
+
41
+ ## Overview
42
+
43
+ `behave-format` is a deterministic, opinionated formatter for Behave `.feature` files. It consumes the canonical domain model from [behave-model](https://github.com/MathiasPaulenko/behave-model) and produces clean, consistent, beautifully formatted output.
44
+
45
+ **Key principle:** behave-format does NOT parse Gherkin. It does NOT lint. It does NOT validate. It ONLY transforms a `behave-model.Project` into formatted `.feature` files.
46
+
47
+ ```text
48
+ .feature files → behave-model (domain model) → behave-format → formatted .feature files
49
+ ```
50
+
51
+ ## Features
52
+
53
+ - **Opinionated** — minimal configuration, sensible defaults
54
+ - **Deterministic** — same input always produces same output
55
+ - **Idempotent** — `format(format(x)) == format(x)`
56
+ - **Fast** — handles thousands of feature files efficiently
57
+ - **CI-friendly** — `--check` mode with exit code 1 when formatting is needed
58
+ - **Safe** — never changes semantics (names, step text, table values, docstrings)
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ pip install behave-format
64
+ ```
65
+
66
+ ## Quick Start
67
+
68
+ ### CLI
69
+
70
+ ```bash
71
+ # Format files in place (default)
72
+ behave-format features/
73
+
74
+ # Check mode (CI) — exit 1 if formatting is needed
75
+ behave-format --check features/
76
+
77
+ # Diff mode — show differences without writing
78
+ behave-format --diff features/
79
+ ```
80
+
81
+ ### Python API
82
+
83
+ ```python
84
+ from behave_model import load_project
85
+ from behave_format import format_project, render_project, Settings
86
+
87
+ project = load_project("features/")
88
+
89
+ # Format the project model in place
90
+ format_project(project)
91
+
92
+ # Or render to text
93
+ text = render_project(project, Settings())
94
+ ```
95
+
96
+ ## Formatting Rules
97
+
98
+ ### Tags
99
+
100
+ Tags are sorted alphabetically by default:
101
+
102
+ ```gherkin
103
+ @api @smoke
104
+ ```
105
+
106
+ ### Features
107
+
108
+ - One blank line before each Feature
109
+ - Clean title formatting
110
+
111
+ ### Scenarios
112
+
113
+ - One blank line before each Scenario
114
+ - Two-space indentation for steps
115
+
116
+ ```gherkin
117
+ Given user exists
118
+ When user logs in
119
+ Then dashboard is shown
120
+ ```
121
+
122
+ ### Tables
123
+
124
+ Tables are always aligned:
125
+
126
+ Before:
127
+
128
+ ```gherkin
129
+ |user|password|
130
+ |john|123|
131
+ ```
132
+
133
+ After:
134
+
135
+ ```gherkin
136
+ | user | password |
137
+ | john | 123 |
138
+ ```
139
+
140
+ ### Blank Lines
141
+
142
+ - No trailing blank lines
143
+ - No multiple consecutive empty lines
144
+ - Consistent spacing between blocks
145
+
146
+ ### Indentation
147
+
148
+ - Spaces only (no tabs)
149
+ - Default: 2 spaces
150
+
151
+ ## Configuration
152
+
153
+ Minimal configuration via `pyproject.toml`:
154
+
155
+ ```toml
156
+ [tool.behave-format]
157
+ indent = 2
158
+ sort_tags = true
159
+ sort_features = false
160
+ sort_scenarios = false
161
+ line_length = 120
162
+ ```
163
+
164
+ | Option | Default | Description |
165
+ |--------|---------|-------------|
166
+ | `indent` | `2` | Number of spaces for indentation |
167
+ | `sort_tags` | `true` | Sort tags alphabetically |
168
+ | `sort_features` | `false` | Sort features by name |
169
+ | `sort_scenarios` | `false` | Sort scenarios by name |
170
+ | `line_length` | `120` | Maximum line length (reference) |
171
+
172
+ ## Before / After
173
+
174
+ ### Before
175
+
176
+ ```gherkin
177
+ @smoke @auth
178
+ Feature: Login
179
+ As a user
180
+ I want to log in
181
+
182
+ Background:
183
+ Given a database connection
184
+
185
+ @happy
186
+ Scenario: Successful login
187
+ Given the user is on the login page
188
+ When the user enters "admin" and "password"
189
+ Then the user should be logged in
190
+ ```
191
+
192
+ ### After
193
+
194
+ ```gherkin
195
+ @auth @smoke
196
+ Feature: Login
197
+ As a user
198
+ I want to log in
199
+
200
+ Background:
201
+ Given a database connection
202
+
203
+ @happy
204
+ Scenario: Successful login
205
+ Given the user is on the login page
206
+ When the user enters "admin" and "password"
207
+ Then the user should be logged in
208
+ ```
209
+
210
+ ## Architecture
211
+
212
+ ```text
213
+ behave_format/
214
+ ├── config/
215
+ │ └── settings.py # Settings dataclass + pyproject.toml loader
216
+ ├── pipeline/
217
+ │ ├── normalize.py # Whitespace, indentation, tag normalization
218
+ │ ├── sort.py # Sort tags, features, scenarios
219
+ │ ├── align.py # Table alignment, trailing whitespace
220
+ │ ├── rules.py # Formatting rules registry
221
+ │ └── formatter.py # Main orchestrator (format_project)
222
+ ├── printer/
223
+ │ ├── feature_printer.py
224
+ │ ├── scenario_printer.py
225
+ │ ├── step_printer.py
226
+ │ ├── table_printer.py
227
+ │ └── tag_printer.py
228
+ └── cli/
229
+ └── main.py # CLI entry point
230
+ ```
231
+
232
+ ## Pipeline
233
+
234
+ 1. **Normalize** — clean whitespace, standardize indentation, normalize tags
235
+ 2. **Sort** — order tags (alphabetically by default), optionally features and scenarios
236
+ 3. **Align** — align table columns, remove trailing spaces
237
+ 4. **Print** — convert `behave-model` → `.feature` text (deterministic)
238
+
239
+ ## Safety
240
+
241
+ The formatter NEVER changes semantics:
242
+
243
+ - Feature names: preserved
244
+ - Scenario names: preserved
245
+ - Step text: preserved (only whitespace normalized)
246
+ - DocString content: preserved
247
+ - Table values: preserved (only alignment changes)
248
+ - Comments content: preserved
249
+
250
+ ## Integration
251
+
252
+ `behave-format` integrates naturally with the Behave ecosystem:
253
+
254
+ - [behave-model](https://github.com/MathiasPaulenko/behave-model) — single source of truth
255
+ - [behave-lint](https://github.com/MathiasPaulenko/behave-lint) — linting
256
+ - [behave-modern-json-report](https://github.com/MathiasPaulenko/behave-modern-json-report)
257
+ - [behave-modern-report](https://github.com/MathiasPaulenko/behave-modern-report)
258
+ - [behave-markdown-report](https://github.com/MathiasPaulenko/behave-markdown-report)
259
+
260
+ ## Development
261
+
262
+ ```bash
263
+ pip install -e ".[dev]"
264
+ pytest tests/ -v
265
+ ruff check .
266
+ ruff format --check .
267
+ ```
268
+
269
+ ## Contributing
270
+
271
+ Contributions are welcome! Please:
272
+
273
+ 1. Fork the repository
274
+ 2. Create a feature branch
275
+ 3. Run `ruff check .` and `pytest tests/` before submitting
276
+ 4. Open a Pull Request
277
+
278
+ ## License
279
+
280
+ MIT