From b4e13a0713d93dbf5fd71414c97c8699095c7969 Mon Sep 17 00:00:00 2001 From: Alessio Bogon <778703+youtux@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:32:43 +0100 Subject: [PATCH] Injecting fixtures is much easier in pytest 8.1! Move logic for older pythons in the `compat.py` module. --- src/pytest_bdd/compat.py | 63 +++++++++++++++++++++++++++++++++----- src/pytest_bdd/scenario.py | 4 +-- src/pytest_bdd/steps.py | 42 ------------------------- 3 files changed, 57 insertions(+), 52 deletions(-) diff --git a/src/pytest_bdd/compat.py b/src/pytest_bdd/compat.py index 6e126b77..079f7de0 100644 --- a/src/pytest_bdd/compat.py +++ b/src/pytest_bdd/compat.py @@ -2,28 +2,75 @@ from collections.abc import Sequence from importlib.metadata import version +from typing import Any -from _pytest.fixtures import FixtureDef as _FixtureDef -from _pytest.fixtures import FixtureManager +from _pytest.fixtures import FixtureDef, FixtureManager, FixtureRequest from _pytest.nodes import Node from packaging.version import parse as parse_version pytest_version = parse_version(version("pytest")) -__all__ = ["getfixturedefs", "FixtureDef"] +__all__ = ["getfixturedefs", "inject_fixture"] if pytest_version.release >= (8, 1): - def getfixturedefs(fixturemanager: FixtureManager, fixturename: str, node: Node) -> Sequence[_FixtureDef] | None: + def getfixturedefs(fixturemanager: FixtureManager, fixturename: str, node: Node) -> Sequence[FixtureDef] | None: return fixturemanager.getfixturedefs(fixturename, node) - def FixtureDef(fixturemanager, **kwargs): - kwargs.setdefault("config", fixturemanager.config) - return _FixtureDef(**kwargs) + def inject_fixture(request: FixtureRequest, arg: str, value: Any) -> None: + """Inject fixture into pytest fixture request. + + :param request: pytest fixture request + :param arg: argument name + :param value: argument value + """ + + request._fixturemanager._register_fixture( + name=arg, + func=lambda: value, + nodeid=request.node.nodeid, + ) else: def getfixturedefs(fixturemanager: FixtureManager, fixturename: str, node: Node) -> Sequence[FixtureDef] | None: return fixturemanager.getfixturedefs(fixturename, node.nodeid) - FixtureDef = _FixtureDef + def inject_fixture(request: FixtureRequest, arg: str, value: Any) -> None: + """Inject fixture into pytest fixture request. + + :param request: pytest fixture request + :param arg: argument name + :param value: argument value + """ + fd = FixtureDef( + fixturemanager=request._fixturemanager, + baseid=None, + argname=arg, + func=lambda: value, + scope="function", + params=None, + ) + fd.cached_result = (value, 0, None) + + old_fd = request._fixture_defs.get(arg) + add_fixturename = arg not in request.fixturenames + + def fin() -> None: + request._fixturemanager._arg2fixturedefs[arg].remove(fd) + + if old_fd is not None: + request._fixture_defs[arg] = old_fd + + if add_fixturename: + request._pyfuncitem._fixtureinfo.names_closure.remove(arg) + + request.addfinalizer(fin) + + # inject fixture definition + request._fixturemanager._arg2fixturedefs.setdefault(arg, []).append(fd) + + # inject fixture value in request cache + request._fixture_defs[arg] = fd + if add_fixturename: + request._pyfuncitem._fixtureinfo.names_closure.append(arg) diff --git a/src/pytest_bdd/scenario.py b/src/pytest_bdd/scenario.py index 2fad5ee1..a8c176fd 100644 --- a/src/pytest_bdd/scenario.py +++ b/src/pytest_bdd/scenario.py @@ -23,9 +23,9 @@ from typing_extensions import ParamSpec from . import exceptions -from .compat import getfixturedefs +from .compat import getfixturedefs, inject_fixture from .feature import get_feature, get_features -from .steps import StepFunctionContext, get_step_fixture_name, inject_fixture +from .steps import StepFunctionContext, get_step_fixture_name from .utils import CONFIG_STACK, get_args, get_caller_module_locals, get_caller_module_path if TYPE_CHECKING: diff --git a/src/pytest_bdd/steps.py b/src/pytest_bdd/steps.py index 247d9cb3..b72766e7 100644 --- a/src/pytest_bdd/steps.py +++ b/src/pytest_bdd/steps.py @@ -201,45 +201,3 @@ def find_unique_name(name: str, seen: Iterable[str]) -> str: new_name = f"{name}_{i}" if new_name not in seen: return new_name - - -def inject_fixture(request: FixtureRequest, arg: str, value: Any) -> None: - """Inject fixture into pytest fixture request. - - :param request: pytest fixture request - :param arg: argument name - :param value: argument value - """ - - fd = compat.FixtureDef( - fixturemanager=request._fixturemanager, - baseid=None, - argname=arg, - func=lambda: value, - scope="function", - params=None, - ) - fd.cached_result = (value, 0, None) - - old_fd = request._fixture_defs.get(arg) - add_fixturename = arg not in request.fixturenames - - def fin() -> None: - # TODO: Still required? - request._fixturemanager._arg2fixturedefs[arg].remove(fd) - - if old_fd is not None: - request._fixture_defs[arg] = old_fd - - if add_fixturename: - request._pyfuncitem._fixtureinfo.names_closure.remove(arg) - - request.addfinalizer(fin) - - # inject fixture definition - request._fixturemanager._arg2fixturedefs.setdefault(arg, []).append(fd) - - # inject fixture value in request cache - request._fixture_defs[arg] = fd - if add_fixturename: - request._pyfuncitem._fixtureinfo.names_closure.append(arg)