Skip to content

Commit 0e6b221

Browse files
committed
Ensure all the exceptions cat be spied on
This change replaces `Exception` with `BaseException` in the `spy()` method provided by the `mocker` fixture. This enables the caller to spy on things like `KeyboardInterrupt`, `GeneratorExit` and `SystemExit` exceptions that hasn't been possible before because of a bug. Before this change, any occurances of the above exceptions caused `spy()` to assign `None` to both `spy_return` and `spy_exception` attributes. Fixes #215
1 parent 9e1464b commit 0e6b221

File tree

3 files changed

+32
-6
lines changed

3 files changed

+32
-6
lines changed

CHANGELOG.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,20 @@
33

44
* Add `mock.seal` alias to the `mocker` fixture (`#211`_). Thanks `@coiax`_ for the PR.
55

6+
* Fixed spying on exceptions not covered by the ``Exception``
7+
superclass (`#215`_), like ``KeyboardInterrupt`` -- PR `#216`_
8+
by `@webknjaz`_.
9+
10+
Before the fix, both ``spy_return`` and ``spy_exception``
11+
whenever such an exception happened. And after this fix,
12+
``spy_exception`` is set to a correct value of an exception
13+
that has actually happened.
14+
615
.. _@coiax: https://github.com/coiax
16+
.. _@webknjaz: https://github.com/sponsors/webknjaz
717
.. _#211: https://github.com/pytest-dev/pytest-mock/pull/211
18+
.. _#215: https://github.com/pytest-dev/pytest-mock/pull/215
19+
.. _#216: https://github.com/pytest-dev/pytest-mock/pull/216
820

921
3.3.1 (2020-08-24)
1022
------------------

src/pytest_mock/plugin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def wrapper(*args, **kwargs):
114114
spy_obj.spy_exception = None
115115
try:
116116
r = method(*args, **kwargs)
117-
except Exception as e:
117+
except BaseException as e:
118118
spy_obj.spy_exception = e
119119
raise
120120
else:
@@ -126,7 +126,7 @@ async def async_wrapper(*args, **kwargs):
126126
spy_obj.spy_exception = None
127127
try:
128128
r = await method(*args, **kwargs)
129-
except Exception as e:
129+
except BaseException as e:
130130
spy_obj.spy_exception = e
131131
raise
132132
else:

tests/test_pytest_mock.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import platform
33
import sys
44
from contextlib import contextmanager
5-
from typing import Callable, Any, Tuple, Generator
5+
from typing import Callable, Any, Tuple, Generator, Type
66
from unittest.mock import MagicMock
77

88
import pytest
@@ -235,17 +235,31 @@ def bar(self, arg):
235235
assert spy.spy_return == 20
236236

237237

238-
def test_instance_method_spy_exception(mocker: MockerFixture) -> None:
238+
# Ref: https://docs.python.org/3/library/exceptions.html#exception-hierarchy
239+
@pytest.mark.parametrize(
240+
"exc_cls",
241+
(
242+
BaseException,
243+
Exception,
244+
GeneratorExit, # BaseException
245+
KeyboardInterrupt, # BaseException
246+
RuntimeError, # regular Exception
247+
SystemExit, # BaseException
248+
),
249+
)
250+
def test_instance_method_spy_exception(
251+
exc_cls: Type[BaseException], mocker: MockerFixture,
252+
) -> None:
239253
class Foo:
240254
def bar(self, arg):
241-
raise Exception("Error with {}".format(arg))
255+
raise exc_cls("Error with {}".format(arg))
242256

243257
foo = Foo()
244258
spy = mocker.spy(foo, "bar")
245259

246260
expected_calls = []
247261
for i, v in enumerate([10, 20]):
248-
with pytest.raises(Exception, match="Error with {}".format(v)) as exc_info:
262+
with pytest.raises(exc_cls, match="Error with {}".format(v)):
249263
foo.bar(arg=v)
250264

251265
expected_calls.append(mocker.call(arg=v))

0 commit comments

Comments
 (0)