Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failure in fixture cleanup code causing a crash when --exitfirst is set #10490

Closed
kriek opened this issue Nov 9, 2022 · 1 comment
Closed
Labels
topic: fixtures anything involving fixtures directly or indirectly

Comments

@kriek
Copy link

kriek commented Nov 9, 2022

I've noticed a corner case where a failure during fixture cleanup code can crash pytest when --exitfirst is set. Minimal reproduction example is below. Note that a very similar crash can also be observed with no --exitfirst but a KeyboardInterrupt instead. Another minimal example is also provided below.

pytest and operating system versions

Windows 10
python 3.10.8
pytest 7.2.0

Minimal example:

test_exit_first.py content:

import pytest

@pytest.fixture(scope="module")  # mind the scope
def fixture():
    yield
    assert False

def test_1(fixture):
    assert False

def test_2():  # mind the presence of another test after the first failing one
    pass
$ PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest test_exit_first.py -v -x
================================= test session starts ==================================
platform win32 -- Python 3.10.8, pytest-7.2.0, pluggy-1.0.0 -- D:\Python310\python.exe
cachedir: .pytest_cache
rootdir: D:\tmp
collected 2 items

test_exit_first.py::test_1 FAILED                                                 [ 50%]
Traceback (most recent call last):
  File "D:\Python310\lib\runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "D:\Python310\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "D:\Python310\Scripts\pytest.exe\__main__.py", line 7, in <module>
    sys.exit(console_main())
  File "D:\Python310\lib\site-packages\_pytest\config\__init__.py", line 190, in console_main
    code = main()
  File "D:\Python310\lib\site-packages\_pytest\config\__init__.py", line 167, in main
    ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main(
  File "D:\Python310\lib\site-packages\pluggy\_hooks.py", line 265, in __call__
    return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
  File "D:\Python310\lib\site-packages\pluggy\_manager.py", line 80, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
  File "D:\Python310\lib\site-packages\pluggy\_callers.py", line 60, in _multicall
    return outcome.get_result()
  File "D:\Python310\lib\site-packages\pluggy\_result.py", line 60, in get_result
    raise ex[1].with_traceback(ex[2])
  File "D:\Python310\lib\site-packages\pluggy\_callers.py", line 39, in _multicall
    res = hook_impl.function(*args)
  File "D:\Python310\lib\site-packages\_pytest\main.py", line 317, in pytest_cmdline_main
    return wrap_session(config, _main)
  File "D:\Python310\lib\site-packages\_pytest\main.py", line 305, in wrap_session
    config.hook.pytest_sessionfinish(
  File "D:\Python310\lib\site-packages\pluggy\_hooks.py", line 265, in __call__
    return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
  File "D:\Python310\lib\site-packages\pluggy\_manager.py", line 80, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
  File "D:\Python310\lib\site-packages\pluggy\_callers.py", line 55, in _multicall
    gen.send(outcome)
  File "D:\Python310\lib\site-packages\_pytest\terminal.py", line 808, in pytest_sessionfinish
    outcome.get_result()
  File "D:\Python310\lib\site-packages\pluggy\_result.py", line 60, in get_result
    raise ex[1].with_traceback(ex[2])
  File "D:\Python310\lib\site-packages\pluggy\_callers.py", line 39, in _multicall
    res = hook_impl.function(*args)
  File "D:\Python310\lib\site-packages\_pytest\runner.py", line 106, in pytest_sessionfinish
    session._setupstate.teardown_exact(None)
  File "D:\Python310\lib\site-packages\_pytest\runner.py", line 530, in teardown_exact
    raise exc
  File "D:\Python310\lib\site-packages\_pytest\runner.py", line 523, in teardown_exact
    fin()
  File "D:\Python310\lib\site-packages\_pytest\fixtures.py", line 684, in <lambda>
    subrequest.node.addfinalizer(lambda: fixturedef.finish(request=subrequest))
  File "D:\Python310\lib\site-packages\_pytest\fixtures.py", line 1036, in finish
    raise exc
  File "D:\Python310\lib\site-packages\_pytest\fixtures.py", line 1029, in finish
    func()
  File "D:\Python310\lib\site-packages\_pytest\fixtures.py", line 916, in _teardown_yield_fixture
    next(it)
  File "D:\tmp\test_exit_first.py", line 6, in fixture
    assert False
AssertionError: assert False

Alternative KeyboardInterrupt example

test_keyboard_interrupt.py

import pytest
import time

@pytest.fixture(scope="module")
def fixture():
    yield
    assert False

def test_1(fixture):
    time.sleep(1)  # Ctrl-C here
$ PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest test_keyboard_interrupt.py -v
============================================== test session starts ===============================================
platform win32 -- Python 3.10.8, pytest-7.2.0, pluggy-1.0.0 -- D:\Python310\python.exe
cachedir: .pytest_cache
rootdir: D:\tmp
collected 1 item

test_keyboard_interrupt.py::test_1
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! KeyboardInterrupt !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
D:\tmp\test_keyboard_interrupt.py:10: KeyboardInterrupt
(to show a full traceback on KeyboardInterrupt use --full-trace)
Traceback (most recent call last):
  File "D:\Python310\lib\runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "D:\Python310\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "D:\Python310\Scripts\pytest.exe\__main__.py", line 7, in <module>
    sys.exit(console_main())
  File "D:\Python310\lib\site-packages\_pytest\config\__init__.py", line 190, in console_main
    code = main()
  File "D:\Python310\lib\site-packages\_pytest\config\__init__.py", line 167, in main
    ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main(
  File "D:\Python310\lib\site-packages\pluggy\_hooks.py", line 265, in __call__
    return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
  File "D:\Python310\lib\site-packages\pluggy\_manager.py", line 80, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
  File "D:\Python310\lib\site-packages\pluggy\_callers.py", line 60, in _multicall
    return outcome.get_result()
  File "D:\Python310\lib\site-packages\pluggy\_result.py", line 60, in get_result
    raise ex[1].with_traceback(ex[2])
  File "D:\Python310\lib\site-packages\pluggy\_callers.py", line 39, in _multicall
    res = hook_impl.function(*args)
  File "D:\Python310\lib\site-packages\_pytest\main.py", line 317, in pytest_cmdline_main
    return wrap_session(config, _main)
  File "D:\Python310\lib\site-packages\_pytest\main.py", line 305, in wrap_session
    config.hook.pytest_sessionfinish(
  File "D:\Python310\lib\site-packages\pluggy\_hooks.py", line 265, in __call__
    return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
  File "D:\Python310\lib\site-packages\pluggy\_manager.py", line 80, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
  File "D:\Python310\lib\site-packages\pluggy\_callers.py", line 55, in _multicall
    gen.send(outcome)
  File "D:\Python310\lib\site-packages\_pytest\terminal.py", line 808, in pytest_sessionfinish
    outcome.get_result()
  File "D:\Python310\lib\site-packages\pluggy\_result.py", line 60, in get_result
    raise ex[1].with_traceback(ex[2])
  File "D:\Python310\lib\site-packages\pluggy\_callers.py", line 39, in _multicall
    res = hook_impl.function(*args)
  File "D:\Python310\lib\site-packages\_pytest\runner.py", line 106, in pytest_sessionfinish
    session._setupstate.teardown_exact(None)
  File "D:\Python310\lib\site-packages\_pytest\runner.py", line 530, in teardown_exact
    raise exc
  File "D:\Python310\lib\site-packages\_pytest\runner.py", line 523, in teardown_exact
    fin()
  File "D:\Python310\lib\site-packages\_pytest\fixtures.py", line 684, in <lambda>
    subrequest.node.addfinalizer(lambda: fixturedef.finish(request=subrequest))
  File "D:\Python310\lib\site-packages\_pytest\fixtures.py", line 1036, in finish
    raise exc
  File "D:\Python310\lib\site-packages\_pytest\fixtures.py", line 1029, in finish
    func()
  File "D:\Python310\lib\site-packages\_pytest\fixtures.py", line 916, in _teardown_yield_fixture
    next(it)
  File "D:\tmp\test_keyboard_interrupt.py", line 7, in fixture
    assert False
AssertionError: assert False
@Zac-HD Zac-HD added the topic: fixtures anything involving fixtures directly or indirectly label Nov 12, 2022
@bluetech
Copy link
Member

bluetech commented Jan 4, 2024

This is fixed by #11721 I believe -- will be part of pytest 8.0.0.

@bluetech bluetech closed this as completed Jan 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: fixtures anything involving fixtures directly or indirectly
Projects
None yet
Development

No branches or pull requests

3 participants