You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

211 lines
6.2 KiB
Python

# Copyright (c) 2008 AG Projects
# Author: Denis Bilenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import sys
import gevent.testing as greentest
import weakref
import time
import gc
from gevent import sleep
from gevent import Timeout
from gevent import get_hub
from gevent.testing.timing import SMALL_TICK as DELAY
from gevent.testing import flaky
class Error(Exception):
pass
class _UpdateNowProxy(object):
update_now_calls = 0
def __init__(self, loop):
self.loop = loop
def __getattr__(self, name):
return getattr(self.loop, name)
def update_now(self):
self.update_now_calls += 1
self.loop.update_now()
class _UpdateNowWithTimerProxy(_UpdateNowProxy):
def timer(self, *_args, **_kwargs):
return _Timer(self)
class _Timer(object):
pending = False
active = False
def __init__(self, loop):
self.loop = loop
def start(self, *_args, **kwargs):
if kwargs.get("update"):
self.loop.update_now()
self.pending = self.active = True
def stop(self):
self.active = self.pending = False
def close(self):
"Does nothing"
class Test(greentest.TestCase):
def test_timeout_calls_update_now(self):
hub = get_hub()
loop = hub.loop
proxy = _UpdateNowWithTimerProxy(loop)
hub.loop = proxy
try:
with Timeout(DELAY * 2) as t:
self.assertTrue(t.pending)
finally:
hub.loop = loop
self.assertEqual(1, proxy.update_now_calls)
def test_sleep_calls_update_now(self):
hub = get_hub()
loop = hub.loop
proxy = _UpdateNowProxy(loop)
hub.loop = proxy
try:
sleep(0.01)
finally:
hub.loop = loop
self.assertEqual(1, proxy.update_now_calls)
@greentest.skipOnAppVeyor("Timing is flaky, especially under Py 3.4/64-bit")
@greentest.skipOnPyPy3OnCI("Timing is flaky, especially under Py 3.4/64-bit")
@greentest.reraises_flaky_timeout((Timeout, AssertionError))
def test_api(self):
# Nothing happens if with-block finishes before the timeout expires
t = Timeout(DELAY * 2)
self.assertFalse(t.pending, t)
with t:
self.assertTrue(t.pending, t)
sleep(DELAY)
# check if timer was actually cancelled
self.assertFalse(t.pending, t)
sleep(DELAY * 2)
# An exception will be raised if it's not
with self.assertRaises(Timeout) as exc:
with Timeout(DELAY) as t:
sleep(DELAY * 10)
self.assertIs(exc.exception, t)
# You can customize the exception raised:
with self.assertRaises(IOError):
with Timeout(DELAY, IOError("Operation takes way too long")):
sleep(DELAY * 10)
# Providing classes instead of values should be possible too:
with self.assertRaises(ValueError):
with Timeout(DELAY, ValueError):
sleep(DELAY * 10)
try:
1 / 0
except ZeroDivisionError:
with self.assertRaises(ZeroDivisionError):
with Timeout(DELAY, sys.exc_info()[0]):
sleep(DELAY * 10)
raise AssertionError('should not get there')
raise AssertionError('should not get there')
else:
raise AssertionError('should not get there')
# It's possible to cancel the timer inside the block:
with Timeout(DELAY) as timer:
timer.cancel()
sleep(DELAY * 2)
# To silent the exception before exiting the block, pass False as second parameter.
XDELAY = 0.1
start = time.time()
with Timeout(XDELAY, False):
sleep(XDELAY * 2)
delta = (time.time() - start)
self.assertTimeWithinRange(delta, 0, XDELAY * 2)
# passing None as seconds disables the timer
with Timeout(None):
sleep(DELAY)
sleep(DELAY)
def test_ref(self):
err = Error()
err_ref = weakref.ref(err)
with Timeout(DELAY * 2, err):
sleep(DELAY)
del err
gc.collect()
self.assertFalse(err_ref(), err_ref)
@flaky.reraises_flaky_race_condition()
def test_nested_timeout(self):
with Timeout(DELAY, False):
with Timeout(DELAY * 10, False):
sleep(DELAY * 3 * 20)
raise AssertionError('should not get there')
with Timeout(DELAY) as t1:
with Timeout(DELAY * 20) as t2:
with self.assertRaises(Timeout) as exc:
sleep(DELAY * 30)
self.assertIs(exc.exception, t1)
self.assertFalse(t1.pending, t1)
self.assertTrue(t2.pending, t2)
self.assertFalse(t2.pending)
with Timeout(DELAY * 20) as t1:
with Timeout(DELAY) as t2:
with self.assertRaises(Timeout) as exc:
sleep(DELAY * 30)
self.assertIs(exc.exception, t2)
self.assertTrue(t1.pending, t1)
self.assertFalse(t2.pending, t2)
self.assertFalse(t1.pending)
if __name__ == '__main__':
greentest.main()