Repository: spyder-ide/spyder

Issue:
Kernel error trigerred by an asyncio conflict with SpyderKernelApp
## Problem Description
Since the update to 5.1.5 a patch software called nest_asyncio that  deals with async issues has stopped working due to a conflict that Spyder raises when it is run.  The nest_asyncio  code is on github and it patches asyncio to allow nested event loops.  I upgraded from Spyder ver 5.0.5 (when everything was working fine) now going back to version Spyder 5.0.5  doesn't solve the problem because some files have been altered with the upgrade to 5.1.5.

### What steps reproduce the problem?
1.  pip install nest_asyncio (Install in terminal window)
2.  import nest_asyncio
3.  nest_asyncio.apply()

### What is the expected output? 
It solves an  API issue with nested event loops.  It should not raise any error messages.

### Paste Traceback/Error Below (if applicable)
[SpyderKernelApp] ERROR | Exception in message handler:
Traceback (most recent call last):
  File "C:\Users\ynika\anaconda3\lib\site-packages\spyder_kernels\comms\frontendcomm.py", line 164, in poll_one
    asyncio.run(handler(out_stream, ident, msg))
  File "C:\Users\ynika\anaconda3\lib\site-packages\nest_asyncio.py", line 32, in run
    return loop.run_until_complete(future)
  File "C:\Users\ynika\anaconda3\lib\site-packages\nest_asyncio.py", line 60, in run_until_complete
    f = asyncio.ensure_future(future, loop=self)
  File "C:\Users\ynika\anaconda3\lib\asyncio\tasks.py", line 680, in ensure_future
    raise TypeError('An asyncio.Future, a coroutine or an awaitable is '
TypeError: An asyncio.Future, a coroutine or an awaitable is required
[SpyderKernelApp] ERROR | Exception in message handler:
```python-traceback

PASTE TRACEBACK HERE
Traceback (most recent call last):
  File "C:\Users\ynika\anaconda3\lib\site-packages\spyder_kernels\comms\frontendcomm.py", line 164, in poll_one
    asyncio.run(handler(out_stream, ident, msg))
  File "C:\Users\ynika\anaconda3\lib\site-packages\nest_asyncio.py", line 32, in run
    return loop.run_until_complete(future)
  File "C:\Users\ynika\anaconda3\lib\site-packages\nest_asyncio.py", line 60, in run_until_complete
    f = asyncio.ensure_future(future, loop=self)
  File "C:\Users\ynika\anaconda3\lib\asyncio\tasks.py", line 680, in ensure_future
    raise TypeError('An asyncio.Future, a coroutine or an awaitable is '
TypeError: An asyncio.Future, a coroutine or an awaitable is required

## Versions
* Spyder version:  5.1.5
* Python version:  3.9.7
* Qt version: 5.9.7
* PyQt version: 5.9.2
* Operating System name/version: Win 10 Pro

### Dependencies

# Mandatory:
atomicwrites >=1.2.0          :  1.4.0 (OK)
chardet >=2.0.0               :  4.0.0 (OK)
cloudpickle >=0.5.0           :  2.0.0 (OK)
cookiecutter >=1.6.0          :  1.7.3 (OK)
diff_match_patch >=20181111   :  20200713 (OK)
intervaltree                  :  None (OK)
IPython >=7.6.0               :  7.27.0 (OK)
jedi >=0.17.2;<0.19.0         :  0.18.0 (OK)
jsonschema >=3.2.0            :  3.2.0 (OK)
keyring >=17.0.0              :  23.2.1 (OK)
nbconvert >=4.0               :  6.1.0 (OK)
numpydoc >=0.6.0              :  1.1.0 (OK)
paramiko >=2.4.0              :  2.7.2 (OK)
parso >=0.7.0;<0.9.0          :  0.8.2 (OK)
pexpect >=4.4.0               :  4.8.0 (OK)
pickleshare >=0.4             :  0.7.5 (OK)
psutil >=5.3                  :  5.8.0 (OK)
pygments >=2.0                :  2.10.0 (OK)
pylint >=2.5.0;<2.10.0        :  2.9.6 (OK)
pyls_spyder >=0.4.0           :  0.4.0 (OK)
pylsp >=1.2.2;<1.3.0          :  1.2.2 (OK)
pylsp_black >=1.0.0           :  None (OK)
qdarkstyle =3.0.2             :  3.0.2 (OK)
qstylizer >=0.1.10            :  0.2.1 (OK)
qtawesome >=1.0.2             :  1.0.3 (OK)
qtconsole >=5.1.0             :  5.1.1 (OK)
qtpy >=1.5.0                  :  1.11.1 (OK)
rtree >=0.9.7                 :  0.9.7 (OK)
setuptools >=49.6.0           :  58.0.4 (OK)
sphinx >=0.6.6                :  4.2.0 (OK)
spyder_kernels >=2.1.1;<2.2.0 :  2.1.1 (OK)
textdistance >=4.2.0          :  4.2.1 (OK)
three_merge >=0.1.1           :  0.1.1 (OK)
watchdog                      :  2.1.5 (OK)
zmq >=17                      :  22.2.1 (OK)

# Optional:
cython >=0.21                 :  0.29.24 (OK)
matplotlib >=2.0.0            :  3.4.3 (OK)
numpy >=1.7                   :  1.19.3 (OK)
pandas >=1.1.1                :  1.3.3 (OK)
scipy >=0.17.0                :  1.7.1 (OK)
sympy >=0.7.3                 :  1.8 (OK)



Discussion:

Resolving patch:
diff --git a/external-deps/spyder-kernels/spyder_kernels/comms/frontendcomm.py b/external-deps/spyder-kernels/spyder_kernels/comms/frontendcomm.py
--- a/external-deps/spyder-kernels/spyder_kernels/comms/frontendcomm.py
+++ b/external-deps/spyder-kernels/spyder_kernels/comms/frontendcomm.py
@@ -15,7 +15,6 @@
 import threading
 import time
 
-import ipykernel
 from IPython.core.getipython import get_ipython
 from jupyter_client.localinterfaces import localhost
 from tornado import ioloop
@@ -146,38 +145,35 @@ def poll_one(self):
             return
 
         handler = self.kernel.shell_handlers.get(msg_type, None)
-        if handler is None:
-            self.kernel.log.warning("Unknown message type: %r", msg_type)
-        else:
-            try:
-                if not PY2:
-                    import asyncio
-                    if (not getattr(asyncio, 'run', False) or
-                            ipykernel.__version__[0] < '6'):
-                        # This is required for Python 3.6, which doesn't have
-                        # asyncio.run or ipykernel versions less than 6. The
-                        # nice thing is that ipykernel 6, which requires
-                        # asyncio, doesn't support Python 3.6.
-                        handler(out_stream, ident, msg)
-                    else:
-                        # This is needed for ipykernel 6+
-                        asyncio.run(handler(out_stream, ident, msg))
-                else:
-                    handler(out_stream, ident, msg)
-            except ValueError as e:
-                # This avoids showing an unnecessary message about expected
-                # coroutines.
+        try:
+            if handler is None:
+                self.kernel.log.warning("Unknown message type: %r", msg_type)
                 return
-            except Exception:
-                self.kernel.log.error("Exception in message handler:",
-                                      exc_info=True)
+            if PY2:
+                handler(out_stream, ident, msg)
                 return
 
-        sys.stdout.flush()
-        sys.stderr.flush()
-        # Flush to ensure reply is sent
-        if out_stream:
-            out_stream.flush(zmq.POLLOUT)
+            import asyncio
+
+            if (getattr(asyncio, 'run', False) and
+                    asyncio.iscoroutinefunction(handler)):
+                # This is needed for ipykernel 6+
+                asyncio.run(handler(out_stream, ident, msg))
+            else:
+                # This is required for Python 3.6, which doesn't have
+                # asyncio.run or ipykernel versions less than 6. The
+                # nice thing is that ipykernel 6, which requires
+                # asyncio, doesn't support Python 3.6.
+                handler(out_stream, ident, msg)
+        except Exception:
+            self.kernel.log.error(
+                "Exception in message handler:", exc_info=True)
+        finally:
+            sys.stdout.flush()
+            sys.stderr.flush()
+            # Flush to ensure reply is sent
+            if out_stream:
+                out_stream.flush(zmq.POLLOUT)
 
     def remote_call(self, comm_id=None, blocking=False, callback=None,
                     timeout=None):


Test patch: