Lemmons/pytest-raises

Receive INTERNAL Error if no exception is thrown

Closed this issue · 1 comments

Hello, good plugin, I like it.
I have an INTERNAL ERROR with pytest 7.2.2 - 7.3.0 with the plugin pytest-raises.

If I decorate a test with '@pytest.mark.raises(exception=Exception)', where Exception can be any type of exception.
If this test finishes without raising an exception, pytest prints INTERNAL ERROR and stop the execution with code 3.

A basic example of broken test

import pytest

@pytest.mark.raises(exception=Exception)
def test_not_raises_exception() -> None:
    assert True
Traceback
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/main.py", line 269, in wrap_session
INTERNALERROR>     session.exitstatus = doit(config, session) or 0
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/main.py", line 323, in _main
INTERNALERROR>     config.hook.pytest_runtestloop(session=session)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 265, in __call__
INTERNALERROR>     return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 80, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 60, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 60, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 39, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/main.py", line 348, in pytest_runtestloop
INTERNALERROR>     item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 265, in __call__
INTERNALERROR>     return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 80, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 60, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 60, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 39, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 114, in pytest_runtest_protocol
INTERNALERROR>     runtestprotocol(item, nextitem=nextitem)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 133, in runtestprotocol
INTERNALERROR>     reports.append(call_and_report(item, "call", log))
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 224, in call_and_report
INTERNALERROR>     report: TestReport = hook.pytest_runtest_makereport(item=item, call=call)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_hooks.py", line 265, in __call__
INTERNALERROR>     return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_manager.py", line 80, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 55, in _multicall
INTERNALERROR>     gen.send(outcome)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/skipping.py", line 266, in pytest_runtest_makereport
INTERNALERROR>     rep = outcome.get_result()
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_result.py", line 60, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/pluggy/_callers.py", line 39, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/runner.py", line 368, in pytest_runtest_makereport
INTERNALERROR>     return TestReport.from_item_and_call(item, call)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/reports.py", line 363, in from_item_and_call
INTERNALERROR>     longrepr = item.repr_failure(excinfo)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/python.py", line 1833, in repr_failure
INTERNALERROR>     return self._repr_failure_py(excinfo, style=style)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/nodes.py", line 484, in _repr_failure_py
INTERNALERROR>     return excinfo.getrepr(
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/_code/code.py", line 671, in getrepr
INTERNALERROR>     return fmt.repr_excinfo(self)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/_code/code.py", line 987, in repr_excinfo
INTERNALERROR>     return ExceptionChainRepr(repr_chain)
INTERNALERROR>   File "/home/xyf/test_raises/.venv/lib/python3.9/site-packages/_pytest/_code/code.py", line 1039, in __init__
INTERNALERROR>     reprtraceback=chain[-1][0],
INTERNALERROR> IndexError: list index out of range

Process finished with exit code 3

The problem is present only if no exception is thrown.
Then the exception raised line 84 'raise ExceptionClass(failure_message)' in '_pytest_raises_validation' is hidden because '__tracebackhide__' is True.
If in the function '_pytest_fail_by_mark_or_set_excinfo' '__tracebackhide__' is set to False, pytest does not stop the execution, but the traceback is bad.

Bad Traceback
item = <Function test_not_raises_exception>
outcome = <pluggy._result._Result object at 0x7fc688f3e4f0>
marker_name = 'raises'
ExceptionClass = <class 'pytest_raises.pytest_raises.ExpectedException'>
failure_message = "Expected exception <class 'Exception'>, but it did not raise"
traceback = None

    def _pytest_fail_by_mark_or_set_excinfo(item, outcome, marker_name, ExceptionClass, failure_message, traceback):
        """
        Defer a test failure to a later stage, or set ``excinfo`` of ``outcome``, depending
        on ``marker_name``.  This function should only be called for test items that have
        failed -- the end result of calling this function for *any* ``item`` / ``outcome``
        is that the test will fail.
    
        .. warning::
    
            **This is a "private" function not intended to be called directly by external projects!**
    
        Depending on the stage at which this function is called, one of two actions will
        be performed:
    
        1. ``marker_name='setup_raises'``: a "secret" marker will be added to ``item``
           indicating that the test failed.  This marker is then checked at a later stage
           when it is safe to fail.  See documentation for :func:`_pytest_raises_validation`
           for more information.
        2. ``marker_name='raises'``: the ``outcome.excinfo`` will be populated with an
           exception traceback that will eventually (through ``pytest``) mark the test as
           failed.
    
        **Parameters**
    
        ``item``
            The ``pytest`` test item, e.g., what is supplied to
            ``pytest_runtest_setup(item)`` or ``pytest_runtest_call(item)``.
    
        ``outcome``
            The ``pytest`` test outcome for the ``@pytest.hookimpl(hookwrapper=True)`` hook
            wrappers, where ``outcome = yield``.
    
        ``marker_name``
            The string marker name.  Values are **assumed** to be ``'setup_raises'`` or
            ``'raises'`` **only**.
    
            - ``'setup_raises'``: call originates from ``pytest_runtest_setup`` hook wrapper.
            - ``'raises'``: call originates from ``pytest_runtest_call`` hook wrapper.
    
        ``ExceptionClass``
            The exception class to re-raise.  Expected to be :class:`ExpectedException` or
            :class:`ExpectedMessage`, but not strictly required.
    
        ``failure_message``
            The string failure message to mark with or re-raise, depending on the value
            of ``marker_name``.
    
        ``traceback``
            The traceback information if available, ``None`` otherwise.
        """
        # pylint: disable=unused-variable
        __tracebackhide__ = False
        if marker_name == 'setup_raises':
            # In the later stage when `fail` is called, it is nice to "simulate" an
            # exception by putting the expected exception class's name as a prefix.
            failure_message = '{}: {}'.format(ExceptionClass.__name__, failure_message)
            item.add_marker(pytest.mark.setup_raises_expected_exc_or_message_not_found(failure_message))
        else:  # marker_name == 'raises'
            # Avoid "while handling exception another exception occurred" scenarios.
            if issubclass(ExceptionClass, PytestRaisesUsageError):
                failure_message = '{}: {}'.format(ExceptionClass.__name__, failure_message)
                pytest.fail(failure_message, pytrace=False)
            else:
                try:
>                   raise ExceptionClass(failure_message)
E                   pytest_raises.pytest_raises.ExpectedException: Expected exception <class 'Exception'>, but it did not raise

.venv/lib/python3.9/site-packages/pytest_raises/pytest_raises.py:84: ExpectedException

I don't know how to fix this problem.

I have installed this dependencies:
Python = 3.9.5
coverage = "^7.1.0"
pytest = "<8.0.0"
pytest-asyncio = "<1.0.0"
pytest-cov = "^4.0.0"
pytest-raises = "<1.0.0"
pytest-xdist = {extras = ["psutil"], version = "^3.2.1"}
pytest-rich = "^0.1.1"
pytest-timer = "^0.0.11"
attrs = "^22.2.0"