2016-11-28 4 views
2

Я пытаюсь научить себя асинхронной функциональности Python. Для этого я создал асинхронный веб-скребок. Я хотел бы ограничить общее количество подключений, которые я открыл, чтобы быть хорошим гражданином на серверах. Я знаю, что семафор - это хорошее решение, а библиотека асинхронно имеет встроенный класс semaphore. Моя проблема заключается в том, что Python жалуется при использовании yield from в функции async, когда вы комбинируете и await синтаксис. Ниже приведен точный синтаксис я использую ...Python asyncio.semaphore в async-await function

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 

Возводя это исключение:

File "<ipython-input-3-9b9bdb963407>", line 14 
    with (yield from sema): 
     ^
SyntaxError: 'yield from' inside async function 

Некоторые из возможных решений я могу думать ...

  1. Просто используйте @asyncio.coroutine декоратора
  2. Использовать threading.Semaphore? Это похоже на то, что это может вызвать другие проблемы.
  3. Попробуйте это в бета-версии Python 3.6 для причины this.

Я очень новичок в асинхронной функциональности Python, поэтому мне не хватало чего-то очевидного.

ответ

2

ОК, так что это действительно глупо, но я просто заменяю yield fromawait в менеджере контекста семафора, и он отлично работает.

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 
+4

Еще лучше, вы можете использовать инструкцию 'async with':' async with sema: [...] ' – Vincent

+0

Async также интересен для меня, я нашел вашу тему интересной и проголосовал за ее оставить здесь. Кстати, не могли бы вы поделиться всем кодом для ex на github? –

+0

Да, я сделаю это и опубликую ссылку в комментариях чуть позже. –

6

Вы можете использовать async with заявление, чтобы получить асинхронный менеджер контекста:

#!/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")) 

Пример взят из here. Страница также является хорошей грунтовкой для asyncio и aiohttp в целом.

+0

Я нахожу пример очень интересным, однако что-то меня удивляет. Это строка: async with sema, session.get (url) в качестве ответа: Почему вы используете sema, session ... в том же менеджере контекста? Что делает эта линия? Не могли бы вы немного объяснить? – Liviu

+0

Ничего особенного в этом нет, я думаю, что этот ответ поможет: https://stackoverflow.com/questions/3024925/python-create-a-with-block-on-several-context-managers –