[issue7245] better Ctrl-C support in pdb (program can be resumed) - Code Review (original) (raw)
OLD
NEW
1 #! /usr/bin/env python
1 #! /usr/bin/env python
2
2
3 """A Python debugger."""
3 """A Python debugger."""
4
4
5 # (See pdb.doc for documentation.)
5 # (See pdb.doc for documentation.)
6
6
7 import sys
7 import sys
8 import linecache
8 import linecache
9 import cmd
9 import cmd
10 import bdb
10 import bdb
11 from repr import Repr
11 from repr import Repr
12 import os
12 import os
13 import re
13 import re
14 import pprint
14 import pprint
15 import traceback
15 import traceback
16 import signal
17
16
18
17
19
18 class Restart(Exception):
20 class Restart(Exception):
19 """Causes a debugger to be restarted for the debugged python program."""
21 """Causes a debugger to be restarted for the debugged python program."""
20 pass
22 pass
21
23
22 # Create a custom safe Repr instance and increase its maxstring.
24 # Create a custom safe Repr instance and increase its maxstring.
23 # The default of 30 truncates error messages too easily.
25 # The default of 30 truncates error messages too easily.
24 _repr = Repr()
26 _repr = Repr()
25 _repr.maxstring = 200
27 _repr.maxstring = 200
(...skipping 25 matching lines...) Expand all Loading...
51
53
52 # Interaction prompt line will separate file and call info from code
54 # Interaction prompt line will separate file and call info from code
53 # text using value of line_prefix string. A newline and arrow may
55 # text using value of line_prefix string. A newline and arrow may
54 # be to your liking. You can set it once pdb is imported using the
56 # be to your liking. You can set it once pdb is imported using the
55 # command "pdb.line_prefix = '\n% '".
57 # command "pdb.line_prefix = '\n% '".
56 # line_prefix = ': ' # Use this to get the old situation back
58 # line_prefix = ': ' # Use this to get the old situation back
57 line_prefix = '\n-> ' # Probably a better default
59 line_prefix = '\n-> ' # Probably a better default
58
60
59 class Pdb(bdb.Bdb, cmd.Cmd):
61 class Pdb(bdb.Bdb, cmd.Cmd):
60
62
63 def sigint_handler(self, signum, frame):
64 if self.allow_kbdint:
65 raise KeyboardInterrupt()
66 print >>self.stdout, "\nProgram interrupted. (Use 'cont' to resume)."
67 self.set_step()
68 self.set_trace(frame)
69
61 def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None):
70 def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None):
62 bdb.Bdb.__init__(self, skip=skip)
71 bdb.Bdb.__init__(self, skip=skip)
63 cmd.Cmd.__init__(self, completekey, stdin, stdout)
72 cmd.Cmd.__init__(self, completekey, stdin, stdout)
64 if stdout:
73 if stdout:
65 self.use_rawinput = 0
74 self.use_rawinput = 0
66 self.prompt = '(Pdb) '
75 self.prompt = '(Pdb) '
67 self.aliases = {}
76 self.aliases = {}
68 self.mainpyfile = ''
77 self.mainpyfile = ''
69 self._wait_for_mainpyfile = 0
78 self._wait_for_mainpyfile = 0
70 # Try to load readline if it exists
79 # Try to load readline if it exists
71 try:
80 try:
72 import readline
81 import readline
73 except ImportError:
82 except ImportError:
74 pass
83 pass
84 signal.signal(signal.SIGINT, self.sigint_handler)
75
85
76 # Read $HOME/.pdbrc and ./.pdbrc
86 # Read $HOME/.pdbrc and ./.pdbrc
77 self.rcLines = []
87 self.rcLines = []
78 if 'HOME' in os.environ:
88 if 'HOME' in os.environ:
79 envHome = os.environ['HOME']
89 envHome = os.environ['HOME']
80 try:
90 try:
81 rcFile = open(os.path.join(envHome, ".pdbrc"))
91 rcFile = open(os.path.join(envHome, ".pdbrc"))
82 except IOError:
92 except IOError:
83 pass
93 pass
84 else:
94 else:
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading...
169 currentbp = self.currentbp
179 currentbp = self.currentbp
170 self.currentbp = 0
180 self.currentbp = 0
171 lastcmd_back = self.lastcmd
181 lastcmd_back = self.lastcmd
172 self.setup(frame, None)
182 self.setup(frame, None)
173 for line in self.commands[currentbp]:
183 for line in self.commands[currentbp]:
174 self.onecmd(line)
184 self.onecmd(line)
175 self.lastcmd = lastcmd_back
185 self.lastcmd = lastcmd_back
176 if not self.commands_silent[currentbp]:
186 if not self.commands_silent[currentbp]:
177 self.print_stack_entry(self.stack[self.curindex])
187 self.print_stack_entry(self.stack[self.curindex])
178 if self.commands_doprompt[currentbp]:
188 if self.commands_doprompt[currentbp]:
179 self.cmdloop()
189 self._cmdloop()
180 self.forget()
190 self.forget()
181 return
191 return
182 return 1
192 return 1
183
193
184 def user_return(self, frame, return_value):
194 def user_return(self, frame, return_value):
185 """This function is called when a return trap is set here."""
195 """This function is called when a return trap is set here."""
186 frame.f_locals['__return__'] = return_value
196 frame.f_locals['__return__'] = return_value
187 print >>self.stdout, '--Return--'
197 print >>self.stdout, '--Return--'
188 self.interaction(frame, None)
198 self.interaction(frame, None)
189
199
190 def user_exception(self, frame, exc_info):
200 def user_exception(self, frame, exc_info):
191 exc_type, exc_value, exc_traceback = exc_info
201 exc_type, exc_value, exc_traceback = exc_info
192 """This function is called if an exception occurs,
202 """This function is called if an exception occurs,
193 but only if we are to stop at or just below this level."""
203 but only if we are to stop at or just below this level."""
194 frame.f_locals['__exception__'] = exc_type, exc_value
204 frame.f_locals['__exception__'] = exc_type, exc_value
195 if type(exc_type) == type(''):
205 if type(exc_type) == type(''):
196 exc_type_name = exc_type
206 exc_type_name = exc_type
197 else: exc_type_name = exc_type.__name__
207 else: exc_type_name = exc_type.__name__
198 print >>self.stdout, exc_type_name + ':', _saferepr(exc_value)
208 print >>self.stdout, exc_type_name + ':', _saferepr(exc_value)
199 self.interaction(frame, exc_traceback)
209 self.interaction(frame, exc_traceback)
200
210
201 # General interaction function
211 # General interaction function
212 def _cmdloop(self):
213 while 1:
214 try:ยท
215 # keyboard interrupts allow for an easy way to interrupt
216 # cancel current command
217 self.allow_kbdint = True
218 self.cmdloop()
219 self.allow_kbdint = False
220 break
221 except KeyboardInterrupt:
222 print >>self.stdout, '--KeyboardInterrupt--'
202
223
203 def interaction(self, frame, traceback):
224 def interaction(self, frame, traceback):
204 self.setup(frame, traceback)
225 self.setup(frame, traceback)
205 self.print_stack_entry(self.stack[self.curindex])
226 self.print_stack_entry(self.stack[self.curindex])
206 self.cmdloop()
227 self._cmdloop()
207 self.forget()
228 self.forget()
208
229
209 def displayhook(self, obj):
230 def displayhook(self, obj):
210 """Custom displayhook for the exec in default(), which prevents
231 """Custom displayhook for the exec in default(), which prevents
211 assignment of the _ variable in the builtins.
232 assignment of the _ variable in the builtins.
212 """
233 """
213 # reproduce the behavior of the standard displayhook, not printing None
234 # reproduce the behavior of the standard displayhook, not printing None
214 if obj is not None:
235 if obj is not None:
215 print repr(obj)
236 print repr(obj)
216
237
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading...
322 print >>self.stdout, "Usage : commands [bnum]\n ..." \
343 print >>self.stdout, "Usage : commands [bnum]\n ..." \
323 "\n end"
344 "\n end"
324 return
345 return
325 self.commands_bnum = bnum
346 self.commands_bnum = bnum
326 self.commands[bnum] = []
347 self.commands[bnum] = []
327 self.commands_doprompt[bnum] = True
348 self.commands_doprompt[bnum] = True
328 self.commands_silent[bnum] = False
349 self.commands_silent[bnum] = False
329 prompt_back = self.prompt
350 prompt_back = self.prompt
330 self.prompt = '(com) '
351 self.prompt = '(com) '
331 self.commands_defining = True
352 self.commands_defining = True
332 self.cmdloop()
353 try:
333 self.commands_defining = False
354 self.cmdloop()
334 self.prompt = prompt_back
355 except (KeyboardInterrupt, IOError):
356 #it appears that that when pdb is reading input from a pipe
357 # we may get IOErrors, rather than KeyboardInterrupt
358 self.commands.pop(bnum) # remove this cmd list
359 self.commands_doprompt.pop(bnum)
360 self.commands_silent.pop(bnum)
361 raise KeyboardInterrupt()
362 finally:
363 self.commands_defining = False
364 self.prompt = prompt_back
335
365
336 def do_break(self, arg, temporary = 0):
366 def do_break(self, arg, temporary = 0):
337 # break [ ([filename:]lineno | function) [, "condition"] ]
367 # break [ ([filename:]lineno | function) [, "condition"] ]
338 if not arg:
368 if not arg:
339 if self.breaks: # There's at least one
369 if self.breaks: # There's at least one
340 print >>self.stdout, "Num Type Disp Enb Where"
370 print >>self.stdout, "Num Type Disp Enb Where"
341 for bp in bdb.Breakpoint.bpbynumber:
371 for bp in bdb.Breakpoint.bpbynumber:
342 if bp:
372 if bp:
343 bp.bpprint(self.stdout)
373 bp.bpprint(self.stdout)
344 return
374 return
(...skipping 973 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading...
1318 t = sys.exc_info()[2]
1348 t = sys.exc_info()[2]
1319 pdb.interaction(None, t)
1349 pdb.interaction(None, t)
1320 print "Post mortem debugger finished. The " + mainpyfile + \
1350 print "Post mortem debugger finished. The " + mainpyfile + \
1321 " will be restarted"
1351 " will be restarted"
1322
1352
1323
1353
1324 # When invoked as main program, invoke the debugger on a script
1354 # When invoked as main program, invoke the debugger on a script
1325 if __name__ == '__main__':
1355 if __name__ == '__main__':
1326 import pdb
1356 import pdb
1327 pdb.main()
1357 pdb.main()
OLD
NEW