-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Open
Description
I think the example is self explanatory
Description
When using monkeypatch fixture to patch and attribute with raising=False, AttributeError is raised at test tear down when attribute is tried to be restituted.
Output of pip list
Package Version
---------- -------
colorama 0.4.6
iniconfig 2.3.0
packaging 26.0
pip 26.0
pluggy 1.6.0
Pygments 2.19.2
pytest 9.0.2
setuptools 80.10.2
wheel 0.46.3
Pytest and operating system versions
Windows 11 with pytest 9.0.2
Minimal example
import pytest
something_or_none = None
def fake_do_something(*_, **__) -> int:
return 7
def test_something(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(
something_or_none, 'do_something', fake_do_something, raising=False
)
assert 7 == something_or_none.do_something('a', 'b', c='c')
When doing pytest <test_file.py> I am getting (paths are masked):
========================================= test session starts =========================================
platform win32 -- Python 3.12.12, pytest-9.0.2, pluggy-1.6.0
rootdir: ...
collected 1 item
...\test_example.py FE [100%]
=============================================== ERRORS ================================================
_________________________________ ERROR at teardown of test_something _________________________________
cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0x0000023F84FC5940>, when = 'teardown'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
@classmethod
def from_call(
cls,
func: Callable[[], TResult],
when: Literal["collect", "setup", "call", "teardown"],
reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None,
) -> CallInfo[TResult]:
"""Call func, wrapping the result in a CallInfo.
:param func:
The function to call. Called without arguments.
:type func: Callable[[], _pytest.runner.TResult]
:param when:
The phase in which the function is called.
:param reraise:
Exception or exceptions that shall propagate if raised by the
function, instead of being wrapped in the CallInfo.
"""
excinfo = None
instant = timing.Instant()
try:
> result: TResult | None = func()
^^^^^^
..\..\..\...\Lib\site-packages\_pytest\runner.py:353:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\..\..\...\Lib\site-packages\_pytest\runner.py:245: in <lambda>
lambda: runtest_hook(item=item, **kwds),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\...\Lib\site-packages\pluggy\_hooks.py:512: in __call__
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\...\Lib\site-packages\pluggy\_manager.py:120: in _hookexec
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
..\..\..\...\Lib\site-packages\_pytest\logging.py:858: in pytest_runtest_teardown
yield
..\..\..\...\Lib\site-packages\_pytest\capture.py:905: in pytest_runtest_teardown
return (yield)
^^^^^
..\..\..\...\Lib\site-packages\_pytest\runner.py:194: in pytest_runtest_teardown
item.session._setupstate.teardown_exact(nextitem)
..\..\..\...\Lib\site-packages\_pytest\runner.py:566: in teardown_exact
raise exceptions[0]
..\..\..\...\Lib\site-packages\_pytest\runner.py:555: in teardown_exact
fin()
..\..\..\...\Lib\site-packages\_pytest\fixtures.py:1053: in finish
raise exceptions[0]
..\..\..\...\Lib\site-packages\_pytest\fixtures.py:1042: in finish
fin()
..\..\..\...\Lib\site-packages\_pytest\fixtures.py:924: in _teardown_yield_fixture
next(it)
..\..\..\...\Lib\site-packages\_pytest\monkeypatch.py:59: in monkeypatch
mpatch.undo()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <_pytest.monkeypatch.MonkeyPatch object at 0x0000023F8683D4C0>
def undo(self) -> None:
"""Undo previous changes.
This call consumes the undo stack. Calling it a second time has no
effect unless you do more monkeypatching after the undo call.
There is generally no need to call `undo()`, since it is
called automatically during tear-down.
.. note::
The same `monkeypatch` fixture is used across a
single test function invocation. If `monkeypatch` is used both by
the test function itself and one of the test fixtures,
calling `undo()` will undo all of the changes made in
both functions.
Prefer to use :meth:`context() <pytest.MonkeyPatch.context>` instead.
"""
for obj, name, value in reversed(self._setattr):
if value is not notset:
setattr(obj, name, value)
else:
> delattr(obj, name)
E AttributeError: 'NoneType' object has no attribute 'do_something'
..\..\..\...\Lib\site-packages\_pytest\monkeypatch.py:416: AttributeError
============================================== FAILURES ===============================================
___________________________________________ test_something ____________________________________________
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x0000023F8683D4C0>
def test_something(monkeypatch: pytest.MonkeyPatch):
> monkeypatch.setattr(
something_or_none, 'do_something', fake_do_something, raising=False
)
E AttributeError: 'NoneType' object has no attribute 'do_something'
...\tests\test_example.py:12: AttributeError
======================================= short test summary info =======================================
FAILED .../tests/test_example.py::test_something - AttributeError: 'NoneType' object has no attribute 'do_something'
ERROR .../tests/test_example.py::test_something - AttributeError: 'NoneType' object has no attribute 'do_something'
===================================== 1 failed, 1 error in 0.22s ======================================
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels