<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 asyncio IO Loop
Create an event loop (which automatically becomes the default event loop in the context).
Step1:  Run a simple callback as soon as possible
Step2:  Coroutine Examples
Coroutines can be directly scheduled in the eventloop.
Step3:  You can use as many awaits as you like in a couroutine
Step4:  All normal control structures can be used
Step5:  Exceptions work just like you would expect
Step6:  Multiple Coroutines can be combined and executed concurrently
Step7:  Note that this only took one second, not two!
Automatic Checks
Step8:  Note that nothing happens as long as the coroutine is not awaited.
Even the synchronous print is not executed.
Step9:  Not awaiting a coroutine raises an error.
Awaiting a coroutine "later" is ok though.
Step10:  Awaiting something that is not awaitable raises an error.
Step11:  Async for-loop
Prepare a simple MongoDB collection to show this feature.
Step12:  The async for-loop saves us the boilerplate code to await each next value. Note that it runs sequentially (i.e., the elements are fetched after each other).
Step13:  Async Context Manager
Step14:  One example is using locks (even though this doesn't require async exiting).
<ASSISTANT_TASK:>
Python Code:
import asyncio
loop = asyncio.get_event_loop()
Explanation: asyncio IO Loop
Create an event loop (which automatically becomes the default event loop in the context).
End of explanation
def hello_world():
    print('Hello World!')
    loop.stop()
loop.call_soon(hello_world)
loop.run_forever()
Explanation: Run a simple callback as soon as possible:
End of explanation
async def aprint(text):
    await asyncio.sleep(1)
    print(text)
    return 42
loop.run_until_complete(aprint('Hello world!'))
Explanation: Coroutine Examples
Coroutines can be directly scheduled in the eventloop.
End of explanation
async def aprint_twice(text):
    await asyncio.sleep(1)
    print(text)    
    await asyncio.sleep(1)
    print(text + ' (once more)')
    return 42
loop.run_until_complete(aprint_twice('Hello world!'))
Explanation: You can use as many awaits as you like in a couroutine:
End of explanation
async def aprint_twice():
    for i in range(1, 7):
        await asyncio.sleep(0.5)
        if i % 2:
            print('even')
        else:
            print('uneven, waiting some more...')
            await asyncio.sleep(1)
loop.run_until_complete(aprint_twice())
Explanation: All normal control structures can be used:
End of explanation
async def raiser():
    await asyncio.sleep(1)
    raise ValueError()
    
async def catcher():
    try:
        await raiser()
    except ValueError:
        print('caught something')
        
loop.run_until_complete(catcher())
Explanation: Exceptions work just like you would expect
End of explanation
tasks = asyncio.gather(aprint('Task 1'), aprint('Task 2'))
loop.run_until_complete(tasks)
Explanation: Multiple Coroutines can be combined and executed concurrently:
End of explanation
async def remember_me():
    print('I started.')
    await aprint('Did I forget something?')
a = remember_me()
Explanation: Note that this only took one second, not two!
Automatic Checks
End of explanation
a = 42
Explanation: Note that nothing happens as long as the coroutine is not awaited.
Even the synchronous print is not executed.
End of explanation
a = aprint('Did I forget something?')
loop.run_until_complete(a)
del(a)
Explanation: Not awaiting a coroutine raises an error.
Awaiting a coroutine "later" is ok though.
End of explanation
async def fail():
    await aprint
    
loop.run_until_complete(fail())
Explanation: Awaiting something that is not awaitable raises an error.
End of explanation
from motor.motor_asyncio import AsyncIOMotorClient
collection = AsyncIOMotorClient().aiotest.test
loop.run_until_complete(collection.insert({'value': i} for i in range(10)))
Explanation: Async for-loop
Prepare a simple MongoDB collection to show this feature.
End of explanation
async def f():
    async for doc in collection.find():
        print(doc)
        
loop.run_until_complete(f())
loop.run_until_complete(collection.drop())
Explanation: The async for-loop saves us the boilerplate code to await each next value. Note that it runs sequentially (i.e., the elements are fetched after each other).
End of explanation
class AsyncContextManager:
    async def __aenter__(self):
        await aprint('entering context')
    async def __aexit__(self, exc_type, exc, tb):
        await aprint('exiting context')
async def use_async_context():
    async with AsyncContextManager():
        print('Hello World!')
        
loop.run_until_complete(use_async_context())
Explanation: Async Context Manager
End of explanation
lock = asyncio.Lock()
async def use_lock():
    async with lock:
        await asyncio.sleep(1)
        print('one after the other...')
tasks = asyncio.gather(use_lock(), use_lock())
loop.run_until_complete(tasks)
Explanation: One example is using locks (even though this doesn't require async exiting).
End of explanation
<END_TASK>