From 825fcf804b9dbd691c83f6167f1327cc488c04b9 Mon Sep 17 00:00:00 2001 From: Nate Clark Date: Mon, 23 Oct 2023 10:52:29 -0400 Subject: [PATCH 1/2] Issue #362: Only unconfigure if configured `pytest_unconfigure` can be called even if `pytest_configure` was not. In order to avoid errors during unconfigure only perform the unconfigure if the configure was performed. Add a counter which is incremented before configure to track the number of times pytest started initialization so that the config is not popped early from the stack. --- src/pytest_bdd/plugin.py | 15 +++++++++++++-- tests/test_hooks.py | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/pytest_bdd/plugin.py b/src/pytest_bdd/plugin.py index 486cdf8..7ad8ff6 100644 --- a/src/pytest_bdd/plugin.py +++ b/src/pytest_bdd/plugin.py @@ -21,6 +21,10 @@ if TYPE_CHECKING: from .parser import Feature, Scenario, Step +# Counter to track how many times pytest initialization was started +_INIT_COUNT = 0 + + def pytest_addhooks(pluginmanager: PytestPluginManager) -> None: """Register plugin hooks.""" from pytest_bdd import hooks @@ -52,6 +56,8 @@ def _pytest_bdd_example() -> dict: def pytest_addoption(parser: Parser) -> None: """Add pytest-bdd options.""" + global _INIT_COUNT + _INIT_COUNT += 1 add_bdd_ini(parser) cucumber_json.add_options(parser) generation.add_options(parser) @@ -66,14 +72,19 @@ def add_bdd_ini(parser: Parser) -> None: def pytest_configure(config: Config) -> None: """Configure all subplugins.""" CONFIG_STACK.append(config) + assert _INIT_COUNT == len(CONFIG_STACK) cucumber_json.configure(config) gherkin_terminal_reporter.configure(config) def pytest_unconfigure(config: Config) -> None: """Unconfigure all subplugins.""" - CONFIG_STACK.pop() - cucumber_json.unconfigure(config) + global _INIT_COUNT + assert len(CONFIG_STACK) <= _INIT_COUNT + if len(CONFIG_STACK) == _INIT_COUNT: + CONFIG_STACK.pop() + cucumber_json.unconfigure(config) + _INIT_COUNT -= 1 @pytest.hookimpl(hookwrapper=True) diff --git a/tests/test_hooks.py b/tests/test_hooks.py index e3fda90..53b44f7 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -135,3 +135,21 @@ def test_pytest_bdd_after_scenario_called_after_scenario(pytester): assert scenario_1.name == "Scenario 1" assert scenario_2.name == "Scenario 2" + + +def test_pytest_unconfigure_without_configure(pytester): + """ + Simulate a plugin forcing an exit during configuration before bdd is configured + https://github.com/pytest-dev/pytest-bdd/issues/362 + """ + pytester.makeconftest( + """ + import pytest + + def pytest_configure(config): + pytest.exit("Exit during configure", 0) + """ + ) + + result = pytester.runpytest() + assert result.ret == 0 From c23282819b605885e64ba864159812d7546be81c Mon Sep 17 00:00:00 2001 From: Alessio Bogon <778703+youtux@users.noreply.github.com> Date: Sat, 11 Nov 2023 20:35:24 +0100 Subject: [PATCH 2/2] No need to keep a counter, just try to pop from the stack if any --- src/pytest_bdd/plugin.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/pytest_bdd/plugin.py b/src/pytest_bdd/plugin.py index 7ad8ff6..08ceaa0 100644 --- a/src/pytest_bdd/plugin.py +++ b/src/pytest_bdd/plugin.py @@ -21,10 +21,6 @@ if TYPE_CHECKING: from .parser import Feature, Scenario, Step -# Counter to track how many times pytest initialization was started -_INIT_COUNT = 0 - - def pytest_addhooks(pluginmanager: PytestPluginManager) -> None: """Register plugin hooks.""" from pytest_bdd import hooks @@ -56,8 +52,6 @@ def _pytest_bdd_example() -> dict: def pytest_addoption(parser: Parser) -> None: """Add pytest-bdd options.""" - global _INIT_COUNT - _INIT_COUNT += 1 add_bdd_ini(parser) cucumber_json.add_options(parser) generation.add_options(parser) @@ -72,19 +66,15 @@ def add_bdd_ini(parser: Parser) -> None: def pytest_configure(config: Config) -> None: """Configure all subplugins.""" CONFIG_STACK.append(config) - assert _INIT_COUNT == len(CONFIG_STACK) cucumber_json.configure(config) gherkin_terminal_reporter.configure(config) def pytest_unconfigure(config: Config) -> None: """Unconfigure all subplugins.""" - global _INIT_COUNT - assert len(CONFIG_STACK) <= _INIT_COUNT - if len(CONFIG_STACK) == _INIT_COUNT: + if CONFIG_STACK: CONFIG_STACK.pop() - cucumber_json.unconfigure(config) - _INIT_COUNT -= 1 + cucumber_json.unconfigure(config) @pytest.hookimpl(hookwrapper=True)