async def io_loop(request):
    """Mostly obsolete fixture for tornado event loop

    Main purpose is to register cleanup (close) after we're done with the loop.
    The main reason to depend on this fixture is to ensure your cleanup
    happens before the io_loop is closed.
    """
    io_loop = AsyncIOMainLoop()
    event_loop = asyncio.get_running_loop()
    assert asyncio.get_event_loop() is event_loop
    assert io_loop.asyncio_loop is event_loop

    def _close():
        # cleanup everything
        try:
            event_loop.run_until_complete(event_loop.shutdown_asyncgens())
        except (asyncio.CancelledError, RuntimeError):
            pass
        io_loop.close(all_fds=True)

        # workaround pytest-asyncio trying to cleanup after loop is closed
        # problem introduced in pytest-asyncio 0.25.2
        def noop(*args, **kwargs):
            warn("Loop used after close...", RuntimeWarning, stacklevel=2)
            return

        event_loop.run_until_complete = noop

    request.addfinalizer(_close)
    return io_loop
Mostly obsolete fixture for tornado event loop

Main purpose is to register cleanup (close) after we're done with the loop.
The main reason to depend on this fixture is to ensure your cleanup
happens before the io_loop is closed.
jupyterhub/tests/conftest.py