Skip to content

Commit c04d37d

Browse files
committed
Merge branch 'master' into lowercase-keywords-error
# Conflicts: # tests/feature/test_steps.py
2 parents 2b16694 + 36c3fa4 commit c04d37d

File tree

12 files changed

+128
-52
lines changed

12 files changed

+128
-52
lines changed

.github/workflows/main.yml

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,28 @@ jobs:
1313
include:
1414
- python-version: "3.8"
1515
toxfactor: py3.8
16-
ignore-typecheck-outcome: true
16+
ignore-typecheck-outcome: false
1717
ignore-test-outcome: false
1818
- python-version: "3.9"
1919
toxfactor: py3.9
20-
ignore-typecheck-outcome: true
20+
ignore-typecheck-outcome: false
2121
ignore-test-outcome: false
2222
- python-version: "3.10"
2323
toxfactor: py3.10
24-
ignore-typecheck-outcome: true
24+
ignore-typecheck-outcome: false
2525
ignore-test-outcome: false
2626
- python-version: "3.11"
2727
toxfactor: py3.11
28-
ignore-typecheck-outcome: true
28+
ignore-typecheck-outcome: false
2929
ignore-test-outcome: false
3030
- python-version: "3.12"
3131
toxfactor: py3.12
32-
ignore-typecheck-outcome: true
32+
ignore-typecheck-outcome: false
3333
ignore-test-outcome: false
3434
- python-version: "3.13-dev"
3535
toxfactor: py3.13
36-
ignore-typecheck-outcome: true
37-
ignore-test-outcome: true
36+
ignore-typecheck-outcome: false
37+
ignore-test-outcome: false
3838

3939
steps:
4040
- uses: actions/checkout@v3
@@ -47,7 +47,7 @@ jobs:
4747

4848
- name: Install poetry
4949
run: |
50-
python -m pip install poetry==1.8.2
50+
python -m pip install poetry==1.8.3
5151
5252
- name: Configure poetry
5353
run: |
@@ -88,3 +88,28 @@ jobs:
8888
token: ${{ secrets.CODECOV_TOKEN }}
8989
fail_ci_if_error: false
9090
verbose: true # optional (default = false)
91+
92+
pypi-publish:
93+
name: Upload release to PyPI
94+
runs-on: ubuntu-latest
95+
environment:
96+
name: pypi
97+
url: https://pypi.org/p/pytest-bdd
98+
permissions:
99+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
100+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
101+
needs: test-run
102+
steps:
103+
- uses: actions/checkout@v4
104+
- name: Set up Python
105+
uses: actions/setup-python@v5
106+
- name: Install pypa/build
107+
run: >-
108+
python3 -m
109+
pip install
110+
build
111+
--user
112+
- name: Build a binary wheel and a source tarball
113+
run: python3 -m build
114+
- name: Publish package distributions to PyPI
115+
uses: pypa/gh-action-pypi-publish@release/v1

CHANGES.rst

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,19 @@ Changelog
33

44
Unreleased
55
----------
6-
- Fix an issue when only the first Step would inject a fixture, while later steps would not be able to.
7-
- Test against the latest versions of pytest (8.2, 8.3).
8-
- Use `gherkin-official` parser to replace custom parsing logic.
6+
7+
8.0.0b1
8+
----------
9+
- Use `gherkin-official` parser to replace custom parsing logic. This will make pytest-bdd more compatible with the Gherkin specification.
910
- Multiline steps must now always use triple-quotes for the additional lines.
1011
- All feature files must now use the keyword `Feature:` to be considered valid.
1112
- Tags can no longer have spaces (e.g. "@tag one" "@tag two" are no longer valid).
1213

14+
7.3.0
15+
----------
16+
- Fix an issue when only the first Step would inject a fixture, while later steps would not be able to.
17+
- Test against the latest versions of pytest (8.2, 8.3).
18+
1319
7.2.0
1420
----------
1521
- Fix compatibility issue with Python 3.13.

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "pytest-bdd"
3-
version = "7.2.0"
3+
version = "8.0.0b1"
44
description = "BDD for pytest"
55
authors = ["Oleg Pidsadnyi <[email protected]>", "Anatoly Bubenkov <[email protected]>"]
66
maintainers = ["Alessio Bogon <[email protected]>"]
@@ -94,6 +94,7 @@ python_version = "3.8"
9494
warn_return_any = true
9595
warn_unused_configs = true
9696
files = "src/pytest_bdd/**/*.py"
97+
disallow_untyped_defs = true
9798

9899
[[tool.mypy.overrides]]
99100
module = ["parse", "parse_type"]

pytest.ini

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[pytest]
22
testpaths = tests
33
filterwarnings =
4-
error
4+
# only ignore errors from the pytest_bdd package
5+
error:::(src)?\.pytest_bdd.*

src/pytest_bdd/compat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def inject_fixture(request: FixtureRequest, arg: str, value: Any) -> None:
3434
# if there was already one registered, so we need to force its value
3535
# to the one we want to inject.
3636
fixture_def = request._get_active_fixturedef(arg)
37-
fixture_def.cached_result = (value, None, None)
37+
fixture_def.cached_result = (value, None, None) # type: ignore
3838

3939
else:
4040

src/pytest_bdd/exceptions.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,14 @@ class NoScenariosFound(Exception):
2626
class GherkinParseError(Exception):
2727
"""Base class for all Gherkin parsing errors."""
2828

29-
def __init__(self, message, line, line_content, filename):
29+
def __init__(self, message: str, line: int, line_content: str, filename: str) -> None:
3030
super().__init__(message)
3131
self.message = message
3232
self.line = line
3333
self.line_content = line_content
3434
self.filename = filename
35-
self.line = line
36-
self.line_content = line_content
37-
self.filename = filename
3835

39-
def __str__(self):
36+
def __str__(self) -> str:
4037
return f"{self.message}\nLine number: {self.line}\nLine: {self.line_content}\nFile: {self.filename}"
4138

4239

src/pytest_bdd/feature.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def get_feature(base_path: str, filename: str, encoding: str = "utf-8") -> Featu
5757
return feature
5858

5959

60-
def get_features(paths: list[str], **kwargs) -> list[Feature]:
60+
def get_features(paths: list[str], encoding: str = "utf-8") -> list[Feature]:
6161
"""Get features for given paths.
6262
6363
:param list paths: `list` of paths (file or dirs)
@@ -71,10 +71,10 @@ def get_features(paths: list[str], **kwargs) -> list[Feature]:
7171
seen_names.add(path)
7272
if os.path.isdir(path):
7373
file_paths = list(glob.iglob(os.path.join(path, "**", "*.feature"), recursive=True))
74-
_features.extend(get_features(file_paths, **kwargs))
74+
_features.extend(get_features(file_paths, encoding=encoding))
7575
else:
7676
base, name = os.path.split(path)
77-
feature = get_feature(base, name, **kwargs)
77+
feature = get_feature(base, name, encoding=encoding)
7878
_features.append(feature)
7979
_features.sort(key=lambda _feature: _feature.name or _feature.filename)
8080
return _features

src/pytest_bdd/gherkin_parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ def get_gherkin_document(abs_filename: str, encoding: str = "utf-8") -> GherkinD
309309

310310
def handle_gherkin_parser_error(
311311
raw_error: str, line: int, line_content: str, filename: str, original_exception: Exception | None = None
312-
):
312+
) -> None:
313313
"""Map the error message to a specific exception type and raise it."""
314314
# Split the raw_error into individual lines
315315
error_lines = raw_error.splitlines()

src/pytest_bdd/hooks.py

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,71 @@
11
from __future__ import annotations
22

3+
from collections.abc import Callable
4+
35
import pytest
6+
from _pytest.fixtures import FixtureRequest
7+
8+
from pytest_bdd.parser import Feature, Scenario, Step
49

510
"""Pytest-bdd pytest hooks."""
611

712

8-
def pytest_bdd_before_scenario(request, feature, scenario):
13+
def pytest_bdd_before_scenario(request: FixtureRequest, feature: Feature, scenario: Scenario) -> object:
914
"""Called before scenario is executed."""
1015

1116

12-
def pytest_bdd_after_scenario(request, feature, scenario):
17+
def pytest_bdd_after_scenario(request: FixtureRequest, feature: Feature, scenario: Scenario) -> object:
1318
"""Called after scenario is executed."""
1419

1520

16-
def pytest_bdd_before_step(request, feature, scenario, step, step_func):
21+
def pytest_bdd_before_step(
22+
request: FixtureRequest, feature: Feature, scenario: Scenario, step: Step, step_func: Callable[..., object]
23+
) -> object:
1724
"""Called before step function is set up."""
1825

1926

20-
def pytest_bdd_before_step_call(request, feature, scenario, step, step_func, step_func_args):
27+
def pytest_bdd_before_step_call(
28+
request: FixtureRequest,
29+
feature: Feature,
30+
scenario: Scenario,
31+
step: Step,
32+
step_func: Callable[..., object],
33+
step_func_args: dict[str, object],
34+
) -> object:
2135
"""Called before step function is executed."""
2236

2337

24-
def pytest_bdd_after_step(request, feature, scenario, step, step_func, step_func_args):
38+
def pytest_bdd_after_step(
39+
request: FixtureRequest,
40+
feature: Feature,
41+
scenario: Scenario,
42+
step: Step,
43+
step_func: Callable[..., object],
44+
step_func_args: dict[str, object],
45+
) -> object:
2546
"""Called after step function is successfully executed."""
2647

2748

28-
def pytest_bdd_step_error(request, feature, scenario, step, step_func, step_func_args, exception):
49+
def pytest_bdd_step_error(
50+
request: FixtureRequest,
51+
feature: Feature,
52+
scenario: Scenario,
53+
step: Step,
54+
step_func: Callable[..., object],
55+
step_func_args: dict[str, object],
56+
exception: Exception,
57+
) -> object:
2958
"""Called when step function failed to execute."""
3059

3160

32-
def pytest_bdd_step_func_lookup_error(request, feature, scenario, step, exception):
61+
def pytest_bdd_step_func_lookup_error(
62+
request: FixtureRequest, feature: Feature, scenario: Scenario, step: Step, exception: Exception
63+
) -> object:
3364
"""Called when step lookup failed."""
3465

3566

3667
@pytest.hookspec(firstresult=True)
37-
def pytest_bdd_apply_tag(tag, function):
68+
def pytest_bdd_apply_tag(tag: str, function: Callable[..., object]) -> object:
3869
"""Apply a tag (from a ``.feature`` file) to the given scenario.
3970
4071
The default implementation does the equivalent of

src/pytest_bdd/parser.py

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,6 @@ class Step:
218218
failed (bool): Whether the step has failed (internal use only).
219219
scenario (Optional[ScenarioTemplate]): The scenario to which this step belongs (internal use only).
220220
background (Optional[Background]): The background to which this step belongs (internal use only).
221-
lines (List[str]): Additional lines for the step (internal use only).
222221
"""
223222

224223
type: str
@@ -229,7 +228,6 @@ class Step:
229228
failed: bool = field(init=False, default=False)
230229
scenario: ScenarioTemplate | None = field(init=False, default=None)
231230
background: Background | None = field(init=False, default=None)
232-
lines: list[str] = field(init=False, default_factory=list)
233231

234232
def __init__(self, name: str, type: str, indent: int, line_number: int, keyword: str) -> None:
235233
"""Initialize a step.
@@ -332,22 +330,6 @@ def get_tag_names(tag_data: list[GherkinTag]) -> set[str]:
332330
"""
333331
return {tag.name.lstrip("@") for tag in tag_data}
334332

335-
@staticmethod
336-
def get_step_type(keyword: str) -> str | None:
337-
"""Map a step keyword to its corresponding type.
338-
339-
Args:
340-
keyword (str): The keyword for the step (e.g., 'given', 'when', 'then').
341-
342-
Returns:
343-
Optional[str]: The type of the step, or None if the keyword is unknown.
344-
"""
345-
return {
346-
"given": GIVEN,
347-
"when": WHEN,
348-
"then": THEN,
349-
}.get(keyword)
350-
351333
def parse_steps(self, steps_data: list[GherkinStep]) -> list[Step]:
352334
"""Parse a list of step data into Step objects.
353335
@@ -358,7 +340,7 @@ def parse_steps(self, steps_data: list[GherkinStep]) -> list[Step]:
358340
List[Step]: A list of Step objects.
359341
"""
360342

361-
def get_step_content(_gherkin_step):
343+
def get_step_content(_gherkin_step: GherkinStep) -> str:
362344
step_name = strip_comments(_gherkin_step.text)
363345
if _gherkin_step.docString:
364346
step_name = f"{step_name}\n{_gherkin_step.docString.content}"

0 commit comments

Comments
 (0)