##// END OF EJS Templates
Merge pull request #12993 from Carreau/readonlydefaultfalse...
Matthias Bussonnier -
r26553:51ddf3be merge
parent child Browse files
Show More
@@ -1,924 +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 self._predicates = {"tbhide": True, "readonly": True, "ipython_internal": True}
286 self._predicates = {"tbhide": True, "readonly": False, "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 574 print(
575 575 f"Usage: skip_predicates <type> <value>, with <type> one of {set(self._predicates.keys())}"
576 576 )
577 577 return
578 578
579 579 type_, value = type_value
580 580 if type_ not in self._predicates:
581 581 print(f"{type_!r} not in {set(self._predicates.keys())}")
582 582 return
583 583 if value.lower() not in ("true", "yes", "1", "no", "false", "0"):
584 584 print(
585 585 f"{value!r} is invalid - use one of ('true', 'yes', '1', 'no', 'false', '0')"
586 586 )
587 587 return
588 588
589 589 self._predicates[type_] = value.lower() in ("true", "yes", "1")
590 590 if not any(self._predicates.values()):
591 591 print(
592 592 "Warning, all predicates set to False, skip_hidden may not have any effects."
593 593 )
594 594
595 595 def do_skip_hidden(self, arg):
596 596 """
597 597 Change whether or not we should skip frames with the
598 598 __tracebackhide__ attribute.
599 599 """
600 600 if not arg.strip():
601 601 print(
602 602 f"skip_hidden = {self.skip_hidden}, use 'yes','no', 'true', or 'false' to change."
603 603 )
604 604 elif arg.strip().lower() in ("true", "yes"):
605 605 self.skip_hidden = True
606 606 elif arg.strip().lower() in ("false", "no"):
607 607 self.skip_hidden = False
608 608 if not any(self._predicates.values()):
609 609 print(
610 610 "Warning, all predicates set to False, skip_hidden may not have any effects."
611 611 )
612 612
613 613 def do_list(self, arg):
614 614 """Print lines of code from the current stack frame
615 615 """
616 616 self.lastcmd = 'list'
617 617 last = None
618 618 if arg:
619 619 try:
620 620 x = eval(arg, {}, {})
621 621 if type(x) == type(()):
622 622 first, last = x
623 623 first = int(first)
624 624 last = int(last)
625 625 if last < first:
626 626 # Assume it's a count
627 627 last = first + last
628 628 else:
629 629 first = max(1, int(x) - 5)
630 630 except:
631 631 print('*** Error in argument:', repr(arg), file=self.stdout)
632 632 return
633 633 elif self.lineno is None:
634 634 first = max(1, self.curframe.f_lineno - 5)
635 635 else:
636 636 first = self.lineno + 1
637 637 if last is None:
638 638 last = first + 10
639 639 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
640 640
641 641 # vds: >>
642 642 lineno = first
643 643 filename = self.curframe.f_code.co_filename
644 644 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
645 645 # vds: <<
646 646
647 647 do_l = do_list
648 648
649 649 def getsourcelines(self, obj):
650 650 lines, lineno = inspect.findsource(obj)
651 651 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
652 652 # must be a module frame: do not try to cut a block out of it
653 653 return lines, 1
654 654 elif inspect.ismodule(obj):
655 655 return lines, 1
656 656 return inspect.getblock(lines[lineno:]), lineno+1
657 657
658 658 def do_longlist(self, arg):
659 659 """Print lines of code from the current stack frame.
660 660
661 661 Shows more lines than 'list' does.
662 662 """
663 663 self.lastcmd = 'longlist'
664 664 try:
665 665 lines, lineno = self.getsourcelines(self.curframe)
666 666 except OSError as err:
667 667 self.error(err)
668 668 return
669 669 last = lineno + len(lines)
670 670 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
671 671 do_ll = do_longlist
672 672
673 673 def do_debug(self, arg):
674 674 """debug code
675 675 Enter a recursive debugger that steps through the code
676 676 argument (which is an arbitrary expression or statement to be
677 677 executed in the current environment).
678 678 """
679 679 trace_function = sys.gettrace()
680 680 sys.settrace(None)
681 681 globals = self.curframe.f_globals
682 682 locals = self.curframe_locals
683 683 p = self.__class__(completekey=self.completekey,
684 684 stdin=self.stdin, stdout=self.stdout)
685 685 p.use_rawinput = self.use_rawinput
686 686 p.prompt = "(%s) " % self.prompt.strip()
687 687 self.message("ENTERING RECURSIVE DEBUGGER")
688 688 sys.call_tracing(p.run, (arg, globals, locals))
689 689 self.message("LEAVING RECURSIVE DEBUGGER")
690 690 sys.settrace(trace_function)
691 691 self.lastcmd = p.lastcmd
692 692
693 693 def do_pdef(self, arg):
694 694 """Print the call signature for any callable object.
695 695
696 696 The debugger interface to %pdef"""
697 697 namespaces = [
698 698 ("Locals", self.curframe_locals),
699 699 ("Globals", self.curframe.f_globals),
700 700 ]
701 701 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
702 702
703 703 def do_pdoc(self, arg):
704 704 """Print the docstring for an object.
705 705
706 706 The debugger interface to %pdoc."""
707 707 namespaces = [
708 708 ("Locals", self.curframe_locals),
709 709 ("Globals", self.curframe.f_globals),
710 710 ]
711 711 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
712 712
713 713 def do_pfile(self, arg):
714 714 """Print (or run through pager) the file where an object is defined.
715 715
716 716 The debugger interface to %pfile.
717 717 """
718 718 namespaces = [
719 719 ("Locals", self.curframe_locals),
720 720 ("Globals", self.curframe.f_globals),
721 721 ]
722 722 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
723 723
724 724 def do_pinfo(self, arg):
725 725 """Provide detailed information about an object.
726 726
727 727 The debugger interface to %pinfo, i.e., obj?."""
728 728 namespaces = [
729 729 ("Locals", self.curframe_locals),
730 730 ("Globals", self.curframe.f_globals),
731 731 ]
732 732 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
733 733
734 734 def do_pinfo2(self, arg):
735 735 """Provide extra detailed information about an object.
736 736
737 737 The debugger interface to %pinfo2, i.e., obj??."""
738 738 namespaces = [
739 739 ("Locals", self.curframe_locals),
740 740 ("Globals", self.curframe.f_globals),
741 741 ]
742 742 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
743 743
744 744 def do_psource(self, arg):
745 745 """Print (or run through pager) the source code for an object."""
746 746 namespaces = [
747 747 ("Locals", self.curframe_locals),
748 748 ("Globals", self.curframe.f_globals),
749 749 ]
750 750 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
751 751
752 752 def do_where(self, arg):
753 753 """w(here)
754 754 Print a stack trace, with the most recent frame at the bottom.
755 755 An arrow indicates the "current frame", which determines the
756 756 context of most commands. 'bt' is an alias for this command.
757 757
758 758 Take a number as argument as an (optional) number of context line to
759 759 print"""
760 760 if arg:
761 761 try:
762 762 context = int(arg)
763 763 except ValueError as err:
764 764 self.error(err)
765 765 return
766 766 self.print_stack_trace(context)
767 767 else:
768 768 self.print_stack_trace()
769 769
770 770 do_w = do_where
771 771
772 772 def stop_here(self, frame):
773 773 hidden = False
774 774 if self.skip_hidden:
775 775 hidden = self._hidden_predicate(frame)
776 776 if hidden:
777 777 Colors = self.color_scheme_table.active_colors
778 778 ColorsNormal = Colors.Normal
779 779 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
780 780
781 781 return super().stop_here(frame)
782 782
783 783 def do_up(self, arg):
784 784 """u(p) [count]
785 785 Move the current frame count (default one) levels up in the
786 786 stack trace (to an older frame).
787 787
788 788 Will skip hidden frames.
789 789 """
790 790 # modified version of upstream that skips
791 791 # frames with __tracebackhide__
792 792 if self.curindex == 0:
793 793 self.error("Oldest frame")
794 794 return
795 795 try:
796 796 count = int(arg or 1)
797 797 except ValueError:
798 798 self.error("Invalid frame count (%s)" % arg)
799 799 return
800 800 skipped = 0
801 801 if count < 0:
802 802 _newframe = 0
803 803 else:
804 804 counter = 0
805 805 hidden_frames = self.hidden_frames(self.stack)
806 806 for i in range(self.curindex - 1, -1, -1):
807 807 if hidden_frames[i] and self.skip_hidden:
808 808 skipped += 1
809 809 continue
810 810 counter += 1
811 811 if counter >= count:
812 812 break
813 813 else:
814 814 # if no break occured.
815 815 self.error(
816 816 "all frames above hidden, use `skip_hidden False` to get get into those."
817 817 )
818 818 return
819 819
820 820 Colors = self.color_scheme_table.active_colors
821 821 ColorsNormal = Colors.Normal
822 822 _newframe = i
823 823 self._select_frame(_newframe)
824 824 if skipped:
825 825 print(
826 826 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
827 827 )
828 828
829 829 def do_down(self, arg):
830 830 """d(own) [count]
831 831 Move the current frame count (default one) levels down in the
832 832 stack trace (to a newer frame).
833 833
834 834 Will skip hidden frames.
835 835 """
836 836 if self.curindex + 1 == len(self.stack):
837 837 self.error("Newest frame")
838 838 return
839 839 try:
840 840 count = int(arg or 1)
841 841 except ValueError:
842 842 self.error("Invalid frame count (%s)" % arg)
843 843 return
844 844 if count < 0:
845 845 _newframe = len(self.stack) - 1
846 846 else:
847 847 counter = 0
848 848 skipped = 0
849 849 hidden_frames = self.hidden_frames(self.stack)
850 850 for i in range(self.curindex + 1, len(self.stack)):
851 851 if hidden_frames[i] and self.skip_hidden:
852 852 skipped += 1
853 853 continue
854 854 counter += 1
855 855 if counter >= count:
856 856 break
857 857 else:
858 858 self.error(
859 859 "all frames bellow hidden, use `skip_hidden False` to get get into those."
860 860 )
861 861 return
862 862
863 863 Colors = self.color_scheme_table.active_colors
864 864 ColorsNormal = Colors.Normal
865 865 if skipped:
866 866 print(
867 867 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
868 868 )
869 869 _newframe = i
870 870
871 871 self._select_frame(_newframe)
872 872
873 873 do_d = do_down
874 874 do_u = do_up
875 875
876 876 def do_context(self, context):
877 877 """context number_of_lines
878 878 Set the number of lines of source code to show when displaying
879 879 stacktrace information.
880 880 """
881 881 try:
882 882 new_context = int(context)
883 883 if new_context <= 0:
884 884 raise ValueError()
885 885 self.context = new_context
886 886 except ValueError:
887 887 self.error("The 'context' command requires a positive integer argument.")
888 888
889 889
890 890 class InterruptiblePdb(Pdb):
891 891 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
892 892
893 893 def cmdloop(self):
894 894 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
895 895 try:
896 896 return OldPdb.cmdloop(self)
897 897 except KeyboardInterrupt:
898 898 self.stop_here = lambda frame: False
899 899 self.do_quit("")
900 900 sys.settrace(None)
901 901 self.quitting = False
902 902 raise
903 903
904 904 def _cmdloop(self):
905 905 while True:
906 906 try:
907 907 # keyboard interrupts allow for an easy way to cancel
908 908 # the current command, so allow them during interactive input
909 909 self.allow_kbdint = True
910 910 self.cmdloop()
911 911 self.allow_kbdint = False
912 912 break
913 913 except KeyboardInterrupt:
914 914 self.message('--KeyboardInterrupt--')
915 915 raise
916 916
917 917
918 918 def set_trace(frame=None):
919 919 """
920 920 Start debugging from `frame`.
921 921
922 922 If frame is not specified, debugging starts from caller's frame.
923 923 """
924 924 Pdb().set_trace(frame or sys._getframe().f_back)
General Comments 0
You need to be logged in to leave comments. Login now