pytest-bdd
pytest-bdd copied to clipboard
Python 3.13: test_generate_missing fails with unbounded recursion
Using the current master
, 30ba3f7ba151768f227613eb2a186d4bc5c262aa:
$ python3.13 --version
Python 3.13.0a1
$ tox -e py3.13
[…]
====================================== FAILURES =======================================
________________________________ test_generate_missing ________________________________
pytester = <Pytester PosixPath('/tmp/pytest-of-ben/pytest-22/test_generate_missing0')>
def test_generate_missing(pytester):
"""Test generate missing command."""
pytester.makefile(
".feature",
generation=textwrap.dedent(
"""\
Feature: Missing code generation
Background:
Given I have a foobar
Scenario: Scenario tests which are already bound to the tests stay as is
Given I have a bar
Scenario: Code is generated for scenarios which are not bound to any tests
Given I have a bar
Scenario: Code is generated for scenario steps which are not yet defined(implemented)
Given I have a custom bar
"""
),
)
pytester.makepyfile(
textwrap.dedent(
"""\
import functools
from pytest_bdd import scenario, given
scenario = functools.partial(scenario, "generation.feature")
@given("I have a bar")
def _():
return "bar"
@scenario("Scenario tests which are already bound to the tests stay as is")
def test_foo():
pass
@scenario("Code is generated for scenario steps which are not yet defined(implemented)")
def test_missing_steps():
pass
"""
)
)
result = pytester.runpytest("--generate-missing", "--feature", "generation.feature")
result.assert_outcomes(passed=0, failed=0, errors=0)
assert not result.stderr.str()
> assert result.ret == 0
E assert <ExitCode.INTERNAL_ERROR: 3> == 0
E + where <ExitCode.INTERNAL_ERROR: 3> = <RunResult ret=3 len(stdout.lines)=19 len(stderr.lines)=0 duration=0.04s>.ret
pytester = <Pytester PosixPath('/tmp/pytest-of-ben/pytest-22/test_generate_missing0')>
result = <RunResult ret=3 len(stdout.lines)=19 len(stderr.lines)=0 duration=0.04s>
/home/ben/src/forks/pytest-bdd/tests/generation/test_generate_missing.py:69: AssertionError
-------------------------------- Captured stdout call ---------------------------------
================================= test session starts =================================
platform linux -- Python 3.13.0a1, pytest-7.4.3, pluggy-1.3.0
rootdir: /tmp/pytest-of-ben/pytest-22/test_generate_missing0
plugins: bdd-7.0.0
collected 2 items
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR> File "/home/ben/src/forks/pytest-bdd/.tox/py3.13/lib/python3.13/site-packages/_pytest/main.py", line 271, in wrap_session
INTERNALERROR> session.exitstatus = doit(config, session) or 0
INTERNALERROR> ^^^^^^^^^^^^^^^^^^^^^
INTERNALERROR> File "/home/ben/src/forks/pytest-bdd/.tox/py3.13/lib/python3.13/site-packages/pytest_bdd/generation.py", line 184, in _show_missing_code_main
INTERNALERROR> if scenario in scenarios:
INTERNALERROR> ^^^^^^^^^^^^^^^^^^^^^
INTERNALERROR> File "<string>", line 4, in __eq__
INTERNALERROR> File "<string>", line 4, in __eq__
INTERNALERROR> File "<string>", line 4, in __eq__
INTERNALERROR> [Previous line repeated 489 more times]
INTERNALERROR> RecursionError: maximum recursion depth exceeded in comparison
================================ no tests ran in 0.01s ================================
=============================== short test summary info ===============================
FAILED tests/generation/test_generate_missing.py::test_generate_missing - assert <ExitCode.INTERNAL_ERROR: 3> == 0
====================== 1 failed, 119 passed, 1 skipped in 9.83s =======================
py3.13: exit 1 (10.25 seconds) /home/ben/src/forks/pytest-bdd> pytest -vvl pid=1875907
.pkg: _exit> python /usr/lib/python3.11/site-packages/pyproject_api/_backend.py True poetry.core.masonry.api
py3.13: FAIL code 1 (17.65=setup[7.40]+cmd[10.25] seconds)
evaluation failed :( (17.71 seconds)
This is due to the following in parser.py
:
@dataclass
class Feature:
scenarios: OrderedDict[str, ScenarioTemplate]
[...]
@dataclass
class ScenarioTemplate:
feature: Feature
[...]
That is, a Feature
references all its ScenarioTemplate
s, and a ScenarioTemplate
references all its Feature
s.
Both are in a dataclass
field with the default compare=True
, so comparing scenarios (if scenario in scenarios:
in _show_missing_code_main
) results in infinite recursion.
Perhaps Feature
should be compared by identity rather than by its contents? If that's the case, the fix would be to use:
@dataclass(eq=False)
class Feature:
...