Make debugging's pytest_configure re-entrant
This is relevant when using runpytest in-process. Fixes: E def test_1(testdir): E testdir.runpytest() E > __import__('pdb').set_trace() E E ../../test_trace_after_runpytest.py:3: E …/Vcs/pytest/src/_pytest/debugging.py:81: in set_trace E tw = _pytest.config.create_terminal_writer(cls._config) E E config = None, args = (), kwargs = {}, tw = <py._io.terminalwriter.TerminalWriter object at 0x7f1097088160> E E def create_terminal_writer(config, *args, **kwargs): E """Create a TerminalWriter instance configured according to the options E in the config object. Every code which requires a TerminalWriter object E and has access to a config object should use this function. E """ E tw = py.io.TerminalWriter(*args, **kwargs) E > if config.option.color == "yes": E E AttributeError: 'NoneType' object has no attribute 'option'
This commit is contained in:
parent
21725e9304
commit
e61e81a7b5
|
@ -0,0 +1 @@
|
||||||
|
Fix nested usage of debugging plugin (pdb), e.g. with pytester's ``testdir.runpytest``.
|
|
@ -47,17 +47,24 @@ def pytest_configure(config):
|
||||||
if config.getvalue("usepdb"):
|
if config.getvalue("usepdb"):
|
||||||
config.pluginmanager.register(PdbInvoke(), "pdbinvoke")
|
config.pluginmanager.register(PdbInvoke(), "pdbinvoke")
|
||||||
|
|
||||||
old = (pdb.set_trace, pytestPDB._pluginmanager)
|
pytestPDB._saved.append(
|
||||||
|
(pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config, pytestPDB._pdb_cls)
|
||||||
def fin():
|
)
|
||||||
pdb.set_trace, pytestPDB._pluginmanager = old
|
|
||||||
pytestPDB._config = None
|
|
||||||
pytestPDB._pdb_cls = pdb.Pdb
|
|
||||||
|
|
||||||
pdb.set_trace = pytestPDB.set_trace
|
pdb.set_trace = pytestPDB.set_trace
|
||||||
pytestPDB._pluginmanager = config.pluginmanager
|
pytestPDB._pluginmanager = config.pluginmanager
|
||||||
pytestPDB._config = config
|
pytestPDB._config = config
|
||||||
pytestPDB._pdb_cls = pdb_cls
|
pytestPDB._pdb_cls = pdb_cls
|
||||||
|
|
||||||
|
# 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._pdb_cls,
|
||||||
|
) = pytestPDB._saved.pop()
|
||||||
|
|
||||||
config._cleanup.append(fin)
|
config._cleanup.append(fin)
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,6 +74,7 @@ class pytestPDB(object):
|
||||||
_pluginmanager = None
|
_pluginmanager = None
|
||||||
_config = None
|
_config = None
|
||||||
_pdb_cls = pdb.Pdb
|
_pdb_cls = pdb.Pdb
|
||||||
|
_saved = []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_trace(cls, set_break=True):
|
def set_trace(cls, set_break=True):
|
||||||
|
|
|
@ -784,3 +784,30 @@ class TestTraceOption:
|
||||||
assert "1 passed" in rest
|
assert "1 passed" in rest
|
||||||
assert "reading from stdin while output" not in rest
|
assert "reading from stdin while output" not in rest
|
||||||
TestPDB.flush(child)
|
TestPDB.flush(child)
|
||||||
|
|
||||||
|
|
||||||
|
def test_trace_after_runpytest(testdir):
|
||||||
|
"""Test that debugging's pytest_configure is re-entrant."""
|
||||||
|
p1 = testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
from _pytest.debugging import pytestPDB
|
||||||
|
|
||||||
|
def test_outer(testdir):
|
||||||
|
from _pytest.debugging import pytestPDB
|
||||||
|
|
||||||
|
assert len(pytestPDB._saved) == 1
|
||||||
|
|
||||||
|
testdir.runpytest("-k test_inner")
|
||||||
|
|
||||||
|
__import__('pdb').set_trace()
|
||||||
|
|
||||||
|
def test_inner(testdir):
|
||||||
|
assert len(pytestPDB._saved) == 2
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
child = testdir.spawn_pytest("-p pytester %s -k test_outer" % p1)
|
||||||
|
child.expect(r"\(Pdb")
|
||||||
|
child.sendline("c")
|
||||||
|
rest = child.read().decode("utf8")
|
||||||
|
TestPDB.flush(child)
|
||||||
|
assert child.exitstatus == 0, rest
|
||||||
|
|
Loading…
Reference in New Issue