match_face/.venv/Lib/site-packages/gevent/tests/test__event.py

449 lines
14 KiB
Python
Raw Normal View History

from __future__ import absolute_import
from __future__ import print_function
from __future__ import division
import weakref
import gevent
from gevent.event import Event, AsyncResult
import gevent.testing as greentest
from gevent.testing.six import xrange
from gevent.testing.timing import AbstractGenericGetTestCase
from gevent.testing.timing import AbstractGenericWaitTestCase
from gevent.testing.timing import SMALL_TICK
from gevent.testing.timing import SMALL_TICK_MAX_ADJ
DELAY = SMALL_TICK + SMALL_TICK_MAX_ADJ
class TestEventWait(AbstractGenericWaitTestCase):
def wait(self, timeout):
Event().wait(timeout=timeout)
def test_cover(self):
str(Event())
class TestGeventWaitOnEvent(AbstractGenericWaitTestCase):
def wait(self, timeout):
gevent.wait([Event()], timeout=timeout)
def test_set_during_wait(self):
# https://github.com/gevent/gevent/issues/771
# broke in the refactoring. we must not add new links
# while we're running the callback
event = Event()
def setter():
event.set()
def waiter():
s = gevent.spawn(setter)
# let the setter set() the event;
# when this method returns we'll be running in the Event._notify_links callback
# (that is, it switched to us)
res = event.wait()
self.assertTrue(res)
self.assertTrue(event.ready())
s.join() # make sure it's dead
# Clear the event. Now we can't wait for the event without
# another set to happen.
event.clear()
self.assertFalse(event.ready())
# Before the bug fix, this would return "immediately" with
# event in the result list, because the _notify_links loop would
# immediately add the waiter and call it
o = gevent.wait((event,), timeout=0.01)
self.assertFalse(event.ready())
self.assertNotIn(event, o)
gevent.spawn(waiter).join()
class TestAsyncResultWait(AbstractGenericWaitTestCase):
def wait(self, timeout):
AsyncResult().wait(timeout=timeout)
class TestWaitAsyncResult(AbstractGenericWaitTestCase):
def wait(self, timeout):
gevent.wait([AsyncResult()], timeout=timeout)
class TestAsyncResultGet(AbstractGenericGetTestCase):
def wait(self, timeout):
AsyncResult().get(timeout=timeout)
class MyException(Exception):
pass
class TestAsyncResult(greentest.TestCase):
def test_link(self):
ar = AsyncResult()
self.assertRaises(TypeError, ar.rawlink, None)
ar.unlink(None) # doesn't raise
ar.unlink(None) # doesn't raise
str(ar) # cover
def test_set_exc(self):
log = []
e = AsyncResult()
self.assertEqual(e.exc_info, ())
self.assertEqual(e.exception, None)
def waiter():
with self.assertRaises(MyException) as exc:
e.get()
log.append(('caught', exc.exception))
gevent.spawn(waiter)
obj = MyException()
e.set_exception(obj)
gevent.sleep(0)
self.assertEqual(log, [('caught', obj)])
def test_set(self):
event1 = AsyncResult()
timer_exc = MyException('interrupted')
# Notice that this test is racy:
# After DELAY, we set the event. We also try to immediately
# raise the exception with a timer of 0 --- but that depends
# on cycling the loop. Hence the fairly large value for DELAY.
g = gevent.spawn_later(DELAY, event1.set, 'hello event1')
self._close_on_teardown(g.kill)
with gevent.Timeout.start_new(0, timer_exc):
with self.assertRaises(MyException) as exc:
event1.get()
self.assertIs(timer_exc, exc.exception)
def test_set_with_timeout(self):
event2 = AsyncResult()
X = object()
result = gevent.with_timeout(DELAY, event2.get, timeout_value=X)
self.assertIs(
result, X,
'Nobody sent anything to event2 yet it received %r' % (result, ))
def test_nonblocking_get(self):
ar = AsyncResult()
self.assertRaises(gevent.Timeout, ar.get, block=False)
self.assertRaises(gevent.Timeout, ar.get_nowait)
class TestAsyncResultCrossThread(greentest.TestCase):
def _makeOne(self):
return AsyncResult()
def _setOne(self, one):
one.set('from main')
BG_WAIT_DELAY = 60
def _check_pypy_switch(self):
# On PyPy 7.3.3, switching to the main greenlet of a thread from a
# different thread silently does nothing. We can't detect the cross-thread
# switch, and so this test breaks
# https://foss.heptapod.net/pypy/pypy/-/issues/3381
if greentest.PYPY:
import sys
if sys.pypy_version_info[:3] <= (7, 3, 3): # pylint:disable=no-member
self.skipTest("PyPy bug: https://foss.heptapod.net/pypy/pypy/-/issues/3381")
@greentest.ignores_leakcheck
def test_cross_thread_use(self, timed_wait=False, wait_in_bg=False):
# Issue 1739.
# AsyncResult has *never* been thread safe, and using it from one
# thread to another is not safe. However, in some very careful use cases
# that can actually work.
#
# This test makes sure it doesn't hang in one careful use
# scenario.
self.assertNotMonkeyPatched() # Need real threads, event objects
from threading import Thread as NativeThread
from threading import Event as NativeEvent
if not wait_in_bg:
self._check_pypy_switch()
test = self
class Thread(NativeThread):
def __init__(self):
NativeThread.__init__(self)
self.daemon = True
self.running_event = NativeEvent()
self.finished_event = NativeEvent()
self.async_result = test._makeOne()
self.result = '<never set>'
def run(self):
# Give the loop in this thread something to do
g_event = Event()
def spin():
while not g_event.is_set():
g_event.wait(DELAY * 2)
glet = gevent.spawn(spin)
def work():
self.running_event.set()
# If we use a timed wait(), the bug doesn't manifest.
# This is probably because the loop wakes up to handle the timer,
# and notices the callback.
# See https://github.com/gevent/gevent/issues/1735
if timed_wait:
self.result = self.async_result.wait(test.BG_WAIT_DELAY)
else:
self.result = self.async_result.wait()
if wait_in_bg:
# This results in a separate code path
worker = gevent.spawn(work)
worker.join()
del worker
else:
work()
g_event.set()
glet.join()
del glet
self.finished_event.set()
gevent.get_hub().destroy(destroy_loop=True)
thread = Thread()
thread.start()
try:
thread.running_event.wait()
self._setOne(thread.async_result)
thread.finished_event.wait(DELAY * 5)
finally:
thread.join(DELAY * 15)
self._check_result(thread.result)
def _check_result(self, result):
self.assertEqual(result, 'from main')
def test_cross_thread_use_bg(self):
self.test_cross_thread_use(timed_wait=False, wait_in_bg=True)
def test_cross_thread_use_timed(self):
self.test_cross_thread_use(timed_wait=True, wait_in_bg=False)
def test_cross_thread_use_timed_bg(self):
self.test_cross_thread_use(timed_wait=True, wait_in_bg=True)
@greentest.ignores_leakcheck
def test_cross_thread_use_set_in_bg(self):
self.assertNotMonkeyPatched() # Need real threads, event objects
from threading import Thread as NativeThread
from threading import Event as NativeEvent
self._check_pypy_switch()
test = self
class Thread(NativeThread):
def __init__(self):
NativeThread.__init__(self)
self.daemon = True
self.running_event = NativeEvent()
self.finished_event = NativeEvent()
self.async_result = test._makeOne()
self.result = '<never set>'
def run(self):
self.running_event.set()
test._setOne(self.async_result)
self.finished_event.set()
gevent.get_hub().destroy(destroy_loop=True)
thread = Thread()
glet = None
try:
glet = gevent.spawn(thread.start)
result = thread.async_result.wait(self.BG_WAIT_DELAY)
finally:
thread.join(DELAY * 15)
if glet is not None:
glet.join(DELAY)
self._check_result(result)
@greentest.ignores_leakcheck
def test_cross_thread_use_set_in_bg2(self):
# Do it again to make sure it works multiple times.
self.test_cross_thread_use_set_in_bg()
class TestEventCrossThread(TestAsyncResultCrossThread):
def _makeOne(self):
return Event()
def _setOne(self, one):
one.set()
def _check_result(self, result):
self.assertTrue(result)
class TestAsyncResultAsLinkTarget(greentest.TestCase):
error_fatal = False
def test_set(self):
g = gevent.spawn(lambda: 1)
s1, s2, s3 = AsyncResult(), AsyncResult(), AsyncResult()
g.link(s1)
g.link_value(s2)
g.link_exception(s3)
self.assertEqual(s1.get(), 1)
self.assertEqual(s2.get(), 1)
X = object()
result = gevent.with_timeout(DELAY, s3.get, timeout_value=X)
self.assertIs(result, X)
def test_set_exception(self):
def func():
raise greentest.ExpectedException('TestAsyncResultAsLinkTarget.test_set_exception')
g = gevent.spawn(func)
s1, s2, s3 = AsyncResult(), AsyncResult(), AsyncResult()
g.link(s1)
g.link_value(s2)
g.link_exception(s3)
self.assertRaises(greentest.ExpectedException, s1.get)
X = object()
result = gevent.with_timeout(DELAY, s2.get, timeout_value=X)
self.assertIs(result, X)
self.assertRaises(greentest.ExpectedException, s3.get)
class TestEvent_SetThenClear(greentest.TestCase):
N = 1
def test(self):
e = Event()
waiters = [gevent.spawn(e.wait) for i in range(self.N)]
gevent.sleep(0.001)
e.set()
e.clear()
for greenlet in waiters:
greenlet.join()
class TestEvent_SetThenClear100(TestEvent_SetThenClear):
N = 100
class TestEvent_SetThenClear1000(TestEvent_SetThenClear):
N = 1000
class TestWait(greentest.TestCase):
N = 5
count = None
timeout = 1
period = timeout / 100.0
def _sender(self, events, asyncs):
while events or asyncs:
gevent.sleep(self.period)
if events:
events.pop().set()
gevent.sleep(self.period)
if asyncs:
asyncs.pop().set()
@greentest.skipOnAppVeyor("Not all results have arrived sometimes due to timer issues")
def test(self):
events = [Event() for _ in xrange(self.N)]
asyncs = [AsyncResult() for _ in xrange(self.N)]
max_len = len(events) + len(asyncs)
sender = gevent.spawn(self._sender, events, asyncs)
results = gevent.wait(events + asyncs, count=self.count, timeout=self.timeout)
if self.timeout is None:
expected_len = max_len
else:
expected_len = min(max_len, self.timeout / self.period)
if self.count is None:
self.assertTrue(sender.ready(), sender)
else:
expected_len = min(self.count, expected_len)
self.assertFalse(sender.ready(), sender)
sender.kill()
self.assertEqual(expected_len, len(results), (expected_len, len(results), results))
class TestWait_notimeout(TestWait):
timeout = None
class TestWait_count1(TestWait):
count = 1
class TestWait_count2(TestWait):
count = 2
class TestEventBasics(greentest.TestCase):
def test_weakref(self):
# Event objects should allow weakrefs
e = Event()
r = weakref.ref(e)
self.assertIs(e, r())
del e
del r
def test_wait_while_notifying(self):
# If someone calls wait() on an Event that is
# ready, and notifying other waiters, that new
# waiter still runs at the end, but this does not
# require a trip around the event loop.
# See https://github.com/gevent/gevent/issues/1520
event = Event()
results = []
def wait_then_append(arg):
event.wait()
results.append(arg)
gevent.spawn(wait_then_append, 1)
gevent.spawn(wait_then_append, 2)
gevent.idle()
self.assertEqual(2, event.linkcount())
check = gevent.get_hub().loop.check()
check.start(results.append, 4)
event.set()
wait_then_append(3)
self.assertEqual(results, [1, 2, 3])
# Note that the check event DID NOT run.
check.stop()
check.close()
def test_gevent_wait_twice_when_already_set(self):
event = Event()
event.set()
# First one works fine.
result = gevent.wait([event])
self.assertEqual(result, [event])
# Second one used to fail with an AssertionError,
# now it passes
result = gevent.wait([event])
self.assertEqual(result, [event])
del AbstractGenericGetTestCase
del AbstractGenericWaitTestCase
if __name__ == '__main__':
greentest.main()