From 55a3df23bf938720c8ac2c23a26e2c989d66870d Mon Sep 17 00:00:00 2001 From: Kostiantyn Goloveshko Date: Sun, 1 Sep 2024 17:16:54 +0300 Subject: [PATCH] Check and specify compatibility workarounds --- src/pytest_bdd/allure_logging.py | 3 ++- src/pytest_bdd/hook.py | 3 +-- src/pytest_bdd/plugin.py | 32 +++++++++++++++++++++++---- src/pytest_bdd/struct_bdd/plugin.py | 17 +++++++++++--- src/pytest_bdd/utils.py | 8 ++++--- tests/allure_/test_allure_outline.py | 2 ++ tests/allure_/test_allure_scenario.py | 2 ++ tests/feature/test_steps.py | 2 +- tox.ini | 2 +- 9 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/pytest_bdd/allure_logging.py b/src/pytest_bdd/allure_logging.py index 6bc6eddb..0672648f 100644 --- a/src/pytest_bdd/allure_logging.py +++ b/src/pytest_bdd/allure_logging.py @@ -9,6 +9,7 @@ from pydantic import BaseModel as PydanticBaseModel from pytest_bdd.compatibility.allure import ALLURE_INSTALLED +from pytest_bdd.compatibility.pytest import PYTEST81 if ALLURE_INSTALLED: from allure_commons import hookimpl @@ -34,7 +35,7 @@ def __init__(self, allure_logger, allure_cache): def register_if_allure_accessible(cls, config): pluginmanager = config.pluginmanager allure_accessible = pluginmanager.hasplugin("allure_pytest") and config.option.allure_report_dir - if allure_accessible: + if allure_accessible and not PYTEST81: allure_plugin_manager.get_plugins() listener = next( diff --git a/src/pytest_bdd/hook.py b/src/pytest_bdd/hook.py index 7cfb7784..69d9bec0 100644 --- a/src/pytest_bdd/hook.py +++ b/src/pytest_bdd/hook.py @@ -2,7 +2,6 @@ from enum import Enum from inspect import isfunction, isgeneratorfunction, signature from itertools import count, product, starmap -from operator import attrgetter from typing import Optional, Union from _pytest.mark import Mark @@ -57,7 +56,7 @@ def hook(request: FixtureRequest, *args, **kwargs): { HookKind.mark: request.node.iter_markers(), HookKind.tag: map( - lambda tag: Mark(tag.name, args=tuple(), kwargs={}), # type: ignore[no-any-return] + lambda tag: Mark(tag.name, args=tuple(), kwargs={}, _ispytest=True), # type: ignore[no-any-return] request.getfixturevalue("scenario").tags, ), }[_kind] diff --git a/src/pytest_bdd/plugin.py b/src/pytest_bdd/plugin.py index 666e9d8a..4998e459 100644 --- a/src/pytest_bdd/plugin.py +++ b/src/pytest_bdd/plugin.py @@ -20,6 +20,7 @@ from pytest_bdd.collector import FeatureFileModule as FeatureFileCollector from pytest_bdd.collector import Module as ModuleCollector from pytest_bdd.compatibility.pytest import ( + PYTEST7, Config, FixtureRequest, Mark, @@ -144,12 +145,24 @@ def pytest_unconfigure(config: Config) -> None: cucumber_json.unconfigure(config) -@pytest.hookimpl(hookwrapper=True) -def pytest_pycollect_makemodule(path, parent, module_path=None): +def _pytest_pycollect_makemodule(): with patch("_pytest.python.Module", new=ModuleCollector): yield +if PYTEST7: + + @pytest.hookimpl(hookwrapper=True) + def pytest_pycollect_makemodule(parent, module_path): + yield from _pytest_pycollect_makemodule() + +else: + + @pytest.hookimpl(hookwrapper=True) + def pytest_pycollect_makemodule(path, parent): + yield from _pytest_pycollect_makemodule() + + @pytest.hookimpl(tryfirst=True) def pytest_plugin_registered(plugin, manager): if hasattr(plugin, "__file__") and isinstance(plugin, (type, ModuleType)): @@ -298,8 +311,8 @@ def pytest_cmdline_main(config: Config) -> Optional[int]: return generation.cmdline_main(config) -def pytest_collect_file(parent: Collector, path, file_path=None): - file_path = file_path or Path(path) +def _pytest_collect_file(parent: Collector, file_path=None): + file_path = Path(file_path) config = parent.session.config is_enabled_feature_autoload = config.getoption("feature_autoload") if is_enabled_feature_autoload is None: @@ -314,6 +327,17 @@ def pytest_collect_file(parent: Collector, path, file_path=None): return FeatureFileCollector.build(parent=parent, file_path=file_path) +if PYTEST7: # Done intentionally because of API change + + def pytest_collect_file(parent: Collector, file_path): + return _pytest_collect_file(parent=parent, file_path=file_path) + +else: + + def pytest_collect_file(parent: Collector, path): # type: ignore[misc] + return _pytest_collect_file(parent=parent, file_path=path) + + @pytest.mark.trylast def pytest_bdd_convert_tag_to_marks(feature, scenario, tag) -> Optional[Collection[Union[Mark, MarkDecorator]]]: return [getattr(pytest.mark, tag)] diff --git a/src/pytest_bdd/struct_bdd/plugin.py b/src/pytest_bdd/struct_bdd/plugin.py index 475a66f3..5f5b9666 100644 --- a/src/pytest_bdd/struct_bdd/plugin.py +++ b/src/pytest_bdd/struct_bdd/plugin.py @@ -5,7 +5,7 @@ import pytest -from pytest_bdd.compatibility.pytest import Config, Module +from pytest_bdd.compatibility.pytest import PYTEST7, Config, Module from pytest_bdd.mimetypes import Mimetype from pytest_bdd.struct_bdd.model import StepPrototype from pytest_bdd.struct_bdd.parser import StructBDDParser @@ -45,11 +45,22 @@ def pytest_bdd_is_collectible(self, config: Config, path: Path): if str(path).endswith(f".bdd.{extension_suffix.value}"): return True - @pytest.hookimpl(hookwrapper=True) - def pytest_pycollect_makemodule(self, path, parent, module_path=None): + def _pytest_pycollect_makemodule(self): outcome = yield res = outcome.get_result() if isinstance(res, Module): for member_name, member in getmembers(res.module): if isinstance(member, StepPrototype) and member_name.startswith("test_"): setattr(res.module, member_name, member.as_test(res.module.__file__)) + + if PYTEST7: + + @pytest.hookimpl(hookwrapper=True) + def pytest_pycollect_makemodule(self, parent, module_path): + yield from self._pytest_pycollect_makemodule() + + else: + + @pytest.hookimpl(hookwrapper=True) + def pytest_pycollect_makemodule(self, path, parent): + yield from self._pytest_pycollect_makemodule() diff --git a/src/pytest_bdd/utils.py b/src/pytest_bdd/utils.py index a30b827e..dcd51d62 100644 --- a/src/pytest_bdd/utils.py +++ b/src/pytest_bdd/utils.py @@ -29,7 +29,9 @@ runtime_checkable, ) -from pytest_bdd.compatibility.pytest import PYTEST8, PYTEST81, FixtureDef, fail +from _pytest.fixtures import FixtureDef, FixtureRequest + +from pytest_bdd.compatibility.pytest import PYTEST81, fail from pytest_bdd.const import ALPHA_REGEX, PYTHON_REPLACE_REGEX if TYPE_CHECKING: # pragma: no cover @@ -147,9 +149,8 @@ def instantiate_from_collection_or_bool( return cls(bool_or_items, warm_up_keys=warm_up_keys) -def inject_fixture(request, arg, value): +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 @@ -162,6 +163,7 @@ def inject_fixture(request, arg, value): func=lambda: value, scope="function", params=None, + _ispytest=True, ) fd.cached_result = (value, 0, None) diff --git a/tests/allure_/test_allure_outline.py b/tests/allure_/test_allure_outline.py index dbc069a4..3f2fdf93 100644 --- a/tests/allure_/test_allure_outline.py +++ b/tests/allure_/test_allure_outline.py @@ -2,9 +2,11 @@ from pytest_bdd import scenario from pytest_bdd.compatibility.allure import ALLURE_INSTALLED +from pytest_bdd.compatibility.pytest import PYTEST81 @scenario("testdata/allure_/outline.feature", "Scenario outline") @mark.skipif(not ALLURE_INSTALLED, reason="Allure is not installed") +@mark.skipif(PYTEST81, reason="Allure uses deprecated APIs") def test_scenario_outline(): pass diff --git a/tests/allure_/test_allure_scenario.py b/tests/allure_/test_allure_scenario.py index 6d408b41..b0c9f03b 100644 --- a/tests/allure_/test_allure_scenario.py +++ b/tests/allure_/test_allure_scenario.py @@ -2,9 +2,11 @@ from pytest_bdd import scenario from pytest_bdd.compatibility.allure import ALLURE_INSTALLED +from pytest_bdd.compatibility.pytest import PYTEST81 @scenario("testdata/allure_//scenario.feature", "Simple passed scenario") @mark.skipif(not ALLURE_INSTALLED, reason="Allure is not installed") +@mark.skipif(PYTEST81, reason="Allure uses deprecated APIs") def test_simple_passed_scenario(): pass diff --git a/tests/feature/test_steps.py b/tests/feature/test_steps.py index 69b762f4..eb5e51a1 100644 --- a/tests/feature/test_steps.py +++ b/tests/feature/test_steps.py @@ -121,7 +121,7 @@ def test_steps( second_foo, request ): - # Original fixture values are recieved from test parameters + # Original fixture values are received from test parameters assert first_foo == 'first_foo' assert second_foo == 'second_foo' diff --git a/tox.ini b/tox.ini index ef59fb48..181fb939 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ envlist = py312-pytest{625, 83, 82, 81, 80, 74, 73, 72, 71, 70, latest}-mypy-lin py312-pytest{625, 83, 82, 81, 80, 74, 73, 72, 71, 70, latest}-coverage-lin py312-pytestlatest-gherkin{24, latest}-xdist-coverage-{lin, win, mac} - py312-pytestlatets-allure-coverage-{lin, win, mac} + py312-pytest80-allure-coverage-{lin, win, mac} py39-pytest{62, 61, 60, 54, 53, 52, 51, 50}-coverage-lin py38-pytest{62, 54}-coverage-{win, mac} py{py39, py38, 38}-pytest{62, 54}-coverage-lin