From 2344982d7fec6c37727288621d3d3dbf7291f53b Mon Sep 17 00:00:00 2001
From: Daniel Hahler <git@thequod.de>
Date: Thu, 21 Nov 2019 18:42:37 +0100
Subject: [PATCH] main: wrap_session: handle exit.Exception with
 notify_exception

Fixes https://github.com/pytest-dev/pytest/issues/6257.
Via https://github.com/blueyed/pytest/pull/132.
---
 changelog/6257.improvement.rst |  1 +
 src/_pytest/main.py            | 14 ++++++---
 testing/test_main.py           | 52 ++++++++++++++++++++++++++++++++++
 3 files changed, 63 insertions(+), 4 deletions(-)
 create mode 100644 changelog/6257.improvement.rst
 create mode 100644 testing/test_main.py

diff --git a/changelog/6257.improvement.rst b/changelog/6257.improvement.rst
new file mode 100644
index 000000000..a21558286
--- /dev/null
+++ b/changelog/6257.improvement.rst
@@ -0,0 +1 @@
+Handle `exit.Exception` raised in `notify_exception` (via `pytest_internalerror`), e.g. when quitting pdb from post mortem.
diff --git a/src/_pytest/main.py b/src/_pytest/main.py
index b4261c188..fc92ec10f 100644
--- a/src/_pytest/main.py
+++ b/src/_pytest/main.py
@@ -212,11 +212,17 @@ def wrap_session(config, doit):
             config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
             session.exitstatus = exitstatus
         except:  # noqa
-            excinfo = _pytest._code.ExceptionInfo.from_current()
-            config.notify_exception(excinfo, config.option)
             session.exitstatus = ExitCode.INTERNAL_ERROR
-            if excinfo.errisinstance(SystemExit):
-                sys.stderr.write("mainloop: caught unexpected SystemExit!\n")
+            excinfo = _pytest._code.ExceptionInfo.from_current()
+            try:
+                config.notify_exception(excinfo, config.option)
+            except exit.Exception as exc:
+                if exc.returncode is not None:
+                    session.exitstatus = exc.returncode
+                sys.stderr.write("{}: {}\n".format(type(exc).__name__, exc))
+            else:
+                if excinfo.errisinstance(SystemExit):
+                    sys.stderr.write("mainloop: caught unexpected SystemExit!\n")
 
     finally:
         excinfo = None  # Explicitly break reference cycle.
diff --git a/testing/test_main.py b/testing/test_main.py
new file mode 100644
index 000000000..b47791b29
--- /dev/null
+++ b/testing/test_main.py
@@ -0,0 +1,52 @@
+import pytest
+from _pytest.main import ExitCode
+
+
+@pytest.mark.parametrize(
+    "ret_exc",
+    (
+        pytest.param((None, ValueError)),
+        pytest.param((42, SystemExit)),
+        pytest.param((False, SystemExit)),
+    ),
+)
+def test_wrap_session_notify_exception(ret_exc, testdir):
+    returncode, exc = ret_exc
+    c1 = testdir.makeconftest(
+        """
+        import pytest
+
+        def pytest_sessionstart():
+            raise {exc}("boom")
+
+        def pytest_internalerror(excrepr, excinfo):
+            returncode = {returncode!r}
+            if returncode is not False:
+                pytest.exit("exiting after %s..." % excinfo.typename, returncode={returncode!r})
+    """.format(
+            returncode=returncode, exc=exc.__name__
+        )
+    )
+    result = testdir.runpytest()
+    if returncode:
+        assert result.ret == returncode
+    else:
+        assert result.ret == ExitCode.INTERNAL_ERROR
+    assert result.stdout.lines[0] == "INTERNALERROR> Traceback (most recent call last):"
+
+    if exc == SystemExit:
+        assert result.stdout.lines[-3:] == [
+            'INTERNALERROR>   File "{}", line 4, in pytest_sessionstart'.format(c1),
+            'INTERNALERROR>     raise SystemExit("boom")',
+            "INTERNALERROR> SystemExit: boom",
+        ]
+    else:
+        assert result.stdout.lines[-3:] == [
+            'INTERNALERROR>   File "{}", line 4, in pytest_sessionstart'.format(c1),
+            'INTERNALERROR>     raise ValueError("boom")',
+            "INTERNALERROR> ValueError: boom",
+        ]
+    if returncode is False:
+        assert result.stderr.lines == ["mainloop: caught unexpected SystemExit!"]
+    else:
+        assert result.stderr.lines == ["Exit: exiting after {}...".format(exc.__name__)]