@@ -81,6 +81,7 @@ class pytestPDB(object):
81
81
_config = None
82
82
_saved = []
83
83
_recursive_debug = 0
84
+ _wrapped_pdb_cls = None
84
85
85
86
@classmethod
86
87
def _is_capturing (cls , capman ):
@@ -89,156 +90,167 @@ def _is_capturing(cls, capman):
89
90
return False
90
91
91
92
@classmethod
92
- def _import_pdb_cls (cls ):
93
+ def _import_pdb_cls (cls , capman ):
93
94
if not cls ._config :
94
95
# Happens when using pytest.set_trace outside of a test.
95
96
return pdb .Pdb
96
97
97
- pdb_cls = cls ._config .getvalue ("usepdb_cls" )
98
- if not pdb_cls :
99
- return pdb .Pdb
98
+ usepdb_cls = cls ._config .getvalue ("usepdb_cls" )
99
+
100
+ if cls ._wrapped_pdb_cls and cls ._wrapped_pdb_cls [0 ] == usepdb_cls :
101
+ return cls ._wrapped_pdb_cls [1 ]
100
102
101
- modname , classname = pdb_cls
103
+ if usepdb_cls :
104
+ modname , classname = usepdb_cls
102
105
103
- try :
104
- __import__ (modname )
105
- mod = sys .modules [modname ]
106
+ try :
107
+ __import__ (modname )
108
+ mod = sys .modules [modname ]
106
109
107
- # Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp).
108
- parts = classname .split ("." )
109
- pdb_cls = getattr (mod , parts [0 ])
110
- for part in parts [1 :]:
111
- pdb_cls = getattr (pdb_cls , part )
110
+ # Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp).
111
+ parts = classname .split ("." )
112
+ pdb_cls = getattr (mod , parts [0 ])
113
+ for part in parts [1 :]:
114
+ pdb_cls = getattr (pdb_cls , part )
115
+ except Exception as exc :
116
+ value = ":" .join ((modname , classname ))
117
+ raise UsageError (
118
+ "--pdbcls: could not import {!r}: {}" .format (value , exc )
119
+ )
120
+ else :
121
+ pdb_cls = pdb .Pdb
112
122
113
- return pdb_cls
114
- except Exception as exc :
115
- value = ":" .join ((modname , classname ))
116
- raise UsageError ("--pdbcls: could not import {!r}: {}" .format (value , exc ))
123
+ wrapped_cls = cls ._get_pdb_wrapper_class (pdb_cls , capman )
124
+ cls ._wrapped_pdb_cls = (usepdb_cls , wrapped_cls )
125
+ return wrapped_cls
117
126
118
127
@classmethod
119
- def _init_pdb (cls , * args , ** kwargs ):
128
+ def _get_pdb_wrapper_class (cls , pdb_cls , capman ):
129
+ import _pytest .config
130
+
131
+ class PytestPdbWrapper (pdb_cls , object ):
132
+ _pytest_capman = capman
133
+ _continued = False
134
+
135
+ def do_debug (self , arg ):
136
+ cls ._recursive_debug += 1
137
+ ret = super (PytestPdbWrapper , self ).do_debug (arg )
138
+ cls ._recursive_debug -= 1
139
+ return ret
140
+
141
+ def do_continue (self , arg ):
142
+ ret = super (PytestPdbWrapper , self ).do_continue (arg )
143
+ if cls ._recursive_debug == 0 :
144
+ tw = _pytest .config .create_terminal_writer (cls ._config )
145
+ tw .line ()
146
+
147
+ capman = self ._pytest_capman
148
+ capturing = pytestPDB ._is_capturing (capman )
149
+ if capturing :
150
+ if capturing == "global" :
151
+ tw .sep (">" , "PDB continue (IO-capturing resumed)" )
152
+ else :
153
+ tw .sep (
154
+ ">" ,
155
+ "PDB continue (IO-capturing resumed for %s)"
156
+ % capturing ,
157
+ )
158
+ capman .resume ()
159
+ else :
160
+ tw .sep (">" , "PDB continue" )
161
+ cls ._pluginmanager .hook .pytest_leave_pdb (config = cls ._config , pdb = self )
162
+ self ._continued = True
163
+ return ret
164
+
165
+ do_c = do_cont = do_continue
166
+
167
+ def do_quit (self , arg ):
168
+ """Raise Exit outcome when quit command is used in pdb.
169
+
170
+ This is a bit of a hack - it would be better if BdbQuit
171
+ could be handled, but this would require to wrap the
172
+ whole pytest run, and adjust the report etc.
173
+ """
174
+ ret = super (PytestPdbWrapper , self ).do_quit (arg )
175
+
176
+ if cls ._recursive_debug == 0 :
177
+ outcomes .exit ("Quitting debugger" )
178
+
179
+ return ret
180
+
181
+ do_q = do_quit
182
+ do_exit = do_quit
183
+
184
+ def setup (self , f , tb ):
185
+ """Suspend on setup().
186
+
187
+ Needed after do_continue resumed, and entering another
188
+ breakpoint again.
189
+ """
190
+ ret = super (PytestPdbWrapper , self ).setup (f , tb )
191
+ if not ret and self ._continued :
192
+ # pdb.setup() returns True if the command wants to exit
193
+ # from the interaction: do not suspend capturing then.
194
+ if self ._pytest_capman :
195
+ self ._pytest_capman .suspend_global_capture (in_ = True )
196
+ return ret
197
+
198
+ def get_stack (self , f , t ):
199
+ stack , i = super (PytestPdbWrapper , self ).get_stack (f , t )
200
+ if f is None :
201
+ # Find last non-hidden frame.
202
+ i = max (0 , len (stack ) - 1 )
203
+ while i and stack [i ][0 ].f_locals .get ("__tracebackhide__" , False ):
204
+ i -= 1
205
+ return stack , i
206
+
207
+ return PytestPdbWrapper
208
+
209
+ @classmethod
210
+ def _init_pdb (cls , method , * args , ** kwargs ):
120
211
""" Initialize PDB debugging, dropping any IO capturing. """
121
212
import _pytest .config
122
213
123
214
if cls ._pluginmanager is not None :
124
215
capman = cls ._pluginmanager .getplugin ("capturemanager" )
125
- if capman :
126
- capman .suspend (in_ = True )
216
+ else :
217
+ capman = None
218
+ if capman :
219
+ capman .suspend (in_ = True )
220
+
221
+ if cls ._config :
127
222
tw = _pytest .config .create_terminal_writer (cls ._config )
128
223
tw .line ()
224
+
129
225
if cls ._recursive_debug == 0 :
130
226
# Handle header similar to pdb.set_trace in py37+.
131
227
header = kwargs .pop ("header" , None )
132
228
if header is not None :
133
229
tw .sep (">" , header )
134
230
else :
135
231
capturing = cls ._is_capturing (capman )
136
- if capturing :
137
- if capturing == "global" :
138
- tw .sep (">" , "PDB set_trace (IO-capturing turned off)" )
139
- else :
140
- tw .sep (
141
- ">" ,
142
- "PDB set_trace (IO-capturing turned off for %s)"
143
- % capturing ,
144
- )
232
+ if capturing == "global" :
233
+ tw .sep (">" , "PDB %s (IO-capturing turned off)" % (method ,))
234
+ elif capturing :
235
+ tw .sep (
236
+ ">" ,
237
+ "PDB %s (IO-capturing turned off for %s)"
238
+ % (method , capturing ),
239
+ )
145
240
else :
146
- tw .sep (">" , "PDB set_trace" )
147
-
148
- pdb_cls = cls ._import_pdb_cls ()
149
-
150
- class PytestPdbWrapper (pdb_cls , object ):
151
- _pytest_capman = capman
152
- _continued = False
153
-
154
- def do_debug (self , arg ):
155
- cls ._recursive_debug += 1
156
- ret = super (PytestPdbWrapper , self ).do_debug (arg )
157
- cls ._recursive_debug -= 1
158
- return ret
159
-
160
- def do_continue (self , arg ):
161
- ret = super (PytestPdbWrapper , self ).do_continue (arg )
162
- if cls ._recursive_debug == 0 :
163
- tw = _pytest .config .create_terminal_writer (cls ._config )
164
- tw .line ()
165
-
166
- capman = self ._pytest_capman
167
- capturing = pytestPDB ._is_capturing (capman )
168
- if capturing :
169
- if capturing == "global" :
170
- tw .sep (">" , "PDB continue (IO-capturing resumed)" )
171
- else :
172
- tw .sep (
173
- ">" ,
174
- "PDB continue (IO-capturing resumed for %s)"
175
- % capturing ,
176
- )
177
- capman .resume ()
178
- else :
179
- tw .sep (">" , "PDB continue" )
180
- cls ._pluginmanager .hook .pytest_leave_pdb (
181
- config = cls ._config , pdb = self
182
- )
183
- self ._continued = True
184
- return ret
185
-
186
- do_c = do_cont = do_continue
187
-
188
- def do_quit (self , arg ):
189
- """Raise Exit outcome when quit command is used in pdb.
190
-
191
- This is a bit of a hack - it would be better if BdbQuit
192
- could be handled, but this would require to wrap the
193
- whole pytest run, and adjust the report etc.
194
- """
195
- ret = super (PytestPdbWrapper , self ).do_quit (arg )
196
-
197
- if cls ._recursive_debug == 0 :
198
- outcomes .exit ("Quitting debugger" )
199
-
200
- return ret
201
-
202
- do_q = do_quit
203
- do_exit = do_quit
204
-
205
- def setup (self , f , tb ):
206
- """Suspend on setup().
207
-
208
- Needed after do_continue resumed, and entering another
209
- breakpoint again.
210
- """
211
- ret = super (PytestPdbWrapper , self ).setup (f , tb )
212
- if not ret and self ._continued :
213
- # pdb.setup() returns True if the command wants to exit
214
- # from the interaction: do not suspend capturing then.
215
- if self ._pytest_capman :
216
- self ._pytest_capman .suspend_global_capture (in_ = True )
217
- return ret
218
-
219
- def get_stack (self , f , t ):
220
- stack , i = super (PytestPdbWrapper , self ).get_stack (f , t )
221
- if f is None :
222
- # Find last non-hidden frame.
223
- i = max (0 , len (stack ) - 1 )
224
- while i and stack [i ][0 ].f_locals .get (
225
- "__tracebackhide__" , False
226
- ):
227
- i -= 1
228
- return stack , i
229
-
230
- _pdb = PytestPdbWrapper (** kwargs )
241
+ tw .sep (">" , "PDB %s" % (method ,))
242
+
243
+ _pdb = cls ._import_pdb_cls (capman )(** kwargs )
244
+
245
+ if cls ._pluginmanager :
231
246
cls ._pluginmanager .hook .pytest_enter_pdb (config = cls ._config , pdb = _pdb )
232
- else :
233
- pdb_cls = cls ._import_pdb_cls ()
234
- _pdb = pdb_cls (** kwargs )
235
247
return _pdb
236
248
237
249
@classmethod
238
250
def set_trace (cls , * args , ** kwargs ):
239
251
"""Invoke debugging via ``Pdb.set_trace``, dropping any IO capturing."""
240
252
frame = sys ._getframe ().f_back
241
- _pdb = cls ._init_pdb (* args , ** kwargs )
253
+ _pdb = cls ._init_pdb ("set_trace" , * args , ** kwargs )
242
254
_pdb .set_trace (frame )
243
255
244
256
@@ -265,7 +277,7 @@ def pytest_pyfunc_call(self, pyfuncitem):
265
277
266
278
267
279
def _test_pytest_function (pyfuncitem ):
268
- _pdb = pytestPDB ._init_pdb ()
280
+ _pdb = pytestPDB ._init_pdb ("runcall" )
269
281
testfunction = pyfuncitem .obj
270
282
pyfuncitem .obj = _pdb .runcall
271
283
if "func" in pyfuncitem ._fixtureinfo .argnames : # pragma: no branch
@@ -315,7 +327,7 @@ def _postmortem_traceback(excinfo):
315
327
316
328
317
329
def post_mortem (t ):
318
- p = pytestPDB ._init_pdb ()
330
+ p = pytestPDB ._init_pdb ("post_mortem" )
319
331
p .reset ()
320
332
p .interaction (None , t )
321
333
if p .quitting :
0 commit comments