diff -r b2889ab48d11 Lib/asyncio/base_events.py --- a/Lib/asyncio/base_events.py Fri Dec 05 02:43:29 2014 -0500 +++ b/Lib/asyncio/base_events.py Fri Dec 05 10:10:09 2014 +0100 @@ -420,8 +420,8 @@ class BaseEventLoop(events.AbstractEvent """ try: current = events.get_event_loop() - except AssertionError: - return + except RuntimeError: + current = None if current is not self: raise RuntimeError( "Non-thread-safe operation invoked on an event loop other " diff -r b2889ab48d11 Lib/asyncio/events.py --- a/Lib/asyncio/events.py Fri Dec 05 02:43:29 2014 -0500 +++ b/Lib/asyncio/events.py Fri Dec 05 10:10:09 2014 +0100 @@ -517,9 +517,9 @@ class BaseDefaultEventLoopPolicy(Abstrac not self._local._set_called and isinstance(threading.current_thread(), threading._MainThread)): self.set_event_loop(self.new_event_loop()) - assert self._local._loop is not None, \ - ('There is no current event loop in thread %r.' % - threading.current_thread().name) + if self._local._loop is None: + raise RuntimeError('There is no current event loop in thread %r.' + % threading.current_thread().name) return self._local._loop def set_event_loop(self, loop): diff -r b2889ab48d11 Lib/asyncio/test_utils.py --- a/Lib/asyncio/test_utils.py Fri Dec 05 02:43:29 2014 -0500 +++ b/Lib/asyncio/test_utils.py Fri Dec 05 10:10:09 2014 +0100 @@ -402,8 +402,15 @@ def get_function_source(func): class TestCase(unittest.TestCase): def set_event_loop(self, loop, *, cleanup=True): assert loop is not None + + # Monkey-patch the _assert_is_current_event_loop() method to not check + # if the called event loop is the event loop of the policy, even in + # debug mode. + loop._assert_is_current_event_loop = lambda: None + # ensure that the event loop is passed explicitly in asyncio events.set_event_loop(None) + if cleanup: self.addCleanup(loop.close) diff -r b2889ab48d11 Lib/test/test_asyncio/test_base_events.py --- a/Lib/test/test_asyncio/test_base_events.py Fri Dec 05 02:43:29 2014 -0500 +++ b/Lib/test/test_asyncio/test_base_events.py Fri Dec 05 10:10:09 2014 +0100 @@ -5,6 +5,7 @@ import logging import math import socket import sys +import threading import time import unittest from unittest import mock @@ -144,28 +145,93 @@ class BaseEventLoopTests(test_utils.Test # are really slow self.assertLessEqual(dt, 0.9, dt) - def test_assert_is_current_event_loop(self): + def check_is_current_event_loop(self, loop, debug): def cb(): pass + loop.set_debug(debug) + if debug: + msg = ("Non-thread-safe operation invoked on an event loop other " + "than the current one") + with self.assertRaisesRegex(RuntimeError, msg): + loop.call_soon(cb) + with self.assertRaisesRegex(RuntimeError, msg): + loop.call_later(60, cb) + with self.assertRaisesRegex(RuntimeError, msg): + loop.call_at(loop.time() + 60, cb) + else: + loop.call_soon(cb) + loop.call_later(60, cb) + loop.call_at(loop.time() + 60, cb) + + def test_assert_is_current_event_loop(self): + # TestCase.set_event_loop() monkey patches the + # _assert_is_current_event_loop() method, so create a fresh event loop + self.loop.close() + self.loop = base_events.BaseEventLoop() + self.addCleanup(self.loop.close) + + # create a second event loop other_loop = base_events.BaseEventLoop() other_loop._selector = mock.Mock() asyncio.set_event_loop(other_loop) # raise RuntimeError if the event loop is different in debug mode - self.loop.set_debug(True) - with self.assertRaises(RuntimeError): - self.loop.call_soon(cb) - with self.assertRaises(RuntimeError): - self.loop.call_later(60, cb) - with self.assertRaises(RuntimeError): - self.loop.call_at(self.loop.time() + 60, cb) + self.check_is_current_event_loop(self.loop, True) # check disabled if debug mode is disabled - self.loop.set_debug(False) - self.loop.call_soon(cb) - self.loop.call_later(60, cb) - self.loop.call_at(self.loop.time() + 60, cb) + self.check_is_current_event_loop(self.loop, False) + + def test_assert_is_current_event_loop_thread(self): + # TestCase.set_event_loop() monkey patches the + # _assert_is_current_event_loop() method, so create a fresh event loop + self.loop.close() + self.loop = base_events.BaseEventLoop() + self.addCleanup(self.loop.close) + asyncio.set_event_loop(self.loop) + + def check_in_thread(loop, debug, create_loop, result): + result['result'] = 'not set' + try: + if create_loop: + loop2 = base_events.BaseEventLoop() + try: + asyncio.set_event_loop(loop2) + self.check_is_current_event_loop(loop, debug) + finally: + asyncio.set_event_loop(None) + loop2.close() + else: + self.check_is_current_event_loop(loop, debug) + except Exception as exc: + result['result'] = exc + else: + result['result'] = 'ok' + + def test_thread(loop, debug, create_loop=False): + ns = {} + thread = threading.Thread(target=check_in_thread, + args=(loop, debug, create_loop, ns)) + thread.start() + thread.join() + + result = ns['result'] + if isinstance(result, Exception): + raise result + self.assertEqual(result, 'ok') + + # raise RuntimeError if the thread has no event loop + test_thread(self.loop, True) + + # check disabled if debug mode is disabled + test_thread(self.loop, False) + + # raise RuntimeError if the event loop of the thread is not the called + # event loop + test_thread(self.loop, True, create_loop=True) + + # check disabled if debug mode is disabled + test_thread(self.loop, False, create_loop=True) def test_run_once_in_executor_handle(self): def cb(): diff -r b2889ab48d11 Lib/test/test_asyncio/test_events.py --- a/Lib/test/test_asyncio/test_events.py Fri Dec 05 02:43:29 2014 -0500 +++ b/Lib/test/test_asyncio/test_events.py Fri Dec 05 10:10:09 2014 +0100 @@ -2252,14 +2252,14 @@ class PolicyTests(unittest.TestCase): def test_get_event_loop_after_set_none(self): policy = asyncio.DefaultEventLoopPolicy() policy.set_event_loop(None) - self.assertRaises(AssertionError, policy.get_event_loop) + self.assertRaises(RuntimeError, policy.get_event_loop) @mock.patch('asyncio.events.threading.current_thread') def test_get_event_loop_thread(self, m_current_thread): def f(): policy = asyncio.DefaultEventLoopPolicy() - self.assertRaises(AssertionError, policy.get_event_loop) + self.assertRaises(RuntimeError, policy.get_event_loop) th = threading.Thread(target=f) th.start() diff -r b2889ab48d11 Lib/test/test_asyncio/test_subprocess.py --- a/Lib/test/test_asyncio/test_subprocess.py Fri Dec 05 02:43:29 2014 -0500 +++ b/Lib/test/test_asyncio/test_subprocess.py Fri Dec 05 10:10:09 2014 +0100 @@ -208,19 +208,12 @@ if sys.platform != 'win32': def setUp(self): policy = asyncio.get_event_loop_policy() self.loop = policy.new_event_loop() - - # ensure that the event loop is passed explicitly in asyncio - policy.set_event_loop(None) + self.set_event_loop(self.loop) watcher = self.Watcher() watcher.attach_loop(self.loop) policy.set_child_watcher(watcher) - - def tearDown(self): - policy = asyncio.get_event_loop_policy() - policy.set_child_watcher(None) - self.loop.close() - super().tearDown() + self.addCleanup(policy.set_child_watcher, None) class SubprocessSafeWatcherTests(SubprocessWatcherMixin, test_utils.TestCase): @@ -237,17 +230,8 @@ else: class SubprocessProactorTests(SubprocessMixin, test_utils.TestCase): def setUp(self): - policy = asyncio.get_event_loop_policy() self.loop = asyncio.ProactorEventLoop() - - # ensure that the event loop is passed explicitly in asyncio - policy.set_event_loop(None) - - def tearDown(self): - policy = asyncio.get_event_loop_policy() - self.loop.close() - policy.set_event_loop(None) - super().tearDown() + self.set_event_loop(self.loop) if __name__ == '__main__':