2

I define aiohttp.Applcation and unittest for the Application like the below.

# main.py

import asyncio
from aiohttp import web

async def background_task():
    await asyncio.sleep(5)

async def start_up_test(app):
    app.loop.create_task(background_task())

def get_application(bg_tasks=[]):
    app = web.Application()
    app.on_startup.append(start_up_test)
    return app

# main_test.py

import main
from aiohttp import test_utils

class TestMain(test_utils.AioHTTPTestCase):

    async def get_application(self):
        return main.get_application()

    @test_utils.unittest_run_loop
    async def test_main(self):
        pass

$ python -m unittest main_test
Task exception was never retrieved
future: <Task finished coro=<background_task() done, defined at /home/sangmin/workspace/main.py:78> exception=RuntimeError("There is no current event loop in thread 'MainThread'.",)>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
    result = coro.send(None)
  File "/home/sangmin/workspace/main.py", line 79, in background_task
    await asyncio.sleep(5)
  File "/usr/lib/python3.5/asyncio/tasks.py", line 510, in sleep
    loop = events.get_event_loop()
  File "/usr/lib/python3.5/asyncio/events.py", line 632, in get_event_loop
    return get_event_loop_policy().get_event_loop()
  File "/usr/lib/python3.5/asyncio/events.py", line 578, in get_event_loop
    % threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'MainThread'.
.
----------------------------------------------------------------------
Ran 1 test in 0.021s

OK

The TestCase raises an exception saying There is no current event loop in thread 'MainThread'.

Then I found aiohttp DOC mentioning about main loop while unit-testing.

Some libraries like motor, aioes and others depend on the asyncio loop for executing the code. When running your normal program, these libraries pick the main event loop by doing asyncio.get_event_loop.

The problem during testing is that there is no main loop assigned because an independent loop for each test is created without assigning it as the main one.

According to the DOC, the cause of the exception is because asyncio.get_event_loop is called while no main loop is assigned on unittest and I can see await asyncio.sleep(1) access main loop by calling get_event_loop in above Trace.

Then, I am so confused since I think Application should be running over test loop served by AioHTTPTestCase and all of the coroutines in the Application also should use the same loop not main loop.

What do I have missed and how do I fix it?

-----Edit-----

Tracing aiohttp source code, I could make the issue simplify.

import asyncio

async def test():
    await asyncio.sleep(1)

if __name__ == '__main__':
    # setUp method of AioHTTPTestCase clear main loop and return new loop. 
    asyncio.set_event_loop(None)
    loop = asyncio.new_event_loop()
    
    # unittest_run_loop decorator run coroutine over the new loop.
    loop.run_until_complete(test())
    
    # This simple code also raises the same exception.

This behavior is normal for asyncio.

Community
  • 1
  • 1
SangminKim
  • 8,358
  • 14
  • 69
  • 125
  • If there's no default event loop you need to pass the loop to sleep eg, `await asyncio.sleep(1, loop=sloop)` – SColvin Feb 05 '18 at 16:28

0 Answers0