annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/debugger.py @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
rev   line source
jpayne@69 1 import bdb
jpayne@69 2 import os
jpayne@69 3
jpayne@69 4 from tkinter import *
jpayne@69 5 from tkinter.ttk import Frame, Scrollbar
jpayne@69 6
jpayne@69 7 from idlelib import macosx
jpayne@69 8 from idlelib.scrolledlist import ScrolledList
jpayne@69 9 from idlelib.window import ListedToplevel
jpayne@69 10
jpayne@69 11
jpayne@69 12 class Idb(bdb.Bdb):
jpayne@69 13
jpayne@69 14 def __init__(self, gui):
jpayne@69 15 self.gui = gui # An instance of Debugger or proxy of remote.
jpayne@69 16 bdb.Bdb.__init__(self)
jpayne@69 17
jpayne@69 18 def user_line(self, frame):
jpayne@69 19 if self.in_rpc_code(frame):
jpayne@69 20 self.set_step()
jpayne@69 21 return
jpayne@69 22 message = self.__frame2message(frame)
jpayne@69 23 try:
jpayne@69 24 self.gui.interaction(message, frame)
jpayne@69 25 except TclError: # When closing debugger window with [x] in 3.x
jpayne@69 26 pass
jpayne@69 27
jpayne@69 28 def user_exception(self, frame, info):
jpayne@69 29 if self.in_rpc_code(frame):
jpayne@69 30 self.set_step()
jpayne@69 31 return
jpayne@69 32 message = self.__frame2message(frame)
jpayne@69 33 self.gui.interaction(message, frame, info)
jpayne@69 34
jpayne@69 35 def in_rpc_code(self, frame):
jpayne@69 36 if frame.f_code.co_filename.count('rpc.py'):
jpayne@69 37 return True
jpayne@69 38 else:
jpayne@69 39 prev_frame = frame.f_back
jpayne@69 40 prev_name = prev_frame.f_code.co_filename
jpayne@69 41 if 'idlelib' in prev_name and 'debugger' in prev_name:
jpayne@69 42 # catch both idlelib/debugger.py and idlelib/debugger_r.py
jpayne@69 43 # on both Posix and Windows
jpayne@69 44 return False
jpayne@69 45 return self.in_rpc_code(prev_frame)
jpayne@69 46
jpayne@69 47 def __frame2message(self, frame):
jpayne@69 48 code = frame.f_code
jpayne@69 49 filename = code.co_filename
jpayne@69 50 lineno = frame.f_lineno
jpayne@69 51 basename = os.path.basename(filename)
jpayne@69 52 message = "%s:%s" % (basename, lineno)
jpayne@69 53 if code.co_name != "?":
jpayne@69 54 message = "%s: %s()" % (message, code.co_name)
jpayne@69 55 return message
jpayne@69 56
jpayne@69 57
jpayne@69 58 class Debugger:
jpayne@69 59
jpayne@69 60 vstack = vsource = vlocals = vglobals = None
jpayne@69 61
jpayne@69 62 def __init__(self, pyshell, idb=None):
jpayne@69 63 if idb is None:
jpayne@69 64 idb = Idb(self)
jpayne@69 65 self.pyshell = pyshell
jpayne@69 66 self.idb = idb # If passed, a proxy of remote instance.
jpayne@69 67 self.frame = None
jpayne@69 68 self.make_gui()
jpayne@69 69 self.interacting = 0
jpayne@69 70 self.nesting_level = 0
jpayne@69 71
jpayne@69 72 def run(self, *args):
jpayne@69 73 # Deal with the scenario where we've already got a program running
jpayne@69 74 # in the debugger and we want to start another. If that is the case,
jpayne@69 75 # our second 'run' was invoked from an event dispatched not from
jpayne@69 76 # the main event loop, but from the nested event loop in 'interaction'
jpayne@69 77 # below. So our stack looks something like this:
jpayne@69 78 # outer main event loop
jpayne@69 79 # run()
jpayne@69 80 # <running program with traces>
jpayne@69 81 # callback to debugger's interaction()
jpayne@69 82 # nested event loop
jpayne@69 83 # run() for second command
jpayne@69 84 #
jpayne@69 85 # This kind of nesting of event loops causes all kinds of problems
jpayne@69 86 # (see e.g. issue #24455) especially when dealing with running as a
jpayne@69 87 # subprocess, where there's all kinds of extra stuff happening in
jpayne@69 88 # there - insert a traceback.print_stack() to check it out.
jpayne@69 89 #
jpayne@69 90 # By this point, we've already called restart_subprocess() in
jpayne@69 91 # ScriptBinding. However, we also need to unwind the stack back to
jpayne@69 92 # that outer event loop. To accomplish this, we:
jpayne@69 93 # - return immediately from the nested run()
jpayne@69 94 # - abort_loop ensures the nested event loop will terminate
jpayne@69 95 # - the debugger's interaction routine completes normally
jpayne@69 96 # - the restart_subprocess() will have taken care of stopping
jpayne@69 97 # the running program, which will also let the outer run complete
jpayne@69 98 #
jpayne@69 99 # That leaves us back at the outer main event loop, at which point our
jpayne@69 100 # after event can fire, and we'll come back to this routine with a
jpayne@69 101 # clean stack.
jpayne@69 102 if self.nesting_level > 0:
jpayne@69 103 self.abort_loop()
jpayne@69 104 self.root.after(100, lambda: self.run(*args))
jpayne@69 105 return
jpayne@69 106 try:
jpayne@69 107 self.interacting = 1
jpayne@69 108 return self.idb.run(*args)
jpayne@69 109 finally:
jpayne@69 110 self.interacting = 0
jpayne@69 111
jpayne@69 112 def close(self, event=None):
jpayne@69 113 try:
jpayne@69 114 self.quit()
jpayne@69 115 except Exception:
jpayne@69 116 pass
jpayne@69 117 if self.interacting:
jpayne@69 118 self.top.bell()
jpayne@69 119 return
jpayne@69 120 if self.stackviewer:
jpayne@69 121 self.stackviewer.close(); self.stackviewer = None
jpayne@69 122 # Clean up pyshell if user clicked debugger control close widget.
jpayne@69 123 # (Causes a harmless extra cycle through close_debugger() if user
jpayne@69 124 # toggled debugger from pyshell Debug menu)
jpayne@69 125 self.pyshell.close_debugger()
jpayne@69 126 # Now close the debugger control window....
jpayne@69 127 self.top.destroy()
jpayne@69 128
jpayne@69 129 def make_gui(self):
jpayne@69 130 pyshell = self.pyshell
jpayne@69 131 self.flist = pyshell.flist
jpayne@69 132 self.root = root = pyshell.root
jpayne@69 133 self.top = top = ListedToplevel(root)
jpayne@69 134 self.top.wm_title("Debug Control")
jpayne@69 135 self.top.wm_iconname("Debug")
jpayne@69 136 top.wm_protocol("WM_DELETE_WINDOW", self.close)
jpayne@69 137 self.top.bind("<Escape>", self.close)
jpayne@69 138 #
jpayne@69 139 self.bframe = bframe = Frame(top)
jpayne@69 140 self.bframe.pack(anchor="w")
jpayne@69 141 self.buttons = bl = []
jpayne@69 142 #
jpayne@69 143 self.bcont = b = Button(bframe, text="Go", command=self.cont)
jpayne@69 144 bl.append(b)
jpayne@69 145 self.bstep = b = Button(bframe, text="Step", command=self.step)
jpayne@69 146 bl.append(b)
jpayne@69 147 self.bnext = b = Button(bframe, text="Over", command=self.next)
jpayne@69 148 bl.append(b)
jpayne@69 149 self.bret = b = Button(bframe, text="Out", command=self.ret)
jpayne@69 150 bl.append(b)
jpayne@69 151 self.bret = b = Button(bframe, text="Quit", command=self.quit)
jpayne@69 152 bl.append(b)
jpayne@69 153 #
jpayne@69 154 for b in bl:
jpayne@69 155 b.configure(state="disabled")
jpayne@69 156 b.pack(side="left")
jpayne@69 157 #
jpayne@69 158 self.cframe = cframe = Frame(bframe)
jpayne@69 159 self.cframe.pack(side="left")
jpayne@69 160 #
jpayne@69 161 if not self.vstack:
jpayne@69 162 self.__class__.vstack = BooleanVar(top)
jpayne@69 163 self.vstack.set(1)
jpayne@69 164 self.bstack = Checkbutton(cframe,
jpayne@69 165 text="Stack", command=self.show_stack, variable=self.vstack)
jpayne@69 166 self.bstack.grid(row=0, column=0)
jpayne@69 167 if not self.vsource:
jpayne@69 168 self.__class__.vsource = BooleanVar(top)
jpayne@69 169 self.bsource = Checkbutton(cframe,
jpayne@69 170 text="Source", command=self.show_source, variable=self.vsource)
jpayne@69 171 self.bsource.grid(row=0, column=1)
jpayne@69 172 if not self.vlocals:
jpayne@69 173 self.__class__.vlocals = BooleanVar(top)
jpayne@69 174 self.vlocals.set(1)
jpayne@69 175 self.blocals = Checkbutton(cframe,
jpayne@69 176 text="Locals", command=self.show_locals, variable=self.vlocals)
jpayne@69 177 self.blocals.grid(row=1, column=0)
jpayne@69 178 if not self.vglobals:
jpayne@69 179 self.__class__.vglobals = BooleanVar(top)
jpayne@69 180 self.bglobals = Checkbutton(cframe,
jpayne@69 181 text="Globals", command=self.show_globals, variable=self.vglobals)
jpayne@69 182 self.bglobals.grid(row=1, column=1)
jpayne@69 183 #
jpayne@69 184 self.status = Label(top, anchor="w")
jpayne@69 185 self.status.pack(anchor="w")
jpayne@69 186 self.error = Label(top, anchor="w")
jpayne@69 187 self.error.pack(anchor="w", fill="x")
jpayne@69 188 self.errorbg = self.error.cget("background")
jpayne@69 189 #
jpayne@69 190 self.fstack = Frame(top, height=1)
jpayne@69 191 self.fstack.pack(expand=1, fill="both")
jpayne@69 192 self.flocals = Frame(top)
jpayne@69 193 self.flocals.pack(expand=1, fill="both")
jpayne@69 194 self.fglobals = Frame(top, height=1)
jpayne@69 195 self.fglobals.pack(expand=1, fill="both")
jpayne@69 196 #
jpayne@69 197 if self.vstack.get():
jpayne@69 198 self.show_stack()
jpayne@69 199 if self.vlocals.get():
jpayne@69 200 self.show_locals()
jpayne@69 201 if self.vglobals.get():
jpayne@69 202 self.show_globals()
jpayne@69 203
jpayne@69 204 def interaction(self, message, frame, info=None):
jpayne@69 205 self.frame = frame
jpayne@69 206 self.status.configure(text=message)
jpayne@69 207 #
jpayne@69 208 if info:
jpayne@69 209 type, value, tb = info
jpayne@69 210 try:
jpayne@69 211 m1 = type.__name__
jpayne@69 212 except AttributeError:
jpayne@69 213 m1 = "%s" % str(type)
jpayne@69 214 if value is not None:
jpayne@69 215 try:
jpayne@69 216 m1 = "%s: %s" % (m1, str(value))
jpayne@69 217 except:
jpayne@69 218 pass
jpayne@69 219 bg = "yellow"
jpayne@69 220 else:
jpayne@69 221 m1 = ""
jpayne@69 222 tb = None
jpayne@69 223 bg = self.errorbg
jpayne@69 224 self.error.configure(text=m1, background=bg)
jpayne@69 225 #
jpayne@69 226 sv = self.stackviewer
jpayne@69 227 if sv:
jpayne@69 228 stack, i = self.idb.get_stack(self.frame, tb)
jpayne@69 229 sv.load_stack(stack, i)
jpayne@69 230 #
jpayne@69 231 self.show_variables(1)
jpayne@69 232 #
jpayne@69 233 if self.vsource.get():
jpayne@69 234 self.sync_source_line()
jpayne@69 235 #
jpayne@69 236 for b in self.buttons:
jpayne@69 237 b.configure(state="normal")
jpayne@69 238 #
jpayne@69 239 self.top.wakeup()
jpayne@69 240 # Nested main loop: Tkinter's main loop is not reentrant, so use
jpayne@69 241 # Tcl's vwait facility, which reenters the event loop until an
jpayne@69 242 # event handler sets the variable we're waiting on
jpayne@69 243 self.nesting_level += 1
jpayne@69 244 self.root.tk.call('vwait', '::idledebugwait')
jpayne@69 245 self.nesting_level -= 1
jpayne@69 246 #
jpayne@69 247 for b in self.buttons:
jpayne@69 248 b.configure(state="disabled")
jpayne@69 249 self.status.configure(text="")
jpayne@69 250 self.error.configure(text="", background=self.errorbg)
jpayne@69 251 self.frame = None
jpayne@69 252
jpayne@69 253 def sync_source_line(self):
jpayne@69 254 frame = self.frame
jpayne@69 255 if not frame:
jpayne@69 256 return
jpayne@69 257 filename, lineno = self.__frame2fileline(frame)
jpayne@69 258 if filename[:1] + filename[-1:] != "<>" and os.path.exists(filename):
jpayne@69 259 self.flist.gotofileline(filename, lineno)
jpayne@69 260
jpayne@69 261 def __frame2fileline(self, frame):
jpayne@69 262 code = frame.f_code
jpayne@69 263 filename = code.co_filename
jpayne@69 264 lineno = frame.f_lineno
jpayne@69 265 return filename, lineno
jpayne@69 266
jpayne@69 267 def cont(self):
jpayne@69 268 self.idb.set_continue()
jpayne@69 269 self.abort_loop()
jpayne@69 270
jpayne@69 271 def step(self):
jpayne@69 272 self.idb.set_step()
jpayne@69 273 self.abort_loop()
jpayne@69 274
jpayne@69 275 def next(self):
jpayne@69 276 self.idb.set_next(self.frame)
jpayne@69 277 self.abort_loop()
jpayne@69 278
jpayne@69 279 def ret(self):
jpayne@69 280 self.idb.set_return(self.frame)
jpayne@69 281 self.abort_loop()
jpayne@69 282
jpayne@69 283 def quit(self):
jpayne@69 284 self.idb.set_quit()
jpayne@69 285 self.abort_loop()
jpayne@69 286
jpayne@69 287 def abort_loop(self):
jpayne@69 288 self.root.tk.call('set', '::idledebugwait', '1')
jpayne@69 289
jpayne@69 290 stackviewer = None
jpayne@69 291
jpayne@69 292 def show_stack(self):
jpayne@69 293 if not self.stackviewer and self.vstack.get():
jpayne@69 294 self.stackviewer = sv = StackViewer(self.fstack, self.flist, self)
jpayne@69 295 if self.frame:
jpayne@69 296 stack, i = self.idb.get_stack(self.frame, None)
jpayne@69 297 sv.load_stack(stack, i)
jpayne@69 298 else:
jpayne@69 299 sv = self.stackviewer
jpayne@69 300 if sv and not self.vstack.get():
jpayne@69 301 self.stackviewer = None
jpayne@69 302 sv.close()
jpayne@69 303 self.fstack['height'] = 1
jpayne@69 304
jpayne@69 305 def show_source(self):
jpayne@69 306 if self.vsource.get():
jpayne@69 307 self.sync_source_line()
jpayne@69 308
jpayne@69 309 def show_frame(self, stackitem):
jpayne@69 310 self.frame = stackitem[0] # lineno is stackitem[1]
jpayne@69 311 self.show_variables()
jpayne@69 312
jpayne@69 313 localsviewer = None
jpayne@69 314 globalsviewer = None
jpayne@69 315
jpayne@69 316 def show_locals(self):
jpayne@69 317 lv = self.localsviewer
jpayne@69 318 if self.vlocals.get():
jpayne@69 319 if not lv:
jpayne@69 320 self.localsviewer = NamespaceViewer(self.flocals, "Locals")
jpayne@69 321 else:
jpayne@69 322 if lv:
jpayne@69 323 self.localsviewer = None
jpayne@69 324 lv.close()
jpayne@69 325 self.flocals['height'] = 1
jpayne@69 326 self.show_variables()
jpayne@69 327
jpayne@69 328 def show_globals(self):
jpayne@69 329 gv = self.globalsviewer
jpayne@69 330 if self.vglobals.get():
jpayne@69 331 if not gv:
jpayne@69 332 self.globalsviewer = NamespaceViewer(self.fglobals, "Globals")
jpayne@69 333 else:
jpayne@69 334 if gv:
jpayne@69 335 self.globalsviewer = None
jpayne@69 336 gv.close()
jpayne@69 337 self.fglobals['height'] = 1
jpayne@69 338 self.show_variables()
jpayne@69 339
jpayne@69 340 def show_variables(self, force=0):
jpayne@69 341 lv = self.localsviewer
jpayne@69 342 gv = self.globalsviewer
jpayne@69 343 frame = self.frame
jpayne@69 344 if not frame:
jpayne@69 345 ldict = gdict = None
jpayne@69 346 else:
jpayne@69 347 ldict = frame.f_locals
jpayne@69 348 gdict = frame.f_globals
jpayne@69 349 if lv and gv and ldict is gdict:
jpayne@69 350 ldict = None
jpayne@69 351 if lv:
jpayne@69 352 lv.load_dict(ldict, force, self.pyshell.interp.rpcclt)
jpayne@69 353 if gv:
jpayne@69 354 gv.load_dict(gdict, force, self.pyshell.interp.rpcclt)
jpayne@69 355
jpayne@69 356 def set_breakpoint_here(self, filename, lineno):
jpayne@69 357 self.idb.set_break(filename, lineno)
jpayne@69 358
jpayne@69 359 def clear_breakpoint_here(self, filename, lineno):
jpayne@69 360 self.idb.clear_break(filename, lineno)
jpayne@69 361
jpayne@69 362 def clear_file_breaks(self, filename):
jpayne@69 363 self.idb.clear_all_file_breaks(filename)
jpayne@69 364
jpayne@69 365 def load_breakpoints(self):
jpayne@69 366 "Load PyShellEditorWindow breakpoints into subprocess debugger"
jpayne@69 367 for editwin in self.pyshell.flist.inversedict:
jpayne@69 368 filename = editwin.io.filename
jpayne@69 369 try:
jpayne@69 370 for lineno in editwin.breakpoints:
jpayne@69 371 self.set_breakpoint_here(filename, lineno)
jpayne@69 372 except AttributeError:
jpayne@69 373 continue
jpayne@69 374
jpayne@69 375 class StackViewer(ScrolledList):
jpayne@69 376
jpayne@69 377 def __init__(self, master, flist, gui):
jpayne@69 378 if macosx.isAquaTk():
jpayne@69 379 # At least on with the stock AquaTk version on OSX 10.4 you'll
jpayne@69 380 # get a shaking GUI that eventually kills IDLE if the width
jpayne@69 381 # argument is specified.
jpayne@69 382 ScrolledList.__init__(self, master)
jpayne@69 383 else:
jpayne@69 384 ScrolledList.__init__(self, master, width=80)
jpayne@69 385 self.flist = flist
jpayne@69 386 self.gui = gui
jpayne@69 387 self.stack = []
jpayne@69 388
jpayne@69 389 def load_stack(self, stack, index=None):
jpayne@69 390 self.stack = stack
jpayne@69 391 self.clear()
jpayne@69 392 for i in range(len(stack)):
jpayne@69 393 frame, lineno = stack[i]
jpayne@69 394 try:
jpayne@69 395 modname = frame.f_globals["__name__"]
jpayne@69 396 except:
jpayne@69 397 modname = "?"
jpayne@69 398 code = frame.f_code
jpayne@69 399 filename = code.co_filename
jpayne@69 400 funcname = code.co_name
jpayne@69 401 import linecache
jpayne@69 402 sourceline = linecache.getline(filename, lineno)
jpayne@69 403 sourceline = sourceline.strip()
jpayne@69 404 if funcname in ("?", "", None):
jpayne@69 405 item = "%s, line %d: %s" % (modname, lineno, sourceline)
jpayne@69 406 else:
jpayne@69 407 item = "%s.%s(), line %d: %s" % (modname, funcname,
jpayne@69 408 lineno, sourceline)
jpayne@69 409 if i == index:
jpayne@69 410 item = "> " + item
jpayne@69 411 self.append(item)
jpayne@69 412 if index is not None:
jpayne@69 413 self.select(index)
jpayne@69 414
jpayne@69 415 def popup_event(self, event):
jpayne@69 416 "override base method"
jpayne@69 417 if self.stack:
jpayne@69 418 return ScrolledList.popup_event(self, event)
jpayne@69 419
jpayne@69 420 def fill_menu(self):
jpayne@69 421 "override base method"
jpayne@69 422 menu = self.menu
jpayne@69 423 menu.add_command(label="Go to source line",
jpayne@69 424 command=self.goto_source_line)
jpayne@69 425 menu.add_command(label="Show stack frame",
jpayne@69 426 command=self.show_stack_frame)
jpayne@69 427
jpayne@69 428 def on_select(self, index):
jpayne@69 429 "override base method"
jpayne@69 430 if 0 <= index < len(self.stack):
jpayne@69 431 self.gui.show_frame(self.stack[index])
jpayne@69 432
jpayne@69 433 def on_double(self, index):
jpayne@69 434 "override base method"
jpayne@69 435 self.show_source(index)
jpayne@69 436
jpayne@69 437 def goto_source_line(self):
jpayne@69 438 index = self.listbox.index("active")
jpayne@69 439 self.show_source(index)
jpayne@69 440
jpayne@69 441 def show_stack_frame(self):
jpayne@69 442 index = self.listbox.index("active")
jpayne@69 443 if 0 <= index < len(self.stack):
jpayne@69 444 self.gui.show_frame(self.stack[index])
jpayne@69 445
jpayne@69 446 def show_source(self, index):
jpayne@69 447 if not (0 <= index < len(self.stack)):
jpayne@69 448 return
jpayne@69 449 frame, lineno = self.stack[index]
jpayne@69 450 code = frame.f_code
jpayne@69 451 filename = code.co_filename
jpayne@69 452 if os.path.isfile(filename):
jpayne@69 453 edit = self.flist.open(filename)
jpayne@69 454 if edit:
jpayne@69 455 edit.gotoline(lineno)
jpayne@69 456
jpayne@69 457
jpayne@69 458 class NamespaceViewer:
jpayne@69 459
jpayne@69 460 def __init__(self, master, title, dict=None):
jpayne@69 461 width = 0
jpayne@69 462 height = 40
jpayne@69 463 if dict:
jpayne@69 464 height = 20*len(dict) # XXX 20 == observed height of Entry widget
jpayne@69 465 self.master = master
jpayne@69 466 self.title = title
jpayne@69 467 import reprlib
jpayne@69 468 self.repr = reprlib.Repr()
jpayne@69 469 self.repr.maxstring = 60
jpayne@69 470 self.repr.maxother = 60
jpayne@69 471 self.frame = frame = Frame(master)
jpayne@69 472 self.frame.pack(expand=1, fill="both")
jpayne@69 473 self.label = Label(frame, text=title, borderwidth=2, relief="groove")
jpayne@69 474 self.label.pack(fill="x")
jpayne@69 475 self.vbar = vbar = Scrollbar(frame, name="vbar")
jpayne@69 476 vbar.pack(side="right", fill="y")
jpayne@69 477 self.canvas = canvas = Canvas(frame,
jpayne@69 478 height=min(300, max(40, height)),
jpayne@69 479 scrollregion=(0, 0, width, height))
jpayne@69 480 canvas.pack(side="left", fill="both", expand=1)
jpayne@69 481 vbar["command"] = canvas.yview
jpayne@69 482 canvas["yscrollcommand"] = vbar.set
jpayne@69 483 self.subframe = subframe = Frame(canvas)
jpayne@69 484 self.sfid = canvas.create_window(0, 0, window=subframe, anchor="nw")
jpayne@69 485 self.load_dict(dict)
jpayne@69 486
jpayne@69 487 dict = -1
jpayne@69 488
jpayne@69 489 def load_dict(self, dict, force=0, rpc_client=None):
jpayne@69 490 if dict is self.dict and not force:
jpayne@69 491 return
jpayne@69 492 subframe = self.subframe
jpayne@69 493 frame = self.frame
jpayne@69 494 for c in list(subframe.children.values()):
jpayne@69 495 c.destroy()
jpayne@69 496 self.dict = None
jpayne@69 497 if not dict:
jpayne@69 498 l = Label(subframe, text="None")
jpayne@69 499 l.grid(row=0, column=0)
jpayne@69 500 else:
jpayne@69 501 #names = sorted(dict)
jpayne@69 502 ###
jpayne@69 503 # Because of (temporary) limitations on the dict_keys type (not yet
jpayne@69 504 # public or pickleable), have the subprocess to send a list of
jpayne@69 505 # keys, not a dict_keys object. sorted() will take a dict_keys
jpayne@69 506 # (no subprocess) or a list.
jpayne@69 507 #
jpayne@69 508 # There is also an obscure bug in sorted(dict) where the
jpayne@69 509 # interpreter gets into a loop requesting non-existing dict[0],
jpayne@69 510 # dict[1], dict[2], etc from the debugger_r.DictProxy.
jpayne@69 511 ###
jpayne@69 512 keys_list = dict.keys()
jpayne@69 513 names = sorted(keys_list)
jpayne@69 514 ###
jpayne@69 515 row = 0
jpayne@69 516 for name in names:
jpayne@69 517 value = dict[name]
jpayne@69 518 svalue = self.repr.repr(value) # repr(value)
jpayne@69 519 # Strip extra quotes caused by calling repr on the (already)
jpayne@69 520 # repr'd value sent across the RPC interface:
jpayne@69 521 if rpc_client:
jpayne@69 522 svalue = svalue[1:-1]
jpayne@69 523 l = Label(subframe, text=name)
jpayne@69 524 l.grid(row=row, column=0, sticky="nw")
jpayne@69 525 l = Entry(subframe, width=0, borderwidth=0)
jpayne@69 526 l.insert(0, svalue)
jpayne@69 527 l.grid(row=row, column=1, sticky="nw")
jpayne@69 528 row = row+1
jpayne@69 529 self.dict = dict
jpayne@69 530 # XXX Could we use a <Configure> callback for the following?
jpayne@69 531 subframe.update_idletasks() # Alas!
jpayne@69 532 width = subframe.winfo_reqwidth()
jpayne@69 533 height = subframe.winfo_reqheight()
jpayne@69 534 canvas = self.canvas
jpayne@69 535 self.canvas["scrollregion"] = (0, 0, width, height)
jpayne@69 536 if height > 300:
jpayne@69 537 canvas["height"] = 300
jpayne@69 538 frame.pack(expand=1)
jpayne@69 539 else:
jpayne@69 540 canvas["height"] = height
jpayne@69 541 frame.pack(expand=0)
jpayne@69 542
jpayne@69 543 def close(self):
jpayne@69 544 self.frame.destroy()
jpayne@69 545
jpayne@69 546 if __name__ == "__main__":
jpayne@69 547 from unittest import main
jpayne@69 548 main('idlelib.idle_test.test_debugger', verbosity=2, exit=False)
jpayne@69 549
jpayne@69 550 # TODO: htest?