[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