Add trigger_exception_handler() for post-mortem debugging of caught exceptions#1996
Add trigger_exception_handler() for post-mortem debugging of caught exceptions#1996nshepperd wants to merge 10 commits into
Build #20260717.5 had test failures
Details
- Failed: 3 (0.01%)
- Passed: 17,283 (81.54%)
- Other: 3,910 (18.45%)
- Total: 21,196
Annotations
Check failure on line 2610 in Build log
azure-pipelines / debugpy-test
Build log #L2610
PowerShell exited with code '1'.
Check failure on line 3100 in Build log
azure-pipelines / debugpy-test
Build log #L3100
PowerShell exited with code '1'.
Check failure on line 2928 in Build log
azure-pipelines / debugpy-test
Build log #L2928
PowerShell exited with code '1'.
Check failure on line 1 in test_attach_pid_client[program-int]
azure-pipelines / debugpy-test
test_attach_pid_client[program-int]
failed on teardown with "KeyError: <_pytest.stash.StashKey object at 0x7fc1ff4428e0>"
Raw output
cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0x7fc1fd896820>
when = 'teardown'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)
@classmethod
def from_call(
cls,
func: Callable[[], TResult],
when: Literal["collect", "setup", "call", "teardown"],
reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None,
) -> CallInfo[TResult]:
"""Call func, wrapping the result in a CallInfo.
:param func:
The function to call. Called without arguments.
:type func: Callable[[], _pytest.runner.TResult]
:param when:
The phase in which the function is called.
:param reraise:
Exception or exceptions that shall propagate if raised by the
function, instead of being wrapped in the CallInfo.
"""
excinfo = None
instant = timing.Instant()
try:
> result: TResult | None = func()
^^^^^^
.tox/py314/lib/python3.14/site-packages/_pytest/runner.py:361:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.tox/py314/lib/python3.14/site-packages/_pytest/runner.py:250: in <lambda>
lambda: runtest_hook(item=item, **kwds),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.tox/py314/lib/python3.14/site-packages/pluggy/_hooks.py:512: in __call__
return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.tox/py314/lib/python3.14/site-packages/pluggy/_manager.py:120: in _hookexec
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.tox/py314/lib/python3.14/site-packages/_pytest/logging.py:873: in pytest_runtest_teardown
yield
.tox/py314/lib/python3.14/site-packages/_pytest/capture.py:905: in pytest_runtest_teardown
return (yield)
^^^^^
.tox/py314/lib/python3.14/site-packages/_pytest/runner.py:199: in pytest_runtest_teardown
item.session._setupstate.teardown_exact(nextitem)
.tox/py314/lib/python3.14/site-packages/_pytest/runner.py:579: in teardown_exact
raise exceptions[0]
.tox/py314/lib/python3.14/site-packages/_pytest/runner.py:568: in teardown_exact
fin()
.tox/py314/lib/python3.14/site-packages/_pytest/fixtures.py:1166: in finish
raise exceptions[0]
.tox/py314/lib/python3.14/site-packages/_pytest/fixtures.py:1156: in finish
fin()
.tox/py314/lib/python3.14/site-packages/_pytest/fixtures.py:1014: in _teardown_yield_fixture
next(it)
.tox/py314/lib/python3.14/site-packages/_pytest/tmpdir.py:305: in tmp_path
result_dict = request.node.stash[tmppath_result_key]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <_pytest.stash.Stash object at 0x7fc1fdbce920>
key = <_pytest.stash.StashKey object at 0x7fc1ff4428e0>
def __getitem__(self, key: StashKey[T]) -> T:
"""Get the value for key.
Raises ``KeyError`` if the key wasn't set before.
"""
> return cast(T, self._storage[key])
^^^^^^^^^^^^^^^^^^
E KeyError: <_pytest.stash.StashKey object at 0x7fc1ff4428e0>
.tox/py314/lib/python3.14/site-packages/_pytest/stash.py:84: KeyError
azure-pipelines / debugpy-test
test_break_on_system_exit_empty[1-launch(console=integratedTerminal)-program]
AssertionError: assert -1 == 2147942406
+ where -1 = psutil.Popen(pid=6288, status='terminated', exitcode=-1, started='22:58:54').returncode
+ where psutil.Popen(pid=6288, status='terminated', exitcode=-1, started='22:58:54') = <tests.debug.session.Session object at 0x00000122E9746930>.debuggee
+ and 2147942406 = <tests.debug.session.Session object at 0x00000122E9746930>.exit_code
Raw output
pyfile = <function pyfile.<locals>.factory at 0x00000122E9839C60>
target = <class 'tests.debug.targets.Program'>
run = launch(console=integratedTerminal), exit_code = 1
@pytest.mark.parametrize("target", targets.all_named)
@pytest.mark.parametrize("run", runners.all)
@pytest.mark.parametrize("exit_code", [0, 1, 3])
def test_break_on_system_exit_empty(pyfile, target, run, exit_code):
@pyfile
def code_to_debug():
import debuggee
import sys
debuggee.setup()
exit_code = eval(sys.argv[1])
print("sys.exit(%r)" % (exit_code,))
sys.exit(exit_code)
> with debug.Session() as session:
^^^^^^^^^^^^^^^
tests\debugpy\test_exception.py:350:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests\debug\session.py:255: in __exit__
self.wait_for_exit()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <tests.debug.session.Session object at 0x00000122E9746930>
def wait_for_exit(self):
if self.debuggee is not None:
log.info("Waiting for {0} to exit ...", self.debuggee_id)
try:
self.debuggee.wait()
except Exception:
pass
finally:
watchdog.unregister_spawn(self.debuggee.pid, self.debuggee_id)
self.wait_for_terminated()
# FIXME: "exited" event is not properly reported in attach scenarios at the
# moment, so the exit code is only checked if it's present.
if self.debuggee is not None and self.exit_code is not None:
> assert self.debuggee.returncode == self.exit_code
E AssertionError: assert -1 == 2147942406
E + where -1 = psutil.Popen(pid=6288, status='terminated', exitcode=-1, started='22:58:54').returncode
E + where psutil.Popen(pid=6288, status='terminated', exitcode=-1, started='22:58:54') = <tests.debug.session.Session object at 0x00000122E9746930>.debuggee
E + and 2147942406 = <tests.debug.session.Session object at 0x00000122E9746930>.exit_code
tests\debug\session.py:990: AssertionError
Check failure on line 1 in test_thread_count[program-launch-3]
azure-pipelines / debugpy-test
test_thread_count[program-launch-3]
assert 4 == 3
+ where 4 = len([{\n "id": 1,\n "name": "MainThread"\n}, {\n "id": 2,\n "name": "Dummy-6"\n}, {\n "id": 3,\n "name": "Thread-7 (worker)"\n}, {\n "id": 4,\n "name": "Thread-8 (worker)"\n}])
Raw output
pyfile = <function pyfile.<locals>.factory at 0x00000235FAC73C70>
target = <class 'tests.debug.targets.Program'>, run = launch, count = 3
@pytest.mark.parametrize("count", [1, 3])
def test_thread_count(pyfile, target, run, count):
@pyfile
def code_to_debug():
import debuggee
import threading
import time
import sys
debuggee.setup()
stop = False # noqa: F841
def worker(tid, offset):
i = 0
global stop
while not stop:
time.sleep(0.01)
i += 1
threads = []
if sys.argv[1] != "1":
for i in [111, 222]:
thread = threading.Thread(target=worker, args=(i, len(threads)))
threads.append(thread)
thread.start()
print("check here") # @bp
stop = True # noqa: F841
with debug.Session() as session:
with run(session, target(code_to_debug, args=[str(count)])):
session.set_breakpoints(code_to_debug, all)
session.wait_for_stop()
threads = session.request("threads")
> assert len(threads["threads"]) == count
E assert 4 == 3
E + where 4 = len([{\n "id": 1,\n "name": "MainThread"\n}, {\n "id": 2,\n "name": "Dummy-6"\n}, {\n "id": 3,\n "name": "Thread-7 (worker)"\n}, {\n "id": 4,\n "name": "Thread-8 (worker)"\n}])
tests\debugpy\test_threads.py:46: AssertionError