diff --git a/binder/environment.yml b/binder/environment.yml index e3bc3809..64f77d90 100644 --- a/binder/environment.yml +++ b/binder/environment.yml @@ -5,6 +5,6 @@ dependencies: - numpy - pandas - matplotlib - - nteract-scrapbook + - scrapbook - nbformat - nbclient diff --git a/nbclient/tests/test_util.py b/nbclient/tests/test_util.py index dc206826..e40c639c 100644 --- a/nbclient/tests/test_util.py +++ b/nbclient/tests/test_util.py @@ -20,31 +20,23 @@ async def _test(): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) - try: - event_loop = loop.run_until_complete(_test()) - assert event_loop is loop - finally: - asyncio._set_running_loop(None) # it seems nest_asyncio doesn't reset this + event_loop = loop.run_until_complete(_test()) + assert event_loop is loop def test_nested_asyncio_with_no_ioloop(): asyncio.set_event_loop(None) - try: - assert some_async_function() == 42 - finally: - asyncio._set_running_loop(None) # it seems nest_asyncio doesn't reset this + assert some_async_function() == 42 def test_nested_asyncio_with_tornado(): # This tests if tornado accepts the pure-Python Futures, see # https://github.com/tornadoweb/tornado/issues/2753 - # https://github.com/erdewit/nest_asyncio/issues/23 asyncio.set_event_loop(asyncio.new_event_loop()) ioloop = tornado.ioloop.IOLoop.current() async def some_async_function(): future: asyncio.Future = asyncio.ensure_future(asyncio.sleep(0.1)) - # this future is a different future after nested-asyncio has patched # the asyncio module, check if tornado likes it: ioloop.add_future(future, lambda f: f.result()) # type:ignore await future @@ -56,8 +48,6 @@ def some_sync_function(): async def run(): # calling some_async_function directly should work assert await some_async_function() == 42 - # but via a sync function (using nested-asyncio) can lead to issues: - # https://github.com/tornadoweb/tornado/issues/2753 assert some_sync_function() == 42 ioloop.run_sync(run) diff --git a/nbclient/util.py b/nbclient/util.py index 4d6b689d..480e40eb 100644 --- a/nbclient/util.py +++ b/nbclient/util.py @@ -3,106 +3,10 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. -import asyncio import inspect -import sys -from typing import Any, Awaitable, Callable, Optional, TypeVar, Union +from typing import Any, Callable, Optional - -def check_ipython() -> None: - # original from vaex/asyncio.py - IPython = sys.modules.get('IPython') - if IPython: - version_str = IPython.__version__ - # We get rid of any trailing ".dev" - version_str = version_str.replace(".dev", "") - - IPython_version = tuple(map(int, version_str.split('.'))) - if IPython_version < (7, 0, 0): - raise RuntimeError( - f'You are using IPython {IPython.__version__} ' - 'while we require 7.0.0+, please update IPython' - ) - - -def check_patch_tornado() -> None: - """If tornado is imported, add the patched asyncio.Future to its tuple of acceptable Futures""" - # original from vaex/asyncio.py - if 'tornado' in sys.modules: - import tornado.concurrent - - if asyncio.Future not in tornado.concurrent.FUTURES: - tornado.concurrent.FUTURES = tornado.concurrent.FUTURES + ( - asyncio.Future, - ) # type:ignore[assignment] - - -def just_run(coro: Awaitable) -> Any: - """Make the coroutine run, even if there is an event loop running (using nest_asyncio)""" - try: - loop = asyncio.get_running_loop() - except RuntimeError: - loop = None - if loop is None: - had_running_loop = False - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - else: - had_running_loop = True - if had_running_loop: - # if there is a running loop, we patch using nest_asyncio - # to have reentrant event loops - check_ipython() - import nest_asyncio - - nest_asyncio.apply() - check_patch_tornado() - return loop.run_until_complete(coro) - - -T = TypeVar("T") - - -def run_sync(coro: Callable[..., Awaitable[T]]) -> Callable[..., T]: - """Runs a coroutine and blocks until it has executed. - - An event loop is created if no one already exists. If an event loop is - already running, this event loop execution is nested into the already - running one if `nest_asyncio` is set to True. - - Parameters - ---------- - coro : coroutine - The coroutine to be executed. - - Returns - ------- - result : - Whatever the coroutine returns. - """ - - def wrapped(*args, **kwargs): - return just_run(coro(*args, **kwargs)) - - wrapped.__doc__ = coro.__doc__ - return wrapped - - -async def ensure_async(obj: Union[Awaitable, Any]) -> Any: - """Convert a non-awaitable object to a coroutine if needed, - and await it if it was not already awaited. - """ - if inspect.isawaitable(obj): - try: - result = await obj - except RuntimeError as e: - if str(e) == 'cannot reuse already awaited coroutine': - # obj is already the coroutine's result - return obj - raise - return result - # obj doesn't need to be awaited - return obj +from jupyter_core.utils import ensure_async, run_sync # noqa: F401 async def run_hook(hook: Optional[Callable], **kwargs: Any) -> None: diff --git a/pyproject.toml b/pyproject.toml index 62afba3d..7416b1f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,6 @@ classifiers = [ dependencies = [ "jupyter_client>=6.1.12", "nbformat>=5.1", - "nest_asyncio", "traitlets>=5.3", ] @@ -153,7 +152,6 @@ warn_unused_ignores = true [[tool.mypy.overrides]] module = [ "async_generator.*", - "nest_asyncio.*", "testpath", "xmltodict", ]