[issue17013] Allow waiting on a mock

2020-06-10 Thread Ilya Kulakov


Ilya Kulakov  added the comment:

> That is not true, is actually encouraged to check for singletons like True, 
> False and None.

You're right, just never used it as I never needed an identity check against 
True / False

The PR is re-done to use an additional property call_event instead of extending 
called for backwards compatibility.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2020-06-10 Thread Ilya Kulakov


Ilya Kulakov  added the comment:

As far as I understand it introduces 3 methods that may clash. It's unlikely 
but (I speculate)
is still more likely than an identity check with called.

That being said, the PR can be redone as a subclass. But that implementation 
will not play
as nicely with the planned `awaited` property for asyncio mocks (see 
description in the PR).

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2020-06-10 Thread Pablo Galindo Salgado


Pablo Galindo Salgado  added the comment:

> Current implementation relies on that:

Notice that this is a clear disadvantage over a subclass-based approach, which 
is backwards compatible and preserves the semantics of mock.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2020-06-10 Thread Pablo Galindo Salgado


Pablo Galindo Salgado  added the comment:

> 1. called is almost never used in practice (people just use .assert*)

Sorry but saying "almost never used" is not good enough. Not only because you 
hold incomplete data but because backwards compatibility is mainly binary: it 
breaks or it does not, and this breaks.

> 2. The is True / False is discouraged and is rarely used by itself, let alone 
> in combination with .called

That is not true, is actually encouraged to check for singletons like True, 
False and None.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2020-06-10 Thread Ilya Kulakov


Ilya Kulakov  added the comment:

> Unfortunately, we take backwards compatibility very seriously in the core 
> team and this is a big downside of this proposal.

Current implementation relies on that:
1. called is almost never used in practice (people just use .assert*)
2. The is True / False is discouraged and is rarely used by itself, let alone 
in combination with .called

> Wouldn't that also break any mock that is mocking an object with a 
> "called_event" attribute?

It should break them in the same way as "called" breaks them now.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2020-06-10 Thread Pablo Galindo Salgado


Pablo Galindo Salgado  added the comment:

> Correct, it is not backward compatible in that respect. 

Unfortunately, we take backwards compatibility very seriously in the core team 
and this is a big downside of this proposal.

> I can instead add the called_event property

Wouldn't that also break any mock that is mocking an object with a 
"called_event" attribute?

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2020-06-10 Thread Ilya Kulakov

Ilya Kulakov  added the comment:

Correct, it is not backward compatible in that respect. I did not check 
thoroughly, but a quick lookup shown no such use among public repos on GitHub.

I can instead add the called_event property and make the CallEvent “public”.

Best Regards
Ilya Kulakov

> On Jun 9, 2020, at 11:56 PM, Pablo Galindo Salgado  
> wrote:
> 
> 
> Pablo Galindo Salgado  added the comment:
> 
> isn't PR 20759 backwards incompatible? If I understand correctly, this would 
> break anyone that is checking for identity as :
> 
> assert mymock.called is True
> 
> --
> nosy: +pablogsal
> 
> ___
> Python tracker 
> 
> ___

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2020-06-09 Thread Pablo Galindo Salgado


Pablo Galindo Salgado  added the comment:

isn't PR 20759 backwards incompatible? If I understand correctly, this would 
break anyone that is checking for identity as :

assert mymock.called is True

--
nosy: +pablogsal

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2020-06-09 Thread Ilya Kulakov


Change by Ilya Kulakov :


--
nosy: +Ilya.Kulakov
nosy_count: 11.0 -> 12.0
pull_requests: +19958
pull_request: https://github.com/python/cpython/pull/20759

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2020-04-27 Thread Mario Corchero


Mario Corchero  added the comment:

For the record, I have no strong preference over either implementation. 
@voidspace preferred offline the new mock class, but sadly the rationale is 
lost in the physical word.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2020-01-10 Thread Cheryl Sabella


Change by Cheryl Sabella :


--
versions: +Python 3.9 -Python 3.8

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2019-11-18 Thread Ilya Kulakov


Ilya Kulakov  added the comment:

I have submitted an alternative implementation of this feature heavily inspired 
by _AwaitEvent I wrote for asynctest [0]. 

There was recently an interest from the community towards asynctest to the 
point that got some of its measures merged into CPython [1].

The key features of my implementation [2]:

- Gives meaning to the existing Mock.called property, otherwise not much useful
- Does not require end users to do anything: change is automatically available 
in every Mock (and any subclass of mock that does not override `called`)
- Use of conditionals provides API that allows much richer extension: instead 
of hardcoding conditions as events it allows to wait until arbitrary predicate 
becomes true
- Utilizes existing semantics of python conditionals (both asyncio and 
threading)

Accepting this PR will allow me to bring _AwaitEvent thereby completing mock.py 
with waiting mechanics with identical semantics for both threading-based and 
asyncio-based cases.


0: 
https://github.com/Martiusweb/asynctest/blob/4b1284d6bab1ae90a6e8d88b882413ebbb7e5dce/asynctest/mock.py#L428
1: https://github.com/python/cpython/pull/9296
2: https://github.com/python/cpython/pull/17133

--
nosy: +Kentzo

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2019-11-12 Thread Ilya Kulakov


Change by Ilya Kulakov :


--
pull_requests: +16643
pull_request: https://github.com/python/cpython/pull/17133

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2019-09-13 Thread Mario Corchero


Change by Mario Corchero :


--
pull_requests: +15715
pull_request: https://github.com/python/cpython/pull/16094

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2019-09-12 Thread Mario Corchero


Mario Corchero  added the comment:

Spoke offline with @xtreak, I'll be sending a PR for this to take over the 
existing one.

@lisroach proposed a new name, EventMock to differentiate it from any awaitable 
async notation.

@michael.foord suggested using `mock_timeout` as the argument.

Discussed also to change the naming of the method to `await_until_any_call` to 
make the semantics similar to the one that `Mock` provides. As 
`await_until_called_with` might mislead the user that it applies only to the 
last call.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2019-09-12 Thread Lisa Roach


Change by Lisa Roach :


--
nosy: +lisroach

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2019-04-14 Thread László Kiss Kollár

Change by László Kiss Kollár :


--
nosy: +lkollar

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2019-04-13 Thread Karthikeyan Singaravelan


Change by Karthikeyan Singaravelan :


--
keywords: +patch
pull_requests: +12743
stage: needs patch -> patch review

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2019-04-13 Thread Mario Corchero

Mario Corchero  added the comment:

Kooning great! I would Add a test for the following (I think both fails with 
the proposed impl):

- The mock is called BEFORE calling wait_until_called_with
- I call the mock with arguments and then I call wait for call without 
arguments.
- using keyword arguments 

Also, I don’t have a great solution for it but it might be worth prefixing 
time-out with something in the wait_untill_called_with. In situations where the 
mocked object has a timeout parameter (which is a common argument for 
multithreaded apps).

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2019-04-13 Thread Karthikeyan Singaravelan


Karthikeyan Singaravelan  added the comment:

Thanks Mario for the feedback.

> I see you inherit from Mock, should it inherit from MagicMock?

yes, it can inherit from MagicMock to mock magic methods and to wait on their 
call.

I thought some more about waiting for function call with arguments. One idea 
would be to have a dictionary with args to function as key mapping to an event 
object and to set and wait on that event object. To have wait_until_called that 
is always listening on a per mock object and to have wait_until_called_with 
listening on event object specific to that argument. Below is a sample 
implementation and an example to show it working with wait_until_called and 
wait_until_called_with. wait_until_called_with is something difficult to model 
since it needs per call event object and also need to store relevant key 
mapping to event object that is currently args and doesn't support keyword 
arguments. But wait_until_called is little simpler waiting on a per mock event 
object.

I will open up a PR with some tests.


class WaitableMock(MagicMock):

def __init__(self, *args, **kwargs):
_safe_super(WaitableMock, self).__init__(*args, **kwargs)
self.event_class = kwargs.pop('event_class', threading.Event)
self.event = self.event_class()
self.expected_calls = {}

def _mock_call(self, *args, **kwargs):
ret_value  = _safe_super(WaitableMock, self)._mock_call(*args, **kwargs)

for call in self._mock_mock_calls:
event = self.expected_calls.get(call.args)
if event and not event.is_set():
event.set()

# Always set per mock event object to ensure the function is called for 
wait_until_called.
self.event.set()

return ret_value

def wait_until_called(self, timeout=1):
return self.event.wait(timeout=timeout)

def wait_until_called_with(self, *args, timeout=1):
# If there are args create a per argument list event object and if not 
wait for per mock event object.
if args:
if args not in self.expected_calls:
event = self.event_class()
self.expected_calls[args] = event
else:
event = self.expected_calls[args]
else:
event = self.event

return event.is_set() or event.wait(timeout=timeout)



# Sample program to wait on arguments, magic methods and validating wraps

import multiprocessing
import threading
import time
from unittest.mock import WaitableMock, patch, call

def call_after_sometime(func, *args, delay=1):
time.sleep(delay)
func(*args)

def wraps(*args):
return 1

def foo(*args):
pass

def bar(*args):
pass

with patch('__main__.foo', WaitableMock(event_class=threading.Event, 
wraps=wraps)):
with patch('__main__.bar', WaitableMock(event_class=threading.Event)):
# Test normal call
threading.Thread(target=call_after_sometime, args=(foo, 1), 
kwargs={'delay': 1}).start()
threading.Thread(target=call_after_sometime, args=(bar, 1), 
kwargs={'delay': 5}).start()
print("foo called ", foo.wait_until_called(timeout=2))
print("bar called ", bar.wait_until_called(timeout=2))
foo.assert_called_once()
bar.assert_not_called()

# Test wraps works
assert foo() == 1

# Test magic method
threading.Thread(target=call_after_sometime, args=(foo.__str__, ), 
kwargs={'delay': 1}).start()
print("foo.__str__ called ", foo.__str__.wait_until_called(timeout=2))
print("bar.__str__ called ", bar.__str__.wait_until_called(timeout=2))

foo.reset_mock()
bar.reset_mock()

# Test waiting for arguments
threading.Thread(target=call_after_sometime, args=(bar, 1), 
kwargs={'delay': 1}).start()
threading.Thread(target=call_after_sometime, args=(bar, 2), 
kwargs={'delay': 5}).start()
print("bar called with 1 ", bar.wait_until_called_with(1, timeout=2))
print("bar called with 2 ", bar.wait_until_called_with(2, timeout=2))
time.sleep(5)
print("bar called with 2 ", bar.wait_until_called_with(2))


$ ./python.exe ../backups/bpo17013_mock.py
foo called  True
bar called  False
foo.__str__ called  True
bar.__str__ called  False
bar called with 1  True
bar called with 2  False
bar called with 2  True

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2019-04-13 Thread Mario Corchero


Mario Corchero  added the comment:

I think this is REALLY interesting!, there are many situations where this has 
been useful, it would greatly improve multithreading testing in Python.

Q? I see you inherit from Mock, should it inherit from MagicMock?
I'd say send the PR and the discussion can happen there about the 
implementation, seems there is consensus in this thread.

I would find it OK to start with `wait_until_called` if you want and can be 
discussed in another issue/PR if you don't have the time/think it might not be 
worth.

--

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2019-04-13 Thread Karthikeyan Singaravelan

Karthikeyan Singaravelan  added the comment:

Is there still sufficient interest in this? I gave an initial try at this based 
on my limited knowledge of mock and Antoine's idea. I created a new class 
WaitableMock that inherits from Mock and accepts an event_class with 
threading.Event as default and stores an event object. When call is made then 
via CallableMixin _mock_call is called which I have overridden to set the event 
object. I have wait_until_called that waits on this event object for a given 
timeout to return whether it's called or not. 

I am not sure of implementing wait_until_called_with since I set the event 
object for any call in _mock_call irrespective of argument and since the call 
params for which the event has to be set are passed to wait_until_called_with. 
Perhaps allow passing a call object to wait_until_called_with and and during 
_mock_call set event object only if the call is the same as one passed to 
wait_until_called_with ?

See also issue26467 to support asyncio with mock

Initial implementation 

class WaitableMock(Mock):

def __init__(self, *args, **kwargs):
event_class = kwargs.pop('event_class', threading.Event)
_safe_super(WaitableMock, self).__init__(*args, **kwargs)
self.event = event_class()

def _mock_call(self, *args, **kwargs):
_safe_super(WaitableMock, self)._mock_call(*args, **kwargs)
self.event.set()

def wait_until_called(self, timeout=1):
return self.event.wait(timeout=timeout)

Sample program : 

import multiprocessing
import threading
import time
from unittest.mock import WaitableMock, patch

def call_after_sometime(func, delay=1):
time.sleep(delay)
func()

def foo():
pass

def bar():
pass

with patch('__main__.foo', WaitableMock(event_class=threading.Event)):
with patch('__main__.bar', WaitableMock(event_class=threading.Event)):
threading.Thread(target=call_after_sometime, args=(foo, 1)).start()
threading.Thread(target=call_after_sometime, args=(bar, 5)).start()
print("foo called ", foo.wait_until_called(timeout=2)) # successful
print("bar called ", bar.wait_until_called(timeout=2)) # times out
foo.assert_called_once()
bar.assert_not_called()
# Wait for the bar's thread to complete to verify call is registered
time.sleep(5)
bar.assert_called_once()

# foo is called but waiting for call to bar times out and hence no calls to bar 
are registered though bar is eventually called in the end and the call is 
registered at the end of the program.

➜  cpython git:(master) ✗ time ./python.exe ../backups/bpo17013_mock.py
foo called  True
bar called  False
./python.exe ../backups/bpo17013_mock.py  0.40s user 0.05s system 7% cpu 5.765 
total

--
nosy: +cjw296, mariocj89, xtreak

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2017-12-11 Thread Cheryl Sabella

Change by Cheryl Sabella :


--
stage:  -> needs patch
versions: +Python 3.8 -Python 3.4

___
Python tracker 

___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2015-07-31 Thread Robert Collins

Robert Collins added the comment:

Now at https://github.com/testing-cabal/mock/issues/189

--
nosy: +rbcollins

___
Python tracker rep...@bugs.python.org
http://bugs.python.org/issue17013
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2013-02-04 Thread Michael Foord

Michael Foord added the comment:

There is a similar feature request on the mock issue tracker: 
http://code.google.com/p/mock/issues/detail?id=189

I prefer this proposal to the other one though. (Although technically allowing 
a wait for multiple calls is more flexible.)

--

___
Python tracker rep...@bugs.python.org
http://bugs.python.org/issue17013
___
___
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2013-01-22 Thread Antoine Pitrou

New submission from Antoine Pitrou:

In non-trivial tests, you may want to wait for a method to be called in another 
thread. This is a case where unittest.mock currently doesn't help. It would be 
nice to be able to write:

  myobj.some_method = Mock(side_effect=myobj.some_method)
  # launch some thread
  myobj.some_method.wait_until_called()

And perhaps

  myobj.some_method.wait_until_called_with(...)

(with an optional timeout?)

If we don't want every Mock object to embed a threading.Event, perhaps there 
could be a ThreadedMock subclass?
Or perhaps even:

  WaitableMock(..., event_class=threading.Event)

so that people can pass multiprocessing.Event if they want to wait on the mock 
from another process?

--
components: Library (Lib)
messages: 180377
nosy: michael.foord, pitrou
priority: normal
severity: normal
status: open
title: Allow waiting on a mock
type: enhancement
versions: Python 3.4

___
Python tracker rep...@bugs.python.org
http://bugs.python.org/issue17013
___
___
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2013-01-22 Thread Ezio Melotti

Changes by Ezio Melotti ezio.melo...@gmail.com:


--
nosy: +ezio.melotti

___
Python tracker rep...@bugs.python.org
http://bugs.python.org/issue17013
___
___
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue17013] Allow waiting on a mock

2013-01-22 Thread Jesús Cea Avión

Changes by Jesús Cea Avión j...@jcea.es:


--
nosy: +jcea

___
Python tracker rep...@bugs.python.org
http://bugs.python.org/issue17013
___
___
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com