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