Repository: jupyter/nbclient (language: python)

Issue:
0.5.13: pytest warnings and fails
I'm trying to package your module as an rpm package. So I'm using the typical PEP517 based build, install and test cycle used on building packages from non-root account.
- `python3 -sBm build -w --no-isolation`
- because I'm calling `build` with `--no-isolation` I'm using during all processes only locally installed modules
- install .whl file in </install/prefix>
- run pytest with PYTHONPATH pointing to sitearch and sitelib inside </install/prefix>

Two units are failing and there some pytest wearnings. Here is pytest output:
```console
+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-nbclient-0.5.13-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-nbclient-0.5.13-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.12, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/nbclient-0.5.13
collected 91 items

nbclient/tests/test_client.py .......F.....................................F........................................ [ 94%]
nbclient/tests/test_util.py ...ss                                        [100%]

=================================== FAILURES ===================================
______________ test_run_all_notebooks[JupyterWidgets.ipynb-opts7] ______________

input_name = 'JupyterWidgets.ipynb', opts = {'kernel_name': 'python'}

    @pytest.mark.parametrize(
        ["input_name", "opts"],
        [
            ("Other Comms.ipynb", dict(kernel_name="python")),
            ("Clear Output.ipynb", dict(kernel_name="python")),
            ("Empty Cell.ipynb", dict(kernel_name="python")),
            ("Factorials.ipynb", dict(kernel_name="python")),
            ("HelloWorld.ipynb", dict(kernel_name="python")),
            ("Inline Image.ipynb", dict(kernel_name="python")),
            (
                "Interrupt.ipynb",
                dict(kernel_name="python", timeout=1, interrupt_on_timeout=True, allow_errors=True),
            ),
            ("JupyterWidgets.ipynb", dict(kernel_name="python")),
            ("Skip Exceptions with Cell Tags.ipynb", dict(kernel_name="python")),
            ("Skip Exceptions.ipynb", dict(kernel_name="python", allow_errors=True)),
            ("Skip Execution with Cell Tag.ipynb", dict(kernel_name="python")),
            ("SVG.ipynb", dict(kernel_name="python")),
            ("Unicode.ipynb", dict(kernel_name="python")),
            ("UnicodePy3.ipynb", dict(kernel_name="python")),
            ("update-display-id.ipynb", dict(kernel_name="python")),
            ("Check History in Memory.ipynb", dict(kernel_name="python")),
        ],
    )
    def test_run_all_notebooks(input_name, opts):
        """Runs a series of test notebooks and compares them to their actual output"""
        input_file = os.path.join(current_dir, 'files', input_name)
>       input_nb, output_nb = run_notebook(input_file, opts, notebook_resources())

nbclient/tests/test_client.py:296:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
nbclient/tests/test_client.py:93: in run_notebook
    output_nb = executor.execute()
nbclient/util.py:84: in wrapped
    return just_run(coro(*args, **kwargs))
nbclient/util.py:62: in just_run
    return loop.run_until_complete(coro)
/usr/lib64/python3.8/asyncio/base_events.py:616: in run_until_complete
    return future.result()
nbclient/client.py:663: in async_execute
    await self.async_execute_cell(
nbclient/client.py:965: in async_execute_cell
    await self._check_raise_for_error(cell, cell_index, exec_reply)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <nbclient.client.NotebookClient object at 0x7f848c745550>
cell = {'cell_type': 'code', 'metadata': {'execution': {'iopub.status.busy': '2022-03-11T09:38:24.627291Z', 'iopub.execute_in... 'ipywidgets'"]}], 'source': "import ipywidgets\nlabel = ipywidgets.Label('Hello World')\nlabel", 'execution_count': 1}
cell_index = 0
exec_reply = {'buffers': [], 'content': {'ename': 'ModuleNotFoundError', 'engine_info': {'engine_id': -1, 'engine_uuid': 'a19cb5ee-...e, 'engine': 'a19cb5ee-5291-4c63-81d7-d9f42e82ec09', 'started': '2022-03-11T09:38:24.627755Z', 'status': 'error'}, ...}

    async def _check_raise_for_error(
        self, cell: NotebookNode, cell_index: int, exec_reply: t.Optional[t.Dict]
    ) -> None:

        if exec_reply is None:
            return None

        exec_reply_content = exec_reply['content']
        if exec_reply_content['status'] != 'error':
            return None

        cell_allows_errors = (not self.force_raise_errors) and (
            self.allow_errors
            or exec_reply_content.get('ename') in self.allow_error_names
            or "raises-exception" in cell.metadata.get("tags", [])
        )
        await run_hook(self.on_cell_error, cell=cell, cell_index=cell_index)
        if not cell_allows_errors:
>           raise CellExecutionError.from_cell_and_msg(cell, exec_reply_content)
E           nbclient.exceptions.CellExecutionError: An error occurred while executing the following cell:
E           ------------------
E           import ipywidgets
E           label = ipywidgets.Label('Hello World')
E           label
E           ------------------
E
E           ---------------------------------------------------------------------------
E           ModuleNotFoundError                       Traceback (most recent call last)
E           <IPY-INPUT> in <module>
E           ----> 1 import ipywidgets
E                 2 label = ipywidgets.Label('Hello World')
E                 3 label
E
E           ModuleNotFoundError: No module named 'ipywidgets'
E           ModuleNotFoundError: No module named 'ipywidgets'

nbclient/client.py:862: CellExecutionError
----------------------------- Captured stderr call -----------------------------
[IPKernelApp] WARNING | debugpy_stream undefined, debugging will not be enabled
___________________________ TestExecute.test_widgets ___________________________

self = <nbclient.tests.test_client.TestExecute testMethod=test_widgets>

    def test_widgets(self):
        """Runs a test notebook with widgets and checks the widget state is saved."""
        input_file = os.path.join(current_dir, 'files', 'JupyterWidgets.ipynb')
        opts = dict(kernel_name="python")
        res = self.build_resources()
        res['metadata']['path'] = os.path.dirname(input_file)
>       input_nb, output_nb = run_notebook(input_file, opts, res)

nbclient/tests/test_client.py:736:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
nbclient/tests/test_client.py:93: in run_notebook
    output_nb = executor.execute()
nbclient/util.py:84: in wrapped
    return just_run(coro(*args, **kwargs))
nbclient/util.py:62: in just_run
    return loop.run_until_complete(coro)
/usr/lib/python3.8/site-packages/nest_asyncio.py:81: in run_until_complete
    return f.result()
/usr/lib64/python3.8/asyncio/futures.py:178: in result
    raise self._exception
/usr/lib64/python3.8/asyncio/tasks.py:280: in __step
    result = coro.send(None)
nbclient/client.py:663: in async_execute
    await self.async_execute_cell(
nbclient/client.py:965: in async_execute_cell
    await self._check_raise_for_error(cell, cell_index, exec_reply)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <nbclient.client.NotebookClient object at 0x7f848e3dd4c0>
cell = {'cell_type': 'code', 'metadata': {'execution': {'iopub.status.busy': '2022-03-11T09:39:55.012475Z', 'iopub.execute_in... 'ipywidgets'"]}], 'source': "import ipywidgets\nlabel = ipywidgets.Label('Hello World')\nlabel", 'execution_count': 1}
cell_index = 0
exec_reply = {'buffers': [], 'content': {'ename': 'ModuleNotFoundError', 'engine_info': {'engine_id': -1, 'engine_uuid': '70545c82-...e, 'engine': '70545c82-48b8-4fef-a42c-1a4b19589fbd', 'started': '2022-03-11T09:39:55.013193Z', 'status': 'error'}, ...}

    async def _check_raise_for_error(
        self, cell: NotebookNode, cell_index: int, exec_reply: t.Optional[t.Dict]
    ) -> None:

        if exec_reply is None:
            return None

        exec_reply_content = exec_reply['content']
        if exec_reply_content['status'] != 'error':
            return None

        cell_allows_errors = (not self.force_raise_errors) and (
            self.allow_errors
            or exec_reply_content.get('ename') in self.allow_error_names
            or "raises-exception" in cell.metadata.get("tags", [])
        )
        await run_hook(self.on_cell_error, cell=cell, cell_index=cell_index)
        if not cell_allows_errors:
>           raise CellExecutionError.from_cell_and_msg(cell, exec_reply_content)
E           nbclient.exceptions.CellExecutionError: An error occurred while executing the following cell:
E           ------------------
E           import ipywidgets
E           label = ipywidgets.Label('Hello World')
E           label
E           ------------------
E
E           ---------------------------------------------------------------------------
E           ModuleNotFoundError                       Traceback (most recent call last)
E           <IPY-INPUT> in <module>
E           ----> 1 import ipywidgets
E                 2 label = ipywidgets.Label('Hello World')
E                 3 label
E
E           ModuleNotFoundError: No module named 'ipywidgets'
E           ModuleNotFoundError: No module named 'ipywidgets'

nbclient/client.py:862: CellExecutionError
----------------------------- Captured stderr call -----------------------------
[IPKernelApp] WARNING | debugpy_stream undefined, debugging will not be enabled
=============================== warnings summary ===============================
../../../../../usr/lib/python3.8/site-packages/nbconvert/exporters/html.py:110
  /usr/lib/python3.8/site-packages/nbconvert/exporters/html.py:110: DeprecationWarning: 'contextfilter' is renamed to 'pass_context', the old name will be removed in Jinja 3.1.
    def markdown2html(self, context, source):

nbclient/tests/test_util.py:62
  /home/tkloczko/rpmbuild/BUILD/nbclient-0.5.13/nbclient/tests/test_util.py:62: PytestUnknownMarkWarning: Unknown pytest.mark.asyncio - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
    @pytest.mark.asyncio

nbclient/tests/test_util.py:69
  /home/tkloczko/rpmbuild/BUILD/nbclient-0.5.13/nbclient/tests/test_util.py:69: PytestUnknownMarkWarning: Unknown pytest.mark.asyncio - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
    @pytest.mark.asyncio

nbclient/tests/test_client.py: 110 warnings
  /usr/lib/python3.8/site-packages/nbformat/validator.py:355: DeprecationWarning: Passing a schema to Validator.iter_errors is deprecated and will be removed in a future release. Call validator.evolve(schema=new_schema).iter_errors(...) instead.
    for error in errors:

nbclient/tests/test_client.py::TestRunCell::test_cell_hooks
nbclient/tests/test_client.py::TestRunCell::test_display_data_message
nbclient/tests/test_client.py::TestRunCell::test_error_cell_hooks
nbclient/tests/test_client.py::TestRunCell::test_execution_count_message_ignored_on_override
nbclient/tests/test_client.py::TestRunCell::test_widget_comm_buffer_message_single
  /usr/lib64/python3.8/unittest/mock.py:2030: RuntimeWarning: coroutine 'make_async.<locals>._' was never awaited
    setattr(_type, entry, MagicProxy(entry, self))

nbclient/tests/test_client.py::TestRunCell::test_deadline_iopub
  /usr/lib64/python3.8/unittest/mock.py:740: RuntimeWarning: coroutine 'make_async.<locals>._' was never awaited
    return object.__setattr__(self, name, value)

nbclient/tests/test_client.py::TestRunCell::test_eventual_deadline_iopub
  /home/tkloczko/rpmbuild/BUILD/nbclient-0.5.13/nbclient/tests/test_client.py:936: RuntimeWarning: coroutine 'make_async.<locals>._' was never awaited
    message_mock.side_effect = message_seq(list(message_mock.side_effect)[:-1])

nbclient/tests/test_client.py::TestRunCell::test_eventual_deadline_iopub
  /usr/lib64/python3.8/unittest/mock.py:773: RuntimeWarning: coroutine 'make_async.<locals>._' was never awaited
    return object.__setattr__(self, name, value)

nbclient/tests/test_client.py::TestRunCell::test_execution_count_message
  /usr/lib64/python3.8/unittest/mock.py:1063: RuntimeWarning: coroutine 'make_async.<locals>._' was never awaited
    _safe_super(CallableMixin, self).__init__(

nbclient/tests/test_client.py::TestRunCell::test_non_code_cell_hooks
  /usr/lib64/python3.8/unittest/mock.py:1085: RuntimeWarning: coroutine 'make_async.<locals>._' was never awaited
    return self._execute_mock_call(*args, **kwargs)

nbclient/tests/test_util.py::test_run_hook_sync
nbclient/tests/test_util.py::test_run_hook_async
  /usr/lib/python3.8/site-packages/_pytest/python.py:172: PytestUnhandledCoroutineWarning: async def functions are not natively supported and have been skipped.
  You need to install a suitable plugin for your async framework, for example:
    - anyio
    - pytest-asyncio
    - pytest-tornasync
    - pytest-trio
    - pytest-twisted
    warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid)))

-- Docs: https://docs.pytest.org/en/stable/warnings.html
=========================== short test summary info ============================
SKIPPED [2] ../../../../../usr/lib/python3.8/site-packages/_pytest/python.py:173: async def function and no async plugin installed (see warnings)
FAILED nbclient/tests/test_client.py::test_run_all_notebooks[JupyterWidgets.ipynb-opts7]
FAILED nbclient/tests/test_client.py::TestExecute::test_widgets - nbclient.ex...
====== 2 failed, 87 passed, 2 skipped, 125 warnings in 106.51s (0:01:46) =======
```


Discussion / hints:
[Tests require ipywidgets](https://github.com/jupyter/nbclient/blob/86dcb64e18de6cec3050ca7c2a36ad7519000958/requirements-dev.txt#L3).
After install `ipwidgets` and `pytest-asyncio` there are still some warnings
```console
+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-nbclient-0.6.3-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-nbclient-0.6.3-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.13, pytest-7.1.2, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/nbclient-0.6.3
plugins: asyncio-0.18.2
asyncio: mode=legacy
collected 91 items

nbclient/tests/test_client.py ...................................................................................... [ 94%]
nbclient/tests/test_util.py .....                                        [100%]

=============================== warnings summary ===============================
../../../../../usr/lib/python3.8/site-packages/pytest_asyncio/plugin.py:191
  /usr/lib/python3.8/site-packages/pytest_asyncio/plugin.py:191: DeprecationWarning: The 'asyncio_mode' default value will change to 'strict' in future, please explicitly use 'asyncio_mode=strict' or 'asyncio_mode=auto' in pytest configuration file.
    config.issue_config_time_warning(LEGACY_MODE, stacklevel=2)

nbclient/tests/test_client.py::TestRunCell::test_cell_hooks
nbclient/tests/test_client.py::TestRunCell::test_error_cell_hooks
nbclient/tests/test_client.py::TestRunCell::test_execution_count_message_ignored_on_override
nbclient/tests/test_client.py::TestRunCell::test_non_code_cell_hooks
  /usr/lib64/python3.8/unittest/mock.py:2076: RuntimeWarning: coroutine 'make_async.<locals>._' was never awaited
    self.name = name
  Enable tracemalloc to get traceback where the object was allocated.
  See https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings for more info.

nbclient/tests/test_client.py::TestRunCell::test_deadline_iopub
  /usr/lib64/python3.8/unittest/mock.py:740: RuntimeWarning: coroutine 'make_async.<locals>._' was never awaited
    return object.__setattr__(self, name, value)
  Enable tracemalloc to get traceback where the object was allocated.
  See https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings for more info.

nbclient/tests/test_client.py::TestRunCell::test_display_data_same_id_message
  /usr/lib64/python3.8/enum.py:339: RuntimeWarning: coroutine 'make_async.<locals>._' was never awaited
    return cls.__new__(cls, value)
  Enable tracemalloc to get traceback where the object was allocated.
  See https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings for more info.

nbclient/tests/test_client.py::TestRunCell::test_eventual_deadline_iopub
  /home/tkloczko/rpmbuild/BUILD/nbclient-0.6.3/nbclient/tests/test_client.py:967: RuntimeWarning: coroutine 'make_async.<locals>._' was never awaited
    message_mock.side_effect = message_seq(list(message_mock.side_effect)[:-1])
  Enable tracemalloc to get traceback where the object was allocated.
  See https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings for more info.

nbclient/tests/test_client.py::TestRunCell::test_eventual_deadline_iopub
  /usr/lib64/python3.8/unittest/mock.py:773: RuntimeWarning: coroutine 'make_async.<locals>._' was never awaited
    return object.__setattr__(self, name, value)
  Enable tracemalloc to get traceback where the object was allocated.
  See https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings for more info.

nbclient/tests/test_client.py::TestRunCell::test_widget_comm_buffer_message_single
  /usr/lib/python3.8/site-packages/traitlets/traitlets.py:1561: RuntimeWarning: coroutine 'make_async.<locals>._' was never awaited
    if method_name in c.__dict__:
  Enable tracemalloc to get traceback where the object was allocated.
  See https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings for more info.

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================== 91 passed, 10 warnings in 84.08s (0:01:24) ==================
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:79: PytestWarning: (rm_rf) error removing /tmp/pytest-of-tkloczko/garbage-2d3c13c5-3e7c-4cca-9e3a-d551473c307b
<class 'OSError'>: [Errno 39] Directory not empty: '/tmp/pytest-of-tkloczko/garbage-2d3c13c5-3e7c-4cca-9e3a-d551473c307b'
  warnings.warn(
sys:1: RuntimeWarning: coroutine 'make_async.<locals>._' was never awaited
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
```

Fix patch:
diff --git a/MANIFEST.in b/MANIFEST.in
index ea51c02..d5da1e7 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -3,7 +3,7 @@ include MANIFEST.in
 include requirements.txt
 include requirements-dev.txt
 include *.md
-include tox.ini
+include pytest.ini
 include pyproject.toml
 include .pre-commit-config.yaml
 include nbclient/py.typed
diff --git a/nbclient/util.py b/nbclient/util.py
index 8000a04..0a352c2 100644
--- a/nbclient/util.py
+++ b/nbclient/util.py
@@ -39,16 +39,14 @@ def check_patch_tornado() -> None:
 
 def just_run(coro: Awaitable) -> Any:
     """Make the coroutine run, even if there is an event loop running (using nest_asyncio)"""
-    # original from vaex/asyncio.py
-    loop = asyncio._get_running_loop()
+    try:
+        loop = asyncio.get_running_loop()
+    except RuntimeError:
+        loop = None
     if loop is None:
         had_running_loop = False
-        try:
-            loop = asyncio.get_event_loop()
-        except RuntimeError:
-            # we can still get 'There is no current event loop in ...'
-            loop = asyncio.new_event_loop()
-            asyncio.set_event_loop(loop)
+        loop = asyncio.new_event_loop()
+        asyncio.set_event_loop(loop)
     else:
         had_running_loop = True
     if had_running_loop:


Test patch:
diff --git a/nbclient/tests/test_client.py b/nbclient/tests/test_client.py
index b4dbafa..18b54cc 100644
--- a/nbclient/tests/test_client.py
+++ b/nbclient/tests/test_client.py
@@ -9,6 +9,7 @@
 import warnings
 from base64 import b64decode, b64encode
 from queue import Empty
+from typing import Any
 from unittest.mock import MagicMock, Mock
 
 import nbformat
@@ -78,11 +79,15 @@ class AsyncMock(Mock):
     pass
 
 
-def make_async(mock_value):
-    async def _():
-        return mock_value
-
-    return _()
+def make_future(obj: Any) -> asyncio.Future:
+    try:
+        loop = asyncio.get_running_loop()
+    except RuntimeError:
+        loop = asyncio.new_event_loop()
+        asyncio.set_event_loop(loop)
+    future: asyncio.Future = asyncio.Future(loop=loop)
+    future.set_result(obj)
+    return future
 
 
 def normalize_base64(b64_text):
@@ -169,7 +174,7 @@ def shell_channel_message_mock():
         # Return the message generator for
         # self.kc.shell_channel.get_msg => {'parent_header': {'msg_id': parent_id}}
         return AsyncMock(
-            return_value=make_async(
+            return_value=make_future(
                 NBClientTestsBase.merge_dicts(
                     {
                         'parent_header': {'msg_id': parent_id},
@@ -186,7 +191,7 @@ def iopub_messages_mock():
         return AsyncMock(
             side_effect=[
                 # Default the parent_header so mocks don't need to include this
-                make_async(
+                make_future(
                     NBClientTestsBase.merge_dicts({'parent_header': {'msg_id': parent_id}}, msg)
                 )
                 for msg in messages
@@ -215,7 +220,7 @@ def test_mock_wrapper(self):
                 iopub_channel=MagicMock(get_msg=message_mock),
                 shell_channel=MagicMock(get_msg=shell_channel_message_mock()),
                 execute=MagicMock(return_value=parent_id),
-                is_alive=MagicMock(return_value=make_async(True)),
+                is_alive=MagicMock(return_value=make_future(True)),
             )
             executor.parent_id = parent_id
             return func(self, executor, cell_mock, message_mock)
@@ -387,11 +392,15 @@ def test_async_parallel_notebooks(capfd, tmpdir):
     res = notebook_resources()
 
     with modified_env({"NBEXECUTE_TEST_PARALLEL_TMPDIR": str(tmpdir)}):
-        tasks = [
-            async_run_notebook(input_file.format(label=label), opts, res) for label in ("A", "B")
-        ]
-        loop = asyncio.get_event_loop()
-        loop.run_until_complete(asyncio.gather(*tasks))
+
+        async def run_tasks():
+            tasks = [
+                async_run_notebook(input_file.format(label=label), opts, res)
+                for label in ("A", "B")
+            ]
+            await asyncio.gather(*tasks)
+
+        asyncio.run(run_tasks())
 
     captured = capfd.readouterr()
     assert filter_messages_on_error_output(captured.err) == ""
@@ -412,9 +421,11 @@ def test_many_async_parallel_notebooks(capfd):
     # run once, to trigger creating the original context
     run_notebook(input_file, opts, res)
 
-    tasks = [async_run_notebook(input_file, opts, res) for i in range(4)]
-    loop = asyncio.get_event_loop()
-    loop.run_until_complete(asyncio.gather(*tasks))
+    async def run_tasks():
+        tasks = [async_run_notebook(input_file, opts, res) for i in range(4)]
+        await asyncio.gather(*tasks)
+
+    asyncio.run(run_tasks())
 
     captured = capfd.readouterr()
     assert filter_messages_on_error_output(captured.err) == ""
@@ -966,7 +977,7 @@ def message_seq(messages):
 
         message_mock.side_effect = message_seq(list(message_mock.side_effect)[:-1])
         executor.kc.shell_channel.get_msg = Mock(
-            return_value=make_async({'parent_header': {'msg_id': executor.parent_id}})
+            return_value=make_future({'parent_header': {'msg_id': executor.parent_id}})
         )
         executor.raise_on_iopub_timeout = True
 
diff --git a/nbclient/tests/test_util.py b/nbclient/tests/test_util.py
index 9102257..dc20682 100644
--- a/nbclient/tests/test_util.py
+++ b/nbclient/tests/test_util.py
@@ -14,11 +14,15 @@ async def some_async_function():
 
 
 def test_nested_asyncio_with_existing_ioloop():
-    ioloop = asyncio.new_event_loop()
-    try:
-        asyncio.set_event_loop(ioloop)
+    async def _test():
         assert some_async_function() == 42
-        assert asyncio.get_event_loop() is ioloop
+        return asyncio.get_running_loop()
+
+    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
 
diff --git a/pytest.ini b/pytest.ini
new file mode 100644
index 0000000..2f4c80e
--- /dev/null
+++ b/pytest.ini
@@ -0,0 +1,2 @@
+[pytest]
+asyncio_mode = auto