Skip to content

Commit 71ed673

Browse files
lprnmnsnicoddemus
andauthored
Fix duplicate_iterators for async spies (#604)
mocker.spy supports async functions and documents spy_return_iter for iterator returns when duplicate_iterators=True. The synchronous spy wrapper honors that option, but the async wrapper returned the original iterator without creating the duplicate, leaving spy_return_iter as None. --------- Co-authored-by: Bruno Oliveira <bruno@soliv.dev>
1 parent 0797e3d commit 71ed673

3 files changed

Lines changed: 28 additions & 2 deletions

File tree

‎CHANGELOG.rst‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ Releases
44
Unreleased
55
----------
66

7+
* `#604 <https://github.com/pytest-dev/pytest-mock/pull/604>`_: Fixed ``duplicate_iterators=True`` for async functions spied with ``mocker.spy``.
78
* `#611 <https://github.com/pytest-dev/pytest-mock/pull/611>`_: Fixed async mock assertion introspection to use awaited arguments instead of the latest call's arguments.
8-
9-
109
* `#606 <https://github.com/pytest-dev/pytest-mock/pull/606>`_: ``mocker.resetall(return_value=True, side_effect=True)`` now also applies to non-callable mocks, such as those returned by ``mocker.create_autospec(SomeClass, instance=True)``. Previously both arguments were silently ignored for them.
1110
* `#547 <https://github.com/pytest-dev/pytest-mock/issues/547>`_: Added ``SpyType`` for annotating ``mocker.spy`` results.
1211
* Dropped support for EOL Python 3.9.

‎src/pytest_mock/plugin.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,12 @@ async def async_wrapper(*args, **kwargs):
204204
spy_obj.spy_exception = e
205205
raise
206206
else:
207+
if duplicate_iterators and isinstance(r, Iterator):
208+
r, duplicated_iterator = itertools.tee(r, 2)
209+
spy_obj.spy_return_iter = duplicated_iterator
210+
else:
211+
spy_obj.spy_return_iter = None
212+
207213
spy_obj.spy_return = r
208214
spy_obj.spy_return_list.append(r)
209215
return r

‎tests/test_pytest_mock.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,27 @@ async def bar(self, arg):
679679
assert result == 20
680680

681681

682+
@pytest.mark.asyncio
683+
async def test_async_spy_return_iter_duplicates_iterator_when_enabled(
684+
mocker: MockerFixture,
685+
) -> None:
686+
class Foo:
687+
async def bar(self) -> Iterator[int]:
688+
return iter([0, 1, 2])
689+
690+
foo = Foo()
691+
spy = mocker.spy(foo, "bar", duplicate_iterators=True)
692+
result = await foo.bar()
693+
694+
assert list(result) == [0, 1, 2]
695+
assert spy.spy_return is not None
696+
assert spy.spy_return_iter is not None
697+
assert list(spy.spy_return_iter) == [0, 1, 2]
698+
699+
[return_value] = spy.spy_return_list
700+
assert isinstance(return_value, Iterator)
701+
702+
682703
@contextmanager
683704
def assert_traceback() -> Generator[None, None, None]:
684705
"""

0 commit comments

Comments
 (0)