behave-priority 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 (56) hide show
  1. behave_priority-1.0.0/.gitignore +20 -0
  2. behave_priority-1.0.0/LICENSE +21 -0
  3. behave_priority-1.0.0/PKG-INFO +325 -0
  4. behave_priority-1.0.0/README.md +286 -0
  5. behave_priority-1.0.0/behave_priority/__init__.py +59 -0
  6. behave_priority-1.0.0/behave_priority/config.py +71 -0
  7. behave_priority-1.0.0/behave_priority/exceptions.py +15 -0
  8. behave_priority-1.0.0/behave_priority/hooks.py +390 -0
  9. behave_priority-1.0.0/behave_priority/parallel.py +164 -0
  10. behave_priority-1.0.0/behave_priority/parser.py +132 -0
  11. behave_priority-1.0.0/behave_priority/report.py +398 -0
  12. behave_priority-1.0.0/behave_priority/sorter.py +249 -0
  13. behave_priority-1.0.0/docs/conf.py +60 -0
  14. behave_priority-1.0.0/docs/guides/configuration.rst +179 -0
  15. behave_priority-1.0.0/docs/guides/gherkin_v6.rst +131 -0
  16. behave_priority-1.0.0/docs/guides/integration.rst +151 -0
  17. behave_priority-1.0.0/docs/guides/reports.rst +181 -0
  18. behave_priority-1.0.0/docs/index.rst +293 -0
  19. behave_priority-1.0.0/docs/modules/config.rst +134 -0
  20. behave_priority-1.0.0/docs/modules/exceptions.rst +58 -0
  21. behave_priority-1.0.0/docs/modules/hooks.rst +189 -0
  22. behave_priority-1.0.0/docs/modules/parallel.rst +24 -0
  23. behave_priority-1.0.0/docs/modules/parser.rst +135 -0
  24. behave_priority-1.0.0/docs/modules/report.rst +206 -0
  25. behave_priority-1.0.0/docs/modules/sorter.rst +111 -0
  26. behave_priority-1.0.0/docs/requirements.txt +3 -0
  27. behave_priority-1.0.0/pyproject.toml +93 -0
  28. behave_priority-1.0.0/tests/__init__.py +0 -0
  29. behave_priority-1.0.0/tests/conftest.py +1 -0
  30. behave_priority-1.0.0/tests/e2e/__init__.py +0 -0
  31. behave_priority-1.0.0/tests/e2e/environment.py +61 -0
  32. behave_priority-1.0.0/tests/e2e/features/critical.feature +21 -0
  33. behave_priority-1.0.0/tests/e2e/features/failfast.feature +25 -0
  34. behave_priority-1.0.0/tests/e2e/features/feature_priority.feature +13 -0
  35. behave_priority-1.0.0/tests/e2e/features/outline.feature +19 -0
  36. behave_priority-1.0.0/tests/e2e/features/priority_order.feature +25 -0
  37. behave_priority-1.0.0/tests/e2e/features/rule_priority.feature +23 -0
  38. behave_priority-1.0.0/tests/e2e/features/smoke_tag.feature +18 -0
  39. behave_priority-1.0.0/tests/e2e/steps/steps.py +28 -0
  40. behave_priority-1.0.0/tests/e2e/test_e2e.py +241 -0
  41. behave_priority-1.0.0/tests/integration/__init__.py +0 -0
  42. behave_priority-1.0.0/tests/integration/conftest.py +156 -0
  43. behave_priority-1.0.0/tests/integration/test_failfast.py +83 -0
  44. behave_priority-1.0.0/tests/integration/test_no_config.py +53 -0
  45. behave_priority-1.0.0/tests/integration/test_order.py +107 -0
  46. behave_priority-1.0.0/tests/integration/test_report.py +95 -0
  47. behave_priority-1.0.0/tests/integration/test_smoke_tag.py +77 -0
  48. behave_priority-1.0.0/tests/unit/__init__.py +0 -0
  49. behave_priority-1.0.0/tests/unit/test_config.py +234 -0
  50. behave_priority-1.0.0/tests/unit/test_exceptions.py +68 -0
  51. behave_priority-1.0.0/tests/unit/test_hooks.py +818 -0
  52. behave_priority-1.0.0/tests/unit/test_parallel.py +185 -0
  53. behave_priority-1.0.0/tests/unit/test_parser.py +330 -0
  54. behave_priority-1.0.0/tests/unit/test_report.py +713 -0
  55. behave_priority-1.0.0/tests/unit/test_smoke.py +40 -0
  56. behave_priority-1.0.0/tests/unit/test_sorter.py +730 -0
@@ -0,0 +1,20 @@
1
+ ref/
2
+
3
+ docs/_build/
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ .eggs/
11
+ *.egg
12
+ .venv/
13
+ venv/
14
+ .env
15
+ .mypy_cache/
16
+ .ruff_cache/
17
+ .pytest_cache/
18
+ htmlcov/
19
+ .coverage
20
+ coverage.xml
@@ -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,325 @@
1
+ Metadata-Version: 2.4
2
+ Name: behave-priority
3
+ Version: 1.0.0
4
+ Summary: Priority-based execution for Behave BDD
5
+ Project-URL: Homepage, https://github.com/MathiasPaulenko/behave-priority
6
+ Project-URL: Repository, https://github.com/MathiasPaulenko/behave-priority
7
+ Project-URL: Issues, https://github.com/MathiasPaulenko/behave-priority/issues
8
+ Project-URL: Changelog, https://github.com/MathiasPaulenko/behave-priority/releases
9
+ Author-email: Mathias Paulenko <mathias.paulenko@gmail.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: bdd,behave,cucumber,fail-fast,gherkin,priority,testing
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Framework :: Pytest
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: Implementation :: CPython
23
+ Classifier: Topic :: Software Development :: Testing
24
+ Classifier: Topic :: Software Development :: Testing :: BDD
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.11
27
+ Requires-Dist: behave>=1.2.6
28
+ Provides-Extra: dev
29
+ Requires-Dist: build>=1.0; extra == 'dev'
30
+ Requires-Dist: mypy>=1.10; extra == 'dev'
31
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
32
+ Requires-Dist: pytest>=8.0; extra == 'dev'
33
+ Requires-Dist: ruff>=0.5; extra == 'dev'
34
+ Provides-Extra: docs
35
+ Requires-Dist: furo>=2024.0; extra == 'docs'
36
+ Requires-Dist: sphinx-autodoc-typehints>=2.0; extra == 'docs'
37
+ Requires-Dist: sphinx>=7.0; extra == 'docs'
38
+ Description-Content-Type: text/markdown
39
+
40
+ # behave-priority
41
+
42
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
43
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
44
+ [![Tests](https://img.shields.io/badge/tests-390%20passed-brightgreen.svg)]()
45
+ [![Coverage](https://img.shields.io/badge/coverage-97%25-brightgreen.svg)]()
46
+ [![mypy: strict](https://img.shields.io/badge/mypy-strict-blue.svg)]()
47
+ [![ruff](https://img.shields.io/badge/ruff-passing-brightgreen.svg)]()
48
+
49
+ Priority-based execution for Behave BDD. Execute scenarios ordered by priority, with fail-fast and smoke-first support.
50
+
51
+ ## Problem
52
+
53
+ Behave executes scenarios in file order. There is no way to:
54
+
55
+ - Run critical tests first
56
+ - Stop after N failures (intelligent fail-fast)
57
+ - Run smoke tests before regression
58
+ - Guarantee coverage when time is limited
59
+
60
+ In CI, if critical tests fail, you waste time waiting for the full regression suite to finish.
61
+
62
+ ## Solution
63
+
64
+ `behave-priority` reorders scenario execution by priority tags and provides fail-fast controls — all configured programmatically in `environment.py`, no CLI flags needed.
65
+
66
+ ## Features
67
+
68
+ ### Priority tags
69
+
70
+ - `@priority(1)` tag on scenarios — lower number = higher priority
71
+ - `@feature-priority(1)` at feature level — applies to all scenarios in the feature
72
+ - Scenario-level `@priority(N)` overrides feature-level priority
73
+ - Scenarios without priority tag default to lowest priority (executed last)
74
+
75
+ ### Execution ordering
76
+
77
+ - `order=True` — executes scenarios from highest to lowest priority
78
+ - `priority_tag="smoke"` — executes scenarios with that tag first, then the rest
79
+ - `reverse=True` — executes lowest priority first (useful for debugging)
80
+
81
+ ### Fail-fast
82
+
83
+ - `stop_after_failures=N` — stops execution after N failed scenarios
84
+ - `stop_on_critical=True` — stops if any `@critical` scenario fails
85
+ - Combines with `order=True`: run critical first, stop if they fail, skip regression
86
+
87
+ ### Parallel coordination
88
+
89
+ When running with `behave --parallel=N`, each worker is a separate process. By default, fail-fast is per-worker only. To coordinate fail-fast across all workers:
90
+
91
+ 1. Set the `BEHAVE_PRIORITY_COORD_DIR` environment variable to a shared directory path
92
+ 2. Pass `parallel_coord=True` to `setup_priority`
93
+
94
+ ```bash
95
+ export BEHAVE_PRIORITY_COORD_DIR=/tmp/behave_priority_coord
96
+ behave --parallel=4
97
+ ```
98
+
99
+ ```python
100
+ setup_priority(
101
+ context,
102
+ order=True,
103
+ stop_after_failures=3,
104
+ parallel_coord=True,
105
+ )
106
+ ```
107
+
108
+ Each worker writes its failure state to a JSON file in the coordination directory. `stop_after_failures` and `stop_on_critical` are evaluated globally across all workers. Call `cleanup_parallel_coord(context)` in `after_all` to remove the worker's file.
109
+
110
+ ### Reporting
111
+
112
+ - `report=True` — prints execution order with priorities and timing
113
+ - `report_format="text"` (default) — human-readable table with summary
114
+ - `report_format="json"` — machine-readable JSON with entries and summary
115
+ - `report_format="csv"` — CSV with one row per scenario entry
116
+ - Shows: scenario name, priority value, status (passed/failed/skipped), duration
117
+ - Summary: how many critical passed, how many failed, total time saved by fail-fast
118
+ - `time_saved` estimation uses priority-bucketed averages (scenarios grouped by priority range 0-99, 100-199, etc.)
119
+
120
+ Example with JSON output for CI/CD integration:
121
+
122
+ ```python
123
+ setup_priority(
124
+ context,
125
+ order=True,
126
+ report=True,
127
+ report_format="json",
128
+ )
129
+ ```
130
+
131
+ ## Installation
132
+
133
+ ```bash
134
+ pip install behave-priority
135
+ ```
136
+
137
+ For development:
138
+
139
+ ```bash
140
+ pip install -e ".[dev]"
141
+ ```
142
+
143
+ ## Quick start
144
+
145
+ In your `features/environment.py`:
146
+
147
+ ```python
148
+ from behave_priority import (
149
+ setup_priority,
150
+ before_scenario_hook,
151
+ after_scenario_hook,
152
+ priority_report,
153
+ )
154
+
155
+ def before_all(context):
156
+ setup_priority(
157
+ context,
158
+ order=True,
159
+ stop_after_failures=3,
160
+ stop_on_critical=True,
161
+ report=True,
162
+ )
163
+
164
+ def before_scenario(context, scenario):
165
+ before_scenario_hook(context, scenario)
166
+
167
+ def after_scenario(context, scenario):
168
+ after_scenario_hook(context, scenario)
169
+
170
+ def after_all(context):
171
+ priority_report(context)
172
+ ```
173
+
174
+ In your `.feature` files:
175
+
176
+ ```gherkin
177
+ Feature: User authentication
178
+
179
+ @priority(1)
180
+ @critical
181
+ Scenario: Login with valid credentials
182
+ Given a registered user
183
+ When the user logs in
184
+ Then the user should be authenticated
185
+
186
+ @priority(2)
187
+ Scenario: Login with invalid password
188
+ Given a registered user
189
+ When the user logs in with wrong password
190
+ Then the login should fail
191
+
192
+ @priority(5)
193
+ Scenario: Remember me checkbox
194
+ Given a registered user
195
+ When the user checks remember me
196
+ Then the session should persist
197
+ ```
198
+
199
+ ## API reference
200
+
201
+ ### `setup_priority(context, **kwargs)`
202
+
203
+ Configures priority execution in `before_all`. All parameters are optional.
204
+
205
+ | Parameter | Type | Default | Description |
206
+ |---|---|---|---|
207
+ | `order` | `bool` | `False` | Sort scenarios by priority |
208
+ | `reverse` | `bool` | `False` | Reverse sort order (lowest priority first) |
209
+ | `priority_tag` | `str \| None` | `None` | Tag name to run first (e.g. `"smoke"`) |
210
+ | `stop_after_failures` | `int \| None` | `None` | Stop after N failures |
211
+ | `stop_on_critical` | `bool` | `False` | Stop if any `@critical` scenario fails |
212
+ | `critical_tag` | `str` | `"critical"` | Tag name for critical scenarios |
213
+ | `default_priority` | `int` | `999` | Priority for untagged scenarios |
214
+ | `report` | `bool` | `False` | Print execution report after run |
215
+ | `report_format` | `"text" \| "json" \| "csv"` | `"text"` | Output format for the report |
216
+ | `parallel_coord` | `bool` | `False` | Enable cross-process fail-fast via `BEHAVE_PRIORITY_COORD_DIR` |
217
+
218
+ ### Hook functions
219
+
220
+ - `before_scenario_hook(context, scenario)` — skips scenario if fail-fast triggered
221
+ - `after_scenario_hook(context, scenario)` — records result, checks fail-fast
222
+ - `priority_report(context)` — prints execution report in `after_all`
223
+ - `cleanup_parallel_coord(context)` — removes worker file from coordination directory in `after_all`
224
+
225
+ ### `PriorityConfig`
226
+
227
+ Immutable frozen dataclass with all configuration options. Can be constructed directly for advanced use cases.
228
+
229
+ ### Parser functions
230
+
231
+ - `parse_priority(tags) -> int | None` — parse `@priority(N)` from a tag list
232
+ - `parse_feature_priority(tags) -> int | None` — parse `@feature-priority(N)` from a tag list
233
+ - `resolve_priority(scenario_tags, feature_tags, config, rule_tags=None) -> int` — resolve effective priority (scenario > rule > feature > default)
234
+ - `is_critical(tags, critical_tag) -> bool` — check if scenario is critical
235
+
236
+ ### Exceptions
237
+
238
+ - `PriorityError` — base exception for all behave-priority errors
239
+ - `PriorityParseError` — raised when a priority tag has invalid syntax
240
+
241
+ ## Architecture
242
+
243
+ ```
244
+ behave_priority/
245
+ ├── __init__.py # Public exports
246
+ ├── exceptions.py # PriorityError, PriorityParseError
247
+ ├── config.py # PriorityConfig (frozen dataclass)
248
+ ├── parser.py # Tag priority parsing
249
+ ├── sorter.py # ScenarioSorter — reorders behave's runner
250
+ ├── hooks.py # setup_priority, hook functions, PriorityState
251
+ ├── parallel.py # ParallelCoordinator — cross-process fail-fast
252
+ └── report.py # PriorityReport, ReportEntry, ReportSummary
253
+ ```
254
+
255
+ ### How it works
256
+
257
+ 1. **`before_all`**: `setup_priority()` reads config, sorts features and scenarios by priority
258
+ 2. **`before_scenario`**: hook skips scenario if fail-fast was triggered
259
+ 3. **`after_scenario`**: hook records result, checks fail-fast conditions
260
+ 4. **`after_all`**: `priority_report()` prints execution report if enabled
261
+
262
+ ## Use cases
263
+
264
+ 1. **CI critical-first**: Run `@priority(1)` scenarios first. If any fail, stop immediately. Don't waste 20 minutes on regression.
265
+ 2. **Smoke tests**: Tag smoke tests `@priority(1) @critical`, run with `stop_on_critical=True`. Get smoke results in 30 seconds.
266
+ 3. **Time-limited runs**: In PR pipelines with time budget, `order=True` ensures most important tests run first.
267
+ 4. **Debugging**: `reverse=True` runs obscure/edge-case tests first while you're fresh.
268
+
269
+ ## Requirements
270
+
271
+ - Python >= 3.11
272
+ - behave >= 1.2.6
273
+
274
+ ## Development
275
+
276
+ ```bash
277
+ # Install in development mode
278
+ pip install -e ".[dev]"
279
+
280
+ # Run tests
281
+ pytest
282
+
283
+ # Lint
284
+ ruff check behave_priority/ tests/
285
+
286
+ # Type check
287
+ mypy --strict behave_priority/
288
+
289
+ # Coverage
290
+ pytest --cov=behave_priority --cov-report=term-missing
291
+ ```
292
+
293
+ ## Limitations
294
+
295
+ ### Parallel execution (`--parallel`)
296
+
297
+ When behave runs with `--parallel=N`, each worker process gets its own
298
+ isolated `PriorityState`. This has the following consequences:
299
+
300
+ - **Scenario reordering**: Each worker sorts only its own subset of
301
+ scenarios. Global priority ordering across workers is not guaranteed.
302
+ A `@priority(1)` scenario assigned to worker 2 may run after a
303
+ `@priority(5)` scenario in worker 1.
304
+ - **Fail-fast (`stop_after_failures`)**: By default, only stops scenarios
305
+ within the same worker. With `parallel_coord=True` and
306
+ `BEHAVE_PRIORITY_COORD_DIR` set, failure counts are aggregated globally
307
+ across all workers. See [Parallel coordination](#parallel-coordination).
308
+ - **Critical stop (`stop_on_critical`)**: By default per-worker only.
309
+ With `parallel_coord=True`, a critical failure in any worker triggers
310
+ stop in all workers.
311
+ - **Counters**: `failed_count`, `executed_count`, `critical_failed`, and
312
+ `should_stop` are all per-process. The final report reflects only the
313
+ worker that generated it.
314
+ - **Reports**: Generated independently per worker. Each worker prints
315
+ its own report covering only the scenarios it executed. There is no
316
+ merged or aggregated report.
317
+ - **`time_saved` estimation**: Inaccurate in parallel mode. The
318
+ estimation assumes sequential execution; with N workers, skipped
319
+ scenarios in one worker overlap with execution in others.
320
+ - **`priority_tag`**: Scenarios matching the priority tag are sorted
321
+ first within each worker, but not globally across workers.
322
+
323
+ ## License
324
+
325
+ [MIT](LICENSE)
@@ -0,0 +1,286 @@
1
+ # behave-priority
2
+
3
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
5
+ [![Tests](https://img.shields.io/badge/tests-390%20passed-brightgreen.svg)]()
6
+ [![Coverage](https://img.shields.io/badge/coverage-97%25-brightgreen.svg)]()
7
+ [![mypy: strict](https://img.shields.io/badge/mypy-strict-blue.svg)]()
8
+ [![ruff](https://img.shields.io/badge/ruff-passing-brightgreen.svg)]()
9
+
10
+ Priority-based execution for Behave BDD. Execute scenarios ordered by priority, with fail-fast and smoke-first support.
11
+
12
+ ## Problem
13
+
14
+ Behave executes scenarios in file order. There is no way to:
15
+
16
+ - Run critical tests first
17
+ - Stop after N failures (intelligent fail-fast)
18
+ - Run smoke tests before regression
19
+ - Guarantee coverage when time is limited
20
+
21
+ In CI, if critical tests fail, you waste time waiting for the full regression suite to finish.
22
+
23
+ ## Solution
24
+
25
+ `behave-priority` reorders scenario execution by priority tags and provides fail-fast controls — all configured programmatically in `environment.py`, no CLI flags needed.
26
+
27
+ ## Features
28
+
29
+ ### Priority tags
30
+
31
+ - `@priority(1)` tag on scenarios — lower number = higher priority
32
+ - `@feature-priority(1)` at feature level — applies to all scenarios in the feature
33
+ - Scenario-level `@priority(N)` overrides feature-level priority
34
+ - Scenarios without priority tag default to lowest priority (executed last)
35
+
36
+ ### Execution ordering
37
+
38
+ - `order=True` — executes scenarios from highest to lowest priority
39
+ - `priority_tag="smoke"` — executes scenarios with that tag first, then the rest
40
+ - `reverse=True` — executes lowest priority first (useful for debugging)
41
+
42
+ ### Fail-fast
43
+
44
+ - `stop_after_failures=N` — stops execution after N failed scenarios
45
+ - `stop_on_critical=True` — stops if any `@critical` scenario fails
46
+ - Combines with `order=True`: run critical first, stop if they fail, skip regression
47
+
48
+ ### Parallel coordination
49
+
50
+ When running with `behave --parallel=N`, each worker is a separate process. By default, fail-fast is per-worker only. To coordinate fail-fast across all workers:
51
+
52
+ 1. Set the `BEHAVE_PRIORITY_COORD_DIR` environment variable to a shared directory path
53
+ 2. Pass `parallel_coord=True` to `setup_priority`
54
+
55
+ ```bash
56
+ export BEHAVE_PRIORITY_COORD_DIR=/tmp/behave_priority_coord
57
+ behave --parallel=4
58
+ ```
59
+
60
+ ```python
61
+ setup_priority(
62
+ context,
63
+ order=True,
64
+ stop_after_failures=3,
65
+ parallel_coord=True,
66
+ )
67
+ ```
68
+
69
+ Each worker writes its failure state to a JSON file in the coordination directory. `stop_after_failures` and `stop_on_critical` are evaluated globally across all workers. Call `cleanup_parallel_coord(context)` in `after_all` to remove the worker's file.
70
+
71
+ ### Reporting
72
+
73
+ - `report=True` — prints execution order with priorities and timing
74
+ - `report_format="text"` (default) — human-readable table with summary
75
+ - `report_format="json"` — machine-readable JSON with entries and summary
76
+ - `report_format="csv"` — CSV with one row per scenario entry
77
+ - Shows: scenario name, priority value, status (passed/failed/skipped), duration
78
+ - Summary: how many critical passed, how many failed, total time saved by fail-fast
79
+ - `time_saved` estimation uses priority-bucketed averages (scenarios grouped by priority range 0-99, 100-199, etc.)
80
+
81
+ Example with JSON output for CI/CD integration:
82
+
83
+ ```python
84
+ setup_priority(
85
+ context,
86
+ order=True,
87
+ report=True,
88
+ report_format="json",
89
+ )
90
+ ```
91
+
92
+ ## Installation
93
+
94
+ ```bash
95
+ pip install behave-priority
96
+ ```
97
+
98
+ For development:
99
+
100
+ ```bash
101
+ pip install -e ".[dev]"
102
+ ```
103
+
104
+ ## Quick start
105
+
106
+ In your `features/environment.py`:
107
+
108
+ ```python
109
+ from behave_priority import (
110
+ setup_priority,
111
+ before_scenario_hook,
112
+ after_scenario_hook,
113
+ priority_report,
114
+ )
115
+
116
+ def before_all(context):
117
+ setup_priority(
118
+ context,
119
+ order=True,
120
+ stop_after_failures=3,
121
+ stop_on_critical=True,
122
+ report=True,
123
+ )
124
+
125
+ def before_scenario(context, scenario):
126
+ before_scenario_hook(context, scenario)
127
+
128
+ def after_scenario(context, scenario):
129
+ after_scenario_hook(context, scenario)
130
+
131
+ def after_all(context):
132
+ priority_report(context)
133
+ ```
134
+
135
+ In your `.feature` files:
136
+
137
+ ```gherkin
138
+ Feature: User authentication
139
+
140
+ @priority(1)
141
+ @critical
142
+ Scenario: Login with valid credentials
143
+ Given a registered user
144
+ When the user logs in
145
+ Then the user should be authenticated
146
+
147
+ @priority(2)
148
+ Scenario: Login with invalid password
149
+ Given a registered user
150
+ When the user logs in with wrong password
151
+ Then the login should fail
152
+
153
+ @priority(5)
154
+ Scenario: Remember me checkbox
155
+ Given a registered user
156
+ When the user checks remember me
157
+ Then the session should persist
158
+ ```
159
+
160
+ ## API reference
161
+
162
+ ### `setup_priority(context, **kwargs)`
163
+
164
+ Configures priority execution in `before_all`. All parameters are optional.
165
+
166
+ | Parameter | Type | Default | Description |
167
+ |---|---|---|---|
168
+ | `order` | `bool` | `False` | Sort scenarios by priority |
169
+ | `reverse` | `bool` | `False` | Reverse sort order (lowest priority first) |
170
+ | `priority_tag` | `str \| None` | `None` | Tag name to run first (e.g. `"smoke"`) |
171
+ | `stop_after_failures` | `int \| None` | `None` | Stop after N failures |
172
+ | `stop_on_critical` | `bool` | `False` | Stop if any `@critical` scenario fails |
173
+ | `critical_tag` | `str` | `"critical"` | Tag name for critical scenarios |
174
+ | `default_priority` | `int` | `999` | Priority for untagged scenarios |
175
+ | `report` | `bool` | `False` | Print execution report after run |
176
+ | `report_format` | `"text" \| "json" \| "csv"` | `"text"` | Output format for the report |
177
+ | `parallel_coord` | `bool` | `False` | Enable cross-process fail-fast via `BEHAVE_PRIORITY_COORD_DIR` |
178
+
179
+ ### Hook functions
180
+
181
+ - `before_scenario_hook(context, scenario)` — skips scenario if fail-fast triggered
182
+ - `after_scenario_hook(context, scenario)` — records result, checks fail-fast
183
+ - `priority_report(context)` — prints execution report in `after_all`
184
+ - `cleanup_parallel_coord(context)` — removes worker file from coordination directory in `after_all`
185
+
186
+ ### `PriorityConfig`
187
+
188
+ Immutable frozen dataclass with all configuration options. Can be constructed directly for advanced use cases.
189
+
190
+ ### Parser functions
191
+
192
+ - `parse_priority(tags) -> int | None` — parse `@priority(N)` from a tag list
193
+ - `parse_feature_priority(tags) -> int | None` — parse `@feature-priority(N)` from a tag list
194
+ - `resolve_priority(scenario_tags, feature_tags, config, rule_tags=None) -> int` — resolve effective priority (scenario > rule > feature > default)
195
+ - `is_critical(tags, critical_tag) -> bool` — check if scenario is critical
196
+
197
+ ### Exceptions
198
+
199
+ - `PriorityError` — base exception for all behave-priority errors
200
+ - `PriorityParseError` — raised when a priority tag has invalid syntax
201
+
202
+ ## Architecture
203
+
204
+ ```
205
+ behave_priority/
206
+ ├── __init__.py # Public exports
207
+ ├── exceptions.py # PriorityError, PriorityParseError
208
+ ├── config.py # PriorityConfig (frozen dataclass)
209
+ ├── parser.py # Tag priority parsing
210
+ ├── sorter.py # ScenarioSorter — reorders behave's runner
211
+ ├── hooks.py # setup_priority, hook functions, PriorityState
212
+ ├── parallel.py # ParallelCoordinator — cross-process fail-fast
213
+ └── report.py # PriorityReport, ReportEntry, ReportSummary
214
+ ```
215
+
216
+ ### How it works
217
+
218
+ 1. **`before_all`**: `setup_priority()` reads config, sorts features and scenarios by priority
219
+ 2. **`before_scenario`**: hook skips scenario if fail-fast was triggered
220
+ 3. **`after_scenario`**: hook records result, checks fail-fast conditions
221
+ 4. **`after_all`**: `priority_report()` prints execution report if enabled
222
+
223
+ ## Use cases
224
+
225
+ 1. **CI critical-first**: Run `@priority(1)` scenarios first. If any fail, stop immediately. Don't waste 20 minutes on regression.
226
+ 2. **Smoke tests**: Tag smoke tests `@priority(1) @critical`, run with `stop_on_critical=True`. Get smoke results in 30 seconds.
227
+ 3. **Time-limited runs**: In PR pipelines with time budget, `order=True` ensures most important tests run first.
228
+ 4. **Debugging**: `reverse=True` runs obscure/edge-case tests first while you're fresh.
229
+
230
+ ## Requirements
231
+
232
+ - Python >= 3.11
233
+ - behave >= 1.2.6
234
+
235
+ ## Development
236
+
237
+ ```bash
238
+ # Install in development mode
239
+ pip install -e ".[dev]"
240
+
241
+ # Run tests
242
+ pytest
243
+
244
+ # Lint
245
+ ruff check behave_priority/ tests/
246
+
247
+ # Type check
248
+ mypy --strict behave_priority/
249
+
250
+ # Coverage
251
+ pytest --cov=behave_priority --cov-report=term-missing
252
+ ```
253
+
254
+ ## Limitations
255
+
256
+ ### Parallel execution (`--parallel`)
257
+
258
+ When behave runs with `--parallel=N`, each worker process gets its own
259
+ isolated `PriorityState`. This has the following consequences:
260
+
261
+ - **Scenario reordering**: Each worker sorts only its own subset of
262
+ scenarios. Global priority ordering across workers is not guaranteed.
263
+ A `@priority(1)` scenario assigned to worker 2 may run after a
264
+ `@priority(5)` scenario in worker 1.
265
+ - **Fail-fast (`stop_after_failures`)**: By default, only stops scenarios
266
+ within the same worker. With `parallel_coord=True` and
267
+ `BEHAVE_PRIORITY_COORD_DIR` set, failure counts are aggregated globally
268
+ across all workers. See [Parallel coordination](#parallel-coordination).
269
+ - **Critical stop (`stop_on_critical`)**: By default per-worker only.
270
+ With `parallel_coord=True`, a critical failure in any worker triggers
271
+ stop in all workers.
272
+ - **Counters**: `failed_count`, `executed_count`, `critical_failed`, and
273
+ `should_stop` are all per-process. The final report reflects only the
274
+ worker that generated it.
275
+ - **Reports**: Generated independently per worker. Each worker prints
276
+ its own report covering only the scenarios it executed. There is no
277
+ merged or aggregated report.
278
+ - **`time_saved` estimation**: Inaccurate in parallel mode. The
279
+ estimation assumes sequential execution; with N workers, skipped
280
+ scenarios in one worker overlap with execution in others.
281
+ - **`priority_tag`**: Scenarios matching the priority tag are sorted
282
+ first within each worker, but not globally across workers.
283
+
284
+ ## License
285
+
286
+ [MIT](LICENSE)