Repository: spyder-ide/spyder-kernels

Issue:
Python 2 tests are broken in Conda slots
```
Traceback (most recent call last):
  File "/usr/share/miniconda3/envs/test/bin/pytest", line 11, in <module>
    sys.exit(main())
TypeError: 'NoneType' object is not callable
```

Discussion:

Patch:
diff --git a/spyder_kernels/console/kernel.py b/spyder_kernels/console/kernel.py
index 9444397..911e362 100644
--- a/spyder_kernels/console/kernel.py
+++ b/spyder_kernels/console/kernel.py
@@ -344,7 +344,7 @@ class SpyderKernel(IPythonKernel):
             # Interrupting the eventloop is only implemented when a message is
             # received on the shell channel, but this message is queued and
             # won't be processed because an `execute` message is being
-            # processed. Therefore we process the message here (comm channel)
+            # processed. Therefore we process the message here (control chan.)
             # and request a dummy message to be sent on the shell channel to
             # stop the eventloop. This will call back `_interrupt_eventloop`.
             self.frontend_call().request_interrupt_eventloop()
@@ -535,7 +535,6 @@ class SpyderKernel(IPythonKernel):
         try:
             import matplotlib.pyplot as plt
             plt.close('all')
-            del plt
         except:
             pass
 
@@ -594,7 +593,7 @@ class SpyderKernel(IPythonKernel):
         both locals() and globals() for current frame when debugging
         """
         ns = {}
-        if self.shell.is_debugging() and self.shell.pdb_session.prompt_waiting:
+        if self.shell.is_debugging() and self.shell.pdb_session.curframe:
             # Stopped at a pdb prompt
             ns.update(self.shell.user_ns)
             ns.update(self.shell._pdb_locals)
diff --git a/spyder_kernels/console/start.py b/spyder_kernels/console/start.py
index 45c2530..baa7f92 100644
--- a/spyder_kernels/console/start.py
+++ b/spyder_kernels/console/start.py
@@ -270,11 +270,9 @@ def varexp(line):
         import guiqwt.pyplot as pyplot
     except:
         import matplotlib.pyplot as pyplot
-    __fig__ = pyplot.figure();
-    __items__ = getattr(pyplot, funcname[2:])(
-        ip.kernel._get_current_namespace()[name])
+    pyplot.figure();
+    getattr(pyplot, funcname[2:])(ip.kernel._get_current_namespace()[name])
     pyplot.show()
-    del __fig__, __items__
 
 
 def main():
diff --git a/spyder_kernels/customize/spyderpdb.py b/spyder_kernels/customize/spyderpdb.py
index b14a01c..838e20c 100755
--- a/spyder_kernels/customize/spyderpdb.py
+++ b/spyder_kernels/customize/spyderpdb.py
@@ -105,9 +105,6 @@ class SpyderPdb(ipyPdb, object):  # Inherits `object` to call super() in PY2
         # Keep track of remote filename
         self.remote_filename = None
 
-        # State of the prompt
-        self.prompt_waiting = False
-
         # Line received from the frontend
         self._cmd_input_line = None
 
@@ -263,8 +260,12 @@ class SpyderPdb(ipyPdb, object):  # Inherits `object` to call super() in PY2
                     if out is not None:
                         sys.stdout.flush()
                         sys.stderr.flush()
-                        frontend_request(blocking=False).show_pdb_output(
-                            repr(out))
+                        try:
+                            frontend_request(blocking=False).show_pdb_output(
+                                repr(out))
+                        except (CommError, TimeoutError):
+                            # Fallback
+                            print("pdb out> ", repr(out))
 
             finally:
                 if execute_events:
@@ -360,7 +361,10 @@ class SpyderPdb(ipyPdb, object):  # Inherits `object` to call super() in PY2
         Take a number as argument as an (optional) number of context line to
         print"""
         super(SpyderPdb, self).do_where(arg)
-        frontend_request(blocking=False).do_where()
+        try:
+            frontend_request(blocking=False).do_where()
+        except (CommError, TimeoutError):
+            logger.debug("Could not send where request to the frontend.")
 
     do_w = do_where
 
@@ -660,11 +664,9 @@ class SpyderPdb(ipyPdb, object):  # Inherits `object` to call super() in PY2
                 line = self.cmdqueue.pop(0)
             else:
                 try:
-                    self.prompt_waiting = True
                     line = self.cmd_input(self.prompt)
                 except EOFError:
                     line = 'EOF'
-            self.prompt_waiting = False
             line = self.precmd(line)
             stop = self.onecmd(line)
             stop = self.postcmd(stop, line)


Test patch:
diff --git a/.github/workflows/linux-tests.yml b/.github/workflows/linux-tests.yml
index 63ee8b8..a358e80 100644
--- a/.github/workflows/linux-tests.yml
+++ b/.github/workflows/linux-tests.yml
@@ -22,7 +22,7 @@ jobs:
     strategy:
       fail-fast: false 
       matrix:
-        PYTHON_VERSION: ['2.7', '3.7', '3.8', '3.9']
+        PYTHON_VERSION: ['3.7', '3.8', '3.9']
     timeout-minutes: 20
     steps:
       - name: Checkout branch
diff --git a/.github/workflows/macos-tests.yml b/.github/workflows/macos-tests.yml
index 0e63b69..799bf83 100644
--- a/.github/workflows/macos-tests.yml
+++ b/.github/workflows/macos-tests.yml
@@ -21,7 +21,7 @@ jobs:
     strategy:
       fail-fast: false 
       matrix:
-        PYTHON_VERSION: ['2.7', '3.7', '3.8', '3.9']
+        PYTHON_VERSION: ['3.7', '3.8', '3.9']
     timeout-minutes: 25
     steps:
       - name: Checkout branch
diff --git a/spyder_kernels/console/tests/test_console_kernel.py b/spyder_kernels/console/tests/test_console_kernel.py
index 0c95fd6..b488bf5 100644
--- a/spyder_kernels/console/tests/test_console_kernel.py
+++ b/spyder_kernels/console/tests/test_console_kernel.py
@@ -831,7 +831,6 @@ def test_do_complete(kernel):
     # test pdb
     pdb_obj = SpyderPdb()
     pdb_obj.curframe = inspect.currentframe()
-    pdb_obj.prompt_waiting = True
     pdb_obj.completenames = lambda *ignore: ['baba']
     kernel.shell.pdb_session = pdb_obj
     match = kernel.do_complete('ba', 2)
@@ -890,7 +889,6 @@ def test_comprehensions_with_locals_in_pdb(kernel):
     """
     pdb_obj = SpyderPdb()
     pdb_obj.curframe = inspect.currentframe()
-    pdb_obj.prompt_waiting = True
     pdb_obj.curframe_locals = pdb_obj.curframe.f_locals
     kernel.shell.pdb_session = pdb_obj
 
@@ -917,7 +915,6 @@ def test_comprehensions_with_locals_in_pdb_2(kernel):
     """
     pdb_obj = SpyderPdb()
     pdb_obj.curframe = inspect.currentframe()
-    pdb_obj.prompt_waiting = True
     pdb_obj.curframe_locals = pdb_obj.curframe.f_locals
     kernel.shell.pdb_session = pdb_obj
 
@@ -944,7 +941,6 @@ def test_namespaces_in_pdb(kernel):
     kernel.shell.user_ns["test"] = 0
     pdb_obj = SpyderPdb()
     pdb_obj.curframe = inspect.currentframe()
-    pdb_obj.prompt_waiting = True
     pdb_obj.curframe_locals = pdb_obj.curframe.f_locals
     kernel.shell.pdb_session = pdb_obj
 
@@ -1026,7 +1022,6 @@ def test_functions_with_locals_in_pdb_2(kernel):
     baba = 1
     pdb_obj = SpyderPdb()
     pdb_obj.curframe = inspect.currentframe()
-    pdb_obj.prompt_waiting = True
     pdb_obj.curframe_locals = pdb_obj.curframe.f_locals
     kernel.shell.pdb_session = pdb_obj
 
@@ -1064,7 +1059,6 @@ def test_locals_globals_in_pdb(kernel):
     a = 1
     pdb_obj = SpyderPdb()
     pdb_obj.curframe = inspect.currentframe()
-    pdb_obj.prompt_waiting = True
     pdb_obj.curframe_locals = pdb_obj.curframe.f_locals
     kernel.shell.pdb_session = pdb_obj
 
@@ -1143,5 +1137,49 @@ def test_get_interactive_backend(backend):
             assert value == '0'
 
 
+@flaky(max_runs=3)
+@pytest.mark.skipif(
+    sys.version_info[0] < 3,
+    reason="Fails with python 2")
+def test_debug_namespace(tmpdir):
+    """
+    Test that the kernel uses the proper namespace while debugging.
+    """
+    # Command to start the kernel
+    cmd = "from spyder_kernels.console import start; start.main()"
+
+    with setup_kernel(cmd) as client:
+        # Write code to a file
+        d = tmpdir.join("pdb-ns-test.py")
+        d.write('def func():\n    bb = "hello"\n    breakpoint()\nfunc()')
+
+        # Run code file `d`
+        msg_id = client.execute("runfile(r'{}')".format(to_text_string(d)))
+
+        # make sure that 'bb' returns 'hello'
+        client.get_stdin_msg(timeout=TIMEOUT)
+        client.input('bb')
+
+        t0 = time.time()
+        while True:
+            assert time.time() - t0 < 5
+            msg = client.get_iopub_msg(timeout=TIMEOUT)
+            if msg.get('msg_type') == 'stream':
+                if 'hello' in msg["content"].get("text"):
+                    break
+
+         # make sure that get_value('bb') returns 'hello'
+        client.get_stdin_msg(timeout=TIMEOUT)
+        client.input("get_ipython().kernel.get_value('bb')")
+
+        t0 = time.time()
+        while True:
+            assert time.time() - t0 < 5
+            msg = client.get_iopub_msg(timeout=TIMEOUT)
+            if msg.get('msg_type') == 'stream':
+                if 'hello' in msg["content"].get("text"):
+                    break
+
+
 if __name__ == "__main__":
     pytest.main()