Skip to content

Commit 1588067

Browse files
committed
pdb: fix usage in child thread after main thread exited
Fixes #5228.
1 parent ceca35b commit 1588067

File tree

3 files changed

+47
-9
lines changed

3 files changed

+47
-9
lines changed

changelog/5228.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix ``pdb.set_trace`` wrapper when used in child threads after main thread exited.

src/_pytest/debugging.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,7 @@ def pytest_configure(config):
7777
if config.getvalue("usepdb"):
7878
config.pluginmanager.register(PdbInvoke(), "pdbinvoke")
7979

80-
pytestPDB._saved.append(
81-
(pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config, pytestPDB._pdb_cls)
82-
)
80+
pytestPDB._saved.append(pdb.set_trace)
8381
pdb.set_trace = pytestPDB.set_trace
8482
pytestPDB._pluginmanager = config.pluginmanager
8583
pytestPDB._config = config
@@ -88,12 +86,7 @@ def pytest_configure(config):
8886
# NOTE: not using pytest_unconfigure, since it might get called although
8987
# pytest_configure was not (if another plugin raises UsageError).
9088
def fin():
91-
(
92-
pdb.set_trace,
93-
pytestPDB._pluginmanager,
94-
pytestPDB._config,
95-
pytestPDB._pdb_cls,
96-
) = pytestPDB._saved.pop()
89+
pdb.set_trace = pytestPDB._saved.pop()
9790

9891
config._cleanup.append(fin)
9992

testing/test_pdb.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,3 +1191,47 @@ def test(monkeypatch):
11911191
result = testdir.runpytest(str(p1))
11921192
result.stdout.fnmatch_lines(["E *BdbQuit", "*= 1 failed in*"])
11931193
assert result.ret == 1
1194+
1195+
1196+
def test_pdb_in_thread_after_exit(testdir):
1197+
"""Ensure that pdb.set_trace works after main thread exited already.
1198+
1199+
This tests both continuation after the main thread exited, and a new
1200+
set_trace afterwards.
1201+
"""
1202+
p1 = testdir.makepyfile(
1203+
"""
1204+
import threading
1205+
1206+
main_thread = threading.main_thread()
1207+
1208+
def test():
1209+
evt = threading.Event()
1210+
1211+
def target():
1212+
print("target_" + "start")
1213+
evt.set()
1214+
assert main_thread.is_alive()
1215+
__import__('pdb').set_trace()
1216+
assert not main_thread.is_alive()
1217+
__import__('pdb').set_trace()
1218+
print("target_" + "end")
1219+
1220+
thread = threading.Thread(target=target)
1221+
thread.start()
1222+
1223+
evt.wait()
1224+
"""
1225+
)
1226+
child = testdir.spawn_pytest(str(p1) + " -s")
1227+
child.expect("target_start")
1228+
child.expect("= 1 passed in") # main thread exited
1229+
child.expect(r"\(Pdb")
1230+
child.sendline("c")
1231+
child.expect(r"\(Pdb")
1232+
child.sendline("c")
1233+
child.expect("target_end")
1234+
child.wait()
1235+
rest = child.read().decode("utf8")
1236+
assert "Exception in thread" not in rest
1237+
assert child.exitstatus == 0

0 commit comments

Comments
 (0)