Не удается передать ожидаемое asyncio.run_coroutine_threadsafe - PullRequest
0 голосов
/ 02 сентября 2018

Я заметил, что функция asyncio.run_coroutine_threadsafe не принимает общие ожидаемые объекты, и я не понимаю причину этого ограничения. Наблюдайте

import asyncio


async def native_coro():
    return


@asyncio.coroutine
def generator_based_coro():
    return


class Awaitable:
    def __await__(self):
        return asyncio.Future()


loop = asyncio.get_event_loop()

asyncio.run_coroutine_threadsafe(native_coro(), loop)
asyncio.run_coroutine_threadsafe(generator_based_coro(), loop)
asyncio.run_coroutine_threadsafe(Awaitable(), loop)

Запуск этого с Python 3.6.6 дает

Traceback (most recent call last):
  File "awaitable.py", line 24, in <module>
    asyncio.run_coroutine_threadsafe(Awaitable(), loop)
  File "~/.local/python3.6/lib/python3.6/asyncio/tasks.py", line 714, in run_coroutine_threadsafe
    raise TypeError('A coroutine object is required')
TypeError: A coroutine object is required

, где строка 24 равна asyncio.run_coroutine_threadsafe(Awaitable(), loop).

Я знаю, что могу обернуть свой ожидаемый объект в сопрограмму, определенную как

awaitable = Awaitable()

async def wrapper():
    return await awaitable

asyncio.run_coroutine_threadsafe(wrapper(), loop)

однако я ожидал, что ожидаемое будет действительным аргументом непосредственно для run_coroutine_threadsafe.

Мои вопросы:

  1. В чем причина этого ограничения?
  2. Является ли функция wrapper, определенная выше, наиболее обычным способом передачи ожидаемого в run_coroutine_threadsafe и другие API, для которых требуется async def или определяемая генератором сопрограмма?

1 Ответ

0 голосов
/ 02 сентября 2018

В чем причина этого ограничения?

Глядя на реализацию , причина, конечно, не техническая. Поскольку код уже вызывает ensure_future (а не, скажем, create_task), он будет автоматически работать и работать правильно на любом ожидаемом объекте.

Причину ограничения можно найти на трекере. Функция была добавлена ​​в 2015 году в результате запроса pull . В обсуждении по теме bpo податель явно просит переименовать функцию в ensure_future_threadsafe (параллельно ensure_future) и принять любую приемлемую позицию, поддержанную Юрием Селивановым. Тем не менее, Гвидо был против идея:

Я против этой идеи. В любом случае я не вижу большого важного будущего для этого метода: это всего лишь немного клея между поточным миром и асинхронным миром, и люди узнают, как его использовать, найдя пример.

[...]

Но, честно говоря, я не хочу поощрять переключаться между потоками и циклами событий; Я вижу это как необходимое зло. Имя, которое у нас сейчас есть, отлично от POV того, кто кодирует в многопоточном мире и хочет передать что-то миру асинцио.

Зачем кому-то в многопоточном мире иметь asyncio.future, которого ему нужно ждать? Похоже, что они смешивают два мира - или они должны писать асинхронный код вместо многопоточного кода.

Есть другие комментарии в том же духе, но вышеприведенное в значительной степени суммирует аргумент.

Является ли функция wrapper, определенная выше, наиболее обычным способом передачи ожидаемого в run_coroutine_threadsafe и другие API, которые требуют асинхронного определения или определяемой генератором сопрограммы?

Если вам действительно нужен объект сопрограммы, то что-то вроде wrapper, безусловно, является простым и правильным способом получить его.

Если единственная причина, по которой вы создаете оболочку, - это вызвать run_coroutine_threadsafe, но вы на самом деле не интересуетесь результатом или concurrent.futures.Future, возвращаемым run_coroutine_threadsafe, вы можете избежать упаковки, вызвав call_soon_threadsafe напрямую:

loop.call_soon_threadsafe(asyncio.ensure_future, awaitable)
...