Skip to content

Commit

Permalink
pdb: fix usage in child thread after main thread exited (#17)
Browse files Browse the repository at this point in the history
Fixes pytest-dev#5228.

Original PR (rejected): pytest-dev#5244
  • Loading branch information
blueyed committed Oct 27, 2019
1 parent 1542a1c commit 9129ad3
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 8 deletions.
1 change: 1 addition & 0 deletions changelog/5228.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix ``pdb.set_trace`` wrapper when used in child threads after main thread exited.
10 changes: 2 additions & 8 deletions src/_pytest/debugging.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,15 @@ def pytest_configure(config):
if config.getvalue("usepdb"):
config.pluginmanager.register(PdbInvoke(), "pdbinvoke")

pytestPDB._saved.append(
(pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config)
)
pytestPDB._saved.append(pdb.set_trace)
pdb.set_trace = pytestPDB.set_trace
pytestPDB._pluginmanager = config.pluginmanager
pytestPDB._config = config

# NOTE: not using pytest_unconfigure, since it might get called although
# pytest_configure was not (if another plugin raises UsageError).
def fin():
(
pdb.set_trace,
pytestPDB._pluginmanager,
pytestPDB._config,
) = pytestPDB._saved.pop()
pdb.set_trace = pytestPDB._saved.pop()

config._cleanup.append(fin)

Expand Down
49 changes: 49 additions & 0 deletions testing/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -1266,3 +1266,52 @@ def set_trace(self, *args):
result.stdout.fnmatch_lines(
["*set_trace_called*", "*set_trace_called*", "* 1 passed in *"]
)


def test_pdb_in_thread_after_exit(testdir):
"""Ensure that pdb.set_trace works after main thread exited already.
This tests both continuation after the main thread exited, and a new
set_trace afterwards.
"""
p1 = testdir.makepyfile(
"""
import threading
main_thread = threading.current_thread()
evt = threading.Event()
evt2 = threading.Event()
def test():
def target():
print("target_" + "start")
evt.set()
assert main_thread.is_alive()
__import__('pdb').set_trace()
assert not main_thread.is_alive()
__import__('pdb').set_trace()
print("target_" + "end")
thread = threading.Thread(target=target)
thread.start()
evt.wait()
evt2.wait()
"""
)
child = testdir.spawn_pytest(str(p1) + " -s")
child.expect("target_start")
child.expect(r"\(Pdb")
child.sendline("evt2.set()")
child.expect_exact(
"= \x1b[32m\x1b[1m1 passed\x1b[0m\x1b[32m in"
) # main thread exited
child.sendline("c")
child.expect(r"\(Pdb")
child.sendline("c")
child.expect("target_end")
child.wait()
rest = child.read().decode("utf8")
assert "Exception in thread" not in rest
assert child.exitstatus == 0

0 comments on commit 9129ad3

Please sign in to comment.