Python asyncio.semaphore en la función async-await

Estoy tratando de enseñarme a mí mismo la funcionalidad asíncrona de Python. Para ello he construido un raspador web asíncrono. Me gustaría limitar el número total de conexiones que tengo abiertas para ser un buen ciudadano en los servidores. Sé que los semáforos son una buena solución, y la biblioteca de asyncio tiene una clase de semáforos integrada. Mi problema es que Python se queja cuando usa el yield from en una función async cuando está combinando el yield y la syntax. A continuación se muestra la syntax exacta que estoy usando …

 import asyncio import aiohttp sema = asyncio.BoundedSemaphore(5) async def get_page_text(url): with (yield from sema): try: resp = await aiohttp.request('GET', url) if resp.status == 200: ret_val = await resp.text() except: raise ValueError finally: await resp.release() return ret_val 

Elevando esta excepción:

 File "", line 14 with (yield from sema): ^ SyntaxError: 'yield from' inside async function 

Alguna posible solución que se me ocurra …

  1. Solo usa el decorador @asyncio.coroutine
  2. Utilice enhebrado. Esto parece que puede causar otros problemas.
  3. Prueba esto en la versión beta de Python 3.6 por este motivo.

Soy muy nuevo en la funcionalidad asíncrona de Python, por lo que podría faltar algo obvio.

Puede usar async with sentencia para obtener un administrador de contexto asíncrono:

 #!/usr/local/bin/python3.5 import asyncio from aiohttp import ClientSession sema = asyncio.BoundedSemaphore(5) async def hello(url): async with ClientSession() as session: async with sema, session.get(url) as response: response = await response.read() print(response) loop = asyncio.get_event_loop() loop.run_until_complete(hello("http://httpbin.org/headers")) 

Ejemplo tomado de aquí . La página también es un buen manual para asyncio y aiohttp en general.

Bien, esto es realmente tonto, pero simplemente sustituyo el yield from con await en el administrador de contexto del semáforo y funciona perfectamente.

 sema = asyncio.BoundedSemaphore(5) async def get_page_text(url): with (await sema): try: resp = await aiohttp.request('GET', url) if resp.status == 200: ret_val = await resp.text() except: raise ValueError finally: await resp.release() return ret_val