gh-106670: Fix Pdb handling of chained Exceptions with no stacks. (#108865)

diff --git a/Lib/pdb.py b/Lib/pdb.py
index b603aca..c31f608 100755
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -494,6 +494,8 @@ def interaction(self, frame, tb_or_exc):
                 Pdb._previous_sigint_handler = None
 
         _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc)
+        if isinstance(tb_or_exc, BaseException):
+            assert tb is not None, "main exception must have a traceback"
         with self._hold_exceptions(_chained_exceptions):
             if self.setup(frame, tb):
                 # no interaction desired at this time (happens if .pdbrc contains
@@ -1169,7 +1171,12 @@ def do_exceptions(self, arg):
                 rep = repr(exc)
                 if len(rep) > 80:
                     rep = rep[:77] + "..."
-                self.message(f"{prompt} {ix:>3} {rep}")
+                indicator = (
+                    "  -"
+                    if self._chained_exceptions[ix].__traceback__ is None
+                    else f"{ix:>3}"
+                )
+                self.message(f"{prompt} {indicator} {rep}")
         else:
             try:
                 number = int(arg)
@@ -1177,6 +1184,10 @@ def do_exceptions(self, arg):
                 self.error("Argument must be an integer")
                 return
             if 0 <= number < len(self._chained_exceptions):
+                if self._chained_exceptions[number].__traceback__ is None:
+                    self.error("This exception does not have a traceback, cannot jump to it")
+                    return
+
                 self._chained_exception_index = number
                 self.setup(None, self._chained_exceptions[number].__traceback__)
                 self.print_stack_entry(self.stack[self.curindex])
@@ -2010,19 +2021,27 @@ def post_mortem(t=None):
     If `t` is an exception object, the `exceptions` command makes it possible to
     list and inspect its chained exceptions (if any).
     """
+    return _post_mortem(t, Pdb())
+
+
+def _post_mortem(t, pdb_instance):
+    """
+    Private version of post_mortem, which allow to pass a pdb instance
+    for testing purposes.
+    """
     # handling the default
     if t is None:
         exc = sys.exception()
         if exc is not None:
             t = exc.__traceback__
 
-    if t is None:
+    if t is None or (isinstance(t, BaseException) and t.__traceback__ is None):
         raise ValueError("A valid traceback must be passed if no "
                          "exception is being handled")
 
-    p = Pdb()
-    p.reset()
-    p.interaction(None, t)
+    pdb_instance.reset()
+    pdb_instance.interaction(None, t)
+
 
 def pm():
     """Enter post-mortem debugging of the traceback found in sys.last_exc."""
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index a9edd1a..f6bed84 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -848,9 +848,7 @@ def test_post_mortem_chained():
     ...     try:
     ...         test_function_reraise()
     ...     except Exception as e:
-    ...         # same as pdb.post_mortem(e), but with custom pdb instance.
-    ...         instance.reset()
-    ...         instance.interaction(None, e)
+    ...         pdb._post_mortem(e, instance)
 
     >>> with PdbTestInput([  # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
     ...     'exceptions',
@@ -907,11 +905,18 @@ def test_post_mortem_chained():
 def test_post_mortem_cause_no_context():
     """Test post mortem traceback debugging of chained exception
 
+    >>> def make_exc_with_stack(type_, *content, from_=None):
+    ...     try:
+    ...         raise type_(*content) from from_
+    ...     except Exception as out:
+    ...         return out
+    ...
+
     >>> def main():
     ...     try:
     ...         raise ValueError('Context Not Shown')
     ...     except Exception as e1:
-    ...         raise ValueError("With Cause") from TypeError('The Cause')
+    ...         raise ValueError("With Cause") from make_exc_with_stack(TypeError,'The Cause')
 
     >>> def test_function():
     ...     import pdb;
@@ -919,12 +924,11 @@ def test_post_mortem_cause_no_context():
     ...     try:
     ...         main()
     ...     except Exception as e:
-    ...         # same as pdb.post_mortem(e), but with custom pdb instance.
-    ...         instance.reset()
-    ...         instance.interaction(None, e)
+    ...         pdb._post_mortem(e, instance)
 
     >>> with PdbTestInput([  # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
     ...     'exceptions',
+    ...     'exceptions 0',
     ...     'exceptions 1',
     ...     'up',
     ...     'down',
@@ -934,20 +938,23 @@ def test_post_mortem_cause_no_context():
     ...        test_function()
     ...    except ValueError:
     ...        print('Ok.')
-    > <doctest test.test_pdb.test_post_mortem_cause_no_context[0]>(5)main()
-    -> raise ValueError("With Cause") from TypeError('The Cause')
+    > <doctest test.test_pdb.test_post_mortem_cause_no_context[1]>(5)main()
+    -> raise ValueError("With Cause") from make_exc_with_stack(TypeError,'The Cause')
     (Pdb) exceptions
-      0 TypeError('The Cause')
-    > 1 ValueError('With Cause')
+        0 TypeError('The Cause')
+    >   1 ValueError('With Cause')
+    (Pdb) exceptions 0
+    > <doctest test.test_pdb.test_post_mortem_cause_no_context[0]>(3)make_exc_with_stack()
+    -> raise type_(*content) from from_
     (Pdb) exceptions 1
-    > <doctest test.test_pdb.test_post_mortem_cause_no_context[0]>(5)main()
-    -> raise ValueError("With Cause") from TypeError('The Cause')
+    > <doctest test.test_pdb.test_post_mortem_cause_no_context[1]>(5)main()
+    -> raise ValueError("With Cause") from make_exc_with_stack(TypeError,'The Cause')
     (Pdb) up
-    > <doctest test.test_pdb.test_post_mortem_cause_no_context[1]>(5)test_function()
+    > <doctest test.test_pdb.test_post_mortem_cause_no_context[2]>(5)test_function()
     -> main()
     (Pdb) down
-    > <doctest test.test_pdb.test_post_mortem_cause_no_context[0]>(5)main()
-    -> raise ValueError("With Cause") from TypeError('The Cause')
+    > <doctest test.test_pdb.test_post_mortem_cause_no_context[1]>(5)main()
+    -> raise ValueError("With Cause") from make_exc_with_stack(TypeError,'The Cause')
     (Pdb) exit"""
 
 
@@ -971,9 +978,7 @@ def test_post_mortem_context_of_the_cause():
     ...     try:
     ...         main()
     ...     except Exception as e:
-    ...         # same as pdb.post_mortem(e), but with custom pdb instance.
-    ...         instance.reset()
-    ...         instance.interaction(None, e)
+    ...         pdb._post_mortem(e, instance)
 
     >>> with PdbTestInput([  # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
     ...     'exceptions',
@@ -1046,9 +1051,7 @@ def test_post_mortem_from_none():
     ...     try:
     ...         main()
     ...     except Exception as e:
-    ...         # same as pdb.post_mortem(e), but with custom pdb instance.
-    ...         instance.reset()
-    ...         instance.interaction(None, e)
+    ...         pdb._post_mortem(e, instance)
 
     >>> with PdbTestInput([  # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
     ...     'exceptions',
@@ -1066,6 +1069,64 @@ def test_post_mortem_from_none():
     """
 
 
+def test_post_mortem_from_no_stack():
+    """Test post mortem traceback debugging of chained exception
+
+    especially when one exception has no stack.
+
+    >>> def main():
+    ...     raise Exception() from Exception()
+
+
+    >>> def test_function():
+    ...     import pdb;
+    ...     instance = pdb.Pdb(nosigint=True, readrc=False)
+    ...     try:
+    ...         main()
+    ...     except Exception as e:
+    ...         pdb._post_mortem(e, instance)
+
+    >>> with PdbTestInput(  # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+    ...     ["exceptions",
+    ...      "exceptions 0",
+    ...     "exit"],
+    ... ):
+    ...    try:
+    ...        test_function()
+    ...    except ValueError:
+    ...        print('Correctly reraised.')
+    > <doctest test.test_pdb.test_post_mortem_from_no_stack[0]>(2)main()
+    -> raise Exception() from Exception()
+    (Pdb) exceptions
+        - Exception()
+    >   1 Exception()
+    (Pdb) exceptions 0
+    *** This exception does not have a traceback, cannot jump to it
+    (Pdb) exit
+    """
+
+
+def test_post_mortem_single_no_stack():
+    """Test post mortem called when origin exception has no stack
+
+
+    >>> def test_function():
+    ...     import pdb;
+    ...     instance = pdb.Pdb(nosigint=True, readrc=False)
+    ...     import sys
+    ...     sys.last_exc = Exception()
+    ...     pdb._post_mortem(sys.last_exc, instance)
+
+    >>> with PdbTestInput(  # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+    ...     []
+    ... ):
+    ...    try:
+    ...        test_function()
+    ...    except ValueError as e:
+    ...        print(e)
+    A valid traceback must be passed if no exception is being handled
+    """
+
 def test_post_mortem_complex():
     """Test post mortem traceback debugging of chained exception
 
@@ -1130,9 +1191,7 @@ def test_post_mortem_complex():
     ...     try:
     ...         main()
     ...     except Exception as e:
-    ...         # same as pdb.post_mortem(e), but with custom pdb instance.
-    ...         instance.reset()
-    ...         instance.interaction(None, e)
+    ...         pdb._post_mortem(e, instance)
 
     >>> with PdbTestInput(  # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
     ...     ["exceptions",