Как запустить код асинхронного кода Python в ноутбуке Jupyter?

У меня есть код asyncio, который отлично работает в интерпретаторе Python (CPython 3.6.2). Теперь я хотел бы запустить это в ноутбуке Jupyter с ядром IPython.

Я могу запустить его с помощью

import asyncio
asyncio.get_event_loop().run_forever()

и в то время как это, кажется, работает, это также, кажется, блокирует ноутбук и, кажется, не играет хорошо с записной книжкой.

Я понимаю, что Jupyter использует Tornado под капотом, поэтому я попытался установить цикл событий Tornado, как рекомендовано в документах Tornado:

from tornado.platform.asyncio import AsyncIOMainLoop
AsyncIOMainLoop().install()

Однако это приводит к следующей ошибке:

---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-1-1139449343fc> in <module>()
      1 from tornado.platform.asyncio import AsyncIOMainLoop
----> 2 AsyncIOMainLoop().install()

~\AppData\Local\Continuum\Anaconda3\envs\numismatic\lib\site- packages\tornado\ioloop.py in install(self)
    179         'IOLoop' (e.g.,     :class:'tornado.httpclient.AsyncHTTPClient').
    180         """
--> 181         assert not IOLoop.initialized()
    182         IOLoop._instance = self
    183 

AssertionError: 

Наконец, я нашел следующую страницу: http://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Asynchronous.html

поэтому я добавил ячейку со следующим кодом:

import asyncio
from ipykernel.eventloops import register_integration

@register_integration('asyncio')
def loop_asyncio(kernel):
    '''Start a kernel with asyncio event loop support.'''
    loop = asyncio.get_event_loop()

    def kernel_handler():
        loop.call_soon(kernel.do_one_iteration)
        loop.call_later(kernel._poll_interval, kernel_handler)

    loop.call_soon(kernel_handler)
    try:
        if not loop.is_running():
            loop.run_forever()
    finally:
        loop.run_until_complete(loop.shutdown_asyncgens())
        loop.close()

и в следующей ячейке я побежал:

%gui asyncio

Это сработало, но я не понимаю, почему и как это работает. Может ли кто-нибудь объяснить это мне?

Ответ 1

РЕДАКТИРОВАТЬ 21 ФЕВРАЛЯ 2019 г.: проблема исправлена

Это больше не проблема последней версии Jupyter Notebook. Авторы Jupyter Notebook подробно описали этот случай здесь.

Ответ ниже был оригинальный ответ, который был помечен как правильный.


Это было опубликовано довольно давно, но в случае, если другие люди ищут объяснения и решения проблемы запуска асинхронного кода внутри Jupyter Notebook;

Обновление Jupyter Tornado 5.0 стало бесполезным после добавления собственного цикла событий asyncio:

Terminal output of <code>get_event_loop()</code> Jupyter Notebook output of <code>get_event_loop()</code>

Таким образом, для любой функциональности asyncio, работающей в Jupyter Notebook, вы не можете вызывать run_until_complete(), так как цикл, который вы получите от asyncio.get_event_loop() будет активным.

Вместо этого вы должны добавить задачу в текущий цикл:

import asyncio
loop = asyncio.get_event_loop()
loop.create_task(some_async_function())

Простой пример работы на ноутбуке Jupyter:

enter image description here

Ответ 2

Это больше не проблема в последней версии Jupyter!

https://blog.jupyter.org/ipython-7-0-async-repl-a35ce050f7f7

Просто напишите асинхронную функцию, а затем ждите ее непосредственно в ячейке юпитера.

async def fn():
  print('hello')
  await asyncio.sleep(1)
  print('world')

await fn()

Ответ 3

Недавно я столкнулся с проблемой неспособности запускать асинхронный код в ноутбуке Jupyter. Вопрос обсуждается здесь: https://github.com/jupyter/notebook/issues/3397

Я пробовал одно из решений в обсуждении и решил проблему до сих пор.

pip3 install tornado==4.5.3

Это заменило версию 3.5x торнадо, которая была установлена по умолчанию.

После этого асинхронный код в ноутбуке Jupyter работал так, как ожидалось.

Ответ 4

Мой момент ага с Asyncio в Jupyter выглядит так:

import time,asyncio

async def count():
    print("count one")
    await asyncio.sleep(1)
    print("count four")

async def count_further():
    print("count two")
    await asyncio.sleep(1)
    print("count five")

async def count_even_further():
    print("count three")
    await asyncio.sleep(1)
    print("count six")

async def main():
    await asyncio.gather(count(), count_further(), count_even_further())

s = time.perf_counter()
await main()
elapsed = time.perf_counter() - s
print(f"Script executed in {elapsed:0.2f} seconds.")

Выход:

count one
count two
count three
count four
count five
count six
Script executed in 1.00 seconds.

Первоначально отсюда, но пример не был понятен для меня сначала: https://realpython.com/async-io-python/