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