Skip to content

Conversation

@RonnyPfannschmidt
Copy link
Member

  • prepare example test for stopiteration passover issue
  • WIP: use contextmanagers instead of yield from

yield from is a generator boundary that transfers StopIteration into a RuntimeError

closes #12929

@RonnyPfannschmidt RonnyPfannschmidt added this to the 8.4 milestone Nov 12, 2024
@graingert
Copy link
Member

graingert commented Dec 9, 2024

src/_pytest/threadexception.py and src/_pytest/unraisableexception.py both use regular trylast hooks instead of generators, so the conflicts can be resolved by checking them out from main

@graingert
Copy link
Member

this won't pass without a new Pluggy release

@nicoddemus
Copy link
Member

@RonnyPfannschmidt what is the status here? Do you plan to make a pluggy release yourself?

@RonnyPfannschmidt
Copy link
Member Author

Indeed

I intend to give this priority by the end of the month

as it turns out, StopIteration is not transparent on the boundaries of generators

# Conflicts:
#	src/_pytest/threadexception.py
#	src/_pytest/unraisableexception.py
@graingert
Copy link
Member

@RonnyPfannschmidt I wrote some test cases for this, I hope you find them useful:

def test_stop_iteration_from_collect(pytester: Pytester) -> None:
    pytester.makepyfile(test_it="raise StopIteration('hello')")
    result = pytester.runpytest()
    assert result.ret == ExitCode.INTERRUPTED
    result.assert_outcomes(failed=0, passed=0, errors=1)
    result.stdout.fnmatch_lines(
        [
            "=========================== short test summary info ============================",
            "ERROR test_it.py - StopIteration: hello",
            "!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!",
            "=============================== 1 error in * ===============================",
        ]
    )


def test_stop_iteration_runtest_protocol(pytester: Pytester) -> None:
    pytester.makepyfile(
        test_it="""
        import pytest
        @pytest.fixture
        def fail_setup():
            raise StopIteration(1)
        def test_fail_setup(fail_setup):
            pass
        def test_fail_teardown(request):
            def stop_iteration():
                raise StopIteration(2)
            request.addfinalizer(stop_iteration)
        def test_fail_call():
            raise StopIteration(3)
        """
    )
    result = pytester.runpytest()
    assert result.ret == ExitCode.TESTS_FAILED
    result.assert_outcomes(failed=1, passed=1, errors=2)
    result.stdout.fnmatch_lines(
        [
            "=========================== short test summary info ============================",
            "FAILED test_it.py::test_fail_call - StopIteration: 3",
            "ERROR test_it.py::test_fail_setup - StopIteration: 1",
            "ERROR test_it.py::test_fail_teardown - StopIteration: 2",
            "==================== 1 failed, 1 passed, 2 errors in * =====================",
        ]
    )

@graingert
Copy link
Member

note this is still currently blocked by a release of pytest-dev/pluggy#545

@RonnyPfannschmidt
Copy link
Member Author

i intend to land pytest-dev/pluggy#546 as before the next release of pluggy

@psf-chronographer psf-chronographer bot added the bot:chronographer:provided (automation) changelog entry is part of PR label May 31, 2025
@graingert graingert marked this pull request as ready for review May 31, 2025 18:27
@graingert graingert requested review from jakkdl and nicoddemus May 31, 2025 18:27
@RonnyPfannschmidt
Copy link
Member Author

im ok with squashing

@graingert graingert merged commit 075c5ef into pytest-dev:main May 31, 2025
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided (automation) changelog entry is part of PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tests raising StopIteration break pytest/pluggy

4 participants