diff --git a/python/_restclient/hydrotools/_restclient/_restclient.py b/python/_restclient/hydrotools/_restclient/_restclient.py index 696e4e68..e9d7936e 100644 --- a/python/_restclient/hydrotools/_restclient/_restclient.py +++ b/python/_restclient/hydrotools/_restclient/_restclient.py @@ -288,6 +288,11 @@ def headers(self) -> dict: def close(self) -> None: """ Release aiohttp.ClientSession """ + # Session never instantiated, thus cannot be closed + session = getattr(self, "_session", None) + if session is None: + return + if not self._session.closed: if not self._loop.is_closed(): self._add_to_loop(self._session.close()) diff --git a/python/_restclient/hydrotools/_restclient/async_helpers.py b/python/_restclient/hydrotools/_restclient/async_helpers.py index 288c6e45..91f634b9 100644 --- a/python/_restclient/hydrotools/_restclient/async_helpers.py +++ b/python/_restclient/hydrotools/_restclient/async_helpers.py @@ -19,7 +19,24 @@ def __init__( def _add_to_loop(self, coro: Coroutine): """ Add coro to event loop via run_until_complete """ - return self._loop.run_until_complete(coro) + try: + return self._loop.run_until_complete(coro) + + except RuntimeError as e: + try: + # `RuntimeError: This event loop is already running` thrown by jupyter notebook + # See hydrotools #99 for context and notebook #3397 for detail + import nest_asyncio + + nest_asyncio.apply() + + return self._loop.run_until_complete(coro) + except ModuleNotFoundError: + error_message = ( + "nest_asycnio package not found. Install using `pip install nest_asycnio`.\n" + "See https://github.com/NOAA-OWP/hydrotools/issues/99 for more detail." + ) + raise ModuleNotFoundError(error_message) from e def _wrap_func_in_coro(self, func: Callable, *args, **kwargs): """ Create partial func; wrap and call partial in coro; return coro """ diff --git a/python/_restclient/setup.py b/python/_restclient/setup.py index c6f1b6a5..36c2afac 100644 --- a/python/_restclient/setup.py +++ b/python/_restclient/setup.py @@ -14,7 +14,7 @@ SUBPACKAGE_SLUG = f"{NAMESPACE_PACKAGE_NAME}.{SUBPACKAGE_NAME}" # Subpackage version -VERSION = "3.0.1" +VERSION = "3.0.2" # Package author information AUTHOR = "Austin Raney" diff --git a/python/_restclient/tests/test_restclient.py b/python/_restclient/tests/test_restclient.py index 64657349..77fb77f8 100644 --- a/python/_restclient/tests/test_restclient.py +++ b/python/_restclient/tests/test_restclient.py @@ -150,3 +150,23 @@ def test_build_url(loop): assert client.build_url(base_url) == base_url assert client.build_url(base_url, query_params) == f"{base_url}?key=value" + + +def test_restclient_nest_asyncio_ModuleNotFoundError(loop): + """Test for #99""" + import asyncio + import warnings + + async def test(): + await asyncio.sleep(0.01) + + with warnings.catch_warnings(): + # ignore coroutine not awaited warning for output sake. + # This is not the purpose of this test + warnings.simplefilter("ignore", category=RuntimeWarning) + with pytest.raises(ModuleNotFoundError): + # implicitly verify that `nest_asyncio` is not installed + # this test will need to change if `nest_asyncio` becomes a requirement + RestClient(enable_cache=False) + + loop.run_until_complete(test())