GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-61215: Add Mock.call_event to allow waiting for calls by Kentzo · Pull Request #20759 · python/cpython

Conversation

@Kentzo

@Kentzo Kentzo commented Jun 9, 2020 •
edited by bedevere-bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

This implementation of bpo-17013 is alternative to #16094

Changes are based on my work for asynctest. Specifically on _AwaitEvent that was left out when related code was ported to CPython.

Key features:

  • Does not require end users to do anything: change is automatically available in every Mock
  • Utilizes existing semantics of python conditionals (both asyncio and threading)

Accepting this change will allow me to port _AwaitEvent therefore giving identical semantics to both wait-for-calls and wait-for-awaits.

I will provide necessary typing annotations for typeshed.

Considerations:

  1. This approach changes type of the Mock.called property from bool to private bool-like _CallEvent. However, the only practical problem I could think of is if someone was checking type of Mock.called (e.g. isinstance, is). That does not sound like a plausible problem: after all the property itself is seldom used with exception of conditional expressions where _CallEvent.__bool__ and _CallEvent.__eq__ is sufficient. Added new property instead.
  2. It probably makes sense to provide convenience methods like wait_for_call, but I would like to hear the opinion of the reviewers. Implemented.

CC: @vstinner @tirkarthi @mariocj89

https://bugs.python.org/issue17013

  • Issue: Allow waiting on a mock #61215

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

rohitchormale, tropxy, ppetrid, mattaustin, heckad, puddly, V02460, Thorleon, edgarrmondragon, Kentzo, and arhadthedev reacted with thumbs up emoji
All reactions
  • 👍 11 reactions
@the-knights-who-say-ni the-knights-who-say-ni added the CLA signed label Jun 9, 2020
@bedevere-bot bedevere-bot added the awaiting review label Jun 9, 2020
@Kentzo

Kentzo commented Jun 9, 2020

Copy link
Copy Markdown
Contributor Author

CC @Martiusweb

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kentzo
Kentzo force-pushed the bpo-17013 branch 4 times, most recently from 05c3540 to c023792 Compare June 10, 2020 14:33
@Kentzo Kentzo mentioned this pull request Oct 21, 2020
bpo-17013: Extend Mock.called to allow waiting for calls #17133
Closed
@lisroach lisroach mentioned this pull request Oct 21, 2020
gh-61215: New mock to wait for multi-threaded events to happen #16094
Merged
@Kentzo
Kentzo force-pushed the bpo-17013 branch from 27434f7 to 0d3fc9f Compare October 21, 2020 19:24
@Kentzo Kentzo changed the title bpo-17013: Extend Mock.called to allow waiting for calls bpo-17013: Add Mock.call_event to allow waiting for calls Oct 26, 2020
@Kentzo
Kentzo force-pushed the bpo-17013 branch from 0d3fc9f to e6d5332 Compare January 16, 2021 21:53
@Kentzo Kentzo mentioned this pull request Feb 23, 2021
Current status of the asynctest Martiusweb/asynctest#158
Open
@Kentzo

Kentzo commented Jun 25, 2021

Copy link
Copy Markdown
Contributor Author

Anyone interested in the review?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kentzo
bpo-17013: Add Mock.call_event to allow waiting for calls
06023d9
New methods allow tests to wait for calls executing in other threads.
@Kentzo
Kentzo force-pushed the bpo-17013 branch from e6d5332 to 06023d9 Compare October 15, 2021 23:46
@Kentzo
Kentzo requested a review from cjw296 as a code owner October 15, 2021 23:46
@Kentzo

Kentzo commented Oct 16, 2021

Copy link
Copy Markdown
Contributor Author

Rebased on top of the current main.

Failure of the address sanitizer tests is unrelated to the changes in this PR.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kentzo

Kentzo commented Feb 2, 2022

Copy link
Copy Markdown
Contributor Author

Up

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@pitrou pitrou mentioned this pull request Apr 10, 2022
Allow waiting on a mock #61215
Closed
@ezio-melotti ezio-melotti removed the CLA signed label Jul 13, 2022
@cjw296

cjw296 commented Jun 9, 2023

Copy link
Copy Markdown
Contributor

@mariocj89 / @tirkarthi - I think this makes sense, should we land it if @Kentzo still has energy to rebase it?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mariocj89

mariocj89 commented Jun 9, 2023 via email

Copy link
Copy Markdown
Contributor
IIRC Michael Foord preferred to have a custom Mock class targeted to multi threading. Similarly to how AsyncMock was created. We put together #16094 for that purpose. I am fine with either approach, but would be nice to have something. If he does not have the energy I can rebase rhe other PR as well.
…
On Fri, 9 Jun 2023 at 15:44, Chris Withers ***@***.***> wrote: @mariocj89 <https://github.com/mariocj89> / @tirkarthi <https://github.com/tirkarthi> - I think this makes sense, should we land it if @Kentzo <https://github.com/Kentzo> still has energy to rebase it? — Reply to this email directly, view it on GitHub <#20759 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AA4JV45NCZ3MWLJQZVQWZW3XKMLDLANCNFSM4NZKPGKQ> . You are receiving this because you were mentioned.Message ID: ***@***.***>
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@cjw296

cjw296 commented Jun 9, 2023

Copy link
Copy Markdown
Contributor

Ah, yeah, tough call... I guess for consistency of API, we probably should go with #16094.

Can you ever need both AsyncMock and ThreadingMock in the same object?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mariocj89

mariocj89 commented Jun 9, 2023 via email •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor
Not an asyncio expert, but that would be weird IIUC, as you will probably not want to mix those two paradigms. For sure you will never need them in the same method.
…
On Fri, 9 Jun 2023 at 16:31, Chris Withers ***@***.***> wrote: Ah, yeah, tough call... I guess for consistency of API, we probably should go with #16094 <#16094>. Can you ever need both AsyncMock and ThreadingMock in the same object? — Reply to this email directly, view it on GitHub <#20759 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AA4JV43LREVYMBSGXLPNQRLXKMQS5ANCNFSM4NZKPGKQ> . You are receiving this because you were mentioned.Message ID: ***@***.***>
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kentzo

Kentzo commented Jun 9, 2023 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

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.

Possibility of being used in a multithreading context is an intrinsic property of all Callables. Such differentiation would be akin to adding CallableMock, AttributeMock and so on.

Can you ever need both AsyncMock and ThreadingMock in the same object?

Both mock function calls, so it's feasible for a user to want to know whether an awaitable was instantiated (but not awaited) from some other thread, such as a run loop running in another thread.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@arhadthedev arhadthedev changed the title bpo-17013: Add Mock.call_event to allow waiting for calls gh-61215: Add Mock.call_event to allow waiting for calls Jun 10, 2023
@arhadthedev

arhadthedev commented Jun 10, 2023

Copy link
Copy Markdown
Member

Could you resolve a merge conflict, please?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@cjw296

cjw296 commented Jun 11, 2023

Copy link
Copy Markdown
Contributor

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

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kentzo

Kentzo commented Jun 12, 2023

Copy link
Copy Markdown
Contributor Author

@arhadthedev Will do.

@cjw296

  • If you land this one, the separate class won't be needed
  • If you land the other PR then CallEvent could be salvaged and then I can port AwaitEvent for AsyncMock. However AsyncMock users won't be able to wait for calls.

What are your thoughts on this, perhaps I could word better and clarify the use case?

My tl;dr; is that users should be able to wait for calls of functions and for both calls and awaits of coroutines. This is why I advocate for having call_event in MagicMock (base class) rather than a separate class:

my_object = Mock(...)

my_object.some_function.call_event.wait(...)
# post-call asserts

my_object.some_coroutine.call_event.wait(...)
# post-call but pre-await asserts
my_object.some_coroutine.await_event.wait(...)
# post-await asserts

If you decide to proceed with this PR, one feedback I'd like to get is whether CallEvent/AwaitEvent should have separate methods for waiting (wait(...), wait_for_call(call, ...), wait_for(predicate, ...)) or just one by merging arguments (wait(self, call=None, /, predicate=None, skip=0, timeout=None)).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mariocj89

mariocj89 commented Jun 12, 2023

Copy link
Copy Markdown
Contributor

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).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@cjw296

cjw296 commented Jun 14, 2023

Copy link
Copy Markdown
Contributor

@mariocj89 : Yeah, I think I'm persuaded by the 'separate class' argument, and I like the fact that you can compose in the way you show, so let's go for landing the version in #16094.

FWIW, I now remember that I ended up implementing something similar for Twisted when I needed it. Worse still as I needed to capture calls to methods on existing classes where Mocks cannot be subbed in, and where the actual instance cannot be referenced by test code. One thing I do remember that might be useful here is that I ended up needing a list of calls and a list of things waiting which then had to be matched up. I guess AsyncMock must have this already? Threading may not need this?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@cjw296 cjw296 closed this Jun 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@cjw296 cjw296 Awaiting requested review from cjw296 cjw296 is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

awaiting review

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

7 participants

@Kentzo @cjw296 @mariocj89 @arhadthedev @the-knights-who-say-ni @ezio-melotti @bedevere-bot
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.