Repository: spyder-ide/spyder

Issue:
Debugging after error broken in Spyder 5.4.5 due to missing 'tb-frame'
<!--- **PLEASE READ:** When submitting here, please ensure you've completed the following checklist and checked the boxes to confirm. Issue reports without it may be closed. Thanks! --->

### Issue Report Checklist

* [x] Searched the [issues page](https://github.com/spyder-ide/spyder/issues?q=is%3Aissue) for similar reports
* [x] Read the relevant sections of the [Spyder Troubleshooting Guide](https://github.com/spyder-ide/spyder/wiki/Troubleshooting-Guide-and-FAQ) and followed its advice
* [x] Reproduced the issue after updating with ``conda update spyder`` (or ``pip``, if not using Anaconda)
* [x] Could not reproduce inside ``jupyter qtconsole`` (if console-related)
* [x] Tried basic troubleshooting (if a bug/error)
    * [x] Restarted Spyder
    * [x] Reset preferences with ``spyder --reset``
    * [ ] Reinstalled the latest version of [Anaconda](https://www.anaconda.com/download/)
    * [x] Tried the other applicable steps from the Troubleshooting Guide
* [x] Completed the **Problem Description**, **Steps to Reproduce** and **Version** sections below


## Problem Description

After typing %debug and trying to see what happened after an error, we get, for instance after debugging division by zero, `AttributeError: 'ZeroDivisionError' object has no attribute 'tb_frame'`

### What steps reproduce the problem?

For instance:
```
# testdebugspyder.py
def divide_by_zero():
    return 1/0

divide_by_zero()
```

One can then either call runfile or debugfile to run the file and get the expected ZeroDivisionError: all good so far

```
In[1]: runfile('/Users/mike/Projects/Python/testdebugspyder.py', wdir='/Users/mike/Projects/Python')
Traceback (most recent call last):
  File ~/Library/miniconda3/envs/spyder-env/lib/python3.11/site-packages/spyder_kernels/py3compat.py:356 in compat_exec
    exec(code, globals, locals)
  File ~/Projects/Python/testdebugspyder.py:4
    divide_by_zero()
  File ~/Projects/Python/testdebugspyder.py:2 in divide_by_zero
    return 1/0
ZeroDivisionError: division by zero
```

But then, when trying to use `%debug`, we get this bizarre error:

```
In[2]: %debug
Traceback (most recent call last):
  Cell In[2], line 1
    get_ipython().run_line_magic('debug', '')
  File ~/Library/miniconda3/envs/spyder-env/lib/python3.11/site-packages/IPython/core/interactiveshell.py:2432 in run_line_magic
    result = fn(*args, **kwargs)
  File ~/Library/miniconda3/envs/spyder-env/lib/python3.11/site-packages/IPython/core/magics/execution.py:469 in debug
    self._debug_post_mortem()
  File ~/Library/miniconda3/envs/spyder-env/lib/python3.11/site-packages/IPython/core/magics/execution.py:483 in _debug_post_mortem
    self.shell.debugger(force=True)
  File ~/Library/miniconda3/envs/spyder-env/lib/python3.11/site-packages/IPython/core/interactiveshell.py:1146 in debugger
    self.InteractiveTB.debugger(force=True)
  File ~/Library/miniconda3/envs/spyder-env/lib/python3.11/site-packages/IPython/core/ultratb.py:1253 in debugger
    self.pdb.interaction(None, exc)
  File ~/Library/miniconda3/envs/spyder-env/lib/python3.11/site-packages/spyder_kernels/customize/spyderpdb.py:330 in interaction
    self.setup(frame, traceback)
  File ~/Library/miniconda3/envs/spyder-env/lib/python3.11/pdb.py:278 in setup
    self.stack, self.curindex = self.get_stack(f, tb)
  File ~/Library/miniconda3/envs/spyder-env/lib/python3.11/bdb.py:535 in get_stack
    if t and t.tb_frame is f:
AttributeError: 'ZeroDivisionError' object has no attribute 'tb_frame'
```

### What is the expected output? What do you see instead?
This should spawn an instance of ipdb set right before where the error occurred

## Versions
<!--- You can get this information from Help > About Spyder...
or (if Spyder won't launch) the "conda list" command
from the Anaconda Prompt/Terminal/command line. --->

* Spyder version: 5.4.5  (conda)
* Python version: 3.11.5 64-bit
* Qt version: 5.15.8
* PyQt5 version: 5.15.9
* Operating System: macOS-12.5-arm64-i386-64bit


### Dependencies
<!--- Please go to the menu entry Help > Dependencies,
press the Copy to clipboard button and paste below --->

```
# Mandatory:
applaunchservices >=0.3.0                                                   :  0.3.0 (OK)
atomicwrites >=1.2.0                                                        :  1.4.1 (OK)
chardet >=2.0.0                                                             :  5.2.0 (OK)
cloudpickle >=0.5.0                                                         :  2.2.1 (OK)
cookiecutter >=1.6.0                                                        :  2.3.1 (OK)
diff_match_patch >=20181111                                                 :  20230430 (OK)
intervaltree >=3.0.2                                                        :  3.1.0 (OK)
IPython >=7.31.1,<9.0.0,!=8.8.0,!=8.9.0,!=8.10.0,!=8.11.0,!=8.12.0,!=8.12.1 :  8.15.0 (OK)
jedi >=0.17.2,<0.19.0                                                       :  0.18.2 (OK)
jellyfish >=0.7                                                             :  1.0.1 (OK)
jsonschema >=3.2.0                                                          :  4.19.1 (OK)
keyring >=17.0.0                                                            :  24.2.0 (OK)
nbconvert >=4.0                                                             :  7.8.0 (OK)
numpydoc >=0.6.0                                                            :  1.5.0 (OK)
parso >=0.7.0,<0.9.0                                                        :  0.8.3 (OK)
pexpect >=4.4.0                                                             :  4.8.0 (OK)
pickleshare >=0.4                                                           :  0.7.5 (OK)
psutil >=5.3                                                                :  5.9.5 (OK)
pygments >=2.0                                                              :  2.16.1 (OK)
pylint >=2.5.0,<3.0                                                         :  2.17.6 (OK)
pylint_venv >=3.0.2                                                         :  3.0.2 (OK)
pyls_spyder >=0.4.0                                                         :  0.4.0 (OK)
pylsp >=1.7.4,<1.8.0                                                        :  1.7.4 (OK)
pylsp_black >=1.2.0,<3.0.0                                                  :  1.3.0 (OK)
qdarkstyle >=3.0.2,<3.2.0                                                   :  3.1 (OK)
qstylizer >=0.2.2                                                           :  0.2.2 (OK)
qtawesome >=1.2.1                                                           :  1.2.3 (OK)
qtconsole >=5.4.2,<5.5.0                                                    :  5.4.4 (OK)
qtpy >=2.1.0                                                                :  2.4.0 (OK)
rtree >=0.9.7                                                               :  1.0.1 (OK)
setuptools >=49.6.0                                                         :  68.2.2 (OK)
sphinx >=0.6.6                                                              :  7.2.6 (OK)
spyder_kernels >=2.4.4,<2.5.0                                               :  2.4.4 (OK)
textdistance >=4.2.0                                                        :  4.5.0 (OK)
three_merge >=0.1.1                                                         :  0.1.1 (OK)
watchdog >=0.10.3                                                           :  3.0.0 (OK)
zmq >=22.1.0                                                                :  25.1.1 (OK)

# Optional:
cython >=0.21                                                               :  None (NOK)
matplotlib >=3.0.0                                                          :  None (NOK)
numpy >=1.7                                                                 :  None (NOK)
pandas >=1.1.1                                                              :  None (NOK)
scipy >=0.17.0                                                              :  None (NOK)
sympy >=0.7.3                                                               :  None (NOK)
```



Discussion:
This doesn't happen in just ipython:

```
$ ipython
In [1]: %run testdebugspyder.py
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
File ~/Projects/Python/testdebugspyder.py:4
      1 def divide_by_zero():
      2     return 1/0
----> 4 divide_by_zero()

File ~/Projects/Python/testdebugspyder.py:2, in divide_by_zero()
      1 def divide_by_zero():
----> 2     return 1/0

ZeroDivisionError: division by zero

In [2]: %debug
> /Users/mike/Projects/Python/testdebugspyder.py(2)divide_by_zero()
      1 def divide_by_zero():
----> 2     return 1/0
      3
      4 divide_by_zero()

ipdb>
```

It does happen in Spyder even if using `%run` though.
Seems that downgrading to ipython 8.14 solves this.
Hey @battaglia01, thanks for reporting and glad you found the exact workaround for this (i.e. downgrading IPython).

We'll fix this issue in our next version of Spyder-kernels (2.5.0) that will go along with Spyder 5.5.0, both to be released at the end of the month.

Resolving patch:
diff --git a/external-deps/spyder-kernels/spyder_kernels/customize/spyderpdb.py b/external-deps/spyder-kernels/spyder_kernels/customize/spyderpdb.py
--- a/external-deps/spyder-kernels/spyder_kernels/customize/spyderpdb.py
+++ b/external-deps/spyder-kernels/spyder_kernels/customize/spyderpdb.py
@@ -320,20 +320,32 @@ def interaction(self, frame, traceback):
         If this is from sigint, break on the upper frame.
         If the frame is in spydercustomize.py, quit.
         Notifies spyder and print current code.
-
         """
         if self._pdb_breaking:
             self._pdb_breaking = False
             if frame and frame.f_back:
                 return self.interaction(frame.f_back, traceback)
 
+        # This is necessary to handle chained exceptions in Pdb, support for
+        # which was added in IPython 8.15 and will be the default in Python
+        # 3.13 (see ipython/ipython#14146).
+        if isinstance(traceback, BaseException):
+            _chained_exceptions, tb = self._get_tb_and_exceptions(traceback)
+
+            with self._hold_exceptions(_chained_exceptions):
+                self.interaction(frame, tb)
+
+            return
+
         self.setup(frame, traceback)
         self.print_stack_entry(self.stack[self.curindex])
+
         if self._frontend_notified:
             self._cmdloop()
         else:
             with DebugWrapper(self):
                 self._cmdloop()
+
         self.forget()
 
     def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',


Test patch: