Skip to content

Commit

Permalink
Merge pull request NOAA-OWP#130 from aaraney/re-fix-runtime-error-in-…
Browse files Browse the repository at this point in the history
…restclient

Fix NOAA-OWP#99: "RuntimeError: This event loop is already running" in colab and notebook. Re-PR of NOAA-OWP#100
  • Loading branch information
jarq6c authored Aug 25, 2021
2 parents 129808c + 8226f21 commit 908e000
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 2 deletions.
2 changes: 1 addition & 1 deletion python/_restclient/setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = hydrotools._restclient
version = 3.0.3
version = 3.0.4
author = Austin Raney
author_email = [email protected]
description = General REST api client with built in request caching and retries.
Expand Down
5 changes: 5 additions & 0 deletions python/_restclient/src/hydrotools/_restclient/_restclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
19 changes: 18 additions & 1 deletion python/_restclient/src/hydrotools/_restclient/async_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 """
Expand Down
34 changes: 34 additions & 0 deletions python/_restclient/tests/test_restclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,37 @@ 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"


class ModuleFoundError(Exception):
...


def test_restclient_nest_asyncio_ModuleNotFoundError(loop):
"""Test for #99. Ensure ModuleNotFoundError raised if `nest_asyncio` not installed"""
import asyncio
import warnings

# verify `nest_asyncio` is not installed
try:
import nest_asyncio

error_message = "nest_asyncio installed. Cannot complete test."
raise ModuleFoundError(error_message)
except ModuleNotFoundError:
# this should occur, so continue
pass

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())

0 comments on commit 908e000

Please sign in to comment.