##// END OF EJS Templates
improve error message
Matthias Bussonnier -
Show More
@@ -1,922 +1,924 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 def __init__(self, color_scheme=None, completekey=None,
205 205 stdin=None, stdout=None, context=5, **kwargs):
206 206 """Create a new IPython debugger.
207 207
208 208 :param color_scheme: Deprecated, do not use.
209 209 :param completekey: Passed to pdb.Pdb.
210 210 :param stdin: Passed to pdb.Pdb.
211 211 :param stdout: Passed to pdb.Pdb.
212 212 :param context: Number of lines of source code context to show when
213 213 displaying stacktrace information.
214 214 :param kwargs: Passed to pdb.Pdb.
215 215 The possibilities are python version dependent, see the python
216 216 docs for more info.
217 217 """
218 218
219 219 # Parent constructor:
220 220 try:
221 221 self.context = int(context)
222 222 if self.context <= 0:
223 223 raise ValueError("Context must be a positive integer")
224 224 except (TypeError, ValueError) as e:
225 225 raise ValueError("Context must be a positive integer") from e
226 226
227 227 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
228 228 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
229 229
230 230 # IPython changes...
231 231 self.shell = get_ipython()
232 232
233 233 if self.shell is None:
234 234 save_main = sys.modules['__main__']
235 235 # No IPython instance running, we must create one
236 236 from IPython.terminal.interactiveshell import \
237 237 TerminalInteractiveShell
238 238 self.shell = TerminalInteractiveShell.instance()
239 239 # needed by any code which calls __import__("__main__") after
240 240 # the debugger was entered. See also #9941.
241 241 sys.modules["__main__"] = save_main
242 242
243 243 if color_scheme is not None:
244 244 warnings.warn(
245 245 "The `color_scheme` argument is deprecated since version 5.1",
246 246 DeprecationWarning, stacklevel=2)
247 247 else:
248 248 color_scheme = self.shell.colors
249 249
250 250 self.aliases = {}
251 251
252 252 # Create color table: we copy the default one from the traceback
253 253 # module and add a few attributes needed for debugging
254 254 self.color_scheme_table = exception_colors()
255 255
256 256 # shorthands
257 257 C = coloransi.TermColors
258 258 cst = self.color_scheme_table
259 259
260 260 cst['NoColor'].colors.prompt = C.NoColor
261 261 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
262 262 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
263 263
264 264 cst['Linux'].colors.prompt = C.Green
265 265 cst['Linux'].colors.breakpoint_enabled = C.LightRed
266 266 cst['Linux'].colors.breakpoint_disabled = C.Red
267 267
268 268 cst['LightBG'].colors.prompt = C.Blue
269 269 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
270 270 cst['LightBG'].colors.breakpoint_disabled = C.Red
271 271
272 272 cst['Neutral'].colors.prompt = C.Blue
273 273 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
274 274 cst['Neutral'].colors.breakpoint_disabled = C.Red
275 275
276 276 # Add a python parser so we can syntax highlight source while
277 277 # debugging.
278 278 self.parser = PyColorize.Parser(style=color_scheme)
279 279 self.set_colors(color_scheme)
280 280
281 281 # Set the prompt - the default prompt is '(Pdb)'
282 282 self.prompt = prompt
283 283 self.skip_hidden = True
284 284
285 285 # list of predicates we use to skip frames
286 286 self._predicates = {"tbhide": True, "readonly": True, "ipython_internal": True}
287 287
288 288 def set_colors(self, scheme):
289 289 """Shorthand access to the color table scheme selector method."""
290 290 self.color_scheme_table.set_active_scheme(scheme)
291 291 self.parser.style = scheme
292 292
293 293 def set_trace(self, frame=None):
294 294 if frame is None:
295 295 frame = sys._getframe().f_back
296 296 self.initial_frame = frame
297 297 return super().set_trace(frame)
298 298
299 299 def _hidden_predicate(self, frame):
300 300 """
301 301 Given a frame return whether it it should be hidden or not by IPython.
302 302 """
303 303
304 304 if self._predicates["readonly"]:
305 305 fname = frame.f_code.co_filename
306 306 # we need to check for file existence and interactively define
307 307 # function would otherwise appear as RO.
308 308 if os.path.isfile(fname) and not os.access(fname, os.W_OK):
309 309 return True
310 310
311 311 if self._predicates["tbhide"]:
312 312 if frame in (self.curframe, getattr(self, "initial_frame", None)):
313 313 return False
314 314 else:
315 315 return frame.f_locals.get("__tracebackhide__", False)
316 316
317 317 return False
318 318
319 319 def hidden_frames(self, stack):
320 320 """
321 321 Given an index in the stack return whether it should be skipped.
322 322
323 323 This is used in up/down and where to skip frames.
324 324 """
325 325 # The f_locals dictionary is updated from the actual frame
326 326 # locals whenever the .f_locals accessor is called, so we
327 327 # avoid calling it here to preserve self.curframe_locals.
328 328 # Futhermore, there is no good reason to hide the current frame.
329 329 ip_hide = [self._hidden_predicate(s[0]) for s in stack]
330 330 ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"]
331 331 if ip_start and self._predicates["ipython_internal"]:
332 332 ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)]
333 333 return ip_hide
334 334
335 335 def interaction(self, frame, traceback):
336 336 try:
337 337 OldPdb.interaction(self, frame, traceback)
338 338 except KeyboardInterrupt:
339 339 self.stdout.write("\n" + self.shell.get_exception_only())
340 340
341 341 def precmd(self, line):
342 342 """Perform useful escapes on the command before it is executed."""
343 343
344 344 if line.endswith("??"):
345 345 line = "pinfo2 " + line[:-2]
346 346 elif line.endswith("?"):
347 347 line = "pinfo " + line[:-1]
348 348
349 349 line = super().precmd(line)
350 350
351 351 return line
352 352
353 353 def new_do_frame(self, arg):
354 354 OldPdb.do_frame(self, arg)
355 355
356 356 def new_do_quit(self, arg):
357 357
358 358 if hasattr(self, 'old_all_completions'):
359 359 self.shell.Completer.all_completions = self.old_all_completions
360 360
361 361 return OldPdb.do_quit(self, arg)
362 362
363 363 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
364 364
365 365 def new_do_restart(self, arg):
366 366 """Restart command. In the context of ipython this is exactly the same
367 367 thing as 'quit'."""
368 368 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
369 369 return self.do_quit(arg)
370 370
371 371 def print_stack_trace(self, context=None):
372 372 Colors = self.color_scheme_table.active_colors
373 373 ColorsNormal = Colors.Normal
374 374 if context is None:
375 375 context = self.context
376 376 try:
377 377 context = int(context)
378 378 if context <= 0:
379 379 raise ValueError("Context must be a positive integer")
380 380 except (TypeError, ValueError) as e:
381 381 raise ValueError("Context must be a positive integer") from e
382 382 try:
383 383 skipped = 0
384 384 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
385 385 if hidden and self.skip_hidden:
386 386 skipped += 1
387 387 continue
388 388 if skipped:
389 389 print(
390 390 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
391 391 )
392 392 skipped = 0
393 393 self.print_stack_entry(frame_lineno, context=context)
394 394 if skipped:
395 395 print(
396 396 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
397 397 )
398 398 except KeyboardInterrupt:
399 399 pass
400 400
401 401 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
402 402 context=None):
403 403 if context is None:
404 404 context = self.context
405 405 try:
406 406 context = int(context)
407 407 if context <= 0:
408 408 raise ValueError("Context must be a positive integer")
409 409 except (TypeError, ValueError) as e:
410 410 raise ValueError("Context must be a positive integer") from e
411 411 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
412 412
413 413 # vds: >>
414 414 frame, lineno = frame_lineno
415 415 filename = frame.f_code.co_filename
416 416 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
417 417 # vds: <<
418 418
419 419 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
420 420 if context is None:
421 421 context = self.context
422 422 try:
423 423 context = int(context)
424 424 if context <= 0:
425 425 print("Context must be a positive integer", file=self.stdout)
426 426 except (TypeError, ValueError):
427 427 print("Context must be a positive integer", file=self.stdout)
428 428
429 429 import reprlib
430 430
431 431 ret = []
432 432
433 433 Colors = self.color_scheme_table.active_colors
434 434 ColorsNormal = Colors.Normal
435 435 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
436 436 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
437 437 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
438 438 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
439 439
440 440 frame, lineno = frame_lineno
441 441
442 442 return_value = ''
443 443 if '__return__' in frame.f_locals:
444 444 rv = frame.f_locals['__return__']
445 445 #return_value += '->'
446 446 return_value += reprlib.repr(rv) + '\n'
447 447 ret.append(return_value)
448 448
449 449 #s = filename + '(' + `lineno` + ')'
450 450 filename = self.canonic(frame.f_code.co_filename)
451 451 link = tpl_link % py3compat.cast_unicode(filename)
452 452
453 453 if frame.f_code.co_name:
454 454 func = frame.f_code.co_name
455 455 else:
456 456 func = "<lambda>"
457 457
458 458 call = ''
459 459 if func != '?':
460 460 if '__args__' in frame.f_locals:
461 461 args = reprlib.repr(frame.f_locals['__args__'])
462 462 else:
463 463 args = '()'
464 464 call = tpl_call % (func, args)
465 465
466 466 # The level info should be generated in the same format pdb uses, to
467 467 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
468 468 if frame is self.curframe:
469 469 ret.append('> ')
470 470 else:
471 471 ret.append(" ")
472 472 ret.append("%s(%s)%s\n" % (link, lineno, call))
473 473
474 474 start = lineno - 1 - context//2
475 475 lines = linecache.getlines(filename)
476 476 start = min(start, len(lines) - context)
477 477 start = max(start, 0)
478 478 lines = lines[start : start + context]
479 479
480 480 for i, line in enumerate(lines):
481 481 show_arrow = start + 1 + i == lineno
482 482 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
483 483 ret.append(
484 484 self.__format_line(
485 485 linetpl, filename, start + 1 + i, line, arrow=show_arrow
486 486 )
487 487 )
488 488 return "".join(ret)
489 489
490 490 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
491 491 bp_mark = ""
492 492 bp_mark_color = ""
493 493
494 494 new_line, err = self.parser.format2(line, 'str')
495 495 if not err:
496 496 line = new_line
497 497
498 498 bp = None
499 499 if lineno in self.get_file_breaks(filename):
500 500 bps = self.get_breaks(filename, lineno)
501 501 bp = bps[-1]
502 502
503 503 if bp:
504 504 Colors = self.color_scheme_table.active_colors
505 505 bp_mark = str(bp.number)
506 506 bp_mark_color = Colors.breakpoint_enabled
507 507 if not bp.enabled:
508 508 bp_mark_color = Colors.breakpoint_disabled
509 509
510 510 numbers_width = 7
511 511 if arrow:
512 512 # This is the line with the error
513 513 pad = numbers_width - len(str(lineno)) - len(bp_mark)
514 514 num = '%s%s' % (make_arrow(pad), str(lineno))
515 515 else:
516 516 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
517 517
518 518 return tpl_line % (bp_mark_color + bp_mark, num, line)
519 519
520 520 def print_list_lines(self, filename, first, last):
521 521 """The printing (as opposed to the parsing part of a 'list'
522 522 command."""
523 523 try:
524 524 Colors = self.color_scheme_table.active_colors
525 525 ColorsNormal = Colors.Normal
526 526 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
527 527 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
528 528 src = []
529 529 if filename == "<string>" and hasattr(self, "_exec_filename"):
530 530 filename = self._exec_filename
531 531
532 532 for lineno in range(first, last+1):
533 533 line = linecache.getline(filename, lineno)
534 534 if not line:
535 535 break
536 536
537 537 if lineno == self.curframe.f_lineno:
538 538 line = self.__format_line(
539 539 tpl_line_em, filename, lineno, line, arrow=True
540 540 )
541 541 else:
542 542 line = self.__format_line(
543 543 tpl_line, filename, lineno, line, arrow=False
544 544 )
545 545
546 546 src.append(line)
547 547 self.lineno = lineno
548 548
549 549 print(''.join(src), file=self.stdout)
550 550
551 551 except KeyboardInterrupt:
552 552 pass
553 553
554 554 def do_skip_predicates(self, args):
555 555 """
556 556 Turn on/off individual predicates as to whether a frame should be hidden/skip.
557 557
558 558 The global option to skip (or not) hidden frames is set with skip_hidden
559 559
560 560 To change the value of a predicate
561 561
562 562 skip_predicates key [true|false]
563 563
564 564 Call without arguments to see the current values.
565 565
566 566 """
567 567 if not args.strip():
568 568 print("current predicates:")
569 569 for (p, v) in self._predicates.items():
570 570 print(" ", p, ":", v)
571 571 return
572 572 type_value = args.strip().split(" ")
573 573 if len(type_value) != 2:
574 print("Usage: skip_predicates <type> <value>")
574 print(
575 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
576 )
575 577 return
576 578
577 579 type_, value = type_value
578 580 if type_ not in self._predicates:
579 581 print(f"{type_!r} not in {set(self._predicates.keys())}")
580 582 return
581 583 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
582 584 print(
583 f"{repr(value)} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
585 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
584 586 )
585 587 return
586 588
587 589 self._predicates[type_] = value.lower() in ("true", "yes", "1")
588 590 if not any(self._predicates.values()):
589 591 print(
590 592 "Warning, all predicates set to False, skip_hidden may not have any effects."
591 593 )
592 594
593 595 def do_skip_hidden(self, arg):
594 596 """
595 597 Change whether or not we should skip frames with the
596 598 __tracebackhide__ attribute.
597 599 """
598 600 if not arg.strip():
599 601 print(
600 602 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
601 603 )
602 604 elif arg.strip().lower() in ("true", "yes"):
603 605 self.skip_hidden = True
604 606 elif arg.strip().lower() in ("false", "no"):
605 607 self.skip_hidden = False
606 608 if not any(self._predicates.values()):
607 609 print(
608 610 "Warning, all predicates set to False, skip_hidden may not have any effects."
609 611 )
610 612
611 613 def do_list(self, arg):
612 614 """Print lines of code from the current stack frame
613 615 """
614 616 self.lastcmd = 'list'
615 617 last = None
616 618 if arg:
617 619 try:
618 620 x = eval(arg, {}, {})
619 621 if type(x) == type(()):
620 622 first, last = x
621 623 first = int(first)
622 624 last = int(last)
623 625 if last < first:
624 626 # Assume it's a count
625 627 last = first + last
626 628 else:
627 629 first = max(1, int(x) - 5)
628 630 except:
629 631 print('*** Error in argument:', repr(arg), file=self.stdout)
630 632 return
631 633 elif self.lineno is None:
632 634 first = max(1, self.curframe.f_lineno - 5)
633 635 else:
634 636 first = self.lineno + 1
635 637 if last is None:
636 638 last = first + 10
637 639 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
638 640
639 641 # vds: >>
640 642 lineno = first
641 643 filename = self.curframe.f_code.co_filename
642 644 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
643 645 # vds: <<
644 646
645 647 do_l = do_list
646 648
647 649 def getsourcelines(self, obj):
648 650 lines, lineno = inspect.findsource(obj)
649 651 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
650 652 # must be a module frame: do not try to cut a block out of it
651 653 return lines, 1
652 654 elif inspect.ismodule(obj):
653 655 return lines, 1
654 656 return inspect.getblock(lines[lineno:]), lineno+1
655 657
656 658 def do_longlist(self, arg):
657 659 """Print lines of code from the current stack frame.
658 660
659 661 Shows more lines than 'list' does.
660 662 """
661 663 self.lastcmd = 'longlist'
662 664 try:
663 665 lines, lineno = self.getsourcelines(self.curframe)
664 666 except OSError as err:
665 667 self.error(err)
666 668 return
667 669 last = lineno + len(lines)
668 670 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
669 671 do_ll = do_longlist
670 672
671 673 def do_debug(self, arg):
672 674 """debug code
673 675 Enter a recursive debugger that steps through the code
674 676 argument (which is an arbitrary expression or statement to be
675 677 executed in the current environment).
676 678 """
677 679 trace_function = sys.gettrace()
678 680 sys.settrace(None)
679 681 globals = self.curframe.f_globals
680 682 locals = self.curframe_locals
681 683 p = self.__class__(completekey=self.completekey,
682 684 stdin=self.stdin, stdout=self.stdout)
683 685 p.use_rawinput = self.use_rawinput
684 686 p.prompt = "(%s) " % self.prompt.strip()
685 687 self.message("ENTERING RECURSIVE DEBUGGER")
686 688 sys.call_tracing(p.run, (arg, globals, locals))
687 689 self.message("LEAVING RECURSIVE DEBUGGER")
688 690 sys.settrace(trace_function)
689 691 self.lastcmd = p.lastcmd
690 692
691 693 def do_pdef(self, arg):
692 694 """Print the call signature for any callable object.
693 695
694 696 The debugger interface to %pdef"""
695 697 namespaces = [
696 698 ("Locals", self.curframe_locals),
697 699 ("Globals", self.curframe.f_globals),
698 700 ]
699 701 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
700 702
701 703 def do_pdoc(self, arg):
702 704 """Print the docstring for an object.
703 705
704 706 The debugger interface to %pdoc."""
705 707 namespaces = [
706 708 ("Locals", self.curframe_locals),
707 709 ("Globals", self.curframe.f_globals),
708 710 ]
709 711 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
710 712
711 713 def do_pfile(self, arg):
712 714 """Print (or run through pager) the file where an object is defined.
713 715
714 716 The debugger interface to %pfile.
715 717 """
716 718 namespaces = [
717 719 ("Locals", self.curframe_locals),
718 720 ("Globals", self.curframe.f_globals),
719 721 ]
720 722 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
721 723
722 724 def do_pinfo(self, arg):
723 725 """Provide detailed information about an object.
724 726
725 727 The debugger interface to %pinfo, i.e., obj?."""
726 728 namespaces = [
727 729 ("Locals", self.curframe_locals),
728 730 ("Globals", self.curframe.f_globals),
729 731 ]
730 732 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
731 733
732 734 def do_pinfo2(self, arg):
733 735 """Provide extra detailed information about an object.
734 736
735 737 The debugger interface to %pinfo2, i.e., obj??."""
736 738 namespaces = [
737 739 ("Locals", self.curframe_locals),
738 740 ("Globals", self.curframe.f_globals),
739 741 ]
740 742 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
741 743
742 744 def do_psource(self, arg):
743 745 """Print (or run through pager) the source code for an object."""
744 746 namespaces = [
745 747 ("Locals", self.curframe_locals),
746 748 ("Globals", self.curframe.f_globals),
747 749 ]
748 750 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
749 751
750 752 def do_where(self, arg):
751 753 """w(here)
752 754 Print a stack trace, with the most recent frame at the bottom.
753 755 An arrow indicates the "current frame", which determines the
754 756 context of most commands. 'bt' is an alias for this command.
755 757
756 758 Take a number as argument as an (optional) number of context line to
757 759 print"""
758 760 if arg:
759 761 try:
760 762 context = int(arg)
761 763 except ValueError as err:
762 764 self.error(err)
763 765 return
764 766 self.print_stack_trace(context)
765 767 else:
766 768 self.print_stack_trace()
767 769
768 770 do_w = do_where
769 771
770 772 def stop_here(self, frame):
771 773 hidden = False
772 774 if self.skip_hidden:
773 775 hidden = self._hidden_predicate(frame)
774 776 if hidden:
775 777 Colors = self.color_scheme_table.active_colors
776 778 ColorsNormal = Colors.Normal
777 779 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
778 780
779 781 return super().stop_here(frame)
780 782
781 783 def do_up(self, arg):
782 784 """u(p) [count]
783 785 Move the current frame count (default one) levels up in the
784 786 stack trace (to an older frame).
785 787
786 788 Will skip hidden frames.
787 789 """
788 790 # modified version of upstream that skips
789 791 # frames with __tracebackhide__
790 792 if self.curindex == 0:
791 793 self.error("Oldest frame")
792 794 return
793 795 try:
794 796 count = int(arg or 1)
795 797 except ValueError:
796 798 self.error("Invalid frame count (%s)" % arg)
797 799 return
798 800 skipped = 0
799 801 if count < 0:
800 802 _newframe = 0
801 803 else:
802 804 counter = 0
803 805 hidden_frames = self.hidden_frames(self.stack)
804 806 for i in range(self.curindex - 1, -1, -1):
805 807 if hidden_frames[i] and self.skip_hidden:
806 808 skipped += 1
807 809 continue
808 810 counter += 1
809 811 if counter >= count:
810 812 break
811 813 else:
812 814 # if no break occured.
813 815 self.error(
814 816 "all frames above hidden, use `skip_hidden False` to get get into those."
815 817 )
816 818 return
817 819
818 820 Colors = self.color_scheme_table.active_colors
819 821 ColorsNormal = Colors.Normal
820 822 _newframe = i
821 823 self._select_frame(_newframe)
822 824 if skipped:
823 825 print(
824 826 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
825 827 )
826 828
827 829 def do_down(self, arg):
828 830 """d(own) [count]
829 831 Move the current frame count (default one) levels down in the
830 832 stack trace (to a newer frame).
831 833
832 834 Will skip hidden frames.
833 835 """
834 836 if self.curindex + 1 == len(self.stack):
835 837 self.error("Newest frame")
836 838 return
837 839 try:
838 840 count = int(arg or 1)
839 841 except ValueError:
840 842 self.error("Invalid frame count (%s)" % arg)
841 843 return
842 844 if count < 0:
843 845 _newframe = len(self.stack) - 1
844 846 else:
845 847 counter = 0
846 848 skipped = 0
847 849 hidden_frames = self.hidden_frames(self.stack)
848 850 for i in range(self.curindex + 1, len(self.stack)):
849 851 if hidden_frames[i] and self.skip_hidden:
850 852 skipped += 1
851 853 continue
852 854 counter += 1
853 855 if counter >= count:
854 856 break
855 857 else:
856 858 self.error(
857 859 "all frames bellow hidden, use `skip_hidden False` to get get into those."
858 860 )
859 861 return
860 862
861 863 Colors = self.color_scheme_table.active_colors
862 864 ColorsNormal = Colors.Normal
863 865 if skipped:
864 866 print(
865 867 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
866 868 )
867 869 _newframe = i
868 870
869 871 self._select_frame(_newframe)
870 872
871 873 do_d = do_down
872 874 do_u = do_up
873 875
874 876 def do_context(self, context):
875 877 """context number_of_lines
876 878 Set the number of lines of source code to show when displaying
877 879 stacktrace information.
878 880 """
879 881 try:
880 882 new_context = int(context)
881 883 if new_context <= 0:
882 884 raise ValueError()
883 885 self.context = new_context
884 886 except ValueError:
885 887 self.error("The 'context' command requires a positive integer argument.")
886 888
887 889
888 890 class InterruptiblePdb(Pdb):
889 891 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
890 892
891 893 def cmdloop(self):
892 894 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
893 895 try:
894 896 return OldPdb.cmdloop(self)
895 897 except KeyboardInterrupt:
896 898 self.stop_here = lambda frame: False
897 899 self.do_quit("")
898 900 sys.settrace(None)
899 901 self.quitting = False
900 902 raise
901 903
902 904 def _cmdloop(self):
903 905 while True:
904 906 try:
905 907 # keyboard interrupts allow for an easy way to cancel
906 908 # the current command, so allow them during interactive input
907 909 self.allow_kbdint = True
908 910 self.cmdloop()
909 911 self.allow_kbdint = False
910 912 break
911 913 except KeyboardInterrupt:
912 914 self.message('--KeyboardInterrupt--')
913 915 raise
914 916
915 917
916 918 def set_trace(frame=None):
917 919 """
918 920 Start debugging from `frame`.
919 921
920 922 If frame is not specified, debugging starts from caller's frame.
921 923 """
922 924 Pdb().set_trace(frame or sys._getframe().f_back)
General Comments 0
You need to be logged in to leave comments. Login now