Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
youtux committed Sep 28, 2024
2 parents 89d1afe + 20faa0c commit b1304e0
Show file tree
Hide file tree
Showing 38 changed files with 2,435 additions and 799 deletions.
43 changes: 34 additions & 9 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,27 @@ jobs:
include:
- python-version: "3.8"
toxfactor: py3.8
ignore-typecheck-outcome: true
ignore-typecheck-outcome: false
ignore-test-outcome: false
- python-version: "3.9"
toxfactor: py3.9
ignore-typecheck-outcome: true
ignore-typecheck-outcome: false
ignore-test-outcome: false
- python-version: "3.10"
toxfactor: py3.10
ignore-typecheck-outcome: true
ignore-typecheck-outcome: false
ignore-test-outcome: false
- python-version: "3.11"
toxfactor: py3.11
ignore-typecheck-outcome: true
ignore-typecheck-outcome: false
ignore-test-outcome: false
- python-version: "3.12"
toxfactor: py3.12
ignore-typecheck-outcome: true
ignore-typecheck-outcome: false
ignore-test-outcome: false
- python-version: "3.13-dev"
toxfactor: py3.13
ignore-typecheck-outcome: true
ignore-typecheck-outcome: false
ignore-test-outcome: false

steps:
Expand All @@ -47,7 +47,7 @@ jobs:

- name: Install poetry
run: |
python -m pip install poetry==1.8.2
python -m pip install poetry==1.8.3
- name: Configure poetry
run: |
Expand Down Expand Up @@ -81,10 +81,35 @@ jobs:
coverage combine
coverage xml
- uses: codecov/codecov-action@v3
- uses: codecov/codecov-action@v4
with:
# Explicitly using the token to avoid Codecov rate limit errors
# See https://community.codecov.com/t/upload-issues-unable-to-locate-build-via-github-actions-api/3954
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
fail_ci_if_error: false
verbose: true # optional (default = false)

pypi-publish:
name: Upload release to PyPI
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/pytest-bdd
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
needs: test-run
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
- name: Install pypa/build
run: >-
python3 -m
pip install
build
--user
- name: Build a binary wheel and a source tarball
run: python3 -m build
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
13 changes: 13 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ Changelog
Unreleased
----------
- Update documentation to clarify that `--gherkin-terminal-reporter` needs to be used with `-v` or `-vv`.
- Drop compatibility with pytest < 7.0.0.

8.0.0b1
----------
- Use `gherkin-official` parser to replace custom parsing logic. This will make pytest-bdd more compatible with the Gherkin specification.
- Multiline steps must now always use triple-quotes for the additional lines.
- All feature files must now use the keyword `Feature:` to be considered valid.
- Tags can no longer have spaces (e.g. "@tag one" "@tag two" are no longer valid).

7.3.0
----------
- Fix an issue when only the first Step would inject a fixture, while later steps would not be able to.
- Test against the latest versions of pytest (8.2, 8.3).

7.2.0
----------
Expand Down
367 changes: 199 additions & 168 deletions poetry.lock

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pytest-bdd"
version = "7.2.0"
version = "8.0.0b1"
description = "BDD for pytest"
authors = ["Oleg Pidsadnyi <oleg.pidsadnyi@gmail.com>", "Anatoly Bubenkov <bubenkoff@gmail.com>"]
maintainers = ["Alessio Bogon <778703+youtux@users.noreply.github.com>"]
Expand Down Expand Up @@ -39,9 +39,10 @@ python = ">=3.8"
Mako = "*"
parse = "*"
parse-type = "*"
pytest = ">=6.2.0"
pytest = ">=7.0.0"
typing-extensions = "*"
packaging = "*"
gherkin-official = "^29.0.0"

[tool.poetry.group.dev.dependencies]
tox = ">=4.11.3"
Expand Down Expand Up @@ -93,6 +94,7 @@ python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
files = "src/pytest_bdd/**/*.py"
disallow_untyped_defs = true

[[tool.mypy.overrides]]
module = ["parse", "parse_type"]
Expand Down
3 changes: 2 additions & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[pytest]
testpaths = tests
filterwarnings =
error
# only ignore errors from the pytest_bdd package
error:::(src)?\.pytest_bdd.*
11 changes: 8 additions & 3 deletions src/pytest_bdd/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,22 @@ def inject_fixture(request: FixtureRequest, arg: str, value: Any) -> None:
:param arg: argument name
:param value: argument value
"""

# Ensure there's a fixture definition for the argument
request._fixturemanager._register_fixture(
name=arg,
func=lambda: value,
nodeid=request.node.nodeid,
)
# Note the fixture we just registered will have a lower priority
# if there was already one registered, so we need to force its value
# to the one we want to inject.
fixture_def = request._get_active_fixturedef(arg)
fixture_def.cached_result = (value, None, None) # type: ignore

else:

def getfixturedefs(fixturemanager: FixtureManager, fixturename: str, node: Node) -> Sequence[FixtureDef] | None:
return fixturemanager.getfixturedefs(fixturename, node.nodeid)
return fixturemanager.getfixturedefs(fixturename, node.nodeid) # type: ignore

def inject_fixture(request: FixtureRequest, arg: str, value: Any) -> None:
"""Inject fixture into pytest fixture request.
Expand All @@ -44,7 +49,7 @@ def inject_fixture(request: FixtureRequest, arg: str, value: Any) -> None:
:param value: argument value
"""
fd = FixtureDef(
fixturemanager=request._fixturemanager,
fixturemanager=request._fixturemanager, # type: ignore
baseid=None,
argname=arg,
func=lambda: value,
Expand Down
8 changes: 4 additions & 4 deletions src/pytest_bdd/cucumber_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ def configure(config: Config) -> None:
cucumber_json_path = config.option.cucumber_json_path
# prevent opening json log on worker nodes (xdist)
if cucumber_json_path and not hasattr(config, "workerinput"):
config._bddcucumberjson = LogBDDCucumberJSON(cucumber_json_path)
config.pluginmanager.register(config._bddcucumberjson)
config._bddcucumberjson = LogBDDCucumberJSON(cucumber_json_path) # type: ignore[attr-defined]
config.pluginmanager.register(config._bddcucumberjson) # type: ignore[attr-defined]


def unconfigure(config: Config) -> None:
xml = getattr(config, "_bddcucumberjson", None)
xml = getattr(config, "_bddcucumberjson", None) # type: ignore[attr-defined]
if xml is not None:
del config._bddcucumberjson
del config._bddcucumberjson # type: ignore[attr-defined]
config.pluginmanager.unregister(xml)


Expand Down
42 changes: 33 additions & 9 deletions src/pytest_bdd/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ class ScenarioNotFound(ScenarioValidationError):
"""Scenario Not Found."""


class ExamplesNotValidError(ScenarioValidationError):
"""Example table is not valid."""


class StepDefinitionNotFoundError(Exception):
"""Step definition not found."""

Expand All @@ -27,11 +23,39 @@ class NoScenariosFound(Exception):
"""No scenarios found."""


class FeatureError(Exception):
"""Feature parse error."""
class GherkinParseError(Exception):
"""Base class for all Gherkin parsing errors."""

message = "{0}.\nLine number: {1}.\nLine: {2}.\nFile: {3}"
def __init__(self, message: str, line: int, line_content: str, filename: str) -> None:
super().__init__(message)
self.message = message
self.line = line
self.line_content = line_content
self.filename = filename

def __str__(self) -> str:
"""String representation."""
return self.message.format(*self.args)
return f"{self.message}\nLine number: {self.line}\nLine: {self.line_content}\nFile: {self.filename}"


class FeatureError(GherkinParseError):
pass


class BackgroundError(GherkinParseError):
pass


class ScenarioError(GherkinParseError):
pass


class StepError(GherkinParseError):
pass


class RuleError(GherkinParseError):
pass


class TokenError(GherkinParseError):
pass
21 changes: 10 additions & 11 deletions src/pytest_bdd/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import glob
import os.path

from .parser import Feature, parse_feature
from .parser import Feature, FeatureParser

# Global features dictionary
features: dict[str, Feature] = {}
Expand All @@ -52,30 +52,29 @@ def get_feature(base_path: str, filename: str, encoding: str = "utf-8") -> Featu
full_name = os.path.abspath(os.path.join(base_path, filename))
feature = features.get(full_name)
if not feature:
feature = parse_feature(base_path, filename, encoding=encoding)
feature = FeatureParser(base_path, filename, encoding).parse()
features[full_name] = feature
return feature


def get_features(paths: list[str], **kwargs) -> list[Feature]:
def get_features(paths: list[str], encoding: str = "utf-8") -> list[Feature]:
"""Get features for given paths.
:param list paths: `list` of paths (file or dirs)
:return: `list` of `Feature` objects.
"""
seen_names = set()
features = []
_features = []
for path in paths:
if path not in seen_names:
seen_names.add(path)
if os.path.isdir(path):
features.extend(
get_features(glob.iglob(os.path.join(path, "**", "*.feature"), recursive=True), **kwargs)
)
file_paths = list(glob.iglob(os.path.join(path, "**", "*.feature"), recursive=True))
_features.extend(get_features(file_paths, encoding=encoding))
else:
base, name = os.path.split(path)
feature = get_feature(base, name, **kwargs)
features.append(feature)
features.sort(key=lambda feature: feature.name or feature.filename)
return features
feature = get_feature(base, name, encoding=encoding)
_features.append(feature)
_features.sort(key=lambda _feature: _feature.name or _feature.filename)
return _features
6 changes: 3 additions & 3 deletions src/pytest_bdd/generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing import TYPE_CHECKING, cast

from _pytest._io import TerminalWriter
from mako.lookup import TemplateLookup
from mako.lookup import TemplateLookup # type: ignore

from .compat import getfixturedefs
from .feature import get_features
Expand Down Expand Up @@ -181,11 +181,11 @@ def _show_missing_code_main(config: Config, session: Session) -> None:
features, scenarios, steps = parse_feature_files(config.option.features)

for item in session.items:
if scenario := getattr(item.obj, "__scenario__", None):
if scenario := getattr(item.obj, "__scenario__", None): # type: ignore
if scenario in scenarios:
scenarios.remove(scenario)
for step in scenario.steps:
if _find_step_fixturedef(fm, item, step=step):
if _find_step_fixturedef(fm, item, step=step): # type: ignore
try:
steps.remove(step)
except ValueError:
Expand Down
Loading

0 comments on commit b1304e0

Please sign in to comment.