Skip to content

Commit

Permalink
Adapt to getfixturedefs change in pytest 8.1
Browse files Browse the repository at this point in the history
The signature of this (private) function will change in the upcoming
pytest 8.1 release:
pytest-dev/pytest#11785

Additionally, the `iterparentnodeids` function is removed, so
copy/pasting it for now.

I verified that all tests pass when run against pytest main.
  • Loading branch information
bluetech committed Jan 8, 2024
1 parent 8a694ff commit cc86d99
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 8 deletions.
8 changes: 6 additions & 2 deletions src/pytest_bdd/generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import os.path
from typing import TYPE_CHECKING, cast

import pytest
from _pytest._io import TerminalWriter
from mako.lookup import TemplateLookup

Expand Down Expand Up @@ -127,9 +128,12 @@ def _find_step_fixturedef(
fixturemanager: FixtureManager, item: Function, step: Step
) -> Sequence[FixtureDef[Any]] | None:
"""Find step fixturedef."""
with inject_fixturedefs_for_step(step=step, fixturemanager=fixturemanager, nodeid=item.nodeid):
with inject_fixturedefs_for_step(step=step, fixturemanager=fixturemanager, node=item):
bdd_name = get_step_fixture_name(step=step)
return fixturemanager.getfixturedefs(bdd_name, item.nodeid)
if hasattr(pytest, "version_tuple") and pytest.version_tuple >= (8, 1):
return fixturemanager.getfixturedefs(bdd_name, item)

Check warning on line 134 in src/pytest_bdd/generation.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_bdd/generation.py#L134

Added line #L134 was not covered by tests
else:
return fixturemanager.getfixturedefs(bdd_name, item.nodeid)


def parse_feature_files(paths: list[str], **kwargs: Any) -> tuple[list[Feature], list[ScenarioTemplate], list[Step]]:
Expand Down
62 changes: 56 additions & 6 deletions src/pytest_bdd/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import pytest
from _pytest.fixtures import FixtureDef, FixtureManager, FixtureRequest, call_fixture_func
from _pytest.nodes import iterparentnodeids
from typing_extensions import ParamSpec

from . import exceptions
Expand All @@ -43,7 +42,7 @@
ALPHA_REGEX = re.compile(r"^\d+_*")


def find_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, nodeid: str) -> Iterable[FixtureDef[Any]]:
def find_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, node) -> Iterable[FixtureDef[Any]]:
"""Find the fixture defs that can parse a step."""
# happens to be that _arg2fixturedefs is changed during the iteration so we use a copy
fixture_def_by_name = list(fixturemanager._arg2fixturedefs.items())
Expand All @@ -60,14 +59,65 @@ def find_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, nodeid
if not match:
continue

if fixturedef not in (fixturemanager.getfixturedefs(fixturename, nodeid) or []):
if hasattr(pytest, "version_tuple") and pytest.version_tuple >= (8, 1):
fixturedefs = fixturemanager.getfixturedefs(fixturename, node)

Check warning on line 63 in src/pytest_bdd/scenario.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_bdd/scenario.py#L63

Added line #L63 was not covered by tests
else:
fixturedefs = fixturemanager.getfixturedefs(fixturename, node.nodeid)
if fixturedef not in (fixturedefs or []):
continue

yield fixturedef


# Function copied from pytest 8.0 (removed in later versions).
def iterparentnodeids(nodeid: str) -> Iterator[str]:
"""Return the parent node IDs of a given node ID, inclusive.
For the node ID
"testing/code/test_excinfo.py::TestFormattedExcinfo::test_repr_source"
the result would be
""
"testing"
"testing/code"
"testing/code/test_excinfo.py"
"testing/code/test_excinfo.py::TestFormattedExcinfo"
"testing/code/test_excinfo.py::TestFormattedExcinfo::test_repr_source"
Note that / components are only considered until the first ::.
"""
SEP = "/"
pos = 0
first_colons: Optional[int] = nodeid.find("::")
if first_colons == -1:
first_colons = None
# The root Session node - always present.
yield ""
# Eagerly consume SEP parts until first colons.
while True:
at = nodeid.find(SEP, pos, first_colons)
if at == -1:
break
if at > 0:
yield nodeid[:at]
pos = at + len(SEP)
# Eagerly consume :: parts.
while True:
at = nodeid.find("::", pos)
if at == -1:
break
if at > 0:
yield nodeid[:at]
pos = at + len("::")

Check warning on line 113 in src/pytest_bdd/scenario.py

View check run for this annotation

Codecov / codecov/patch

src/pytest_bdd/scenario.py#L112-L113

Added lines #L112 - L113 were not covered by tests
# The node ID itself.
if nodeid:
yield nodeid


@contextlib.contextmanager
def inject_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, nodeid: str) -> Iterator[None]:
def inject_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, node) -> Iterator[None]:
"""Inject fixture definitions that can parse a step.
We fist iterate over all the fixturedefs that can parse the step.
Expand All @@ -78,7 +128,7 @@ def inject_fixturedefs_for_step(step: Step, fixturemanager: FixtureManager, node
"""
bdd_name = get_step_fixture_name(step=step)

fixturedefs = list(find_fixturedefs_for_step(step=step, fixturemanager=fixturemanager, nodeid=nodeid))
fixturedefs = list(find_fixturedefs_for_step(step=step, fixturemanager=fixturemanager, node=node))

# Sort the fixture definitions by their "path", so that the `bdd_name` fixture will
# respect the fixture scope
Expand Down Expand Up @@ -114,7 +164,7 @@ def get_step_function(request, step: Step) -> StepFunctionContext | None:
__tracebackhide__ = True
bdd_name = get_step_fixture_name(step=step)

with inject_fixturedefs_for_step(step=step, fixturemanager=request._fixturemanager, nodeid=request.node.nodeid):
with inject_fixturedefs_for_step(step=step, fixturemanager=request._fixturemanager, node=request.node):
try:
return cast(StepFunctionContext, request.getfixturevalue(bdd_name))
except pytest.FixtureLookupError:
Expand Down

0 comments on commit cc86d99

Please sign in to comment.