◐ Shell
clean mode source ↗

gh-61215: Add Mock.call_event to allow waiting for calls by Kentzo · Pull Request #20759 · python/cpython

AsyncMock was a forced decision in asynctest because it was a 3rd party package. And probably a necessary one in stdlib, because being an awaitable is part of the function signature.

I was not meaning that the decision was based on "wanting more mock classes", but that in a conversation in the core-dev sprints in 2019 (IIRC), @mfoord said he preferred having more specialized mocks depending on the usecase.

However AsyncMock users won't be able to wait for calls.

I'd say we should add an asyncio specific waiting function. To wait for something to be awaited. I think this is even more of a reason to not have it in MagicMock TBH. What does it mean to "wait" for a coroutine to be called? The implementation here is thread based, again, not an expert on asyncio, but I'm not sure this makes much sense (saying this works for asyncio). If you need a "await_event" as you posted on the last comment I'd rather move that to AsyncMock.

Having the separation will give you "Mock/MagicMock" as general purpose. "ThreadingMock" for multithreading and "AsyncMock" for asyncio/corrutines.
And note that they can be easily combined. You can do something like:

my_obj = MagicMock()
my_obj.corrutine_func = AsyncMock()
my_obj.threading_call = ThreadingMock()

A user can use both mocks. The only situation where this does not work is when you have a corrutine that you want "to wait" for it to be called. but AFAIK that does not make much sense. The implementation for that is probably different isnt it? Which was the main driver of AsyncMock.

@Kentzo / @mariocj89 - would it be crazy to land both these PRs?

I'd land only one (and I'm fine if it is not the one that @tirkarthi and I put together), but I think we need 2 different implementations. One for multi-threading and another for asyncio. This implements multithreading and exposes it in the base class, adding a call_event attribute (hopefully there are not many clashes) to all mocks.

Morevoer, I subjectively find the AP of threadingmock closer to the existing ones:

my_object = Mock(...)
my_object.some_function.call_event.wait(...)
my_object = ThreadingMock(...)
my_object.some_function.wait_until_any_call(...)

Lastly, having a ThreadingMock would allow us to extend the class for other multithreading use-cases without fear of colliding with user defined functions (new class guarantees no backward compatibility issues).