summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_asyncio/test_base_events.py
diff options
context:
space:
mode:
authorYury Selivanov <yselivanov@sprymix.com>2014-02-18 23:02:19 (GMT)
committerYury Selivanov <yselivanov@sprymix.com>2014-02-18 23:02:19 (GMT)
commitff827f08ac9201f56b14cb19ccb9d511434c858b (patch)
tree75a1b4b19c5eb74fa2fbf43d8df438b7c42b6e4d /Lib/test/test_asyncio/test_base_events.py
parent065efc3072c244ba34ce521ba0edaa4168fa8953 (diff)
downloadcpython-ff827f08ac9201f56b14cb19ccb9d511434c858b.zip
cpython-ff827f08ac9201f56b14cb19ccb9d511434c858b.tar.gz
cpython-ff827f08ac9201f56b14cb19ccb9d511434c858b.tar.bz2
asyncio: New error handling API. Issue #20681.
Diffstat (limited to 'Lib/test/test_asyncio/test_base_events.py')
-rw-r--r--Lib/test/test_asyncio/test_base_events.py194
1 files changed, 180 insertions, 14 deletions
diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py
index 9fa9841..f664ccc 100644
--- a/Lib/test/test_asyncio/test_base_events.py
+++ b/Lib/test/test_asyncio/test_base_events.py
@@ -15,6 +15,10 @@ from asyncio import constants
from asyncio import test_utils
+MOCK_ANY = unittest.mock.ANY
+PY34 = sys.version_info >= (3, 4)
+
+
class BaseEventLoopTests(unittest.TestCase):
def setUp(self):
@@ -49,20 +53,21 @@ class BaseEventLoopTests(unittest.TestCase):
self.assertRaises(NotImplementedError, next, iter(gen))
def test__add_callback_handle(self):
- h = asyncio.Handle(lambda: False, ())
+ h = asyncio.Handle(lambda: False, (), self.loop)
self.loop._add_callback(h)
self.assertFalse(self.loop._scheduled)
self.assertIn(h, self.loop._ready)
def test__add_callback_timer(self):
- h = asyncio.TimerHandle(time.monotonic()+10, lambda: False, ())
+ h = asyncio.TimerHandle(time.monotonic()+10, lambda: False, (),
+ self.loop)
self.loop._add_callback(h)
self.assertIn(h, self.loop._scheduled)
def test__add_callback_cancelled_handle(self):
- h = asyncio.Handle(lambda: False, ())
+ h = asyncio.Handle(lambda: False, (), self.loop)
h.cancel()
self.loop._add_callback(h)
@@ -137,15 +142,15 @@ class BaseEventLoopTests(unittest.TestCase):
self.assertRaises(
AssertionError, self.loop.run_in_executor,
- None, asyncio.Handle(cb, ()), ('',))
+ None, asyncio.Handle(cb, (), self.loop), ('',))
self.assertRaises(
AssertionError, self.loop.run_in_executor,
- None, asyncio.TimerHandle(10, cb, ()))
+ None, asyncio.TimerHandle(10, cb, (), self.loop))
def test_run_once_in_executor_cancelled(self):
def cb():
pass
- h = asyncio.Handle(cb, ())
+ h = asyncio.Handle(cb, (), self.loop)
h.cancel()
f = self.loop.run_in_executor(None, h)
@@ -156,7 +161,7 @@ class BaseEventLoopTests(unittest.TestCase):
def test_run_once_in_executor_plain(self):
def cb():
pass
- h = asyncio.Handle(cb, ())
+ h = asyncio.Handle(cb, (), self.loop)
f = asyncio.Future(loop=self.loop)
executor = unittest.mock.Mock()
executor.submit.return_value = f
@@ -175,8 +180,10 @@ class BaseEventLoopTests(unittest.TestCase):
f.cancel() # Don't complain about abandoned Future.
def test__run_once(self):
- h1 = asyncio.TimerHandle(time.monotonic() + 5.0, lambda: True, ())
- h2 = asyncio.TimerHandle(time.monotonic() + 10.0, lambda: True, ())
+ h1 = asyncio.TimerHandle(time.monotonic() + 5.0, lambda: True, (),
+ self.loop)
+ h2 = asyncio.TimerHandle(time.monotonic() + 10.0, lambda: True, (),
+ self.loop)
h1.cancel()
@@ -205,14 +212,15 @@ class BaseEventLoopTests(unittest.TestCase):
m_time.monotonic = monotonic
self.loop._scheduled.append(
- asyncio.TimerHandle(11.0, lambda: True, ()))
+ asyncio.TimerHandle(11.0, lambda: True, (), self.loop))
self.loop._process_events = unittest.mock.Mock()
self.loop._run_once()
self.assertEqual(logging.INFO, m_logger.log.call_args[0][0])
idx = -1
data = [10.0, 10.0, 10.3, 13.0]
- self.loop._scheduled = [asyncio.TimerHandle(11.0, lambda: True, ())]
+ self.loop._scheduled = [asyncio.TimerHandle(11.0, lambda: True, (),
+ self.loop)]
self.loop._run_once()
self.assertEqual(logging.DEBUG, m_logger.log.call_args[0][0])
@@ -225,7 +233,8 @@ class BaseEventLoopTests(unittest.TestCase):
processed = True
handle = loop.call_soon(lambda: True)
- h = asyncio.TimerHandle(time.monotonic() - 1, cb, (self.loop,))
+ h = asyncio.TimerHandle(time.monotonic() - 1, cb, (self.loop,),
+ self.loop)
self.loop._process_events = unittest.mock.Mock()
self.loop._scheduled.append(h)
@@ -287,6 +296,163 @@ class BaseEventLoopTests(unittest.TestCase):
self.loop.run_until_complete, self.loop.subprocess_shell,
asyncio.SubprocessProtocol, 'exit 0', bufsize=4096)
+ def test_default_exc_handler_callback(self):
+ self.loop._process_events = unittest.mock.Mock()
+
+ def zero_error(fut):
+ fut.set_result(True)
+ 1/0
+
+ # Test call_soon (events.Handle)
+ with unittest.mock.patch('asyncio.base_events.logger') as log:
+ fut = asyncio.Future(loop=self.loop)
+ self.loop.call_soon(zero_error, fut)
+ fut.add_done_callback(lambda fut: self.loop.stop())
+ self.loop.run_forever()
+ log.error.assert_called_with(
+ test_utils.MockPattern('Exception in callback.*zero'),
+ exc_info=(ZeroDivisionError, MOCK_ANY, MOCK_ANY))
+
+ # Test call_later (events.TimerHandle)
+ with unittest.mock.patch('asyncio.base_events.logger') as log:
+ fut = asyncio.Future(loop=self.loop)
+ self.loop.call_later(0.01, zero_error, fut)
+ fut.add_done_callback(lambda fut: self.loop.stop())
+ self.loop.run_forever()
+ log.error.assert_called_with(
+ test_utils.MockPattern('Exception in callback.*zero'),
+ exc_info=(ZeroDivisionError, MOCK_ANY, MOCK_ANY))
+
+ def test_default_exc_handler_coro(self):
+ self.loop._process_events = unittest.mock.Mock()
+
+ @asyncio.coroutine
+ def zero_error_coro():
+ yield from asyncio.sleep(0.01, loop=self.loop)
+ 1/0
+
+ # Test Future.__del__
+ with unittest.mock.patch('asyncio.base_events.logger') as log:
+ fut = asyncio.async(zero_error_coro(), loop=self.loop)
+ fut.add_done_callback(lambda *args: self.loop.stop())
+ self.loop.run_forever()
+ fut = None # Trigger Future.__del__ or futures._TracebackLogger
+ if PY34:
+ # Future.__del__ in Python 3.4 logs error with
+ # an actual exception context
+ log.error.assert_called_with(
+ test_utils.MockPattern('.*exception was never retrieved'),
+ exc_info=(ZeroDivisionError, MOCK_ANY, MOCK_ANY))
+ else:
+ # futures._TracebackLogger logs only textual traceback
+ log.error.assert_called_with(
+ test_utils.MockPattern(
+ '.*exception was never retrieved.*ZeroDiv'),
+ exc_info=False)
+
+ def test_set_exc_handler_invalid(self):
+ with self.assertRaisesRegex(TypeError, 'A callable object or None'):
+ self.loop.set_exception_handler('spam')
+
+ def test_set_exc_handler_custom(self):
+ def zero_error():
+ 1/0
+
+ def run_loop():
+ self.loop.call_soon(zero_error)
+ self.loop._run_once()
+
+ self.loop._process_events = unittest.mock.Mock()
+
+ mock_handler = unittest.mock.Mock()
+ self.loop.set_exception_handler(mock_handler)
+ run_loop()
+ mock_handler.assert_called_with(self.loop, {
+ 'exception': MOCK_ANY,
+ 'message': test_utils.MockPattern(
+ 'Exception in callback.*zero_error'),
+ 'handle': MOCK_ANY,
+ })
+ mock_handler.reset_mock()
+
+ self.loop.set_exception_handler(None)
+ with unittest.mock.patch('asyncio.base_events.logger') as log:
+ run_loop()
+ log.error.assert_called_with(
+ test_utils.MockPattern(
+ 'Exception in callback.*zero'),
+ exc_info=(ZeroDivisionError, MOCK_ANY, MOCK_ANY))
+
+ assert not mock_handler.called
+
+ def test_set_exc_handler_broken(self):
+ def run_loop():
+ def zero_error():
+ 1/0
+ self.loop.call_soon(zero_error)
+ self.loop._run_once()
+
+ def handler(loop, context):
+ raise AttributeError('spam')
+
+ self.loop._process_events = unittest.mock.Mock()
+
+ self.loop.set_exception_handler(handler)
+
+ with unittest.mock.patch('asyncio.base_events.logger') as log:
+ run_loop()
+ log.error.assert_called_with(
+ test_utils.MockPattern(
+ 'Unhandled error in exception handler'),
+ exc_info=(AttributeError, MOCK_ANY, MOCK_ANY))
+
+ def test_default_exc_handler_broken(self):
+ _context = None
+
+ class Loop(base_events.BaseEventLoop):
+
+ _selector = unittest.mock.Mock()
+ _process_events = unittest.mock.Mock()
+
+ def default_exception_handler(self, context):
+ nonlocal _context
+ _context = context
+ # Simulates custom buggy "default_exception_handler"
+ raise ValueError('spam')
+
+ loop = Loop()
+ asyncio.set_event_loop(loop)
+
+ def run_loop():
+ def zero_error():
+ 1/0
+ loop.call_soon(zero_error)
+ loop._run_once()
+
+ with unittest.mock.patch('asyncio.base_events.logger') as log:
+ run_loop()
+ log.error.assert_called_with(
+ 'Exception in default exception handler',
+ exc_info=True)
+
+ def custom_handler(loop, context):
+ raise ValueError('ham')
+
+ _context = None
+ loop.set_exception_handler(custom_handler)
+ with unittest.mock.patch('asyncio.base_events.logger') as log:
+ run_loop()
+ log.error.assert_called_with(
+ test_utils.MockPattern('Exception in default exception.*'
+ 'while handling.*in custom'),
+ exc_info=True)
+
+ # Check that original context was passed to default
+ # exception handler.
+ self.assertIn('context', _context)
+ self.assertIs(type(_context['context']['exception']),
+ ZeroDivisionError)
+
class MyProto(asyncio.Protocol):
done = None
@@ -716,7 +882,7 @@ class BaseEventLoopWithSelectorTests(unittest.TestCase):
self.loop._accept_connection(MyProto, sock)
self.assertFalse(sock.close.called)
- @unittest.mock.patch('asyncio.selector_events.logger')
+ @unittest.mock.patch('asyncio.base_events.logger')
def test_accept_connection_exception(self, m_log):
sock = unittest.mock.Mock()
sock.fileno.return_value = 10
@@ -725,7 +891,7 @@ class BaseEventLoopWithSelectorTests(unittest.TestCase):
self.loop.call_later = unittest.mock.Mock()
self.loop._accept_connection(MyProto, sock)
- self.assertTrue(m_log.exception.called)
+ self.assertTrue(m_log.error.called)
self.assertFalse(sock.close.called)
self.loop.remove_reader.assert_called_with(10)
self.loop.call_later.assert_called_with(constants.ACCEPT_RETRY_DELAY,