##// END OF EJS Templates
Remove deprecated debugger chunks.
Matthias Bussonnier -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,1106 +1,1010 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Pdb debugger class.
3 Pdb debugger class.
4
4
5
5
6 This is an extension to PDB which adds a number of new features.
6 This is an extension to PDB which adds a number of new features.
7 Note that there is also the `IPython.terminal.debugger` class which provides UI
7 Note that there is also the `IPython.terminal.debugger` class which provides UI
8 improvements.
8 improvements.
9
9
10 We also strongly recommend to use this via the `ipdb` package, which provides
10 We also strongly recommend to use this via the `ipdb` package, which provides
11 extra configuration options.
11 extra configuration options.
12
12
13 Among other things, this subclass of PDB:
13 Among other things, this subclass of PDB:
14 - supports many IPython magics like pdef/psource
14 - supports many IPython magics like pdef/psource
15 - hide frames in tracebacks based on `__tracebackhide__`
15 - hide frames in tracebacks based on `__tracebackhide__`
16 - allows to skip frames based on `__debuggerskip__`
16 - allows to skip frames based on `__debuggerskip__`
17
17
18 The skipping and hiding frames are configurable via the `skip_predicates`
18 The skipping and hiding frames are configurable via the `skip_predicates`
19 command.
19 command.
20
20
21 By default, frames from readonly files will be hidden, frames containing
21 By default, frames from readonly files will be hidden, frames containing
22 ``__tracebackhide__=True`` will be hidden.
22 ``__tracebackhide__=True`` will be hidden.
23
23
24 Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
24 Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
25 frames value of ``__debuggerskip__`` is ``True`` will be skipped.
25 frames value of ``__debuggerskip__`` is ``True`` will be skipped.
26
26
27 >>> def helpers_helper():
27 >>> def helpers_helper():
28 ... pass
28 ... pass
29 ...
29 ...
30 ... def helper_1():
30 ... def helper_1():
31 ... print("don't step in me")
31 ... print("don't step in me")
32 ... helpers_helpers() # will be stepped over unless breakpoint set.
32 ... helpers_helpers() # will be stepped over unless breakpoint set.
33 ...
33 ...
34 ...
34 ...
35 ... def helper_2():
35 ... def helper_2():
36 ... print("in me neither")
36 ... print("in me neither")
37 ...
37 ...
38
38
39 One can define a decorator that wraps a function between the two helpers:
39 One can define a decorator that wraps a function between the two helpers:
40
40
41 >>> def pdb_skipped_decorator(function):
41 >>> def pdb_skipped_decorator(function):
42 ...
42 ...
43 ...
43 ...
44 ... def wrapped_fn(*args, **kwargs):
44 ... def wrapped_fn(*args, **kwargs):
45 ... __debuggerskip__ = True
45 ... __debuggerskip__ = True
46 ... helper_1()
46 ... helper_1()
47 ... __debuggerskip__ = False
47 ... __debuggerskip__ = False
48 ... result = function(*args, **kwargs)
48 ... result = function(*args, **kwargs)
49 ... __debuggerskip__ = True
49 ... __debuggerskip__ = True
50 ... helper_2()
50 ... helper_2()
51 ... # setting __debuggerskip__ to False again is not necessary
51 ... # setting __debuggerskip__ to False again is not necessary
52 ... return result
52 ... return result
53 ...
53 ...
54 ... return wrapped_fn
54 ... return wrapped_fn
55
55
56 When decorating a function, ipdb will directly step into ``bar()`` by
56 When decorating a function, ipdb will directly step into ``bar()`` by
57 default:
57 default:
58
58
59 >>> @foo_decorator
59 >>> @foo_decorator
60 ... def bar(x, y):
60 ... def bar(x, y):
61 ... return x * y
61 ... return x * y
62
62
63
63
64 You can toggle the behavior with
64 You can toggle the behavior with
65
65
66 ipdb> skip_predicates debuggerskip false
66 ipdb> skip_predicates debuggerskip false
67
67
68 or configure it in your ``.pdbrc``
68 or configure it in your ``.pdbrc``
69
69
70
70
71
71
72 License
72 License
73 -------
73 -------
74
74
75 Modified from the standard pdb.Pdb class to avoid including readline, so that
75 Modified from the standard pdb.Pdb class to avoid including readline, so that
76 the command line completion of other programs which include this isn't
76 the command line completion of other programs which include this isn't
77 damaged.
77 damaged.
78
78
79 In the future, this class will be expanded with improvements over the standard
79 In the future, this class will be expanded with improvements over the standard
80 pdb.
80 pdb.
81
81
82 The original code in this file is mainly lifted out of cmd.py in Python 2.2,
82 The original code in this file is mainly lifted out of cmd.py in Python 2.2,
83 with minor changes. Licensing should therefore be under the standard Python
83 with minor changes. Licensing should therefore be under the standard Python
84 terms. For details on the PSF (Python Software Foundation) standard license,
84 terms. For details on the PSF (Python Software Foundation) standard license,
85 see:
85 see:
86
86
87 https://docs.python.org/2/license.html
87 https://docs.python.org/2/license.html
88
88
89
89
90 All the changes since then are under the same license as IPython.
90 All the changes since then are under the same license as IPython.
91
91
92 """
92 """
93
93
94 #*****************************************************************************
94 #*****************************************************************************
95 #
95 #
96 # This file is licensed under the PSF license.
96 # This file is licensed under the PSF license.
97 #
97 #
98 # Copyright (C) 2001 Python Software Foundation, www.python.org
98 # Copyright (C) 2001 Python Software Foundation, www.python.org
99 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
99 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
100 #
100 #
101 #
101 #
102 #*****************************************************************************
102 #*****************************************************************************
103
103
104 import bdb
104 import bdb
105 import functools
106 import inspect
105 import inspect
107 import linecache
106 import linecache
108 import sys
107 import sys
109 import warnings
108 import warnings
110 import re
109 import re
111 import os
110 import os
112
111
113 from IPython import get_ipython
112 from IPython import get_ipython
114 from IPython.utils import PyColorize
113 from IPython.utils import PyColorize
115 from IPython.utils import coloransi, py3compat
114 from IPython.utils import coloransi, py3compat
116 from IPython.core.excolors import exception_colors
115 from IPython.core.excolors import exception_colors
117
116
118 # skip module docstests
117 # skip module docstests
119 __skip_doctest__ = True
118 __skip_doctest__ = True
120
119
121 prompt = 'ipdb> '
120 prompt = 'ipdb> '
122
121
123 # We have to check this directly from sys.argv, config struct not yet available
122 # We have to check this directly from sys.argv, config struct not yet available
124 from pdb import Pdb as OldPdb
123 from pdb import Pdb as OldPdb
125
124
126 # Allow the set_trace code to operate outside of an ipython instance, even if
125 # Allow the set_trace code to operate outside of an ipython instance, even if
127 # it does so with some limitations. The rest of this support is implemented in
126 # it does so with some limitations. The rest of this support is implemented in
128 # the Tracer constructor.
127 # the Tracer constructor.
129
128
130 DEBUGGERSKIP = "__debuggerskip__"
129 DEBUGGERSKIP = "__debuggerskip__"
131
130
132
131
133 def make_arrow(pad):
132 def make_arrow(pad):
134 """generate the leading arrow in front of traceback or debugger"""
133 """generate the leading arrow in front of traceback or debugger"""
135 if pad >= 2:
134 if pad >= 2:
136 return '-'*(pad-2) + '> '
135 return '-'*(pad-2) + '> '
137 elif pad == 1:
136 elif pad == 1:
138 return '>'
137 return '>'
139 return ''
138 return ''
140
139
141
140
142 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
141 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
143 """Exception hook which handles `BdbQuit` exceptions.
142 """Exception hook which handles `BdbQuit` exceptions.
144
143
145 All other exceptions are processed using the `excepthook`
144 All other exceptions are processed using the `excepthook`
146 parameter.
145 parameter.
147 """
146 """
148 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
147 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
149 DeprecationWarning, stacklevel=2)
148 DeprecationWarning, stacklevel=2)
150 if et == bdb.BdbQuit:
149 if et == bdb.BdbQuit:
151 print('Exiting Debugger.')
150 print('Exiting Debugger.')
152 elif excepthook is not None:
151 elif excepthook is not None:
153 excepthook(et, ev, tb)
152 excepthook(et, ev, tb)
154 else:
153 else:
155 # Backwards compatibility. Raise deprecation warning?
154 # Backwards compatibility. Raise deprecation warning?
156 BdbQuit_excepthook.excepthook_ori(et, ev, tb)
155 BdbQuit_excepthook.excepthook_ori(et, ev, tb)
157
156
158
157
159 def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
158 def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
160 warnings.warn(
159 warnings.warn(
161 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
160 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
162 DeprecationWarning, stacklevel=2)
161 DeprecationWarning, stacklevel=2)
163 print('Exiting Debugger.')
162 print('Exiting Debugger.')
164
163
165
164
166 class Tracer(object):
167 """
168 DEPRECATED
169
170 Class for local debugging, similar to pdb.set_trace.
171
172 Instances of this class, when called, behave like pdb.set_trace, but
173 providing IPython's enhanced capabilities.
174
175 This is implemented as a class which must be initialized in your own code
176 and not as a standalone function because we need to detect at runtime
177 whether IPython is already active or not. That detection is done in the
178 constructor, ensuring that this code plays nicely with a running IPython,
179 while functioning acceptably (though with limitations) if outside of it.
180 """
181
182 def __init__(self, colors=None):
183 """
184 DEPRECATED
185
186 Create a local debugger instance.
187
188 Parameters
189 ----------
190 colors : str, optional
191 The name of the color scheme to use, it must be one of IPython's
192 valid color schemes. If not given, the function will default to
193 the current IPython scheme when running inside IPython, and to
194 'NoColor' otherwise.
195
196 Examples
197 --------
198 ::
199
200 from IPython.core.debugger import Tracer; debug_here = Tracer()
201
202 Later in your code::
203
204 debug_here() # -> will open up the debugger at that point.
205
206 Once the debugger activates, you can use all of its regular commands to
207 step through code, set breakpoints, etc. See the pdb documentation
208 from the Python standard library for usage details.
209 """
210 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
211 "`IPython.core.debugger.Pdb.set_trace()`",
212 DeprecationWarning, stacklevel=2)
213
214 ip = get_ipython()
215 if ip is None:
216 # Outside of ipython, we set our own exception hook manually
217 sys.excepthook = functools.partial(BdbQuit_excepthook,
218 excepthook=sys.excepthook)
219 def_colors = 'NoColor'
220 else:
221 # In ipython, we use its custom exception handler mechanism
222 def_colors = ip.colors
223 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
224
225 if colors is None:
226 colors = def_colors
227
228 # The stdlib debugger internally uses a modified repr from the `repr`
229 # module, that limits the length of printed strings to a hardcoded
230 # limit of 30 characters. That much trimming is too aggressive, let's
231 # at least raise that limit to 80 chars, which should be enough for
232 # most interactive uses.
233 try:
234 from reprlib import aRepr
235 aRepr.maxstring = 80
236 except:
237 # This is only a user-facing convenience, so any error we encounter
238 # here can be warned about but can be otherwise ignored. These
239 # printouts will tell us about problems if this API changes
240 import traceback
241 traceback.print_exc()
242
243 self.debugger = Pdb(colors)
244
245 def __call__(self):
246 """Starts an interactive debugger at the point where called.
247
248 This is similar to the pdb.set_trace() function from the std lib, but
249 using IPython's enhanced debugger."""
250
251 self.debugger.set_trace(sys._getframe().f_back)
252
253
254 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
165 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
255
166
256
167
257 def strip_indentation(multiline_string):
168 def strip_indentation(multiline_string):
258 return RGX_EXTRA_INDENT.sub('', multiline_string)
169 return RGX_EXTRA_INDENT.sub('', multiline_string)
259
170
260
171
261 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
172 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
262 """Make new_fn have old_fn's doc string. This is particularly useful
173 """Make new_fn have old_fn's doc string. This is particularly useful
263 for the ``do_...`` commands that hook into the help system.
174 for the ``do_...`` commands that hook into the help system.
264 Adapted from from a comp.lang.python posting
175 Adapted from from a comp.lang.python posting
265 by Duncan Booth."""
176 by Duncan Booth."""
266 def wrapper(*args, **kw):
177 def wrapper(*args, **kw):
267 return new_fn(*args, **kw)
178 return new_fn(*args, **kw)
268 if old_fn.__doc__:
179 if old_fn.__doc__:
269 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
180 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
270 return wrapper
181 return wrapper
271
182
272
183
273 class Pdb(OldPdb):
184 class Pdb(OldPdb):
274 """Modified Pdb class, does not load readline.
185 """Modified Pdb class, does not load readline.
275
186
276 for a standalone version that uses prompt_toolkit, see
187 for a standalone version that uses prompt_toolkit, see
277 `IPython.terminal.debugger.TerminalPdb` and
188 `IPython.terminal.debugger.TerminalPdb` and
278 `IPython.terminal.debugger.set_trace()`
189 `IPython.terminal.debugger.set_trace()`
279
190
280
191
281 This debugger can hide and skip frames that are tagged according to some predicates.
192 This debugger can hide and skip frames that are tagged according to some predicates.
282 See the `skip_predicates` commands.
193 See the `skip_predicates` commands.
283
194
284 """
195 """
285
196
286 default_predicates = {
197 default_predicates = {
287 "tbhide": True,
198 "tbhide": True,
288 "readonly": False,
199 "readonly": False,
289 "ipython_internal": True,
200 "ipython_internal": True,
290 "debuggerskip": True,
201 "debuggerskip": True,
291 }
202 }
292
203
293 def __init__(self, color_scheme=None, completekey=None,
204 def __init__(self, completekey=None, stdin=None, stdout=None, context=5, **kwargs):
294 stdin=None, stdout=None, context=5, **kwargs):
295 """Create a new IPython debugger.
205 """Create a new IPython debugger.
296
206
297 Parameters
207 Parameters
298 ----------
208 ----------
299 color_scheme : default None
300 Deprecated, do not use.
301 completekey : default None
209 completekey : default None
302 Passed to pdb.Pdb.
210 Passed to pdb.Pdb.
303 stdin : default None
211 stdin : default None
304 Passed to pdb.Pdb.
212 Passed to pdb.Pdb.
305 stdout : default None
213 stdout : default None
306 Passed to pdb.Pdb.
214 Passed to pdb.Pdb.
307 context : int
215 context : int
308 Number of lines of source code context to show when
216 Number of lines of source code context to show when
309 displaying stacktrace information.
217 displaying stacktrace information.
310 **kwargs
218 **kwargs
311 Passed to pdb.Pdb.
219 Passed to pdb.Pdb.
312
220
313 Notes
221 Notes
314 -----
222 -----
315 The possibilities are python version dependent, see the python
223 The possibilities are python version dependent, see the python
316 docs for more info.
224 docs for more info.
317 """
225 """
318
226
319 # Parent constructor:
227 # Parent constructor:
320 try:
228 try:
321 self.context = int(context)
229 self.context = int(context)
322 if self.context <= 0:
230 if self.context <= 0:
323 raise ValueError("Context must be a positive integer")
231 raise ValueError("Context must be a positive integer")
324 except (TypeError, ValueError) as e:
232 except (TypeError, ValueError) as e:
325 raise ValueError("Context must be a positive integer") from e
233 raise ValueError("Context must be a positive integer") from e
326
234
327 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
235 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
328 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
236 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
329
237
330 # IPython changes...
238 # IPython changes...
331 self.shell = get_ipython()
239 self.shell = get_ipython()
332
240
333 if self.shell is None:
241 if self.shell is None:
334 save_main = sys.modules['__main__']
242 save_main = sys.modules['__main__']
335 # No IPython instance running, we must create one
243 # No IPython instance running, we must create one
336 from IPython.terminal.interactiveshell import \
244 from IPython.terminal.interactiveshell import \
337 TerminalInteractiveShell
245 TerminalInteractiveShell
338 self.shell = TerminalInteractiveShell.instance()
246 self.shell = TerminalInteractiveShell.instance()
339 # needed by any code which calls __import__("__main__") after
247 # needed by any code which calls __import__("__main__") after
340 # the debugger was entered. See also #9941.
248 # the debugger was entered. See also #9941.
341 sys.modules["__main__"] = save_main
249 sys.modules["__main__"] = save_main
342
250
343 if color_scheme is not None:
251
344 warnings.warn(
252 color_scheme = self.shell.colors
345 "The `color_scheme` argument is deprecated since version 5.1",
346 DeprecationWarning, stacklevel=2)
347 else:
348 color_scheme = self.shell.colors
349
253
350 self.aliases = {}
254 self.aliases = {}
351
255
352 # Create color table: we copy the default one from the traceback
256 # Create color table: we copy the default one from the traceback
353 # module and add a few attributes needed for debugging
257 # module and add a few attributes needed for debugging
354 self.color_scheme_table = exception_colors()
258 self.color_scheme_table = exception_colors()
355
259
356 # shorthands
260 # shorthands
357 C = coloransi.TermColors
261 C = coloransi.TermColors
358 cst = self.color_scheme_table
262 cst = self.color_scheme_table
359
263
360 cst['NoColor'].colors.prompt = C.NoColor
264 cst['NoColor'].colors.prompt = C.NoColor
361 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
265 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
362 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
266 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
363
267
364 cst['Linux'].colors.prompt = C.Green
268 cst['Linux'].colors.prompt = C.Green
365 cst['Linux'].colors.breakpoint_enabled = C.LightRed
269 cst['Linux'].colors.breakpoint_enabled = C.LightRed
366 cst['Linux'].colors.breakpoint_disabled = C.Red
270 cst['Linux'].colors.breakpoint_disabled = C.Red
367
271
368 cst['LightBG'].colors.prompt = C.Blue
272 cst['LightBG'].colors.prompt = C.Blue
369 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
273 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
370 cst['LightBG'].colors.breakpoint_disabled = C.Red
274 cst['LightBG'].colors.breakpoint_disabled = C.Red
371
275
372 cst['Neutral'].colors.prompt = C.Blue
276 cst['Neutral'].colors.prompt = C.Blue
373 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
277 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
374 cst['Neutral'].colors.breakpoint_disabled = C.Red
278 cst['Neutral'].colors.breakpoint_disabled = C.Red
375
279
376 # Add a python parser so we can syntax highlight source while
280 # Add a python parser so we can syntax highlight source while
377 # debugging.
281 # debugging.
378 self.parser = PyColorize.Parser(style=color_scheme)
282 self.parser = PyColorize.Parser(style=color_scheme)
379 self.set_colors(color_scheme)
283 self.set_colors(color_scheme)
380
284
381 # Set the prompt - the default prompt is '(Pdb)'
285 # Set the prompt - the default prompt is '(Pdb)'
382 self.prompt = prompt
286 self.prompt = prompt
383 self.skip_hidden = True
287 self.skip_hidden = True
384 self.report_skipped = True
288 self.report_skipped = True
385
289
386 # list of predicates we use to skip frames
290 # list of predicates we use to skip frames
387 self._predicates = self.default_predicates
291 self._predicates = self.default_predicates
388
292
389 #
293 #
390 def set_colors(self, scheme):
294 def set_colors(self, scheme):
391 """Shorthand access to the color table scheme selector method."""
295 """Shorthand access to the color table scheme selector method."""
392 self.color_scheme_table.set_active_scheme(scheme)
296 self.color_scheme_table.set_active_scheme(scheme)
393 self.parser.style = scheme
297 self.parser.style = scheme
394
298
395 def set_trace(self, frame=None):
299 def set_trace(self, frame=None):
396 if frame is None:
300 if frame is None:
397 frame = sys._getframe().f_back
301 frame = sys._getframe().f_back
398 self.initial_frame = frame
302 self.initial_frame = frame
399 return super().set_trace(frame)
303 return super().set_trace(frame)
400
304
401 def _hidden_predicate(self, frame):
305 def _hidden_predicate(self, frame):
402 """
306 """
403 Given a frame return whether it it should be hidden or not by IPython.
307 Given a frame return whether it it should be hidden or not by IPython.
404 """
308 """
405
309
406 if self._predicates["readonly"]:
310 if self._predicates["readonly"]:
407 fname = frame.f_code.co_filename
311 fname = frame.f_code.co_filename
408 # we need to check for file existence and interactively define
312 # we need to check for file existence and interactively define
409 # function would otherwise appear as RO.
313 # function would otherwise appear as RO.
410 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
314 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
411 return True
315 return True
412
316
413 if self._predicates["tbhide"]:
317 if self._predicates["tbhide"]:
414 if frame in (self.curframe, getattr(self, "initial_frame", None)):
318 if frame in (self.curframe, getattr(self, "initial_frame", None)):
415 return False
319 return False
416 frame_locals = self._get_frame_locals(frame)
320 frame_locals = self._get_frame_locals(frame)
417 if "__tracebackhide__" not in frame_locals:
321 if "__tracebackhide__" not in frame_locals:
418 return False
322 return False
419 return frame_locals["__tracebackhide__"]
323 return frame_locals["__tracebackhide__"]
420 return False
324 return False
421
325
422 def hidden_frames(self, stack):
326 def hidden_frames(self, stack):
423 """
327 """
424 Given an index in the stack return whether it should be skipped.
328 Given an index in the stack return whether it should be skipped.
425
329
426 This is used in up/down and where to skip frames.
330 This is used in up/down and where to skip frames.
427 """
331 """
428 # The f_locals dictionary is updated from the actual frame
332 # The f_locals dictionary is updated from the actual frame
429 # locals whenever the .f_locals accessor is called, so we
333 # locals whenever the .f_locals accessor is called, so we
430 # avoid calling it here to preserve self.curframe_locals.
334 # avoid calling it here to preserve self.curframe_locals.
431 # Furthermore, there is no good reason to hide the current frame.
335 # Furthermore, there is no good reason to hide the current frame.
432 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
336 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
433 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
337 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
434 if ip_start and self._predicates["ipython_internal"]:
338 if ip_start and self._predicates["ipython_internal"]:
435 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
339 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
436 return ip_hide
340 return ip_hide
437
341
438 def interaction(self, frame, traceback):
342 def interaction(self, frame, traceback):
439 try:
343 try:
440 OldPdb.interaction(self, frame, traceback)
344 OldPdb.interaction(self, frame, traceback)
441 except KeyboardInterrupt:
345 except KeyboardInterrupt:
442 self.stdout.write("\n" + self.shell.get_exception_only())
346 self.stdout.write("\n" + self.shell.get_exception_only())
443
347
444 def precmd(self, line):
348 def precmd(self, line):
445 """Perform useful escapes on the command before it is executed."""
349 """Perform useful escapes on the command before it is executed."""
446
350
447 if line.endswith("??"):
351 if line.endswith("??"):
448 line = "pinfo2 " + line[:-2]
352 line = "pinfo2 " + line[:-2]
449 elif line.endswith("?"):
353 elif line.endswith("?"):
450 line = "pinfo " + line[:-1]
354 line = "pinfo " + line[:-1]
451
355
452 line = super().precmd(line)
356 line = super().precmd(line)
453
357
454 return line
358 return line
455
359
456 def new_do_frame(self, arg):
360 def new_do_frame(self, arg):
457 OldPdb.do_frame(self, arg)
361 OldPdb.do_frame(self, arg)
458
362
459 def new_do_quit(self, arg):
363 def new_do_quit(self, arg):
460
364
461 if hasattr(self, 'old_all_completions'):
365 if hasattr(self, 'old_all_completions'):
462 self.shell.Completer.all_completions = self.old_all_completions
366 self.shell.Completer.all_completions = self.old_all_completions
463
367
464 return OldPdb.do_quit(self, arg)
368 return OldPdb.do_quit(self, arg)
465
369
466 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
370 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
467
371
468 def new_do_restart(self, arg):
372 def new_do_restart(self, arg):
469 """Restart command. In the context of ipython this is exactly the same
373 """Restart command. In the context of ipython this is exactly the same
470 thing as 'quit'."""
374 thing as 'quit'."""
471 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
375 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
472 return self.do_quit(arg)
376 return self.do_quit(arg)
473
377
474 def print_stack_trace(self, context=None):
378 def print_stack_trace(self, context=None):
475 Colors = self.color_scheme_table.active_colors
379 Colors = self.color_scheme_table.active_colors
476 ColorsNormal = Colors.Normal
380 ColorsNormal = Colors.Normal
477 if context is None:
381 if context is None:
478 context = self.context
382 context = self.context
479 try:
383 try:
480 context = int(context)
384 context = int(context)
481 if context <= 0:
385 if context <= 0:
482 raise ValueError("Context must be a positive integer")
386 raise ValueError("Context must be a positive integer")
483 except (TypeError, ValueError) as e:
387 except (TypeError, ValueError) as e:
484 raise ValueError("Context must be a positive integer") from e
388 raise ValueError("Context must be a positive integer") from e
485 try:
389 try:
486 skipped = 0
390 skipped = 0
487 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
391 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
488 if hidden and self.skip_hidden:
392 if hidden and self.skip_hidden:
489 skipped += 1
393 skipped += 1
490 continue
394 continue
491 if skipped:
395 if skipped:
492 print(
396 print(
493 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
397 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
494 )
398 )
495 skipped = 0
399 skipped = 0
496 self.print_stack_entry(frame_lineno, context=context)
400 self.print_stack_entry(frame_lineno, context=context)
497 if skipped:
401 if skipped:
498 print(
402 print(
499 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
403 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
500 )
404 )
501 except KeyboardInterrupt:
405 except KeyboardInterrupt:
502 pass
406 pass
503
407
504 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
408 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
505 context=None):
409 context=None):
506 if context is None:
410 if context is None:
507 context = self.context
411 context = self.context
508 try:
412 try:
509 context = int(context)
413 context = int(context)
510 if context <= 0:
414 if context <= 0:
511 raise ValueError("Context must be a positive integer")
415 raise ValueError("Context must be a positive integer")
512 except (TypeError, ValueError) as e:
416 except (TypeError, ValueError) as e:
513 raise ValueError("Context must be a positive integer") from e
417 raise ValueError("Context must be a positive integer") from e
514 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
418 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
515
419
516 # vds: >>
420 # vds: >>
517 frame, lineno = frame_lineno
421 frame, lineno = frame_lineno
518 filename = frame.f_code.co_filename
422 filename = frame.f_code.co_filename
519 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
423 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
520 # vds: <<
424 # vds: <<
521
425
522 def _get_frame_locals(self, frame):
426 def _get_frame_locals(self, frame):
523 """ "
427 """ "
524 Accessing f_local of current frame reset the namespace, so we want to avoid
428 Accessing f_local of current frame reset the namespace, so we want to avoid
525 that or the following can happen
429 that or the following can happen
526
430
527 ipdb> foo
431 ipdb> foo
528 "old"
432 "old"
529 ipdb> foo = "new"
433 ipdb> foo = "new"
530 ipdb> foo
434 ipdb> foo
531 "new"
435 "new"
532 ipdb> where
436 ipdb> where
533 ipdb> foo
437 ipdb> foo
534 "old"
438 "old"
535
439
536 So if frame is self.current_frame we instead return self.curframe_locals
440 So if frame is self.current_frame we instead return self.curframe_locals
537
441
538 """
442 """
539 if frame is self.curframe:
443 if frame is self.curframe:
540 return self.curframe_locals
444 return self.curframe_locals
541 else:
445 else:
542 return frame.f_locals
446 return frame.f_locals
543
447
544 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
448 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
545 if context is None:
449 if context is None:
546 context = self.context
450 context = self.context
547 try:
451 try:
548 context = int(context)
452 context = int(context)
549 if context <= 0:
453 if context <= 0:
550 print("Context must be a positive integer", file=self.stdout)
454 print("Context must be a positive integer", file=self.stdout)
551 except (TypeError, ValueError):
455 except (TypeError, ValueError):
552 print("Context must be a positive integer", file=self.stdout)
456 print("Context must be a positive integer", file=self.stdout)
553
457
554 import reprlib
458 import reprlib
555
459
556 ret = []
460 ret = []
557
461
558 Colors = self.color_scheme_table.active_colors
462 Colors = self.color_scheme_table.active_colors
559 ColorsNormal = Colors.Normal
463 ColorsNormal = Colors.Normal
560 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
464 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
561 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
465 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
562 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
466 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
563 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
467 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
564
468
565 frame, lineno = frame_lineno
469 frame, lineno = frame_lineno
566
470
567 return_value = ''
471 return_value = ''
568 loc_frame = self._get_frame_locals(frame)
472 loc_frame = self._get_frame_locals(frame)
569 if "__return__" in loc_frame:
473 if "__return__" in loc_frame:
570 rv = loc_frame["__return__"]
474 rv = loc_frame["__return__"]
571 # return_value += '->'
475 # return_value += '->'
572 return_value += reprlib.repr(rv) + "\n"
476 return_value += reprlib.repr(rv) + "\n"
573 ret.append(return_value)
477 ret.append(return_value)
574
478
575 #s = filename + '(' + `lineno` + ')'
479 #s = filename + '(' + `lineno` + ')'
576 filename = self.canonic(frame.f_code.co_filename)
480 filename = self.canonic(frame.f_code.co_filename)
577 link = tpl_link % py3compat.cast_unicode(filename)
481 link = tpl_link % py3compat.cast_unicode(filename)
578
482
579 if frame.f_code.co_name:
483 if frame.f_code.co_name:
580 func = frame.f_code.co_name
484 func = frame.f_code.co_name
581 else:
485 else:
582 func = "<lambda>"
486 func = "<lambda>"
583
487
584 call = ""
488 call = ""
585 if func != "?":
489 if func != "?":
586 if "__args__" in loc_frame:
490 if "__args__" in loc_frame:
587 args = reprlib.repr(loc_frame["__args__"])
491 args = reprlib.repr(loc_frame["__args__"])
588 else:
492 else:
589 args = '()'
493 args = '()'
590 call = tpl_call % (func, args)
494 call = tpl_call % (func, args)
591
495
592 # The level info should be generated in the same format pdb uses, to
496 # The level info should be generated in the same format pdb uses, to
593 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
497 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
594 if frame is self.curframe:
498 if frame is self.curframe:
595 ret.append('> ')
499 ret.append('> ')
596 else:
500 else:
597 ret.append(" ")
501 ret.append(" ")
598 ret.append("%s(%s)%s\n" % (link, lineno, call))
502 ret.append("%s(%s)%s\n" % (link, lineno, call))
599
503
600 start = lineno - 1 - context//2
504 start = lineno - 1 - context//2
601 lines = linecache.getlines(filename)
505 lines = linecache.getlines(filename)
602 start = min(start, len(lines) - context)
506 start = min(start, len(lines) - context)
603 start = max(start, 0)
507 start = max(start, 0)
604 lines = lines[start : start + context]
508 lines = lines[start : start + context]
605
509
606 for i, line in enumerate(lines):
510 for i, line in enumerate(lines):
607 show_arrow = start + 1 + i == lineno
511 show_arrow = start + 1 + i == lineno
608 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
512 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
609 ret.append(
513 ret.append(
610 self.__format_line(
514 self.__format_line(
611 linetpl, filename, start + 1 + i, line, arrow=show_arrow
515 linetpl, filename, start + 1 + i, line, arrow=show_arrow
612 )
516 )
613 )
517 )
614 return "".join(ret)
518 return "".join(ret)
615
519
616 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
520 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
617 bp_mark = ""
521 bp_mark = ""
618 bp_mark_color = ""
522 bp_mark_color = ""
619
523
620 new_line, err = self.parser.format2(line, 'str')
524 new_line, err = self.parser.format2(line, 'str')
621 if not err:
525 if not err:
622 line = new_line
526 line = new_line
623
527
624 bp = None
528 bp = None
625 if lineno in self.get_file_breaks(filename):
529 if lineno in self.get_file_breaks(filename):
626 bps = self.get_breaks(filename, lineno)
530 bps = self.get_breaks(filename, lineno)
627 bp = bps[-1]
531 bp = bps[-1]
628
532
629 if bp:
533 if bp:
630 Colors = self.color_scheme_table.active_colors
534 Colors = self.color_scheme_table.active_colors
631 bp_mark = str(bp.number)
535 bp_mark = str(bp.number)
632 bp_mark_color = Colors.breakpoint_enabled
536 bp_mark_color = Colors.breakpoint_enabled
633 if not bp.enabled:
537 if not bp.enabled:
634 bp_mark_color = Colors.breakpoint_disabled
538 bp_mark_color = Colors.breakpoint_disabled
635
539
636 numbers_width = 7
540 numbers_width = 7
637 if arrow:
541 if arrow:
638 # This is the line with the error
542 # This is the line with the error
639 pad = numbers_width - len(str(lineno)) - len(bp_mark)
543 pad = numbers_width - len(str(lineno)) - len(bp_mark)
640 num = '%s%s' % (make_arrow(pad), str(lineno))
544 num = '%s%s' % (make_arrow(pad), str(lineno))
641 else:
545 else:
642 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
546 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
643
547
644 return tpl_line % (bp_mark_color + bp_mark, num, line)
548 return tpl_line % (bp_mark_color + bp_mark, num, line)
645
549
646 def print_list_lines(self, filename, first, last):
550 def print_list_lines(self, filename, first, last):
647 """The printing (as opposed to the parsing part of a 'list'
551 """The printing (as opposed to the parsing part of a 'list'
648 command."""
552 command."""
649 try:
553 try:
650 Colors = self.color_scheme_table.active_colors
554 Colors = self.color_scheme_table.active_colors
651 ColorsNormal = Colors.Normal
555 ColorsNormal = Colors.Normal
652 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
556 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
653 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
557 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
654 src = []
558 src = []
655 if filename == "<string>" and hasattr(self, "_exec_filename"):
559 if filename == "<string>" and hasattr(self, "_exec_filename"):
656 filename = self._exec_filename
560 filename = self._exec_filename
657
561
658 for lineno in range(first, last+1):
562 for lineno in range(first, last+1):
659 line = linecache.getline(filename, lineno)
563 line = linecache.getline(filename, lineno)
660 if not line:
564 if not line:
661 break
565 break
662
566
663 if lineno == self.curframe.f_lineno:
567 if lineno == self.curframe.f_lineno:
664 line = self.__format_line(
568 line = self.__format_line(
665 tpl_line_em, filename, lineno, line, arrow=True
569 tpl_line_em, filename, lineno, line, arrow=True
666 )
570 )
667 else:
571 else:
668 line = self.__format_line(
572 line = self.__format_line(
669 tpl_line, filename, lineno, line, arrow=False
573 tpl_line, filename, lineno, line, arrow=False
670 )
574 )
671
575
672 src.append(line)
576 src.append(line)
673 self.lineno = lineno
577 self.lineno = lineno
674
578
675 print(''.join(src), file=self.stdout)
579 print(''.join(src), file=self.stdout)
676
580
677 except KeyboardInterrupt:
581 except KeyboardInterrupt:
678 pass
582 pass
679
583
680 def do_skip_predicates(self, args):
584 def do_skip_predicates(self, args):
681 """
585 """
682 Turn on/off individual predicates as to whether a frame should be hidden/skip.
586 Turn on/off individual predicates as to whether a frame should be hidden/skip.
683
587
684 The global option to skip (or not) hidden frames is set with skip_hidden
588 The global option to skip (or not) hidden frames is set with skip_hidden
685
589
686 To change the value of a predicate
590 To change the value of a predicate
687
591
688 skip_predicates key [true|false]
592 skip_predicates key [true|false]
689
593
690 Call without arguments to see the current values.
594 Call without arguments to see the current values.
691
595
692 To permanently change the value of an option add the corresponding
596 To permanently change the value of an option add the corresponding
693 command to your ``~/.pdbrc`` file. If you are programmatically using the
597 command to your ``~/.pdbrc`` file. If you are programmatically using the
694 Pdb instance you can also change the ``default_predicates`` class
598 Pdb instance you can also change the ``default_predicates`` class
695 attribute.
599 attribute.
696 """
600 """
697 if not args.strip():
601 if not args.strip():
698 print("current predicates:")
602 print("current predicates:")
699 for (p, v) in self._predicates.items():
603 for (p, v) in self._predicates.items():
700 print(" ", p, ":", v)
604 print(" ", p, ":", v)
701 return
605 return
702 type_value = args.strip().split(" ")
606 type_value = args.strip().split(" ")
703 if len(type_value) != 2:
607 if len(type_value) != 2:
704 print(
608 print(
705 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
609 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
706 )
610 )
707 return
611 return
708
612
709 type_, value = type_value
613 type_, value = type_value
710 if type_ not in self._predicates:
614 if type_ not in self._predicates:
711 print(f"{type_!r} not in {set(self._predicates.keys())}")
615 print(f"{type_!r} not in {set(self._predicates.keys())}")
712 return
616 return
713 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
617 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
714 print(
618 print(
715 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
619 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
716 )
620 )
717 return
621 return
718
622
719 self._predicates[type_] = value.lower() in ("true", "yes", "1")
623 self._predicates[type_] = value.lower() in ("true", "yes", "1")
720 if not any(self._predicates.values()):
624 if not any(self._predicates.values()):
721 print(
625 print(
722 "Warning, all predicates set to False, skip_hidden may not have any effects."
626 "Warning, all predicates set to False, skip_hidden may not have any effects."
723 )
627 )
724
628
725 def do_skip_hidden(self, arg):
629 def do_skip_hidden(self, arg):
726 """
630 """
727 Change whether or not we should skip frames with the
631 Change whether or not we should skip frames with the
728 __tracebackhide__ attribute.
632 __tracebackhide__ attribute.
729 """
633 """
730 if not arg.strip():
634 if not arg.strip():
731 print(
635 print(
732 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
636 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
733 )
637 )
734 elif arg.strip().lower() in ("true", "yes"):
638 elif arg.strip().lower() in ("true", "yes"):
735 self.skip_hidden = True
639 self.skip_hidden = True
736 elif arg.strip().lower() in ("false", "no"):
640 elif arg.strip().lower() in ("false", "no"):
737 self.skip_hidden = False
641 self.skip_hidden = False
738 if not any(self._predicates.values()):
642 if not any(self._predicates.values()):
739 print(
643 print(
740 "Warning, all predicates set to False, skip_hidden may not have any effects."
644 "Warning, all predicates set to False, skip_hidden may not have any effects."
741 )
645 )
742
646
743 def do_list(self, arg):
647 def do_list(self, arg):
744 """Print lines of code from the current stack frame
648 """Print lines of code from the current stack frame
745 """
649 """
746 self.lastcmd = 'list'
650 self.lastcmd = 'list'
747 last = None
651 last = None
748 if arg:
652 if arg:
749 try:
653 try:
750 x = eval(arg, {}, {})
654 x = eval(arg, {}, {})
751 if type(x) == type(()):
655 if type(x) == type(()):
752 first, last = x
656 first, last = x
753 first = int(first)
657 first = int(first)
754 last = int(last)
658 last = int(last)
755 if last < first:
659 if last < first:
756 # Assume it's a count
660 # Assume it's a count
757 last = first + last
661 last = first + last
758 else:
662 else:
759 first = max(1, int(x) - 5)
663 first = max(1, int(x) - 5)
760 except:
664 except:
761 print('*** Error in argument:', repr(arg), file=self.stdout)
665 print('*** Error in argument:', repr(arg), file=self.stdout)
762 return
666 return
763 elif self.lineno is None:
667 elif self.lineno is None:
764 first = max(1, self.curframe.f_lineno - 5)
668 first = max(1, self.curframe.f_lineno - 5)
765 else:
669 else:
766 first = self.lineno + 1
670 first = self.lineno + 1
767 if last is None:
671 if last is None:
768 last = first + 10
672 last = first + 10
769 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
673 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
770
674
771 # vds: >>
675 # vds: >>
772 lineno = first
676 lineno = first
773 filename = self.curframe.f_code.co_filename
677 filename = self.curframe.f_code.co_filename
774 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
678 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
775 # vds: <<
679 # vds: <<
776
680
777 do_l = do_list
681 do_l = do_list
778
682
779 def getsourcelines(self, obj):
683 def getsourcelines(self, obj):
780 lines, lineno = inspect.findsource(obj)
684 lines, lineno = inspect.findsource(obj)
781 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
685 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
782 # must be a module frame: do not try to cut a block out of it
686 # must be a module frame: do not try to cut a block out of it
783 return lines, 1
687 return lines, 1
784 elif inspect.ismodule(obj):
688 elif inspect.ismodule(obj):
785 return lines, 1
689 return lines, 1
786 return inspect.getblock(lines[lineno:]), lineno+1
690 return inspect.getblock(lines[lineno:]), lineno+1
787
691
788 def do_longlist(self, arg):
692 def do_longlist(self, arg):
789 """Print lines of code from the current stack frame.
693 """Print lines of code from the current stack frame.
790
694
791 Shows more lines than 'list' does.
695 Shows more lines than 'list' does.
792 """
696 """
793 self.lastcmd = 'longlist'
697 self.lastcmd = 'longlist'
794 try:
698 try:
795 lines, lineno = self.getsourcelines(self.curframe)
699 lines, lineno = self.getsourcelines(self.curframe)
796 except OSError as err:
700 except OSError as err:
797 self.error(err)
701 self.error(err)
798 return
702 return
799 last = lineno + len(lines)
703 last = lineno + len(lines)
800 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
704 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
801 do_ll = do_longlist
705 do_ll = do_longlist
802
706
803 def do_debug(self, arg):
707 def do_debug(self, arg):
804 """debug code
708 """debug code
805 Enter a recursive debugger that steps through the code
709 Enter a recursive debugger that steps through the code
806 argument (which is an arbitrary expression or statement to be
710 argument (which is an arbitrary expression or statement to be
807 executed in the current environment).
711 executed in the current environment).
808 """
712 """
809 trace_function = sys.gettrace()
713 trace_function = sys.gettrace()
810 sys.settrace(None)
714 sys.settrace(None)
811 globals = self.curframe.f_globals
715 globals = self.curframe.f_globals
812 locals = self.curframe_locals
716 locals = self.curframe_locals
813 p = self.__class__(completekey=self.completekey,
717 p = self.__class__(completekey=self.completekey,
814 stdin=self.stdin, stdout=self.stdout)
718 stdin=self.stdin, stdout=self.stdout)
815 p.use_rawinput = self.use_rawinput
719 p.use_rawinput = self.use_rawinput
816 p.prompt = "(%s) " % self.prompt.strip()
720 p.prompt = "(%s) " % self.prompt.strip()
817 self.message("ENTERING RECURSIVE DEBUGGER")
721 self.message("ENTERING RECURSIVE DEBUGGER")
818 sys.call_tracing(p.run, (arg, globals, locals))
722 sys.call_tracing(p.run, (arg, globals, locals))
819 self.message("LEAVING RECURSIVE DEBUGGER")
723 self.message("LEAVING RECURSIVE DEBUGGER")
820 sys.settrace(trace_function)
724 sys.settrace(trace_function)
821 self.lastcmd = p.lastcmd
725 self.lastcmd = p.lastcmd
822
726
823 def do_pdef(self, arg):
727 def do_pdef(self, arg):
824 """Print the call signature for any callable object.
728 """Print the call signature for any callable object.
825
729
826 The debugger interface to %pdef"""
730 The debugger interface to %pdef"""
827 namespaces = [
731 namespaces = [
828 ("Locals", self.curframe_locals),
732 ("Locals", self.curframe_locals),
829 ("Globals", self.curframe.f_globals),
733 ("Globals", self.curframe.f_globals),
830 ]
734 ]
831 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
735 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
832
736
833 def do_pdoc(self, arg):
737 def do_pdoc(self, arg):
834 """Print the docstring for an object.
738 """Print the docstring for an object.
835
739
836 The debugger interface to %pdoc."""
740 The debugger interface to %pdoc."""
837 namespaces = [
741 namespaces = [
838 ("Locals", self.curframe_locals),
742 ("Locals", self.curframe_locals),
839 ("Globals", self.curframe.f_globals),
743 ("Globals", self.curframe.f_globals),
840 ]
744 ]
841 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
745 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
842
746
843 def do_pfile(self, arg):
747 def do_pfile(self, arg):
844 """Print (or run through pager) the file where an object is defined.
748 """Print (or run through pager) the file where an object is defined.
845
749
846 The debugger interface to %pfile.
750 The debugger interface to %pfile.
847 """
751 """
848 namespaces = [
752 namespaces = [
849 ("Locals", self.curframe_locals),
753 ("Locals", self.curframe_locals),
850 ("Globals", self.curframe.f_globals),
754 ("Globals", self.curframe.f_globals),
851 ]
755 ]
852 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
756 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
853
757
854 def do_pinfo(self, arg):
758 def do_pinfo(self, arg):
855 """Provide detailed information about an object.
759 """Provide detailed information about an object.
856
760
857 The debugger interface to %pinfo, i.e., obj?."""
761 The debugger interface to %pinfo, i.e., obj?."""
858 namespaces = [
762 namespaces = [
859 ("Locals", self.curframe_locals),
763 ("Locals", self.curframe_locals),
860 ("Globals", self.curframe.f_globals),
764 ("Globals", self.curframe.f_globals),
861 ]
765 ]
862 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
766 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
863
767
864 def do_pinfo2(self, arg):
768 def do_pinfo2(self, arg):
865 """Provide extra detailed information about an object.
769 """Provide extra detailed information about an object.
866
770
867 The debugger interface to %pinfo2, i.e., obj??."""
771 The debugger interface to %pinfo2, i.e., obj??."""
868 namespaces = [
772 namespaces = [
869 ("Locals", self.curframe_locals),
773 ("Locals", self.curframe_locals),
870 ("Globals", self.curframe.f_globals),
774 ("Globals", self.curframe.f_globals),
871 ]
775 ]
872 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
776 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
873
777
874 def do_psource(self, arg):
778 def do_psource(self, arg):
875 """Print (or run through pager) the source code for an object."""
779 """Print (or run through pager) the source code for an object."""
876 namespaces = [
780 namespaces = [
877 ("Locals", self.curframe_locals),
781 ("Locals", self.curframe_locals),
878 ("Globals", self.curframe.f_globals),
782 ("Globals", self.curframe.f_globals),
879 ]
783 ]
880 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
784 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
881
785
882 def do_where(self, arg):
786 def do_where(self, arg):
883 """w(here)
787 """w(here)
884 Print a stack trace, with the most recent frame at the bottom.
788 Print a stack trace, with the most recent frame at the bottom.
885 An arrow indicates the "current frame", which determines the
789 An arrow indicates the "current frame", which determines the
886 context of most commands. 'bt' is an alias for this command.
790 context of most commands. 'bt' is an alias for this command.
887
791
888 Take a number as argument as an (optional) number of context line to
792 Take a number as argument as an (optional) number of context line to
889 print"""
793 print"""
890 if arg:
794 if arg:
891 try:
795 try:
892 context = int(arg)
796 context = int(arg)
893 except ValueError as err:
797 except ValueError as err:
894 self.error(err)
798 self.error(err)
895 return
799 return
896 self.print_stack_trace(context)
800 self.print_stack_trace(context)
897 else:
801 else:
898 self.print_stack_trace()
802 self.print_stack_trace()
899
803
900 do_w = do_where
804 do_w = do_where
901
805
902 def break_anywhere(self, frame):
806 def break_anywhere(self, frame):
903 """
807 """
904
808
905 _stop_in_decorator_internals is overly restrictive, as we may still want
809 _stop_in_decorator_internals is overly restrictive, as we may still want
906 to trace function calls, so we need to also update break_anywhere so
810 to trace function calls, so we need to also update break_anywhere so
907 that is we don't `stop_here`, because of debugger skip, we may still
811 that is we don't `stop_here`, because of debugger skip, we may still
908 stop at any point inside the function
812 stop at any point inside the function
909
813
910 """
814 """
911
815
912 sup = super().break_anywhere(frame)
816 sup = super().break_anywhere(frame)
913 if sup:
817 if sup:
914 return sup
818 return sup
915 if self._predicates["debuggerskip"]:
819 if self._predicates["debuggerskip"]:
916 if DEBUGGERSKIP in frame.f_code.co_varnames:
820 if DEBUGGERSKIP in frame.f_code.co_varnames:
917 return True
821 return True
918 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
822 if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
919 return True
823 return True
920 return False
824 return False
921
825
922 def _is_in_decorator_internal_and_should_skip(self, frame):
826 def _is_in_decorator_internal_and_should_skip(self, frame):
923 """
827 """
924 Utility to tell us whether we are in a decorator internal and should stop.
828 Utility to tell us whether we are in a decorator internal and should stop.
925
829
926
830
927
831
928 """
832 """
929
833
930 # if we are disabled don't skip
834 # if we are disabled don't skip
931 if not self._predicates["debuggerskip"]:
835 if not self._predicates["debuggerskip"]:
932 return False
836 return False
933
837
934 # if frame is tagged, skip by default.
838 # if frame is tagged, skip by default.
935 if DEBUGGERSKIP in frame.f_code.co_varnames:
839 if DEBUGGERSKIP in frame.f_code.co_varnames:
936 return True
840 return True
937
841
938 # if one of the parent frame value set to True skip as well.
842 # if one of the parent frame value set to True skip as well.
939
843
940 cframe = frame
844 cframe = frame
941 while getattr(cframe, "f_back", None):
845 while getattr(cframe, "f_back", None):
942 cframe = cframe.f_back
846 cframe = cframe.f_back
943 if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
847 if self._get_frame_locals(cframe).get(DEBUGGERSKIP):
944 return True
848 return True
945
849
946 return False
850 return False
947
851
948 def stop_here(self, frame):
852 def stop_here(self, frame):
949
853
950 if self._is_in_decorator_internal_and_should_skip(frame) is True:
854 if self._is_in_decorator_internal_and_should_skip(frame) is True:
951 return False
855 return False
952
856
953 hidden = False
857 hidden = False
954 if self.skip_hidden:
858 if self.skip_hidden:
955 hidden = self._hidden_predicate(frame)
859 hidden = self._hidden_predicate(frame)
956 if hidden:
860 if hidden:
957 if self.report_skipped:
861 if self.report_skipped:
958 Colors = self.color_scheme_table.active_colors
862 Colors = self.color_scheme_table.active_colors
959 ColorsNormal = Colors.Normal
863 ColorsNormal = Colors.Normal
960 print(
864 print(
961 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
865 f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n"
962 )
866 )
963 return super().stop_here(frame)
867 return super().stop_here(frame)
964
868
965 def do_up(self, arg):
869 def do_up(self, arg):
966 """u(p) [count]
870 """u(p) [count]
967 Move the current frame count (default one) levels up in the
871 Move the current frame count (default one) levels up in the
968 stack trace (to an older frame).
872 stack trace (to an older frame).
969
873
970 Will skip hidden frames.
874 Will skip hidden frames.
971 """
875 """
972 # modified version of upstream that skips
876 # modified version of upstream that skips
973 # frames with __tracebackhide__
877 # frames with __tracebackhide__
974 if self.curindex == 0:
878 if self.curindex == 0:
975 self.error("Oldest frame")
879 self.error("Oldest frame")
976 return
880 return
977 try:
881 try:
978 count = int(arg or 1)
882 count = int(arg or 1)
979 except ValueError:
883 except ValueError:
980 self.error("Invalid frame count (%s)" % arg)
884 self.error("Invalid frame count (%s)" % arg)
981 return
885 return
982 skipped = 0
886 skipped = 0
983 if count < 0:
887 if count < 0:
984 _newframe = 0
888 _newframe = 0
985 else:
889 else:
986 counter = 0
890 counter = 0
987 hidden_frames = self.hidden_frames(self.stack)
891 hidden_frames = self.hidden_frames(self.stack)
988 for i in range(self.curindex - 1, -1, -1):
892 for i in range(self.curindex - 1, -1, -1):
989 if hidden_frames[i] and self.skip_hidden:
893 if hidden_frames[i] and self.skip_hidden:
990 skipped += 1
894 skipped += 1
991 continue
895 continue
992 counter += 1
896 counter += 1
993 if counter >= count:
897 if counter >= count:
994 break
898 break
995 else:
899 else:
996 # if no break occurred.
900 # if no break occurred.
997 self.error(
901 self.error(
998 "all frames above hidden, use `skip_hidden False` to get get into those."
902 "all frames above hidden, use `skip_hidden False` to get get into those."
999 )
903 )
1000 return
904 return
1001
905
1002 Colors = self.color_scheme_table.active_colors
906 Colors = self.color_scheme_table.active_colors
1003 ColorsNormal = Colors.Normal
907 ColorsNormal = Colors.Normal
1004 _newframe = i
908 _newframe = i
1005 self._select_frame(_newframe)
909 self._select_frame(_newframe)
1006 if skipped:
910 if skipped:
1007 print(
911 print(
1008 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
912 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1009 )
913 )
1010
914
1011 def do_down(self, arg):
915 def do_down(self, arg):
1012 """d(own) [count]
916 """d(own) [count]
1013 Move the current frame count (default one) levels down in the
917 Move the current frame count (default one) levels down in the
1014 stack trace (to a newer frame).
918 stack trace (to a newer frame).
1015
919
1016 Will skip hidden frames.
920 Will skip hidden frames.
1017 """
921 """
1018 if self.curindex + 1 == len(self.stack):
922 if self.curindex + 1 == len(self.stack):
1019 self.error("Newest frame")
923 self.error("Newest frame")
1020 return
924 return
1021 try:
925 try:
1022 count = int(arg or 1)
926 count = int(arg or 1)
1023 except ValueError:
927 except ValueError:
1024 self.error("Invalid frame count (%s)" % arg)
928 self.error("Invalid frame count (%s)" % arg)
1025 return
929 return
1026 if count < 0:
930 if count < 0:
1027 _newframe = len(self.stack) - 1
931 _newframe = len(self.stack) - 1
1028 else:
932 else:
1029 counter = 0
933 counter = 0
1030 skipped = 0
934 skipped = 0
1031 hidden_frames = self.hidden_frames(self.stack)
935 hidden_frames = self.hidden_frames(self.stack)
1032 for i in range(self.curindex + 1, len(self.stack)):
936 for i in range(self.curindex + 1, len(self.stack)):
1033 if hidden_frames[i] and self.skip_hidden:
937 if hidden_frames[i] and self.skip_hidden:
1034 skipped += 1
938 skipped += 1
1035 continue
939 continue
1036 counter += 1
940 counter += 1
1037 if counter >= count:
941 if counter >= count:
1038 break
942 break
1039 else:
943 else:
1040 self.error(
944 self.error(
1041 "all frames below hidden, use `skip_hidden False` to get get into those."
945 "all frames below hidden, use `skip_hidden False` to get get into those."
1042 )
946 )
1043 return
947 return
1044
948
1045 Colors = self.color_scheme_table.active_colors
949 Colors = self.color_scheme_table.active_colors
1046 ColorsNormal = Colors.Normal
950 ColorsNormal = Colors.Normal
1047 if skipped:
951 if skipped:
1048 print(
952 print(
1049 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
953 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
1050 )
954 )
1051 _newframe = i
955 _newframe = i
1052
956
1053 self._select_frame(_newframe)
957 self._select_frame(_newframe)
1054
958
1055 do_d = do_down
959 do_d = do_down
1056 do_u = do_up
960 do_u = do_up
1057
961
1058 def do_context(self, context):
962 def do_context(self, context):
1059 """context number_of_lines
963 """context number_of_lines
1060 Set the number of lines of source code to show when displaying
964 Set the number of lines of source code to show when displaying
1061 stacktrace information.
965 stacktrace information.
1062 """
966 """
1063 try:
967 try:
1064 new_context = int(context)
968 new_context = int(context)
1065 if new_context <= 0:
969 if new_context <= 0:
1066 raise ValueError()
970 raise ValueError()
1067 self.context = new_context
971 self.context = new_context
1068 except ValueError:
972 except ValueError:
1069 self.error("The 'context' command requires a positive integer argument.")
973 self.error("The 'context' command requires a positive integer argument.")
1070
974
1071
975
1072 class InterruptiblePdb(Pdb):
976 class InterruptiblePdb(Pdb):
1073 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
977 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
1074
978
1075 def cmdloop(self, intro=None):
979 def cmdloop(self, intro=None):
1076 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
980 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
1077 try:
981 try:
1078 return OldPdb.cmdloop(self, intro=intro)
982 return OldPdb.cmdloop(self, intro=intro)
1079 except KeyboardInterrupt:
983 except KeyboardInterrupt:
1080 self.stop_here = lambda frame: False
984 self.stop_here = lambda frame: False
1081 self.do_quit("")
985 self.do_quit("")
1082 sys.settrace(None)
986 sys.settrace(None)
1083 self.quitting = False
987 self.quitting = False
1084 raise
988 raise
1085
989
1086 def _cmdloop(self):
990 def _cmdloop(self):
1087 while True:
991 while True:
1088 try:
992 try:
1089 # keyboard interrupts allow for an easy way to cancel
993 # keyboard interrupts allow for an easy way to cancel
1090 # the current command, so allow them during interactive input
994 # the current command, so allow them during interactive input
1091 self.allow_kbdint = True
995 self.allow_kbdint = True
1092 self.cmdloop()
996 self.cmdloop()
1093 self.allow_kbdint = False
997 self.allow_kbdint = False
1094 break
998 break
1095 except KeyboardInterrupt:
999 except KeyboardInterrupt:
1096 self.message('--KeyboardInterrupt--')
1000 self.message('--KeyboardInterrupt--')
1097 raise
1001 raise
1098
1002
1099
1003
1100 def set_trace(frame=None):
1004 def set_trace(frame=None):
1101 """
1005 """
1102 Start debugging from `frame`.
1006 Start debugging from `frame`.
1103
1007
1104 If frame is not specified, debugging starts from caller's frame.
1008 If frame is not specified, debugging starts from caller's frame.
1105 """
1009 """
1106 Pdb().set_trace(frame or sys._getframe().f_back)
1010 Pdb().set_trace(frame or sys._getframe().f_back)
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,578 +1,559 b''
1 """Tests for debugging machinery.
1 """Tests for debugging machinery.
2 """
2 """
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import bdb
7 import bdb
8 import builtins
8 import builtins
9 import os
9 import os
10 import signal
11 import subprocess
12 import sys
10 import sys
13 import time
14 import warnings
15
11
16 from subprocess import PIPE, CalledProcessError, check_output
17 from tempfile import NamedTemporaryFile
12 from tempfile import NamedTemporaryFile
18 from textwrap import dedent
13 from textwrap import dedent
19 from unittest.mock import patch
14 from unittest.mock import patch
20
15
21 from IPython.core import debugger
16 from IPython.core import debugger
22 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
17 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
23 from IPython.testing.decorators import skip_win32
18 from IPython.testing.decorators import skip_win32
24
19
25 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
26 # Helper classes, from CPython's Pdb test suite
21 # Helper classes, from CPython's Pdb test suite
27 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
28
23
29 class _FakeInput(object):
24 class _FakeInput(object):
30 """
25 """
31 A fake input stream for pdb's interactive debugger. Whenever a
26 A fake input stream for pdb's interactive debugger. Whenever a
32 line is read, print it (to simulate the user typing it), and then
27 line is read, print it (to simulate the user typing it), and then
33 return it. The set of lines to return is specified in the
28 return it. The set of lines to return is specified in the
34 constructor; they should not have trailing newlines.
29 constructor; they should not have trailing newlines.
35 """
30 """
36 def __init__(self, lines):
31 def __init__(self, lines):
37 self.lines = iter(lines)
32 self.lines = iter(lines)
38
33
39 def readline(self):
34 def readline(self):
40 line = next(self.lines)
35 line = next(self.lines)
41 print(line)
36 print(line)
42 return line+'\n'
37 return line+'\n'
43
38
44 class PdbTestInput(object):
39 class PdbTestInput(object):
45 """Context manager that makes testing Pdb in doctests easier."""
40 """Context manager that makes testing Pdb in doctests easier."""
46
41
47 def __init__(self, input):
42 def __init__(self, input):
48 self.input = input
43 self.input = input
49
44
50 def __enter__(self):
45 def __enter__(self):
51 self.real_stdin = sys.stdin
46 self.real_stdin = sys.stdin
52 sys.stdin = _FakeInput(self.input)
47 sys.stdin = _FakeInput(self.input)
53
48
54 def __exit__(self, *exc):
49 def __exit__(self, *exc):
55 sys.stdin = self.real_stdin
50 sys.stdin = self.real_stdin
56
51
57 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
58 # Tests
53 # Tests
59 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
60
55
61 def test_longer_repr():
62 from reprlib import repr as trepr
63
64 a = '1234567890'* 7
65 ar = "'1234567890123456789012345678901234567890123456789012345678901234567890'"
66 a_trunc = "'123456789012...8901234567890'"
67 assert trepr(a) == a_trunc
68 # The creation of our tracer modifies the repr module's repr function
69 # in-place, since that global is used directly by the stdlib's pdb module.
70 with warnings.catch_warnings():
71 warnings.simplefilter('ignore', DeprecationWarning)
72 debugger.Tracer()
73 assert trepr(a) == ar
74
75 def test_ipdb_magics():
56 def test_ipdb_magics():
76 '''Test calling some IPython magics from ipdb.
57 '''Test calling some IPython magics from ipdb.
77
58
78 First, set up some test functions and classes which we can inspect.
59 First, set up some test functions and classes which we can inspect.
79
60
80 >>> class ExampleClass(object):
61 >>> class ExampleClass(object):
81 ... """Docstring for ExampleClass."""
62 ... """Docstring for ExampleClass."""
82 ... def __init__(self):
63 ... def __init__(self):
83 ... """Docstring for ExampleClass.__init__"""
64 ... """Docstring for ExampleClass.__init__"""
84 ... pass
65 ... pass
85 ... def __str__(self):
66 ... def __str__(self):
86 ... return "ExampleClass()"
67 ... return "ExampleClass()"
87
68
88 >>> def example_function(x, y, z="hello"):
69 >>> def example_function(x, y, z="hello"):
89 ... """Docstring for example_function."""
70 ... """Docstring for example_function."""
90 ... pass
71 ... pass
91
72
92 >>> old_trace = sys.gettrace()
73 >>> old_trace = sys.gettrace()
93
74
94 Create a function which triggers ipdb.
75 Create a function which triggers ipdb.
95
76
96 >>> def trigger_ipdb():
77 >>> def trigger_ipdb():
97 ... a = ExampleClass()
78 ... a = ExampleClass()
98 ... debugger.Pdb().set_trace()
79 ... debugger.Pdb().set_trace()
99
80
100 >>> with PdbTestInput([
81 >>> with PdbTestInput([
101 ... 'pdef example_function',
82 ... 'pdef example_function',
102 ... 'pdoc ExampleClass',
83 ... 'pdoc ExampleClass',
103 ... 'up',
84 ... 'up',
104 ... 'down',
85 ... 'down',
105 ... 'list',
86 ... 'list',
106 ... 'pinfo a',
87 ... 'pinfo a',
107 ... 'll',
88 ... 'll',
108 ... 'continue',
89 ... 'continue',
109 ... ]):
90 ... ]):
110 ... trigger_ipdb()
91 ... trigger_ipdb()
111 --Return--
92 --Return--
112 None
93 None
113 > <doctest ...>(3)trigger_ipdb()
94 > <doctest ...>(3)trigger_ipdb()
114 1 def trigger_ipdb():
95 1 def trigger_ipdb():
115 2 a = ExampleClass()
96 2 a = ExampleClass()
116 ----> 3 debugger.Pdb().set_trace()
97 ----> 3 debugger.Pdb().set_trace()
117 <BLANKLINE>
98 <BLANKLINE>
118 ipdb> pdef example_function
99 ipdb> pdef example_function
119 example_function(x, y, z='hello')
100 example_function(x, y, z='hello')
120 ipdb> pdoc ExampleClass
101 ipdb> pdoc ExampleClass
121 Class docstring:
102 Class docstring:
122 Docstring for ExampleClass.
103 Docstring for ExampleClass.
123 Init docstring:
104 Init docstring:
124 Docstring for ExampleClass.__init__
105 Docstring for ExampleClass.__init__
125 ipdb> up
106 ipdb> up
126 > <doctest ...>(11)<module>()
107 > <doctest ...>(11)<module>()
127 7 'pinfo a',
108 7 'pinfo a',
128 8 'll',
109 8 'll',
129 9 'continue',
110 9 'continue',
130 10 ]):
111 10 ]):
131 ---> 11 trigger_ipdb()
112 ---> 11 trigger_ipdb()
132 <BLANKLINE>
113 <BLANKLINE>
133 ipdb> down
114 ipdb> down
134 None
115 None
135 > <doctest ...>(3)trigger_ipdb()
116 > <doctest ...>(3)trigger_ipdb()
136 1 def trigger_ipdb():
117 1 def trigger_ipdb():
137 2 a = ExampleClass()
118 2 a = ExampleClass()
138 ----> 3 debugger.Pdb().set_trace()
119 ----> 3 debugger.Pdb().set_trace()
139 <BLANKLINE>
120 <BLANKLINE>
140 ipdb> list
121 ipdb> list
141 1 def trigger_ipdb():
122 1 def trigger_ipdb():
142 2 a = ExampleClass()
123 2 a = ExampleClass()
143 ----> 3 debugger.Pdb().set_trace()
124 ----> 3 debugger.Pdb().set_trace()
144 <BLANKLINE>
125 <BLANKLINE>
145 ipdb> pinfo a
126 ipdb> pinfo a
146 Type: ExampleClass
127 Type: ExampleClass
147 String form: ExampleClass()
128 String form: ExampleClass()
148 Namespace: Local...
129 Namespace: Local...
149 Docstring: Docstring for ExampleClass.
130 Docstring: Docstring for ExampleClass.
150 Init docstring: Docstring for ExampleClass.__init__
131 Init docstring: Docstring for ExampleClass.__init__
151 ipdb> ll
132 ipdb> ll
152 1 def trigger_ipdb():
133 1 def trigger_ipdb():
153 2 a = ExampleClass()
134 2 a = ExampleClass()
154 ----> 3 debugger.Pdb().set_trace()
135 ----> 3 debugger.Pdb().set_trace()
155 <BLANKLINE>
136 <BLANKLINE>
156 ipdb> continue
137 ipdb> continue
157
138
158 Restore previous trace function, e.g. for coverage.py
139 Restore previous trace function, e.g. for coverage.py
159
140
160 >>> sys.settrace(old_trace)
141 >>> sys.settrace(old_trace)
161 '''
142 '''
162
143
163 def test_ipdb_magics2():
144 def test_ipdb_magics2():
164 '''Test ipdb with a very short function.
145 '''Test ipdb with a very short function.
165
146
166 >>> old_trace = sys.gettrace()
147 >>> old_trace = sys.gettrace()
167
148
168 >>> def bar():
149 >>> def bar():
169 ... pass
150 ... pass
170
151
171 Run ipdb.
152 Run ipdb.
172
153
173 >>> with PdbTestInput([
154 >>> with PdbTestInput([
174 ... 'continue',
155 ... 'continue',
175 ... ]):
156 ... ]):
176 ... debugger.Pdb().runcall(bar)
157 ... debugger.Pdb().runcall(bar)
177 > <doctest ...>(2)bar()
158 > <doctest ...>(2)bar()
178 1 def bar():
159 1 def bar():
179 ----> 2 pass
160 ----> 2 pass
180 <BLANKLINE>
161 <BLANKLINE>
181 ipdb> continue
162 ipdb> continue
182
163
183 Restore previous trace function, e.g. for coverage.py
164 Restore previous trace function, e.g. for coverage.py
184
165
185 >>> sys.settrace(old_trace)
166 >>> sys.settrace(old_trace)
186 '''
167 '''
187
168
188 def can_quit():
169 def can_quit():
189 '''Test that quit work in ipydb
170 '''Test that quit work in ipydb
190
171
191 >>> old_trace = sys.gettrace()
172 >>> old_trace = sys.gettrace()
192
173
193 >>> def bar():
174 >>> def bar():
194 ... pass
175 ... pass
195
176
196 >>> with PdbTestInput([
177 >>> with PdbTestInput([
197 ... 'quit',
178 ... 'quit',
198 ... ]):
179 ... ]):
199 ... debugger.Pdb().runcall(bar)
180 ... debugger.Pdb().runcall(bar)
200 > <doctest ...>(2)bar()
181 > <doctest ...>(2)bar()
201 1 def bar():
182 1 def bar():
202 ----> 2 pass
183 ----> 2 pass
203 <BLANKLINE>
184 <BLANKLINE>
204 ipdb> quit
185 ipdb> quit
205
186
206 Restore previous trace function, e.g. for coverage.py
187 Restore previous trace function, e.g. for coverage.py
207
188
208 >>> sys.settrace(old_trace)
189 >>> sys.settrace(old_trace)
209 '''
190 '''
210
191
211
192
212 def can_exit():
193 def can_exit():
213 '''Test that quit work in ipydb
194 '''Test that quit work in ipydb
214
195
215 >>> old_trace = sys.gettrace()
196 >>> old_trace = sys.gettrace()
216
197
217 >>> def bar():
198 >>> def bar():
218 ... pass
199 ... pass
219
200
220 >>> with PdbTestInput([
201 >>> with PdbTestInput([
221 ... 'exit',
202 ... 'exit',
222 ... ]):
203 ... ]):
223 ... debugger.Pdb().runcall(bar)
204 ... debugger.Pdb().runcall(bar)
224 > <doctest ...>(2)bar()
205 > <doctest ...>(2)bar()
225 1 def bar():
206 1 def bar():
226 ----> 2 pass
207 ----> 2 pass
227 <BLANKLINE>
208 <BLANKLINE>
228 ipdb> exit
209 ipdb> exit
229
210
230 Restore previous trace function, e.g. for coverage.py
211 Restore previous trace function, e.g. for coverage.py
231
212
232 >>> sys.settrace(old_trace)
213 >>> sys.settrace(old_trace)
233 '''
214 '''
234
215
235
216
236 def test_interruptible_core_debugger():
217 def test_interruptible_core_debugger():
237 """The debugger can be interrupted.
218 """The debugger can be interrupted.
238
219
239 The presumption is there is some mechanism that causes a KeyboardInterrupt
220 The presumption is there is some mechanism that causes a KeyboardInterrupt
240 (this is implemented in ipykernel). We want to ensure the
221 (this is implemented in ipykernel). We want to ensure the
241 KeyboardInterrupt cause debugging to cease.
222 KeyboardInterrupt cause debugging to cease.
242 """
223 """
243 def raising_input(msg="", called=[0]):
224 def raising_input(msg="", called=[0]):
244 called[0] += 1
225 called[0] += 1
245 assert called[0] == 1, "input() should only be called once!"
226 assert called[0] == 1, "input() should only be called once!"
246 raise KeyboardInterrupt()
227 raise KeyboardInterrupt()
247
228
248 tracer_orig = sys.gettrace()
229 tracer_orig = sys.gettrace()
249 try:
230 try:
250 with patch.object(builtins, "input", raising_input):
231 with patch.object(builtins, "input", raising_input):
251 debugger.InterruptiblePdb().set_trace()
232 debugger.InterruptiblePdb().set_trace()
252 # The way this test will fail is by set_trace() never exiting,
233 # The way this test will fail is by set_trace() never exiting,
253 # resulting in a timeout by the test runner. The alternative
234 # resulting in a timeout by the test runner. The alternative
254 # implementation would involve a subprocess, but that adds issues
235 # implementation would involve a subprocess, but that adds issues
255 # with interrupting subprocesses that are rather complex, so it's
236 # with interrupting subprocesses that are rather complex, so it's
256 # simpler just to do it this way.
237 # simpler just to do it this way.
257 finally:
238 finally:
258 # restore the original trace function
239 # restore the original trace function
259 sys.settrace(tracer_orig)
240 sys.settrace(tracer_orig)
260
241
261
242
262 @skip_win32
243 @skip_win32
263 def test_xmode_skip():
244 def test_xmode_skip():
264 """that xmode skip frames
245 """that xmode skip frames
265
246
266 Not as a doctest as pytest does not run doctests.
247 Not as a doctest as pytest does not run doctests.
267 """
248 """
268 import pexpect
249 import pexpect
269 env = os.environ.copy()
250 env = os.environ.copy()
270 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
251 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
271
252
272 child = pexpect.spawn(
253 child = pexpect.spawn(
273 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
254 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
274 )
255 )
275 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
256 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
276
257
277 child.expect("IPython")
258 child.expect("IPython")
278 child.expect("\n")
259 child.expect("\n")
279 child.expect_exact("In [1]")
260 child.expect_exact("In [1]")
280
261
281 block = dedent(
262 block = dedent(
282 """
263 """
283 def f():
264 def f():
284 __tracebackhide__ = True
265 __tracebackhide__ = True
285 g()
266 g()
286
267
287 def g():
268 def g():
288 raise ValueError
269 raise ValueError
289
270
290 f()
271 f()
291 """
272 """
292 )
273 )
293
274
294 for line in block.splitlines():
275 for line in block.splitlines():
295 child.sendline(line)
276 child.sendline(line)
296 child.expect_exact(line)
277 child.expect_exact(line)
297 child.expect_exact("skipping")
278 child.expect_exact("skipping")
298
279
299 block = dedent(
280 block = dedent(
300 """
281 """
301 def f():
282 def f():
302 __tracebackhide__ = True
283 __tracebackhide__ = True
303 g()
284 g()
304
285
305 def g():
286 def g():
306 from IPython.core.debugger import set_trace
287 from IPython.core.debugger import set_trace
307 set_trace()
288 set_trace()
308
289
309 f()
290 f()
310 """
291 """
311 )
292 )
312
293
313 for line in block.splitlines():
294 for line in block.splitlines():
314 child.sendline(line)
295 child.sendline(line)
315 child.expect_exact(line)
296 child.expect_exact(line)
316
297
317 child.expect("ipdb>")
298 child.expect("ipdb>")
318 child.sendline("w")
299 child.sendline("w")
319 child.expect("hidden")
300 child.expect("hidden")
320 child.expect("ipdb>")
301 child.expect("ipdb>")
321 child.sendline("skip_hidden false")
302 child.sendline("skip_hidden false")
322 child.sendline("w")
303 child.sendline("w")
323 child.expect("__traceba")
304 child.expect("__traceba")
324 child.expect("ipdb>")
305 child.expect("ipdb>")
325
306
326 child.close()
307 child.close()
327
308
328
309
329 skip_decorators_blocks = (
310 skip_decorators_blocks = (
330 """
311 """
331 def helpers_helper():
312 def helpers_helper():
332 pass # should not stop here except breakpoint
313 pass # should not stop here except breakpoint
333 """,
314 """,
334 """
315 """
335 def helper_1():
316 def helper_1():
336 helpers_helper() # should not stop here
317 helpers_helper() # should not stop here
337 """,
318 """,
338 """
319 """
339 def helper_2():
320 def helper_2():
340 pass # should not stop here
321 pass # should not stop here
341 """,
322 """,
342 """
323 """
343 def pdb_skipped_decorator2(function):
324 def pdb_skipped_decorator2(function):
344 def wrapped_fn(*args, **kwargs):
325 def wrapped_fn(*args, **kwargs):
345 __debuggerskip__ = True
326 __debuggerskip__ = True
346 helper_2()
327 helper_2()
347 __debuggerskip__ = False
328 __debuggerskip__ = False
348 result = function(*args, **kwargs)
329 result = function(*args, **kwargs)
349 __debuggerskip__ = True
330 __debuggerskip__ = True
350 helper_2()
331 helper_2()
351 return result
332 return result
352 return wrapped_fn
333 return wrapped_fn
353 """,
334 """,
354 """
335 """
355 def pdb_skipped_decorator(function):
336 def pdb_skipped_decorator(function):
356 def wrapped_fn(*args, **kwargs):
337 def wrapped_fn(*args, **kwargs):
357 __debuggerskip__ = True
338 __debuggerskip__ = True
358 helper_1()
339 helper_1()
359 __debuggerskip__ = False
340 __debuggerskip__ = False
360 result = function(*args, **kwargs)
341 result = function(*args, **kwargs)
361 __debuggerskip__ = True
342 __debuggerskip__ = True
362 helper_2()
343 helper_2()
363 return result
344 return result
364 return wrapped_fn
345 return wrapped_fn
365 """,
346 """,
366 """
347 """
367 @pdb_skipped_decorator
348 @pdb_skipped_decorator
368 @pdb_skipped_decorator2
349 @pdb_skipped_decorator2
369 def bar(x, y):
350 def bar(x, y):
370 return x * y
351 return x * y
371 """,
352 """,
372 """import IPython.terminal.debugger as ipdb""",
353 """import IPython.terminal.debugger as ipdb""",
373 """
354 """
374 def f():
355 def f():
375 ipdb.set_trace()
356 ipdb.set_trace()
376 bar(3, 4)
357 bar(3, 4)
377 """,
358 """,
378 """
359 """
379 f()
360 f()
380 """,
361 """,
381 )
362 )
382
363
383
364
384 def _decorator_skip_setup():
365 def _decorator_skip_setup():
385 import pexpect
366 import pexpect
386
367
387 env = os.environ.copy()
368 env = os.environ.copy()
388 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
369 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
389
370
390 child = pexpect.spawn(
371 child = pexpect.spawn(
391 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
372 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
392 )
373 )
393 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
374 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
394
375
395 child.expect("IPython")
376 child.expect("IPython")
396 child.expect("\n")
377 child.expect("\n")
397
378
398 dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
379 dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
399 in_prompt_number = 1
380 in_prompt_number = 1
400 for cblock in dedented_blocks:
381 for cblock in dedented_blocks:
401 child.expect_exact(f"In [{in_prompt_number}]:")
382 child.expect_exact(f"In [{in_prompt_number}]:")
402 in_prompt_number += 1
383 in_prompt_number += 1
403 for line in cblock.splitlines():
384 for line in cblock.splitlines():
404 child.sendline(line)
385 child.sendline(line)
405 child.expect_exact(line)
386 child.expect_exact(line)
406 child.sendline("")
387 child.sendline("")
407 return child
388 return child
408
389
409
390
410 @skip_win32
391 @skip_win32
411 def test_decorator_skip():
392 def test_decorator_skip():
412 """test that decorator frames can be skipped."""
393 """test that decorator frames can be skipped."""
413
394
414 child = _decorator_skip_setup()
395 child = _decorator_skip_setup()
415
396
416 child.expect_exact("3 bar(3, 4)")
397 child.expect_exact("3 bar(3, 4)")
417 child.expect("ipdb>")
398 child.expect("ipdb>")
418
399
419 child.expect("ipdb>")
400 child.expect("ipdb>")
420 child.sendline("step")
401 child.sendline("step")
421 child.expect_exact("step")
402 child.expect_exact("step")
422
403
423 child.expect_exact("1 @pdb_skipped_decorator")
404 child.expect_exact("1 @pdb_skipped_decorator")
424
405
425 child.sendline("s")
406 child.sendline("s")
426 child.expect_exact("return x * y")
407 child.expect_exact("return x * y")
427
408
428 child.close()
409 child.close()
429
410
430
411
431 @skip_win32
412 @skip_win32
432 def test_decorator_skip_disabled():
413 def test_decorator_skip_disabled():
433 """test that decorator frame skipping can be disabled"""
414 """test that decorator frame skipping can be disabled"""
434
415
435 child = _decorator_skip_setup()
416 child = _decorator_skip_setup()
436
417
437 child.expect_exact("3 bar(3, 4)")
418 child.expect_exact("3 bar(3, 4)")
438
419
439 for input_, expected in [
420 for input_, expected in [
440 ("skip_predicates debuggerskip False", ""),
421 ("skip_predicates debuggerskip False", ""),
441 ("skip_predicates", "debuggerskip : False"),
422 ("skip_predicates", "debuggerskip : False"),
442 ("step", "---> 2 def wrapped_fn"),
423 ("step", "---> 2 def wrapped_fn"),
443 ("step", "----> 3 __debuggerskip__"),
424 ("step", "----> 3 __debuggerskip__"),
444 ("step", "----> 4 helper_1()"),
425 ("step", "----> 4 helper_1()"),
445 ("step", "---> 1 def helper_1():"),
426 ("step", "---> 1 def helper_1():"),
446 ("next", "----> 2 helpers_helper()"),
427 ("next", "----> 2 helpers_helper()"),
447 ("next", "--Return--"),
428 ("next", "--Return--"),
448 ("next", "----> 5 __debuggerskip__ = False"),
429 ("next", "----> 5 __debuggerskip__ = False"),
449 ]:
430 ]:
450 child.expect("ipdb>")
431 child.expect("ipdb>")
451 child.sendline(input_)
432 child.sendline(input_)
452 child.expect_exact(input_)
433 child.expect_exact(input_)
453 child.expect_exact(expected)
434 child.expect_exact(expected)
454
435
455 child.close()
436 child.close()
456
437
457
438
458 @skip_win32
439 @skip_win32
459 def test_decorator_skip_with_breakpoint():
440 def test_decorator_skip_with_breakpoint():
460 """test that decorator frame skipping can be disabled"""
441 """test that decorator frame skipping can be disabled"""
461
442
462 import pexpect
443 import pexpect
463
444
464 env = os.environ.copy()
445 env = os.environ.copy()
465 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
446 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
466
447
467 child = pexpect.spawn(
448 child = pexpect.spawn(
468 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
449 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
469 )
450 )
470 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
451 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
471
452
472 child.expect("IPython")
453 child.expect("IPython")
473 child.expect("\n")
454 child.expect("\n")
474
455
475 ### we need a filename, so we need to exec the full block with a filename
456 ### we need a filename, so we need to exec the full block with a filename
476 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
457 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
477
458
478 name = tf.name[:-3].split("/")[-1]
459 name = tf.name[:-3].split("/")[-1]
479 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
460 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
480 tf.flush()
461 tf.flush()
481 codeblock = f"from {name} import f"
462 codeblock = f"from {name} import f"
482
463
483 dedented_blocks = [
464 dedented_blocks = [
484 codeblock,
465 codeblock,
485 "f()",
466 "f()",
486 ]
467 ]
487
468
488 in_prompt_number = 1
469 in_prompt_number = 1
489 for cblock in dedented_blocks:
470 for cblock in dedented_blocks:
490 child.expect_exact(f"In [{in_prompt_number}]:")
471 child.expect_exact(f"In [{in_prompt_number}]:")
491 in_prompt_number += 1
472 in_prompt_number += 1
492 for line in cblock.splitlines():
473 for line in cblock.splitlines():
493 child.sendline(line)
474 child.sendline(line)
494 child.expect_exact(line)
475 child.expect_exact(line)
495 child.sendline("")
476 child.sendline("")
496
477
497 # as the filename does not exists, we'll rely on the filename prompt
478 # as the filename does not exists, we'll rely on the filename prompt
498 child.expect_exact("47 bar(3, 4)")
479 child.expect_exact("47 bar(3, 4)")
499
480
500 for input_, expected in [
481 for input_, expected in [
501 (f"b {name}.py:3", ""),
482 (f"b {name}.py:3", ""),
502 ("step", "1---> 3 pass # should not stop here except"),
483 ("step", "1---> 3 pass # should not stop here except"),
503 ("step", "---> 38 @pdb_skipped_decorator"),
484 ("step", "---> 38 @pdb_skipped_decorator"),
504 ("continue", ""),
485 ("continue", ""),
505 ]:
486 ]:
506 child.expect("ipdb>")
487 child.expect("ipdb>")
507 child.sendline(input_)
488 child.sendline(input_)
508 child.expect_exact(input_)
489 child.expect_exact(input_)
509 child.expect_exact(expected)
490 child.expect_exact(expected)
510
491
511 child.close()
492 child.close()
512
493
513
494
514 @skip_win32
495 @skip_win32
515 def test_where_erase_value():
496 def test_where_erase_value():
516 """Test that `where` does not access f_locals and erase values."""
497 """Test that `where` does not access f_locals and erase values."""
517 import pexpect
498 import pexpect
518
499
519 env = os.environ.copy()
500 env = os.environ.copy()
520 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
501 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
521
502
522 child = pexpect.spawn(
503 child = pexpect.spawn(
523 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
504 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
524 )
505 )
525 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
506 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
526
507
527 child.expect("IPython")
508 child.expect("IPython")
528 child.expect("\n")
509 child.expect("\n")
529 child.expect_exact("In [1]")
510 child.expect_exact("In [1]")
530
511
531 block = dedent(
512 block = dedent(
532 """
513 """
533 def simple_f():
514 def simple_f():
534 myvar = 1
515 myvar = 1
535 print(myvar)
516 print(myvar)
536 1/0
517 1/0
537 print(myvar)
518 print(myvar)
538 simple_f() """
519 simple_f() """
539 )
520 )
540
521
541 for line in block.splitlines():
522 for line in block.splitlines():
542 child.sendline(line)
523 child.sendline(line)
543 child.expect_exact(line)
524 child.expect_exact(line)
544 child.expect_exact("ZeroDivisionError")
525 child.expect_exact("ZeroDivisionError")
545 child.expect_exact("In [2]:")
526 child.expect_exact("In [2]:")
546
527
547 child.sendline("%debug")
528 child.sendline("%debug")
548
529
549 ##
530 ##
550 child.expect("ipdb>")
531 child.expect("ipdb>")
551
532
552 child.sendline("myvar")
533 child.sendline("myvar")
553 child.expect("1")
534 child.expect("1")
554
535
555 ##
536 ##
556 child.expect("ipdb>")
537 child.expect("ipdb>")
557
538
558 child.sendline("myvar = 2")
539 child.sendline("myvar = 2")
559
540
560 ##
541 ##
561 child.expect_exact("ipdb>")
542 child.expect_exact("ipdb>")
562
543
563 child.sendline("myvar")
544 child.sendline("myvar")
564
545
565 child.expect_exact("2")
546 child.expect_exact("2")
566
547
567 ##
548 ##
568 child.expect("ipdb>")
549 child.expect("ipdb>")
569 child.sendline("where")
550 child.sendline("where")
570
551
571 ##
552 ##
572 child.expect("ipdb>")
553 child.expect("ipdb>")
573 child.sendline("myvar")
554 child.sendline("myvar")
574
555
575 child.expect_exact("2")
556 child.expect_exact("2")
576 child.expect("ipdb>")
557 child.expect("ipdb>")
577
558
578 child.close()
559 child.close()
@@ -1,74 +1,60 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Miscellaneous context managers.
2 """Miscellaneous context managers.
3 """
3 """
4
4
5 import warnings
5 import warnings
6
6
7 # Copyright (c) IPython Development Team.
7 # Copyright (c) IPython Development Team.
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9
9
10 class preserve_keys(object):
10 class preserve_keys(object):
11 """Preserve a set of keys in a dictionary.
11 """Preserve a set of keys in a dictionary.
12
12
13 Upon entering the context manager the current values of the keys
13 Upon entering the context manager the current values of the keys
14 will be saved. Upon exiting, the dictionary will be updated to
14 will be saved. Upon exiting, the dictionary will be updated to
15 restore the original value of the preserved keys. Preserved keys
15 restore the original value of the preserved keys. Preserved keys
16 which did not exist when entering the context manager will be
16 which did not exist when entering the context manager will be
17 deleted.
17 deleted.
18
18
19 Examples
19 Examples
20 --------
20 --------
21
21
22 >>> d = {'a': 1, 'b': 2, 'c': 3}
22 >>> d = {'a': 1, 'b': 2, 'c': 3}
23 >>> with preserve_keys(d, 'b', 'c', 'd'):
23 >>> with preserve_keys(d, 'b', 'c', 'd'):
24 ... del d['a']
24 ... del d['a']
25 ... del d['b'] # will be reset to 2
25 ... del d['b'] # will be reset to 2
26 ... d['c'] = None # will be reset to 3
26 ... d['c'] = None # will be reset to 3
27 ... d['d'] = 4 # will be deleted
27 ... d['d'] = 4 # will be deleted
28 ... d['e'] = 5
28 ... d['e'] = 5
29 ... print(sorted(d.items()))
29 ... print(sorted(d.items()))
30 ...
30 ...
31 [('c', None), ('d', 4), ('e', 5)]
31 [('c', None), ('d', 4), ('e', 5)]
32 >>> print(sorted(d.items()))
32 >>> print(sorted(d.items()))
33 [('b', 2), ('c', 3), ('e', 5)]
33 [('b', 2), ('c', 3), ('e', 5)]
34 """
34 """
35
35
36 def __init__(self, dictionary, *keys):
36 def __init__(self, dictionary, *keys):
37 self.dictionary = dictionary
37 self.dictionary = dictionary
38 self.keys = keys
38 self.keys = keys
39
39
40 def __enter__(self):
40 def __enter__(self):
41 # Actions to perform upon exiting.
41 # Actions to perform upon exiting.
42 to_delete = []
42 to_delete = []
43 to_update = {}
43 to_update = {}
44
44
45 d = self.dictionary
45 d = self.dictionary
46 for k in self.keys:
46 for k in self.keys:
47 if k in d:
47 if k in d:
48 to_update[k] = d[k]
48 to_update[k] = d[k]
49 else:
49 else:
50 to_delete.append(k)
50 to_delete.append(k)
51
51
52 self.to_delete = to_delete
52 self.to_delete = to_delete
53 self.to_update = to_update
53 self.to_update = to_update
54
54
55 def __exit__(self, *exc_info):
55 def __exit__(self, *exc_info):
56 d = self.dictionary
56 d = self.dictionary
57
57
58 for k in self.to_delete:
58 for k in self.to_delete:
59 d.pop(k, None)
59 d.pop(k, None)
60 d.update(self.to_update)
60 d.update(self.to_update)
61
62
63 class NoOpContext(object):
64 """
65 Deprecated
66
67 Context manager that does nothing."""
68
69 def __init__(self):
70 warnings.warn("""NoOpContext is deprecated since IPython 5.0 """,
71 DeprecationWarning, stacklevel=2)
72
73 def __enter__(self): pass
74 def __exit__(self, type, value, traceback): pass
General Comments 0
You need to be logged in to leave comments. Login now