Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-spyder-kernels for 
openSUSE:Factory checked in at 2022-04-11 23:48:48
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-spyder-kernels (Old)
 and      /work/SRC/openSUSE:Factory/.python-spyder-kernels.new.1900 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-spyder-kernels"

Mon Apr 11 23:48:48 2022 rev:33 rq:968083 version:2.3.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-spyder-kernels/python-spyder-kernels.changes  
    2022-01-22 08:19:31.554361259 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-spyder-kernels.new.1900/python-spyder-kernels.changes
    2022-04-11 23:50:17.206356356 +0200
@@ -1,0 +2,24 @@
+Sat Apr  9 22:17:26 UTC 2022 - Ben Greiner <c...@bnavigator.de>
+
+- Update to version 2.3.0
+  * Add new handler to detect the current Matplotlib interactive
+    backend.
+  * Increase minimal required version of ipykernel to 6.9.2, by
+    @ccordoba12
+  * Handle getting dasks objects size, by @dalthviz
+  * Add new handler to detect the current Matplotlib interactive
+    backend, by @ccordoba12
+  * Remove support for outdated and unused Matplotlib backends, by
+    @ccordoba12
+  * Catch FileNotFoundError when trying to get file code from the
+    frontend, by @ccordoba12
+  * Increase minimal required IPython version to 7.31.1, by
+    @ccordoba12 (344)
+  * Avoid modifying the namespace view settings by reference, by
+    @impact27 (320)
+  * Fix issues when debugging comprehensions, by @impact27 (371)
+  * Leave unsaved files in linecache, by @impact27
+  * Print last line of Pdb code and some debugger fixes, by
+    @impact27 (330)
+
+-------------------------------------------------------------------

Old:
----
  python-spyder-kernels-2.2.1.tar.gz

New:
----
  python-spyder-kernels-2.3.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-spyder-kernels.spec ++++++
--- /var/tmp/diff_new_pack.PmghOo/_old  2022-04-11 23:50:17.770349922 +0200
+++ /var/tmp/diff_new_pack.PmghOo/_new  2022-04-11 23:50:17.774349875 +0200
@@ -21,7 +21,7 @@
 # flaky for obs, only test locally
 %bcond_with dasktest
 Name:           python-spyder-kernels
-Version:        2.2.1
+Version:        2.3.0
 Release:        0
 Summary:        Jupyter kernels for Spyder's console
 License:        MIT
@@ -29,6 +29,7 @@
 URL:            https://github.com/spyder-ide/spyder-kernels
 # PyPI tarballs do not include the tests: 
https://github.com/spyder-ide/spyder-kernels/issues/66
 Source:         %{url}/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz
+BuildRequires:  %{python_module base >= 3.7}
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
@@ -37,8 +38,8 @@
 BuildRequires:  %{python_module Pillow}
 BuildRequires:  %{python_module cloudpickle}
 BuildRequires:  %{python_module flaky}
-BuildRequires:  %{python_module ipykernel >= 6.6.1}
-BuildRequires:  %{python_module ipython >= 7.6.0}
+BuildRequires:  %{python_module ipykernel >= 6.9.2}
+BuildRequires:  %{python_module ipython >= 7.31.1}
 BuildRequires:  %{python_module jupyter_client >= 7.1.0}
 BuildRequires:  %{python_module matplotlib}
 BuildRequires:  %{python_module numpy}
@@ -53,8 +54,8 @@
 %endif
 # /SECTION
 Requires:       python-cloudpickle
-Requires:       python-ipykernel >= 6.6.1
-Requires:       python-ipython >= 7.6.0
+Requires:       python-ipykernel >= 6.9.2
+Requires:       python-ipython >= 7.31.1
 Requires:       python-jupyter_client >= 7.1.0
 Requires:       python-pyzmq >= 22.1
 Requires:       python-wurlitzer >= 1.0.3

++++++ python-spyder-kernels-2.2.1.tar.gz -> python-spyder-kernels-2.3.0.tar.gz 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spyder-kernels-2.2.1/.github/workflows/linux-pip-tests.yml 
new/spyder-kernels-2.3.0/.github/workflows/linux-pip-tests.yml
--- old/spyder-kernels-2.2.1/.github/workflows/linux-pip-tests.yml      
2022-01-13 16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/.github/workflows/linux-pip-tests.yml      
2022-03-30 04:17:43.000000000 +0200
@@ -22,6 +22,7 @@
       fail-fast: false 
       matrix:
         PYTHON_VERSION: ['2.7', '3.7', '3.8', '3.9']
+    timeout-minutes: 20
     steps:
       - name: Checkout branch
         uses: actions/checkout@v1
@@ -38,7 +39,10 @@
            python-version: ${{ matrix.PYTHON_VERSION }} 
       - name: Install package and dependencies
         shell: bash -l {0}
-        run: pip install -e .[test]
+        run: |
+          pip install -e .[test]
+          # Zict 2.1.0 is not compatible with Python 3
+          pip install zict==2.0.0
       - name: Show environment information
         shell: bash -l {0}
         run: |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spyder-kernels-2.2.1/.github/workflows/linux-tests.yml 
new/spyder-kernels-2.3.0/.github/workflows/linux-tests.yml
--- old/spyder-kernels-2.2.1/.github/workflows/linux-tests.yml  2022-01-13 
16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/.github/workflows/linux-tests.yml  2022-03-30 
04:17:43.000000000 +0200
@@ -18,11 +18,12 @@
       CI: True
       PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }}
       RUNNER_OS: 'ubuntu'
+      USE_CONDA: 'true'
     strategy:
       fail-fast: false 
       matrix:
         PYTHON_VERSION: ['2.7', '3.7', '3.8', '3.9']
-    timeout-minutes: 10
+    timeout-minutes: 20
     steps:
       - name: Checkout branch
         uses: actions/checkout@v1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spyder-kernels-2.2.1/.github/workflows/macos-tests.yml 
new/spyder-kernels-2.3.0/.github/workflows/macos-tests.yml
--- old/spyder-kernels-2.2.1/.github/workflows/macos-tests.yml  2022-01-13 
16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/.github/workflows/macos-tests.yml  2022-03-30 
04:17:43.000000000 +0200
@@ -22,7 +22,7 @@
       fail-fast: false 
       matrix:
         PYTHON_VERSION: ['2.7', '3.7', '3.8', '3.9']
-    timeout-minutes: 10
+    timeout-minutes: 25
     steps:
       - name: Checkout branch
         uses: actions/checkout@v1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spyder-kernels-2.2.1/CHANGELOG.md 
new/spyder-kernels-2.3.0/CHANGELOG.md
--- old/spyder-kernels-2.2.1/CHANGELOG.md       2022-01-13 16:55:24.000000000 
+0100
+++ new/spyder-kernels-2.3.0/CHANGELOG.md       2022-03-30 04:17:43.000000000 
+0200
@@ -1,5 +1,40 @@
 # History of changes
 
+## Version 2.3.0 (2022-03-30)
+
+### New features
+
+* Add new handler to detect the current Matplotlib interactive backend.
+* Print last line of cells and Pdb code.
+
+### Issues Closed
+
+* [Issue 371](https://github.com/spyder-ide/spyder-kernels/issues/371) - Can't 
debug comprehensions ([PR 
370](https://github.com/spyder-ide/spyder-kernels/pull/370) by 
[@impact27](https://github.com/impact27))
+* [Issue 344](https://github.com/spyder-ide/spyder-kernels/issues/344) - Drop 
support for Python 3.6 by requiring a more recent IPython version ([PR 
373](https://github.com/spyder-ide/spyder-kernels/pull/373) by 
[@ccordoba12](https://github.com/ccordoba12))
+* [Issue 330](https://github.com/spyder-ide/spyder-kernels/issues/330) - 
spydercustomize in debug stack ([PR 
355](https://github.com/spyder-ide/spyder-kernels/pull/355) by 
[@impact27](https://github.com/impact27))
+* [Issue 320](https://github.com/spyder-ide/spyder-kernels/issues/320) - The 
length of the "excluded_names" list increases. ([PR 
372](https://github.com/spyder-ide/spyder-kernels/pull/372) by 
[@impact27](https://github.com/impact27))
+
+In this release 4 issues were closed.
+
+### Pull Requests Merged
+
+* [PR 379](https://github.com/spyder-ide/spyder-kernels/pull/379) - PR: 
Increase minimal required version of ipykernel to 6.9.2, by 
[@ccordoba12](https://github.com/ccordoba12)
+* [PR 377](https://github.com/spyder-ide/spyder-kernels/pull/377) - PR: Handle 
getting dasks objects size, by [@dalthviz](https://github.com/dalthviz)
+* [PR 376](https://github.com/spyder-ide/spyder-kernels/pull/376) - PR: Add 
new handler to detect the current Matplotlib interactive backend, by 
[@ccordoba12](https://github.com/ccordoba12)
+* [PR 375](https://github.com/spyder-ide/spyder-kernels/pull/375) - PR: Remove 
support for outdated and unused Matplotlib backends, by 
[@ccordoba12](https://github.com/ccordoba12)
+* [PR 374](https://github.com/spyder-ide/spyder-kernels/pull/374) - PR: Catch 
FileNotFoundError when trying to get file code from the frontend, by 
[@ccordoba12](https://github.com/ccordoba12)
+* [PR 373](https://github.com/spyder-ide/spyder-kernels/pull/373) - PR: 
Increase minimal required IPython version to 7.31.1, by 
[@ccordoba12](https://github.com/ccordoba12) 
([344](https://github.com/spyder-ide/spyder-kernels/issues/344))
+* [PR 372](https://github.com/spyder-ide/spyder-kernels/pull/372) - PR: Avoid 
modifying the namespace view settings by reference, by 
[@impact27](https://github.com/impact27) 
([320](https://github.com/spyder-ide/spyder-kernels/issues/320))
+* [PR 370](https://github.com/spyder-ide/spyder-kernels/pull/370) - PR: Fix 
issues when debugging comprehensions, by 
[@impact27](https://github.com/impact27) 
([371](https://github.com/spyder-ide/spyder-kernels/issues/371))
+* [PR 369](https://github.com/spyder-ide/spyder-kernels/pull/369) - PR: Leave 
unsaved files in linecache, by [@impact27](https://github.com/impact27)
+* [PR 355](https://github.com/spyder-ide/spyder-kernels/pull/355) - PR: Print 
last line of Pdb code and some debugger fixes, by 
[@impact27](https://github.com/impact27) 
([330](https://github.com/spyder-ide/spyder-kernels/issues/330))
+
+In this release 10 pull requests were closed.
+
+
+----
+
+
 ## Version 2.2.1 (2022-01-13)
 
 ### Issues Closed
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spyder-kernels-2.2.1/requirements/posix.txt 
new/spyder-kernels-2.3.0/requirements/posix.txt
--- old/spyder-kernels-2.2.1/requirements/posix.txt     2022-01-13 
16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/requirements/posix.txt     2022-03-30 
04:17:43.000000000 +0200
@@ -1,6 +1,6 @@
 cloudpickle
-ipykernel>=6.6.1
-ipython>=7.6.0,<8
+ipykernel>=6.9.2
+ipython>=7.31.1,<8
 jupyter_client>=7.1.0
 pyzmq>=22.1.0
 wurlitzer>=1.0.3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spyder-kernels-2.2.1/requirements/python-27.txt 
new/spyder-kernels-2.3.0/requirements/python-27.txt
--- old/spyder-kernels-2.2.1/requirements/python-27.txt 2022-01-13 
16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/requirements/python-27.txt 2022-03-30 
04:17:43.000000000 +0200
@@ -7,3 +7,5 @@
 wurlitzer>=1.0.3
 # To avoid an error with conda
 click =7
+# To avoid a problem with zict
+zict <2.1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spyder-kernels-2.2.1/requirements/tests.txt 
new/spyder-kernels-2.3.0/requirements/tests.txt
--- old/spyder-kernels-2.2.1/requirements/tests.txt     2022-01-13 
16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/requirements/tests.txt     2022-03-30 
04:17:43.000000000 +0200
@@ -11,5 +11,3 @@
 scipy
 xarray
 pillow
-# Remove when Anaconda updates to a newer ipykernel
-ipython_genutils
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spyder-kernels-2.2.1/requirements/windows.txt 
new/spyder-kernels-2.3.0/requirements/windows.txt
--- old/spyder-kernels-2.2.1/requirements/windows.txt   2022-01-13 
16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/requirements/windows.txt   2022-03-30 
04:17:43.000000000 +0200
@@ -1,7 +1,5 @@
 cloudpickle
-ipykernel>=6.6.1
-ipython>=7.6.0,<8
+ipykernel>=6.9.2
+ipython>=7.31.1,<8
 jupyter_client>=7.1.0
 pyzmq>=22.1.0
-# Pin pip version since we are getting 9.x and it causes IPython 6.1.0 to be 
installed
-pip>=19.3.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spyder-kernels-2.2.1/setup.py 
new/spyder-kernels-2.3.0/setup.py
--- old/spyder-kernels-2.2.1/setup.py   2022-01-13 16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/setup.py   2022-03-30 04:17:43.000000000 +0200
@@ -40,9 +40,9 @@
     'backports.functools-lru-cache; python_version<"3"',
     'cloudpickle',
     'ipykernel<5; python_version<"3"',
-    'ipykernel>=6.6.1; python_version>="3"',
+    'ipykernel>=6.9.2; python_version>="3"',
     'ipython<6; python_version<"3"',
-    'ipython>=7.6.0,<8; python_version>="3"',
+    'ipython>=7.31.1,<8; python_version>="3"',
     'jupyter-client>=5.3.4,<6; python_version<"3"',
     'jupyter-client>=7.1.0; python_version>="3"',
     'pyzmq>=17,<20; python_version<"3"',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spyder-kernels-2.2.1/spyder_kernels/_version.py 
new/spyder-kernels-2.3.0/spyder_kernels/_version.py
--- old/spyder-kernels-2.2.1/spyder_kernels/_version.py 2022-01-13 
16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/spyder_kernels/_version.py 2022-03-30 
04:17:43.000000000 +0200
@@ -8,5 +8,5 @@
 
 """Version File."""
 
-VERSION_INFO = (2, 2, 1)
+VERSION_INFO = (2, 3, 0)
 __version__ = '.'.join(map(str, VERSION_INFO))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spyder-kernels-2.2.1/spyder_kernels/comms/frontendcomm.py 
new/spyder-kernels-2.3.0/spyder_kernels/comms/frontendcomm.py
--- old/spyder-kernels-2.2.1/spyder_kernels/comms/frontendcomm.py       
2022-01-13 16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/spyder_kernels/comms/frontendcomm.py       
2022-03-30 04:17:43.000000000 +0200
@@ -34,7 +34,7 @@
     return port
 
 
-def frontend_request(blocking=True, timeout=None):
+def frontend_request(blocking, timeout=None):
     """
     Send a request to the frontend.
 
@@ -261,8 +261,11 @@
         """Call the callback function for the remote call."""
         saved_stdout_write = sys.stdout.write
         saved_stderr_write = sys.stderr.write
-        sys.stdout.write = WriteWrapper(saved_stdout_write, call_name)
-        sys.stderr.write = WriteWrapper(saved_stderr_write, call_name)
+        thread_id = threading.get_ident()
+        sys.stdout.write = WriteWrapper(
+            saved_stdout_write, call_name, thread_id)
+        sys.stderr.write = WriteWrapper(
+            saved_stderr_write, call_name, thread_id)
         try:
             return super(FrontendComm, self)._remote_callback(
                 call_name, call_args, call_kwargs)
@@ -274,9 +277,10 @@
 class WriteWrapper(object):
     """Wrapper to warn user when text is printed."""
 
-    def __init__(self, write, name):
+    def __init__(self, write, name, thread_id):
         self._write = write
         self._name = name
+        self._thread_id = thread_id
         self._warning_shown = False
 
     def is_benign_message(self, message):
@@ -293,6 +297,9 @@
 
     def __call__(self, string):
         """Print warning once."""
+        if self._thread_id != threading.get_ident():
+            return self._write(string)
+
         if not self.is_benign_message(string):
             if not self._warning_shown:
                 self._warning_shown = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spyder-kernels-2.2.1/spyder_kernels/console/kernel.py 
new/spyder-kernels-2.3.0/spyder_kernels/console/kernel.py
--- old/spyder-kernels-2.2.1/spyder_kernels/console/kernel.py   2022-01-13 
16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/spyder_kernels/console/kernel.py   2022-03-30 
04:17:43.000000000 +0200
@@ -12,6 +12,7 @@
 
 # Standard library imports
 from distutils.version import LooseVersion
+import logging
 import os
 import sys
 import threading
@@ -22,9 +23,9 @@
 from traitlets.config.loader import LazyConfigValue
 
 # Local imports
-from spyder_kernels.py3compat import TEXT_TYPES, to_text_string
-from spyder_kernels.comms.frontendcomm import FrontendComm
-from spyder_kernels.py3compat import PY3, input
+from spyder_kernels.py3compat import (
+    TEXT_TYPES, to_text_string, PY3, input, TimeoutError)
+from spyder_kernels.comms.frontendcomm import FrontendComm, CommError
 from spyder_kernels.utils.iofuncs import iofunctions
 from spyder_kernels.utils.mpl import (
     MPL_BACKENDS_FROM_SPYDER, MPL_BACKENDS_TO_SPYDER, INLINE_FIGURE_FORMATS)
@@ -34,6 +35,10 @@
 if PY3:
     import faulthandler
 
+
+logger = logging.getLogger(__name__)
+
+
 # Excluded variables from the Variable Explorer (i.e. they are not
 # shown at all there)
 EXCLUDED_NAMES = ['In', 'Out', 'exit', 'get_ipython', 'quit']
@@ -78,6 +83,7 @@
             'update_syspath': self.update_syspath,
             'is_special_kernel_valid': self.is_special_kernel_valid,
             'get_matplotlib_backend': self.get_matplotlib_backend,
+            'get_mpl_interactive_backend': self.get_mpl_interactive_backend,
             'pdb_input_reply': self.pdb_input_reply,
             '_interrupt_eventloop': self._interrupt_eventloop,
             'enable_faulthandler': self.enable_faulthandler,
@@ -88,7 +94,6 @@
                 call_id, handlers[call_id])
 
         self.namespace_view_settings = {}
-        self._pdb_step = None
         self._mpl_backend_error = None
         self._running_namespace = None
         self._pdb_input_line = None
@@ -301,13 +306,20 @@
             return self.shell.pdb_session.do_complete(code, cursor_pos)
         return self._do_complete(code, cursor_pos)
 
-    def publish_pdb_state(self):
-        """Publish Pdb state."""
-        if self.shell.pdb_session:
-            state = dict(namespace_view = self.get_namespace_view(),
-                         var_properties = self.get_var_properties(),
-                         step = self._pdb_step)
+    def publish_pdb_state(self, step):
+        """
+        Publish Variable Explorer state and Pdb step through
+        send_spyder_msg.
+        """
+        state = dict(
+            namespace_view=self.get_namespace_view(),
+            var_properties=self.get_var_properties(),
+            step=step
+         )
+        try:
             self.frontend_call(blocking=False).pdb_state(state)
+        except (CommError, TimeoutError):
+            logger.debug("Could not send Pdb state to the frontend.")
 
     def set_spyder_breakpoints(self, breakpoints):
         """
@@ -437,6 +449,60 @@
         except Exception:
             return None
 
+    def get_mpl_interactive_backend(self):
+        """
+        Get current Matplotlib interactive backend.
+
+        This is different from the current backend because, for instance, the
+        user can set first the Qt5 backend, then the Inline one. In that case,
+        the current backend is Inline, but the current interactive one is Qt5,
+        and this backend can't be changed without a kernel restart.
+        """
+        # Mapping from frameworks to backend names.
+        mapping = {
+            'qt': 'QtAgg',  # For Matplotlib 3.5+
+            'qt5': 'Qt5Agg',
+            'tk': 'TkAgg',
+            'macosx': 'MacOSX'
+        }
+
+        try:
+            # --- Get interactive framework
+            framework = None
+
+            # This is necessary because _get_running_interactive_framework
+            # can't detect Tk in a Jupyter kernel.
+            if hasattr(self, 'app_wrapper'):
+                if hasattr(self.app_wrapper, 'app'):
+                    import tkinter
+                    if isinstance(self.app_wrapper.app, tkinter.Tk):
+                        framework = 'tk'
+
+            if framework is None:
+                try:
+                    # This is necessary for Matplotlib 3.3.0+
+                    from matplotlib import cbook
+                    framework = cbook._get_running_interactive_framework()
+                except AttributeError:
+                    # For older versions
+                    from matplotlib import backends
+                    framework = backends._get_running_interactive_framework()
+
+            # --- Return backend according to framework
+            if framework is None:
+                # Since no interactive backend has been set yet, this is
+                # equivalent to having the inline one.
+                return 0
+            elif framework in mapping:
+                return MPL_BACKENDS_TO_SPYDER[mapping[framework]]
+            else:
+                # This covers the case of other backends (e.g. Wx or Gtk)
+                # which users can set interactively with the %matplotlib
+                # magic but not through our Preferences.
+                return -1
+        except Exception:
+            return None
+
     def set_matplotlib_backend(self, backend, pylab=False):
         """Set matplotlib backend given a Spyder backend option."""
         mpl_backend = MPL_BACKENDS_FROM_SPYDER[to_text_string(backend)]
@@ -702,7 +768,12 @@
         """
         import traceback
         from IPython.core.getipython import get_ipython
-        import matplotlib
+
+        # Don't proceed further if there's any error while importing Matplotlib
+        try:
+            import matplotlib
+        except Exception:
+            return
 
         generic_error = (
             "\n" + "="*73 + "\n"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spyder-kernels-2.2.1/spyder_kernels/console/tests/test_console_kernel.py 
new/spyder-kernels-2.3.0/spyder_kernels/console/tests/test_console_kernel.py
--- 
old/spyder-kernels-2.2.1/spyder_kernels/console/tests/test_console_kernel.py    
    2022-01-13 16:55:24.000000000 +0100
+++ 
new/spyder-kernels-2.3.0/spyder_kernels/console/tests/test_console_kernel.py    
    2022-03-30 04:17:43.000000000 +0200
@@ -33,6 +33,7 @@
 # Local imports
 from spyder_kernels.py3compat import PY2, PY3, to_text_string
 from spyder_kernels.utils.iofuncs import iofunctions
+from spyder_kernels.utils.mpl import MPL_BACKENDS_FROM_SPYDER
 from spyder_kernels.utils.test_utils import get_kernel, get_log_text
 from spyder_kernels.customize.spyderpdb import SpyderPdb
 
@@ -1093,5 +1094,44 @@
     pdb_obj.curframe_locals = None
 
 
+@flaky(max_runs=3)
+@pytest.mark.parametrize("backend", [None, 'inline', 'tk', 'qt5'])
+@pytest.mark.skipif(PY2, reason="Doesn't work on Python 2")
+@pytest.mark.skipif(
+    not sys.platform.startswith('linux'),
+    reason="Doesn't work reliably on Windows and Mac")
+@pytest.mark.skipif(
+    not bool(os.environ.get('USE_CONDA')),
+    reason="Doesn't work with pip packages")
+def test_get_interactive_backend(backend):
+    """
+    Test that we correctly get the interactive backend set in the kernel.
+    """
+    cmd = "from spyder_kernels.console import start; start.main()"
+
+    with setup_kernel(cmd) as client:
+        # Set backend
+        if backend is not None:
+            client.execute("%matplotlib {}".format(backend))
+            client.get_shell_msg(timeout=TIMEOUT)
+
+        # Get backend
+        code = "backend = get_ipython().kernel.get_mpl_interactive_backend()"
+        client.execute(code, user_expressions={'output': 'backend'})
+        reply = client.get_shell_msg(timeout=TIMEOUT)
+        while 'user_expressions' not in reply['content']:
+            reply = client.get_shell_msg(timeout=TIMEOUT)
+
+        # Get value obtained through user_expressions
+        user_expressions = reply['content']['user_expressions']
+        value = user_expressions['output']['data']['text/plain']
+
+        # Assert we got the right interactive backend
+        if backend is not None:
+            assert MPL_BACKENDS_FROM_SPYDER[value] == backend
+        else:
+            assert value == '0'
+
+
 if __name__ == "__main__":
     pytest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spyder-kernels-2.2.1/spyder_kernels/customize/namespace_manager.py 
new/spyder-kernels-2.3.0/spyder_kernels/customize/namespace_manager.py
--- old/spyder-kernels-2.2.1/spyder_kernels/customize/namespace_manager.py      
2022-01-13 16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/spyder_kernels/customize/namespace_manager.py      
2022-03-30 04:17:43.000000000 +0200
@@ -4,8 +4,9 @@
 # Licensed under the terms of the MIT License
 # (see spyder_kernels/__init__.py for details)
 
-import sys
 import linecache
+import os.path
+import sys
 
 from IPython.core.getipython import get_ipython
 
@@ -21,7 +22,7 @@
     """
 
     def __init__(self, filename, namespace=None, current_namespace=False,
-                 file_code=None):
+                 file_code=None, stack_depth=1):
         self.filename = filename
         self.ns_globals = namespace
         self.ns_locals = None
@@ -30,6 +31,9 @@
         self._previous_main = None
         self._reset_main = False
         self._file_code = file_code
+        ipython_shell = get_ipython()
+        self.context_globals = ipython_shell.get_global_scope(stack_depth + 1)
+        self.context_locals = ipython_shell.get_local_scope(stack_depth + 1)
 
     def __enter__(self):
         """
@@ -39,10 +43,8 @@
         ipython_shell = get_ipython()
         if self.ns_globals is None:
             if self.current_namespace:
-                # stack_depth = parent of calling function
-                stack_depth = 2
-                self.ns_globals = ipython_shell.get_global_scope(stack_depth)
-                self.ns_locals = ipython_shell.get_local_scope(stack_depth)
+                self.ns_globals = self.context_globals
+                self.ns_locals = self.context_locals
                 if '__file__' in self.ns_globals:
                     self._previous_filename = self.ns_globals['__file__']
                 self.ns_globals['__file__'] = self.filename
@@ -91,17 +93,13 @@
             self.ns_globals.pop('__file__')
 
         if not self.current_namespace:
-            # stack_depth = parent of calling function
-            stack_depth = 2
-            ns_globals = ipython_shell.get_global_scope(stack_depth)
-            ns_locals = ipython_shell.get_local_scope(stack_depth)
-            ns_globals.update(self.ns_globals)
-            if ns_locals and self.ns_locals:
-                ns_locals.update(self.ns_locals)
+            self.context_globals.update(self.ns_globals)
+            if self.context_locals and self.ns_locals:
+                self.context_locals.update(self.ns_locals)
 
         if self._previous_main:
             sys.modules['__main__'] = self._previous_main
         elif '__main__' in sys.modules and self._reset_main:
             del sys.modules['__main__']
-        if self.filename in linecache.cache:
+        if self.filename in linecache.cache and os.path.exists(self.filename):
             linecache.cache.pop(self.filename)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spyder-kernels-2.2.1/spyder_kernels/customize/spydercustomize.py 
new/spyder-kernels-2.3.0/spyder_kernels/customize/spydercustomize.py
--- old/spyder-kernels-2.2.1/spyder_kernels/customize/spydercustomize.py        
2022-01-13 16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/spyder_kernels/customize/spydercustomize.py        
2022-03-30 04:17:43.000000000 +0200
@@ -11,6 +11,7 @@
 # Spyder consoles sitecustomize
 #
 
+import ast
 import bdb
 import cmd
 import io
@@ -27,9 +28,12 @@
 
 from spyder_kernels.comms.frontendcomm import CommError, frontend_request
 from spyder_kernels.customize.namespace_manager import NamespaceManager
-from spyder_kernels.customize.spyderpdb import SpyderPdb, enter_debugger
+from spyder_kernels.customize.spyderpdb import SpyderPdb, get_new_debugger
 from spyder_kernels.customize.umr import UserModuleReloader
-from spyder_kernels.py3compat import TimeoutError, PY2, _print, encode
+from spyder_kernels.py3compat import (
+    TimeoutError, PY2, _print, encode, compat_exec, FileNotFoundError)
+from spyder_kernels.customize.utils import (
+    capture_last_Expr, normalise_filename)
 
 if not PY2:
     from IPython.core.inputtransformer2 import (
@@ -377,7 +381,7 @@
 def get_current_file_name():
     """Get the current file name."""
     try:
-        return frontend_request().current_filename()
+        return frontend_request(blocking=True).current_filename()
     except Exception:
         _print("This command failed to be executed because an error occurred"
                " while trying to get the current file name from Spyder's"
@@ -422,7 +426,8 @@
     return '\n' * number_empty_lines + code
 
 
-def exec_code(code, filename, ns_globals, ns_locals=None, post_mortem=False):
+def exec_code(code, filename, ns_globals, ns_locals=None, post_mortem=False,
+              exec_fun=None, capture_last_expression=False):
     """Execute code and display any exception."""
     # Tell IPython to hide this frame (>7.16)
     __tracebackhide__ = True
@@ -432,6 +437,10 @@
         filename = encode(filename)
         code = encode(code)
 
+    if exec_fun is None:
+        # Replace by exec when dropping Python 2
+        exec_fun = compat_exec
+
     ipython_shell = get_ipython()
     is_ipython = os.path.splitext(filename)[1] == '.ipy'
     try:
@@ -439,11 +448,10 @@
             # TODO: remove the try-except and let the SyntaxError raise
             # Because there should not be ipython code in a python file
             try:
-                compiled = compile(
-                    transform_cell(code, indent_only=True), filename, 'exec')
+                ast_code = ast.parse(transform_cell(code, indent_only=True))
             except SyntaxError as e:
                 try:
-                    compiled = compile(transform_cell(code), filename, 'exec')
+                    ast_code = ast.parse(transform_cell(code))
                 except SyntaxError:
                     if PY2:
                         raise e
@@ -461,8 +469,23 @@
                             ".ipy extension.\n")
                         SHOW_INVALID_SYNTAX_MSG = False
         else:
-            compiled = compile(transform_cell(code), filename, 'exec')
-        exec(compiled, ns_globals, ns_locals)
+            ast_code = ast.parse(transform_cell(code))
+
+        if code.rstrip()[-1] == ";":
+            # Supress output with ;
+            capture_last_expression = False
+
+        if capture_last_expression:
+            ast_code, capture_last_expression = capture_last_Expr(
+                ast_code, "_spyder_out")
+
+        exec_fun(compile(ast_code, filename, 'exec'), ns_globals, ns_locals)
+
+        if capture_last_expression:
+            out = ns_globals.pop("_spyder_out", None)
+            if out is not None:
+                return out
+
     except SystemExit as status:
         # ignore exit(0)
         if status.code:
@@ -478,16 +501,17 @@
         else:
             # We ignore the call to exec
             ipython_shell.showtraceback(tb_offset=1)
-    __tracebackhide__ = "__pdb_exit__"
+    finally:
+        __tracebackhide__ = "__pdb_exit__"
 
 
 def get_file_code(filename, save_all=True):
     """Retrive the content of a file."""
     # Get code from spyder
     try:
-        file_code = frontend_request().get_file_code(
+        file_code = frontend_request(blocking=True).get_file_code(
             filename, save_all=save_all)
-    except (CommError, TimeoutError, RuntimeError):
+    except (CommError, TimeoutError, RuntimeError, FileNotFoundError):
         file_code = None
     if file_code is None:
         with open(filename, 'r') as f:
@@ -505,6 +529,14 @@
     post_mortem: boolean, whether to enter post-mortem mode on error
     current_namespace: if true, run the file in the current namespace
     """
+    return _exec_file(
+        filename, args, wdir, namespace,
+        post_mortem, current_namespace, stack_depth=1)
+
+
+def _exec_file(filename=None, args=None, wdir=None, namespace=None,
+               post_mortem=False, current_namespace=False, stack_depth=0,
+               exec_fun=None):
     # Tell IPython to hide this frame (>7.16)
     __tracebackhide__ = True
     ipython_shell = get_ipython()
@@ -543,8 +575,13 @@
         _print("Could not get code from editor.\n")
         return
 
+    # Normalise the filename
+    filename = os.path.abspath(filename)
+    filename = os.path.normcase(filename)
+
     with NamespaceManager(filename, namespace, current_namespace,
-                          file_code=file_code) as (ns_globals, ns_locals):
+                          file_code=file_code, stack_depth=stack_depth + 1
+                          ) as (ns_globals, ns_locals):
         sys.argv = [filename]
         if args is not None:
             for arg in shlex.split(args):
@@ -577,15 +614,17 @@
             else:
                 _print("Working directory {} doesn't exist.\n".format(wdir))
 
-        if __umr__.has_cython:
-            # Cython files
-            with io.open(filename, encoding='utf-8') as f:
-                ipython_shell.run_cell_magic('cython', '', f.read())
-        else:
-            exec_code(file_code, filename, ns_globals, ns_locals,
-                      post_mortem=post_mortem)
-
-        sys.argv = ['']
+        try:
+            if __umr__.has_cython:
+                # Cython files
+                with io.open(filename, encoding='utf-8') as f:
+                    ipython_shell.run_cell_magic('cython', '', f.read())
+            else:
+                exec_code(file_code, filename, ns_globals, ns_locals,
+                          post_mortem=post_mortem, exec_fun=exec_fun,
+                          capture_last_expression=False)
+        finally:
+            sys.argv = ['']
 
 
 # IPykernel 6.3.0+ shadows our runfile because it depends on the Pydev
@@ -612,11 +651,27 @@
         if filename is None:
             return
 
-    enter_debugger(
-        filename, True,
-        "runfile({}" +
-        ", args=%r, wdir=%r, current_namespace=%r)" % (
-            args, wdir, current_namespace))
+    shell = get_ipython()
+    if shell.is_debugging():
+        # Recursive
+        code = (
+            "runfile({}".format(repr(normalise_filename(filename))) +
+            ", args=%r, wdir=%r, current_namespace=%r)" % (
+                args, wdir, current_namespace)
+        )
+
+        shell.pdb_session.enter_recursive_debugger(
+            code, filename, True,
+        )
+    else:
+        debugger = get_new_debugger(filename, True)
+        _exec_file(
+            filename=debugger.canonic(filename),
+            args=args, wdir=wdir,
+            current_namespace=current_namespace,
+            exec_fun=debugger.run,
+            stack_depth=1
+        )
 
 
 builtins.debugfile = debugfile
@@ -626,16 +681,24 @@
     """
     Run a code cell from an editor as a file.
 
-    Currently looks for code in an `ipython` property called `cell_code`.
-    This property must be set by the editor prior to calling this function.
-    This function deletes the contents of `cell_code` upon completion.
-
     Parameters
     ----------
     cellname : str or int
         Cell name or index.
     filename : str
         Needed to allow for proper traceback links.
+    post_mortem: bool
+        Automatically enter post mortem on exception.
+    """
+    # Tell IPython to hide this frame (>7.16)
+    __tracebackhide__ = True
+    return _exec_cell(cellname, filename, post_mortem, stack_depth=1)
+
+
+def _exec_cell(cellname, filename=None, post_mortem=False, stack_depth=0,
+               exec_fun=None):
+    """
+    Execute a code cell with a given exec function.
     """
     # Tell IPython to hide this frame (>7.16)
     __tracebackhide__ = True
@@ -657,7 +720,8 @@
     ipython_shell = get_ipython()
     try:
         # Get code from spyder
-        cell_code = frontend_request().run_cell(cellname, filename)
+        cell_code = frontend_request(
+            blocking=True).run_cell(cellname, filename)
     except Exception:
         _print("This command failed to be executed because an error occurred"
                " while trying to get the cell code from Spyder's"
@@ -676,10 +740,17 @@
         file_code = get_file_code(filename, save_all=False)
     except Exception:
         file_code = None
+
+    # Normalise the filename
+    filename = os.path.abspath(filename)
+    filename = os.path.normcase(filename)
+
     with NamespaceManager(filename, current_namespace=True,
-                          file_code=file_code) as (ns_globals, ns_locals):
-        exec_code(cell_code, filename, ns_globals, ns_locals,
-                  post_mortem=post_mortem)
+                          file_code=file_code, stack_depth=stack_depth + 1
+                          ) as (ns_globals, ns_locals):
+        return exec_code(cell_code, filename, ns_globals, ns_locals,
+                         post_mortem=post_mortem, exec_fun=exec_fun,
+                         capture_last_expression=True)
 
 
 builtins.runcell = runcell
@@ -694,10 +765,24 @@
         if filename is None:
             return
 
-    enter_debugger(
-        filename, False,
-        "runcell({}, ".format(repr(cellname)) +
-        "{})")
+    shell = get_ipython()
+    if shell.is_debugging():
+        # Recursive
+        code = (
+            "runcell({}, ".format(repr(cellname)) +
+            "{})".format(repr(normalise_filename(filename)))
+        )
+        shell.pdb_session.enter_recursive_debugger(
+            code, filename, False,
+        )
+    else:
+        debugger = get_new_debugger(filename, False)
+        _exec_cell(
+            cellname=cellname,
+            filename=debugger.canonic(filename),
+            exec_fun=debugger.run,
+            stack_depth=1
+        )
 
 
 builtins.debugcell = debugcell
@@ -718,7 +803,7 @@
             raise RuntimeError('Could not get cell count from frontend.')
     try:
         # Get code from spyder
-        cell_count = frontend_request().cell_count(filename)
+        cell_count = frontend_request(blocking=True).cell_count(filename)
         return cell_count
     except Exception:
         etype, error, tb = sys.exc_info()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spyder-kernels-2.2.1/spyder_kernels/customize/spyderpdb.py 
new/spyder-kernels-2.3.0/spyder_kernels/customize/spyderpdb.py
--- old/spyder-kernels-2.2.1/spyder_kernels/customize/spyderpdb.py      
2022-01-13 16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/spyder_kernels/customize/spyderpdb.py      
2022-03-30 04:17:43.000000000 +0200
@@ -19,7 +19,7 @@
 from IPython.core.getipython import get_ipython
 
 from spyder_kernels.comms.frontendcomm import CommError, frontend_request
-from spyder_kernels.customize.utils import path_is_library
+from spyder_kernels.customize.utils import path_is_library, capture_last_Expr
 from spyder_kernels.py3compat import TimeoutError, PY2, _print, isidentifier
 
 if not PY2:
@@ -175,6 +175,15 @@
                 if execute_events:
                      get_ipython().events.trigger('pre_execute')
 
+                code_ast = ast.parse(line)
+
+                if line.rstrip()[-1] == ";":
+                    # Supress output with ;
+                    capture_last_expression = False
+                else:
+                    code_ast, capture_last_expression = capture_last_Expr(
+                        code_ast, "_spyderpdb_out")
+
                 if locals is not globals:
                     # Mitigates a behaviour of CPython that makes it difficult
                     # to work with exec and the local namespace
@@ -195,46 +204,57 @@
                     # a copy of the curframe locals. This means that closures
                     # for example are early binding instead of late binding.
 
-                    # Check if line is an expression to print
-                    print_ret = False
-                    try:
-                        code = ast.parse(line + '\n', '<stdin>', 'single')
-                        if len(code.body) == 1:
-                            print_ret = isinstance(code.body[0], ast.Expr)
-                    except SyntaxError:
-                        pass
-
-                    # Create a function and load the locals
-                    globals["_spyderpdb_locals"] = locals
+                    # Create a function
                     indent = "    "
                     code = ["def _spyderpdb_code():"]
+
+                    # Load the locals
+                    globals["_spyderpdb_builtins_locals"] = builtins.locals
+
+                    # Save builtins locals in case it is shadowed
+                    globals["_spyderpdb_locals"] = locals
+
+                    # Load locals if they have a valid name
+                    # In comprehensions, locals could contain ".0" for example
                     code += [indent + "{k} = _spyderpdb_locals['{k}']".format(
-                        k=k) for k in locals]
+                        k=k) for k in locals if isidentifier(k)]
 
-                    # Run the code
-                    if print_ret:
-                        code += [indent + 'print(' + line.strip() + ")"]
-                    else:
-                        code += [indent + l for l in line.splitlines()]
 
                     # Update the locals
-                    code += [indent + "_spyderpdb_locals.update(locals())"]
+                    code += [indent + "_spyderpdb_locals.update("
+                             "_spyderpdb_builtins_locals())"]
 
                     # Run the function
                     code += ["_spyderpdb_code()"]
-                    code = compile('\n'.join(code) + '\n', '<stdin>', 'exec')
-                    try:
-                        exec(code, globals)
-                    finally:
-                        globals.pop("_spyderpdb_locals", None)
-                        globals.pop("_spyderpdb_code", None)
-                else:
-                    try:
-                        code = compile(line + '\n', '<stdin>', 'single')
-                    except SyntaxError:
-                        # Support multiline statments
-                        code = compile(line + '\n', '<stdin>', 'exec')
-                    exec(code, globals)
+
+                    # Cleanup
+                    code += [
+                        "del _spyderpdb_code",
+                        "del _spyderpdb_locals",
+                        "del _spyderpdb_builtins_locals"
+                    ]
+
+                    # Parse the function
+                    fun_ast = ast.parse('\n'.join(code) + '\n')
+
+                    # Inject code_ast in the function before the locals update
+                    fun_ast.body[0].body = (
+                        fun_ast.body[0].body[:-1]  # The locals
+                        + code_ast.body  # Code to run
+                        + fun_ast.body[0].body[-1:]  # Locals update
+                    )
+                    code_ast = fun_ast
+
+                exec(compile(code_ast, "<stdin>", "exec"), globals)
+
+                if capture_last_expression:
+                    out = globals.pop("_spyderpdb_out", None)
+                    if out is not None:
+                        sys.stdout.flush()
+                        sys.stderr.flush()
+                        frontend_request(blocking=False).show_pdb_output(
+                            repr(out))
+
             finally:
                 if execute_events:
                      get_ipython().events.trigger('post_execute')
@@ -329,7 +349,7 @@
         Take a number as argument as an (optional) number of context line to
         print"""
         super(SpyderPdb, self).do_where(arg)
-        frontend_request().do_where()
+        frontend_request(blocking=False).do_where()
 
     do_w = do_where
 
@@ -525,7 +545,7 @@
     def preloop(self):
         """Ask Spyder for breakpoints before the first prompt is created."""
         try:
-            pdb_settings = frontend_request().get_pdb_settings()
+            pdb_settings = frontend_request(blocking=True).get_pdb_settings()
             self.pdb_ignore_lib = pdb_settings['pdb_ignore_lib']
             self.pdb_execute_events = pdb_settings['pdb_execute_events']
             self.pdb_use_exclamation_mark = pdb_settings[
@@ -626,7 +646,7 @@
         """
         Notify spyder on any pdb command.
         """
-        self.notify_spyder(self.curframe)
+        self.notify_spyder()
         return super(SpyderPdb, self).postcmd(stop, line)
 
     if PY2:
@@ -723,16 +743,13 @@
                     logger.debug(
                         "Could not send a Pdb continue call to the frontend.")
 
-    def notify_spyder(self, frame=None):
+    def notify_spyder(self):
         """Send kernel state to the frontend."""
-        if frame is None:
-            frame = self.curframe
 
+        frame = self.curframe
         if frame is None:
             return
 
-        kernel = get_ipython().kernel
-
         # Get filename and line number of the current frame
         fname = self.canonic(frame.f_code.co_filename)
         if PY2:
@@ -747,13 +764,7 @@
         if isinstance(fname, basestring) and isinstance(lineno, int):
             step = dict(fname=fname, lineno=lineno)
 
-        # Publish Pdb state so we can update the Variable Explorer
-        # and the Editor on the Spyder side
-        kernel._pdb_step = step
-        try:
-            kernel.publish_pdb_state()
-        except (CommError, TimeoutError):
-            logger.debug("Could not send Pdb state to the frontend.")
+        get_ipython().kernel.publish_pdb_state(step)
 
     def run(self, cmd, globals=None, locals=None):
         """Debug a statement executed via the exec() function.
@@ -782,43 +793,42 @@
         with DebugWrapper(self):
             super(SpyderPdb, self).runcall(*args, **kwds)
 
-
-def enter_debugger(filename, continue_if_has_breakpoints, code_format):
-    """Enter debugger. Code format should be a format that accept filename."""
-    shell = get_ipython()
-    recursive = shell.is_debugging()
-    if recursive:
-        parent_debugger = shell.pdb_session
+    def enter_recursive_debugger(self, code, filename,
+                                 continue_if_has_breakpoints):
+        """
+        Enter debugger recursively.
+        """
         sys.settrace(None)
-        globals = parent_debugger.curframe.f_globals
-        locals = parent_debugger.curframe_locals
+        globals = self.curframe.f_globals
+        locals = self.curframe_locals
         # Create child debugger
         debugger = SpyderPdb(
-            completekey=parent_debugger.completekey,
-            stdin=parent_debugger.stdin, stdout=parent_debugger.stdout)
-        debugger.use_rawinput = parent_debugger.use_rawinput
-        debugger.prompt = "(%s) " % parent_debugger.prompt.strip()
-    else:
-        debugger = SpyderPdb()
+            completekey=self.completekey,
+            stdin=self.stdin, stdout=self.stdout)
+        debugger.use_rawinput = self.use_rawinput
+        debugger.prompt = "(%s) " % self.prompt.strip()
+
+        filename = debugger.canonic(filename)
+        debugger._wait_for_mainpyfile = True
+        debugger.mainpyfile = filename
+        debugger.continue_if_has_breakpoints = continue_if_has_breakpoints
+        debugger._user_requested_quit = False
+
+        # Enter recursive debugger
+        sys.call_tracing(debugger.run, (code, globals, locals))
+        # Reset parent debugger
+        sys.settrace(self.trace_dispatch)
+        self.lastcmd = debugger.lastcmd
+        get_ipython().pdb_session = self
+
+
+def get_new_debugger(filename, continue_if_has_breakpoints):
+    """Get a new debugger."""
+    debugger = SpyderPdb()
 
     filename = debugger.canonic(filename)
     debugger._wait_for_mainpyfile = True
     debugger.mainpyfile = filename
     debugger.continue_if_has_breakpoints = continue_if_has_breakpoints
     debugger._user_requested_quit = False
-
-    if os.name == 'nt':
-        filename = filename.replace('\\', '/')
-
-    code = code_format.format(repr(filename))
-
-    if recursive:
-        # Enter recursive debugger
-        sys.call_tracing(debugger.run, (code, globals, locals))
-        # Reset parent debugger
-        sys.settrace(parent_debugger.trace_dispatch)
-        parent_debugger.lastcmd = debugger.lastcmd
-        shell.pdb_session = parent_debugger
-    else:
-        # The breakpoint might not be in the cell
-        debugger.run(code)
+    return debugger
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/spyder-kernels-2.2.1/spyder_kernels/customize/utils.py 
new/spyder-kernels-2.3.0/spyder_kernels/customize/utils.py
--- old/spyder-kernels-2.2.1/spyder_kernels/customize/utils.py  2022-01-13 
16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/spyder_kernels/customize/utils.py  2022-03-30 
04:17:43.000000000 +0200
@@ -5,6 +5,7 @@
 
 """Utility functions."""
 
+import ast
 import os
 import re
 import sysconfig
@@ -92,3 +93,33 @@
             return False
     else:
         return False
+
+
+def capture_last_Expr(code_ast, out_varname):
+    """Parse line and modify code to capture in globals the last expression."""
+    # Modify ast code to capture the last expression
+    capture_last_expression = False
+    if (
+        len(code_ast.body)
+        and isinstance(code_ast.body[-1], ast.Expr)
+    ):
+        capture_last_expression = True
+        expr_node = code_ast.body[-1]
+        # Create new assign node
+        assign_node = ast.parse(
+            'globals()[{}] = None'.format(repr(out_varname))).body[0]
+        # Replace None by the value
+        assign_node.value = expr_node.value
+        # Fix line number and column offset
+        assign_node.lineno = expr_node.lineno
+        assign_node.col_offset = expr_node.col_offset
+        code_ast.body[-1] = assign_node
+    return code_ast, capture_last_expression
+
+
+def normalise_filename(filename):
+    """Normalise path for window."""
+    # Recursive
+    if os.name == 'nt':
+        return filename.replace('\\', '/')
+    return filename
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spyder-kernels-2.2.1/spyder_kernels/py3compat.py 
new/spyder-kernels-2.3.0/spyder_kernels/py3compat.py
--- old/spyder-kernels-2.2.1/spyder_kernels/py3compat.py        2022-01-13 
16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/spyder_kernels/py3compat.py        2022-03-30 
04:17:43.000000000 +0200
@@ -317,8 +317,10 @@
 # =============================================================================
 if PY2:
     TimeoutError = RuntimeError
+    FileNotFoundError = IOError
 else:
     TimeoutError = TimeoutError
+    FileNotFoundError = FileNotFoundError
 
 if PY2:
     import re
@@ -348,5 +350,11 @@
         """Encoding is not a problem in python 3."""
         return u
 
+
+def compat_exec(code, globals, locals):
+    # Wrap exec in a function
+    exec(code, globals, locals)
+
+
 if __name__ == '__main__':
     pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spyder-kernels-2.2.1/spyder_kernels/utils/mpl.py 
new/spyder-kernels-2.3.0/spyder_kernels/utils/mpl.py
--- old/spyder-kernels-2.2.1/spyder_kernels/utils/mpl.py        2022-01-13 
16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/spyder_kernels/utils/mpl.py        2022-03-30 
04:17:43.000000000 +0200
@@ -29,12 +29,9 @@
 MPL_BACKENDS_TO_SPYDER = {
     inline_backend: 0,
     'Qt5Agg': 2,
-    'Qt4Agg': 3,
+    'QtAgg': 2,  # For Matplotlib 3.5+
+    'TkAgg': 3,
     'MacOSX': 4,
-    'GTK3Agg': 5,
-    'GTKAgg': 6,
-    'WX': 7,
-    'TkAgg': 8
 }
 
 
@@ -42,8 +39,6 @@
     """Get Matplolib automatic backend option."""
     if is_module_installed('PyQt5'):
         auto_backend = 'qt5'
-    elif is_module_installed('PyQt4'):
-        auto_backend = 'qt4'
     elif is_module_installed('_tkinter'):
         auto_backend = 'tk'
     else:
@@ -56,10 +51,6 @@
     '0': 'inline',
     '1': automatic_backend(),
     '2': 'qt5',
-    '3': 'qt4',
-    '4': 'osx',
-    '5': 'gtk3',
-    '6': 'gtk',
-    '7': 'wx',
-    '8': 'tk'
+    '3': 'tk',
+    '4': 'osx'
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/spyder-kernels-2.2.1/spyder_kernels/utils/nsview.py 
new/spyder-kernels-2.3.0/spyder_kernels/utils/nsview.py
--- old/spyder-kernels-2.2.1/spyder_kernels/utils/nsview.py     2022-01-13 
16:55:24.000000000 +0100
+++ new/spyder-kernels-2.3.0/spyder_kernels/utils/nsview.py     2022-03-30 
04:17:43.000000000 +0200
@@ -85,8 +85,19 @@
 def get_size(item):
     """Return shape/size/len of an item of arbitrary type"""
     try:
-        if (hasattr(item, 'shape') and
-                isinstance(item.shape, (tuple, np.integer))):
+        if (
+            hasattr(item, 'size') and hasattr(item.size, 'compute') or
+            hasattr(item, 'shape') and hasattr(item.shape, 'compute')
+        ):
+            # This is necessary to avoid an error when trying to
+            # get the size/shape of dask objects. We don't compute the
+            # size/shape since such operation could be expensive.
+            # Fixes spyder-ide/spyder#16844
+            return 1
+        elif (
+            hasattr(item, 'shape') and
+            isinstance(item.shape, (tuple, np.integer))
+        ):
             try:
                 if item.shape:
                     # This is needed since values could return as
@@ -648,7 +659,7 @@
     """
     supported_types = get_supported_types()
     assert mode in list(supported_types.keys())
-    excluded_names = settings['excluded_names']
+    excluded_names = list(settings['excluded_names'])
     if more_excluded_names is not None:
         excluded_names += more_excluded_names
     return globalsfilter(

Reply via email to