8000 Merge pull request #3071 from tkf/drun · ipython/ipython@e1ef88f · GitHub
[go: up one dir, main page]

Skip to content

Commit e1ef88f

Browse files
committed
Merge pull request #3071 from tkf/drun
Extend the `%debug` magic to also run a statement under the control of a debugger, if called with arguments. If called with no arguments after an error has occurred, it continues to work as before.
2 parents 9be9a91 + 32cb7ef commit e1ef88f

File tree

1 file changed

+80
-35
lines changed

1 file changed

+80
-35
lines changed

IPython/core/magics/execution.py

Lines changed: 80 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -293,10 +293,31 @@ def pdb(self, parameter_s=''):
293293
self.shell.call_pdb = new_pdb
294294
print 'Automatic pdb calling has been turned',on_off(new_pdb)
295295

296-
@line_magic
297-
def debug(self, parameter_s=''):
298-
"""Activate the interactive debugger in post-mortem mode.
296+
@skip_doctest
297+
@magic_arguments.magic_arguments()
298+
@magic_arguments.argument('--breakpoint', '-b', metavar='FILE:LINE',
299+
help="""
300+
Set break point at LINE in FILE.
301+
"""
302+
)
303+
@magic_arguments.argument('statement', nargs='*',
304+
help="""
305+
Code to run in debugger.
306+
You can omit this in cell magic mode.
307+
"""
308+
)
309+
@line_cell_magic
310+
def debug(self, line='', cell=None):
311+
"""Activate the interactive debugger.
312+
313+
This magic command support two ways of activating debugger.
314+
One is to activate debugger before executing code. This way, you
315+
can set a break point, to step through the code from the point.
316+
You can use this mode by giving statements to execute and optionally
317+
a breakpoint.
299318
319+
The other one is to activate debugger in post-mortem mode. You can
320+
activate this mode simply running %debug without any argument.
300321
If an exception has just occurred, this lets you inspect its stack
301322
frames interactively. Note that this will always work only on the last
302323
traceback that occurred, so you must call this quickly after an
@@ -306,8 +327,27 @@ def debug(self, parameter_s=''):
306327
If you want IPython to automatically do this on every exception, see
307328
the %pdb magic for more details.
308329
"""
330+
args = magic_arguments.parse_argstring(self.debug, line)
331+
332+
if not (args.breakpoint or args.statement or cell):
333+
self._debug_post_mortem()
334+
else:
335+
code = "\n".join(args.statement)
336+
if cell:
337+
code += "\n" + cell
338+
self._debug_exec(code, args.breakpoint)
339+
340+
def _debug_post_mortem(self):
309341
self.shell.debugger(force=True)
310342

343+
def _debug_exec(self, code, breakpoint):
344+
if breakpoint:
345+
(filename, bp_line) = breakpoint.split(':', 1)
346+
bp_line = int(bp_line)
347+
else:
348+
(filename, bp_line) = (None, None)
349+
self._run_with_debugger(code, self.shell.user_ns, filename, bp_line)
350+
311351
@line_magic
312352
def tb(self, s):
313353
"""Print the last traceback with the currently active exception mode.
@@ -563,8 +603,10 @@ def run(self, parameter_s='', runner=None,
563603
stats = self._run_with_profiler(code, opts, code_ns)
564604
else:
565605
if 'd' in opts:
606+
bp_file, bp_line = parse_breakpoint(
607+
opts.get('b', ['1'])[0], filename)
566608
self._run_with_debugger(
567-
code, code_ns, opts.get('b', ['1'])[0], filename)
609+
code, code_ns, filename, bp_line, bp_file)
568610
else:
569611
if 'm' in opts:
570612
def run():
@@ -628,7 +670,8 @@ def run():
628670

629671
return stats
630672

631-
def _run_with_debugger(self, code, code_ns, break_point, filename):
673+
def _run_with_debugger(self, code, code_ns, filename=None,
674+
bp_line=None, bp_file=None):
632675
"""
633676
Run `code` in debugger with a break point.
634677
@@ -638,19 +681,18 @@ def _run_with_debugger(self, code, code_ns, break_point, filename):
638681
Code to execute.
639682
code_ns : dict
640683
A namespace in which `code` is executed.
641-
break_point : str
642-
Line number in the file specified by `filename` argument
643-
or a string in the format ``file:line``. In the latter
644-
case, `filename` is ignored.
645-
See also :func:`.parse_breakpoint`.
646684
filename : str
685+
`code` is ran as if it is in `filename`.
686+
bp_line : int, optional
687+
Line number of the break point.
688+
bp_file : str, optional
647689
Path to the file in which break point is specified.
690+
`filename` is used if not given.
648691
649692
Raises
650693
------
651694
UsageError
652-
If no meaningful break point is given by `break_point` and
653-
`filename`.
695+
If the break point given by `bp_line` is not valid.
654696
655697
"""
656698
deb = debugger.Pdb(self.shell.colors)
@@ -659,33 +701,36 @@ def _run_with_debugger(self, code, code_ns, break_point, filename):
659701
bdb.Breakpoint.next = 1
660702
bdb.Breakpoint.bplist = {}
661703
bdb.Breakpoint.bpbynumber = [None]
662-
# Set an initial breakpoint to stop execution
663-
maxtries = 10
664-
bp_file, bp_line = parse_breakpoint(break_point, filename)
665-
checkline = deb.checkline(bp_file, bp_line)
666-
if not checkline:
667-
for bp in range(bp_line + 1, bp_line + maxtries + 1):
668-
if deb.checkline(bp_file, bp):
669-
break
670-
else:
671-
msg = ("\nI failed to find a valid line to set "
672-
"a breakpoint\n"
673-
"after trying up to line: %s.\n"
674-
"Please set a valid breakpoint manually "
675-
"with the -b option." % bp)
676-
raise UsageError(msg)
677-
# if we find a good linenumber, set the breakpoint
678-
deb.do_break('%s:%s' % (bp_file, bp_line))
679-
680-
# Mimic Pdb._runscript(...)
681-
deb._wait_for_mainpyfile = True
682-
deb.mainpyfile = deb.canonic(filename)
704+
if bp_line is not None:
705+
# Set an initial breakpoint to stop execution
706+
maxtries = 10
707+
bp_file = bp_file or filename
708+
checkline = deb.checkline(bp_file, bp_line)
709+
if not checkline:
710+
for bp in range(bp_line + 1, bp_line + maxtries + 1):
711+
if deb.checkline(bp_file, bp):
712+
break
713+
else:
714+
msg = ("\nI failed to find a valid line to set "
715+
"a breakpoint\n"
716+
"after trying up to line: %s.\n"
717+
"Please set a valid breakpoint manually "
718+
"with the -b option." % bp)
719+
raise UsageError(msg)
720+
# if we find a good linenumber, set the breakpoint
721+
deb.do_break('%s:%s' % (bp_file, bp_line))
722+
723+
if filename:
724+
# Mimic Pdb._runscript(...)
725+
deb._wait_for_mainpyfile = True
726+
deb.mainpyfile = deb.canonic(filename)
683727

684728
# Start file run
685729
print "NOTE: Enter 'c' at the %s prompt to continue execution." % deb.prompt
686730
try:
687-
#save filename so it can be used by methods on the deb object
688-
deb._exec_filename = filename
731+
if filename:
732+
# save filename so it can be used by methods on the deb object
733+
deb._exec_filename = filename
689734
deb.run(code, code_ns)
690735

691736
except:

0 commit comments

Comments
 (0)
0