Merge pull request #4347 from blueyed/pdb-recursive-capture
pdbpp: fix capturing with recursive debugging
This commit is contained in:
		
						commit
						e20e376881
					
				|  | @ -0,0 +1 @@ | ||||||
|  | Fix output capturing when using pdb++ with recursive debugging. | ||||||
|  | @ -75,6 +75,7 @@ class pytestPDB(object): | ||||||
|     _config = None |     _config = None | ||||||
|     _pdb_cls = pdb.Pdb |     _pdb_cls = pdb.Pdb | ||||||
|     _saved = [] |     _saved = [] | ||||||
|  |     _recursive_debug = 0 | ||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def _init_pdb(cls, *args, **kwargs): |     def _init_pdb(cls, *args, **kwargs): | ||||||
|  | @ -87,29 +88,37 @@ class pytestPDB(object): | ||||||
|                 capman.suspend_global_capture(in_=True) |                 capman.suspend_global_capture(in_=True) | ||||||
|             tw = _pytest.config.create_terminal_writer(cls._config) |             tw = _pytest.config.create_terminal_writer(cls._config) | ||||||
|             tw.line() |             tw.line() | ||||||
|             # Handle header similar to pdb.set_trace in py37+. |             if cls._recursive_debug == 0: | ||||||
|             header = kwargs.pop("header", None) |                 # Handle header similar to pdb.set_trace in py37+. | ||||||
|             if header is not None: |                 header = kwargs.pop("header", None) | ||||||
|                 tw.sep(">", header) |                 if header is not None: | ||||||
|             elif capman and capman.is_globally_capturing(): |                     tw.sep(">", header) | ||||||
|                 tw.sep(">", "PDB set_trace (IO-capturing turned off)") |                 elif capman and capman.is_globally_capturing(): | ||||||
|             else: |                     tw.sep(">", "PDB set_trace (IO-capturing turned off)") | ||||||
|                 tw.sep(">", "PDB set_trace") |                 else: | ||||||
|  |                     tw.sep(">", "PDB set_trace") | ||||||
| 
 | 
 | ||||||
|             class _PdbWrapper(cls._pdb_cls, object): |             class _PdbWrapper(cls._pdb_cls, object): | ||||||
|                 _pytest_capman = capman |                 _pytest_capman = capman | ||||||
|                 _continued = False |                 _continued = False | ||||||
| 
 | 
 | ||||||
|  |                 def do_debug(self, arg): | ||||||
|  |                     cls._recursive_debug += 1 | ||||||
|  |                     ret = super(_PdbWrapper, self).do_debug(arg) | ||||||
|  |                     cls._recursive_debug -= 1 | ||||||
|  |                     return ret | ||||||
|  | 
 | ||||||
|                 def do_continue(self, arg): |                 def do_continue(self, arg): | ||||||
|                     ret = super(_PdbWrapper, self).do_continue(arg) |                     ret = super(_PdbWrapper, self).do_continue(arg) | ||||||
|                     if self._pytest_capman: |                     if self._pytest_capman: | ||||||
|                         tw = _pytest.config.create_terminal_writer(cls._config) |                         tw = _pytest.config.create_terminal_writer(cls._config) | ||||||
|                         tw.line() |                         tw.line() | ||||||
|                         if self._pytest_capman.is_globally_capturing(): |                         if cls._recursive_debug == 0: | ||||||
|                             tw.sep(">", "PDB continue (IO-capturing resumed)") |                             if self._pytest_capman.is_globally_capturing(): | ||||||
|                         else: |                                 tw.sep(">", "PDB continue (IO-capturing resumed)") | ||||||
|                             tw.sep(">", "PDB continue") |                             else: | ||||||
|                         self._pytest_capman.resume_global_capture() |                                 tw.sep(">", "PDB continue") | ||||||
|  |                             self._pytest_capman.resume_global_capture() | ||||||
|                     cls._pluginmanager.hook.pytest_leave_pdb( |                     cls._pluginmanager.hook.pytest_leave_pdb( | ||||||
|                         config=cls._config, pdb=self |                         config=cls._config, pdb=self | ||||||
|                     ) |                     ) | ||||||
|  |  | ||||||
|  | @ -513,6 +513,76 @@ class TestPDB(object): | ||||||
|         assert "1 failed" in rest |         assert "1 failed" in rest | ||||||
|         self.flush(child) |         self.flush(child) | ||||||
| 
 | 
 | ||||||
|  |     def test_pdb_interaction_continue_recursive(self, testdir): | ||||||
|  |         p1 = testdir.makepyfile( | ||||||
|  |             mytest=""" | ||||||
|  |             import pdb | ||||||
|  |             import pytest | ||||||
|  | 
 | ||||||
|  |             count_continue = 0 | ||||||
|  | 
 | ||||||
|  |             # Simulates pdbpp, which injects Pdb into do_debug, and uses | ||||||
|  |             # self.__class__ in do_continue. | ||||||
|  |             class CustomPdb(pdb.Pdb, object): | ||||||
|  |                 def do_debug(self, arg): | ||||||
|  |                     import sys | ||||||
|  |                     import types | ||||||
|  | 
 | ||||||
|  |                     newglobals = { | ||||||
|  |                         'Pdb': self.__class__,  # NOTE: different with pdb.Pdb | ||||||
|  |                         'sys': sys, | ||||||
|  |                     } | ||||||
|  |                     if sys.version_info < (3, ): | ||||||
|  |                         do_debug_func = pdb.Pdb.do_debug.im_func | ||||||
|  |                     else: | ||||||
|  |                         do_debug_func = pdb.Pdb.do_debug | ||||||
|  | 
 | ||||||
|  |                     orig_do_debug = types.FunctionType( | ||||||
|  |                         do_debug_func.__code__, newglobals, | ||||||
|  |                         do_debug_func.__name__, do_debug_func.__defaults__, | ||||||
|  |                     ) | ||||||
|  |                     return orig_do_debug(self, arg) | ||||||
|  |                 do_debug.__doc__ = pdb.Pdb.do_debug.__doc__ | ||||||
|  | 
 | ||||||
|  |                 def do_continue(self, *args, **kwargs): | ||||||
|  |                     global count_continue | ||||||
|  |                     count_continue += 1 | ||||||
|  |                     return super(CustomPdb, self).do_continue(*args, **kwargs) | ||||||
|  | 
 | ||||||
|  |             def foo(): | ||||||
|  |                 print("print_from_foo") | ||||||
|  | 
 | ||||||
|  |             def test_1(): | ||||||
|  |                 i = 0 | ||||||
|  |                 print("hello17") | ||||||
|  |                 pytest.set_trace() | ||||||
|  |                 x = 3 | ||||||
|  |                 print("hello18") | ||||||
|  | 
 | ||||||
|  |                 assert count_continue == 2, "unexpected_failure: %d != 2" % count_continue | ||||||
|  |                 pytest.fail("expected_failure") | ||||||
|  |         """ | ||||||
|  |         ) | ||||||
|  |         child = testdir.spawn_pytest("--pdbcls=mytest:CustomPdb %s" % str(p1)) | ||||||
|  |         child.expect(r"PDB set_trace \(IO-capturing turned off\)") | ||||||
|  |         child.expect(r"\n\(Pdb") | ||||||
|  |         child.sendline("debug foo()") | ||||||
|  |         child.expect("ENTERING RECURSIVE DEBUGGER") | ||||||
|  |         child.expect(r"\n\(\(Pdb") | ||||||
|  |         child.sendline("c") | ||||||
|  |         child.expect("LEAVING RECURSIVE DEBUGGER") | ||||||
|  |         assert b"PDB continue" not in child.before | ||||||
|  |         assert b"print_from_foo" in child.before | ||||||
|  |         child.sendline("c") | ||||||
|  |         child.expect(r"PDB continue \(IO-capturing resumed\)") | ||||||
|  |         rest = child.read().decode("utf8") | ||||||
|  |         assert "hello17" in rest  # out is captured | ||||||
|  |         assert "hello18" in rest  # out is captured | ||||||
|  |         assert "1 failed" in rest | ||||||
|  |         assert "Failed: expected_failure" in rest | ||||||
|  |         assert "AssertionError: unexpected_failure" not in rest | ||||||
|  |         self.flush(child) | ||||||
|  | 
 | ||||||
|     def test_pdb_without_capture(self, testdir): |     def test_pdb_without_capture(self, testdir): | ||||||
|         p1 = testdir.makepyfile( |         p1 = testdir.makepyfile( | ||||||
|             """ |             """ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue