def search(w, size, key): if self.search_box is None: _, search_start = self.source.get_focus() self.search_box = SearchBox(self) self.search_AttrMap = urwid.AttrMap( self.search_box, "search box") self.lhs_col.item_types.insert( 0, ("flow", None)) self.lhs_col.widget_list.insert( 0, self.search_AttrMap) self.columns.set_focus(self.lhs_col) self.lhs_col.set_focus(self.search_AttrMap) else: self.columns.set_focus(self.lhs_col) self.lhs_col.set_focus(self.search_AttrMap) self.search_box.restart_search()
class DebuggerUI(FrameVarInfoKeeper): def __init__(self, dbg): FrameVarInfoKeeper.__init__(self) self.debugger = dbg Attr = urwid.AttrMap self.search_box = None self.last_module_filter = "" self.source = urwid.SimpleListWalker([]) self.source_list = urwid.ListBox(self.source) self.source_sigwrap = SignalWrap(self.source_list) self.source_hscroll_start = 0 self.lhs_col = urwid.Pile([ ("weight", 1, urwid.AttrMap(self.source_sigwrap, "source")) ]) self.locals = urwid.SimpleListWalker([]) self.var_list = SignalWrap( urwid.ListBox(self.locals)) self.stack_walker = urwid.SimpleListWalker([]) self.stack_list = SignalWrap( urwid.ListBox(self.stack_walker)) self.bp_walker = urwid.SimpleListWalker([]) self.bp_list = SignalWrap( urwid.ListBox(self.bp_walker)) self.rhs_col = urwid.Pile([ ("weight", float(CONFIG["variables_weight"]), Attr(urwid.Pile([ ("flow", urwid.Text(make_hotkey_markup("_Variables:"))), Attr(self.var_list, "variables"), ]), None, "focused sidebar"),), ("weight", float(CONFIG["stack_weight"]), Attr(urwid.Pile([ ("flow", urwid.Text(make_hotkey_markup("_Stack:"))), Attr(self.stack_list, "stack"), ]), None, "focused sidebar"),), ("weight", float(CONFIG["breakpoints_weight"]), Attr(urwid.Pile([ ("flow", urwid.Text(make_hotkey_markup("_Breakpoints:"))), Attr(self.bp_list, "breakpoint"), ]), None, "focused sidebar"),), ]) self.columns = urwid.Columns( [ ("weight", 1, self.lhs_col), ("weight", float(CONFIG["sidebar_width"]), self.rhs_col), ], dividechars=1) self.caption = urwid.Text("") header = urwid.AttrMap(self.caption, "header") self.top = SignalWrap(urwid.Frame( urwid.AttrMap(self.columns, "background"), header)) from functools import partial def change_rhs_box(name, index, direction, w, size, key): from pudb.settings import save_config _, weight = self.rhs_col.item_types[index] if direction < 0: if weight > 1/5: weight /= 1.25 else: if weight < 5: weight *= 1.25 CONFIG[name+"_weight"] = weight save_config(CONFIG) self.rhs_col.item_types[index] = "weight", weight self.rhs_col._invalidate() # variable listeners -------------------------------------------------- def change_var_state(w, size, key): var, pos = self.var_list._w.get_focus() iinfo = self.get_frame_var_info(read_only=False) \ .get_inspect_info(var.id_path, read_only=False) if key == "\\": iinfo.show_detail = not iinfo.show_detail elif key == "t": iinfo.display_type = "type" elif key == "r": iinfo.display_type = "repr" elif key == "s": iinfo.display_type = "str" elif key == "c": iinfo.display_type = CONFIG["custom_stringifier"] elif key == "h": iinfo.highlighted = not iinfo.highlighted elif key == "@": iinfo.repeated_at_top = not iinfo.repeated_at_top elif key == "*": iinfo.show_private_members = not iinfo.show_private_members self.update_var_view() def edit_inspector_detail(w, size, key): var, pos = self.var_list._w.get_focus() if var is None: return fvi = self.get_frame_var_info(read_only=False) iinfo = fvi.get_inspect_info(var.id_path, read_only=False) buttons = [ ("OK", True), ("Cancel", False), ] if var.watch_expr is not None: watch_edit = urwid.Edit([ ("label", "Watch expression: ") ], var.watch_expr.expression) id_segment = [urwid.AttrMap(watch_edit, "value"), urwid.Text("")] buttons.extend([None, ("Delete", "del")]) title = "Watch Expression Options" else: id_segment = [ labelled_value("Identifier Path: ", var.id_path), urwid.Text(""), ] title = "Variable Inspection Options" rb_grp = [] rb_show_type = urwid.RadioButton(rb_grp, "Show Type", iinfo.display_type == "type") rb_show_repr = urwid.RadioButton(rb_grp, "Show repr()", iinfo.display_type == "repr") rb_show_str = urwid.RadioButton(rb_grp, "Show str()", iinfo.display_type == "str") rb_show_custom = urwid.RadioButton(rb_grp, "Show custom (set in prefs)", iinfo.display_type == CONFIG["custom_stringifier"]) expanded_checkbox = urwid.CheckBox("Expanded", iinfo.show_detail) highlighted_checkbox = urwid.CheckBox("Highlighted", iinfo.highlighted) repeated_at_top_checkbox = urwid.CheckBox("Repeated at top", iinfo.repeated_at_top) show_private_checkbox = urwid.CheckBox("Show private members", iinfo.show_private_members) lb = urwid.ListBox( id_segment+rb_grp+[ urwid.Text(""), expanded_checkbox, highlighted_checkbox, repeated_at_top_checkbox, show_private_checkbox, ]) result = self.dialog(lb, buttons, title=title) if result == True: iinfo.show_detail = expanded_checkbox.get_state() iinfo.highlighted = highlighted_checkbox.get_state() iinfo.repeated_at_top = repeated_at_top_checkbox.get_state() iinfo.show_private_members = show_private_checkbox.get_state() if rb_show_type.get_state(): iinfo.display_type = "type" elif rb_show_repr.get_state(): iinfo.display_type = "repr" elif rb_show_str.get_state(): iinfo.display_type = "str" elif rb_show_custom.get_state(): iinfo.display_type = CONFIG["custom_stringifier"] if var.watch_expr is not None: var.watch_expr.expression = watch_edit.get_edit_text() elif result == "del": for i, watch_expr in enumerate(fvi.watches): if watch_expr is var.watch_expr: del fvi.watches[i] self.update_var_view() def insert_watch(w, size, key): watch_edit = urwid.Edit([ ("label", "Watch expression: ") ]) if self.dialog( urwid.ListBox([ urwid.AttrMap(watch_edit, "value") ]), [ ("OK", True), ("Cancel", False), ], title="Add Watch Expression"): from pudb.var_view import WatchExpression we = WatchExpression(watch_edit.get_edit_text()) fvi = self.get_frame_var_info(read_only=False) fvi.watches.append(we) self.update_var_view() self.var_list.listen("\\", change_var_state) self.var_list.listen("t", change_var_state) self.var_list.listen("r", change_var_state) self.var_list.listen("s", change_var_state) self.var_list.listen("c", change_var_state) self.var_list.listen("h", change_var_state) self.var_list.listen("@", change_var_state) self.var_list.listen("enter", edit_inspector_detail) self.var_list.listen("n", insert_watch) self.var_list.listen("insert", insert_watch) self.var_list.listen("[", partial(change_rhs_box, 'variables', 0, -1)) self.var_list.listen("]", partial(change_rhs_box, 'variables', 0, 1)) # stack listeners ----------------------------------------------------- def examine_frame(w, size, key): from pudb import CONFIG _, pos = self.stack_list._w.get_focus() if CONFIG["current_stack_frame"] == "top": self.debugger.set_frame_index(len(self.debugger.stack)-1-pos) elif CONFIG["current_stack_frame"] == "bottom": self.debugger.set_frame_index(pos) else: raise ValueError("invalid value for 'current_stack_frame' pref") self.stack_list.listen("enter", examine_frame) def move_stack_up(w, size, key): self.debugger.move_up_frame() def move_stack_down(w, size, key): self.debugger.move_down_frame() self.stack_list.listen("u", move_stack_up) self.stack_list.listen("d", move_stack_down) self.stack_list.listen("[", partial(change_rhs_box, 'stack', 1, -1)) self.stack_list.listen("]", partial(change_rhs_box, 'stack', 1, 1)) # breakpoint listeners ----------------------------------------------------- def save_breakpoints(w, size, key): self.debugger.save_breakpoints() def delete_breakpoint(w, size, key): _, pos = self.bp_list._w.get_focus() bp = self._get_bp_list()[pos] if self.shown_file == bp.file: self.source[bp.line-1].set_breakpoint(False) err = self.debugger.clear_break(bp.file, bp.line) if err: self.message("Error clearing breakpoint:\n"+ err) else: self.update_breakpoints() def examine_breakpoint(w, size, key): bp_entry, pos = self.bp_list._w.get_focus() if bp_entry is None: return bp = self._get_bp_list()[pos] if bp.cond is None: cond = "" else: cond = str(bp.cond) enabled_checkbox = urwid.CheckBox( "Enabled", bp.enabled) cond_edit = urwid.Edit([ ("label", "Condition: ") ], cond) ign_count_edit = urwid.IntEdit([ ("label", "Ignore the next N times: ") ], bp.ignore) lb = urwid.ListBox([ labelled_value("File: ", bp.file), labelled_value("Line: ", bp.line), labelled_value("Hits: ", bp.hits), urwid.Text(""), enabled_checkbox, urwid.AttrMap(cond_edit, "value", "value"), urwid.AttrMap(ign_count_edit, "value", "value"), ]) result = self.dialog(lb, [ ("OK", True), ("Cancel", False), None, ("Delete", "del"), ("Location", "loc"), ], title="Edit Breakpoint") if result == True: bp.enabled = enabled_checkbox.get_state() bp.ignore = int(ign_count_edit.value()) cond = cond_edit.get_edit_text() if cond: bp.cond = cond else: bp.cond = None elif result == "loc": self.show_line(bp.line, bp.file) self.columns.set_focus(0) elif result == "del": if self.shown_file == bp.file: self.source[bp.line-1].set_breakpoint(False) err = self.debugger.clear_break(bp.file, bp.line) if err: self.message("Error clearing breakpoint:\n"+ err) else: self.update_breakpoints() self.bp_list.listen("enter", examine_breakpoint) self.bp_list.listen("d", delete_breakpoint) self.bp_list.listen("s", save_breakpoints) self.bp_list.listen("[", partial(change_rhs_box, 'breakpoints', 2, -1)) self.bp_list.listen("]", partial(change_rhs_box, 'breakpoints', 2, 1)) # top-level listeners ------------------------------------------------- def end(): self.debugger.save_breakpoints() self.quit_event_loop = True def next(w, size, key): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: self.debugger.set_next(self.debugger.curframe) end() def step(w, size, key): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: self.debugger.set_step() end() def finish(w, size, key): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: self.debugger.set_return(self.debugger.curframe) end() def cont(w, size, key): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: self.debugger.set_continue() end() def run_to_cursor(w, size, key): if self.debugger.post_mortem: self.message("Post-mortem mode: Can't modify state.") else: sline, pos = self.source.get_focus() lineno = pos+1 from pudb.lowlevel import get_breakpoint_invalid_reason invalid_reason = get_breakpoint_invalid_reason( self.shown_file, lineno) if invalid_reason is not None: self.message( "Cannot run to the line you indicated, " "for the following reason:\n\n" + invalid_reason) else: err = self.debugger.set_break(self.shown_file, pos+1, temporary=True) if err: self.message("Error dealing with breakpoint:\n"+ err) self.debugger.set_continue() end() def move_home(w, size, key): self.source.set_focus(0) def move_end(w, size, key): self.source.set_focus(len(self.source)) def go_to_line(w, size, key): _, line = self.source.get_focus() lineno_edit = urwid.IntEdit([ ("label", "Line number: ") ], line+1) if self.dialog( urwid.ListBox([ labelled_value("File :", self.shown_file), urwid.AttrMap(lineno_edit, "value") ]), [ ("OK", True), ("Cancel", False), ], title="Go to Line Number"): lineno = min(max(0, int(lineno_edit.value())-1), len(self.source)-1) self.source.set_focus(lineno) def move_down(w, size, key): w.keypress(size, "down") def move_up(w, size, key): w.keypress(size, "up") def page_down(w, size, key): w.keypress(size, "page down") def page_up(w, size, key): w.keypress(size, "page up") def move_up(w, size, key): w.keypress(size, "up") def scroll_left(w, size, key): self.source_hscroll_start = max( 0, self.source_hscroll_start - 4) for sl in self.source: sl._invalidate() def scroll_right(w, size, key): self.source_hscroll_start += 4 for sl in self.source: sl._invalidate() def search(w, size, key): if self.search_box is None: _, search_start = self.source.get_focus() self.search_box = SearchBox(self) self.search_AttrMap = urwid.AttrMap( self.search_box, "search box") self.lhs_col.item_types.insert( 0, ("flow", None)) self.lhs_col.widget_list.insert( 0, self.search_AttrMap) self.columns.set_focus(self.lhs_col) self.lhs_col.set_focus(self.search_AttrMap) else: self.columns.set_focus(self.lhs_col) self.lhs_col.set_focus(self.search_AttrMap) self.search_box.restart_search() def search_next(w, size, key): if self.search_box is not None: self.search_box.do_search(1) else: self.message("No previous search term.") def search_previous(w, size, key): if self.search_box is not None: self.search_box.do_search(-1) else: self.message("No previous search term.") def toggle_breakpoint(w, size, key): if self.shown_file: sline, pos = self.source.get_focus() lineno = pos+1 existing_breaks = self.debugger.get_breaks( self.shown_file, lineno) if existing_breaks: err = self.debugger.clear_break(self.shown_file, lineno) sline.set_breakpoint(False) else: from pudb.lowlevel import get_breakpoint_invalid_reason invalid_reason = get_breakpoint_invalid_reason( self.shown_file, pos+1) if invalid_reason is not None: do_set = not self.dialog(urwid.ListBox([ urwid.Text("The breakpoint you just set may be " "invalid, for the following reason:\n\n" + invalid_reason), ]), [ ("Cancel", True), ("Set Anyway", False), ], title="Possibly Invalid Breakpoint", focus_buttons=True) else: do_set = True if do_set: err = self.debugger.set_break(self.shown_file, pos+1) sline.set_breakpoint(True) else: err = None if err: self.message("Error dealing with breakpoint:\n"+ err) self.update_breakpoints() else: raise RuntimeError, "no valid current file" def pick_module(w, size, key): from os.path import splitext import sys def mod_exists(mod): if not hasattr(mod, "__file__"): return False filename = mod.__file__ base, ext = splitext(filename) ext = ext.lower() from os.path import exists if ext == ".pyc": return exists(base+".py") else: return ext == ".py" new_mod_text = SelectableText("-- update me --") new_mod_entry = urwid.AttrMap(new_mod_text, None, "focused selectable") def build_filtered_mod_list(filt_string=""): modules = sorted(name for name, mod in sys.modules.items() if mod_exists(mod)) result = [urwid.AttrMap(SelectableText(mod), None, "focused selectable") for mod in modules if filt_string in mod] new_mod_text.set_text("<<< IMPORT MODULE '%s' >>>" % filt_string) result.append(new_mod_entry) return result def show_mod(mod): filename = self.debugger.canonic(mod.__file__) base, ext = splitext(filename) if ext == ".pyc": ext = ".py" filename = base+".py" self.set_current_file(filename) self.source_list.set_focus(0) class FilterEdit(urwid.Edit): def keypress(self, size, key): result = urwid.Edit.keypress(self, size, key) if result is None: mod_list[:] = build_filtered_mod_list( self.get_edit_text()) return result filt_edit = FilterEdit([("label", "Filter: ")], self.last_module_filter) mod_list = urwid.SimpleListWalker( build_filtered_mod_list(filt_edit.get_edit_text())) lb = urwid.ListBox(mod_list) w = urwid.Pile([ ("flow", urwid.AttrMap(filt_edit, "value")), ("fixed", 1, urwid.SolidFill()), urwid.AttrMap(lb, "selectable")]) while True: result = self.dialog(w, [ ("OK", True), ("Cancel", False), ("Reload", "reload"), ], title="Pick Module") self.last_module_filter = filt_edit.get_edit_text() if result == True: widget, pos = lb.get_focus() if widget is new_mod_entry: new_mod_name = filt_edit.get_edit_text() try: __import__(str(new_mod_name)) except: from traceback import format_exception import sys self.message("Could not import module '%s':\n\n%s" % ( new_mod_name, "".join(format_exception(*sys.exc_info()))), title="Import Error") else: show_mod(sys.modules[str(new_mod_name)]) break else: show_mod(sys.modules[widget.get_text()[0]]) break elif result == False: break elif result == "reload": widget, pos = lb.get_focus() if widget is not new_mod_entry: mod_name = widget.get_text()[0] mod = sys.modules[mod_name] reload(mod) self.message("'%s' was successfully reloaded." % mod_name) elif result == "import": mod = import_new_module(filt_edit.get_edit_text()) if mod is not None: show_mod(mod) break self.source_sigwrap.listen("n", next) self.source_sigwrap.listen("s", step) self.source_sigwrap.listen("f", finish) self.source_sigwrap.listen("r", finish) self.source_sigwrap.listen("c", cont) self.source_sigwrap.listen("t", run_to_cursor) self.source_sigwrap.listen("j", move_down) self.source_sigwrap.listen("k", move_up) self.source_sigwrap.listen("ctrl d", page_down) self.source_sigwrap.listen("ctrl u", page_up) self.source_sigwrap.listen("ctrl f", page_down) self.source_sigwrap.listen("ctrl b", page_up) self.source_sigwrap.listen("h", scroll_left) self.source_sigwrap.listen("l", scroll_right) self.source_sigwrap.listen("/", search) self.source_sigwrap.listen(",", search_previous) self.source_sigwrap.listen(".", search_next) self.source_sigwrap.listen("home", move_home) self.source_sigwrap.listen("end", move_end) self.source_sigwrap.listen("g", move_home) self.source_sigwrap.listen("G", move_end) self.source_sigwrap.listen("L", go_to_line) self.source_sigwrap.listen("b", toggle_breakpoint) self.source_sigwrap.listen("m", pick_module) self.source_sigwrap.listen("u", move_stack_up) self.source_sigwrap.listen("d", move_stack_down) # top-level listeners ------------------------------------------------- def show_output(w, size, key): self.screen.stop() raw_input("Hit Enter to return:") self.screen.start() def show_traceback(w, size, key): if self.current_exc_tuple is not None: from traceback import format_exception result = self.dialog( urwid.ListBox([urwid.Text( "".join(format_exception(*self.current_exc_tuple)))]), [ ("Close", "close"), ("Location", "location") ], title="Exception Viewer", focus_buttons=True, bind_enter_esc=False) if result == "location": self.debugger.set_frame_index(len(self.debugger.stack)-1) else: self.message("No exception available.") def run_shell(w, size, key): self.screen.stop() if not hasattr(self, "have_been_to_shell"): self.have_been_to_shell = True first_shell_run = True else: first_shell_run = False curframe = self.debugger.curframe import pudb.shell as shell if shell.HAVE_IPYTHON and CONFIG["shell"] == "ipython": runner = shell.run_ipython_shell else: runner = shell.run_classic_shell runner(curframe.f_locals, curframe.f_globals, first_shell_run) self.screen.start() self.update_var_view() class RHColumnFocuser: def __init__(self, idx): self.idx = idx def __call__(subself, w, size, key): self.columns.set_focus(self.rhs_col) self.rhs_col.set_focus(self.rhs_col.widget_list[subself.idx]) def max_sidebar(w, size, key): from pudb.settings import save_config weight = 5 CONFIG["sidebar_width"] = weight save_config(CONFIG) self.columns.column_types[1] = "weight", weight self.columns._invalidate() def min_sidebar(w, size, key): from pudb.settings import save_config weight = 1/5 CONFIG["sidebar_width"] = weight save_config(CONFIG) self.columns.column_types[1] = "weight", weight self.columns._invalidate() def grow_sidebar(w, size, key): from pudb.settings import save_config _, weight = self.columns.column_types[1] if weight < 5: weight *= 1.25 CONFIG["sidebar_width"] = weight save_config(CONFIG) self.columns.column_types[1] = "weight", weight self.columns._invalidate() def shrink_sidebar(w, size, key): from pudb.settings import save_config _, weight = self.columns.column_types[1] if weight > 1/5: weight /= 1.25 CONFIG["sidebar_width"] = weight save_config(CONFIG) self.columns.column_types[1] = "weight", weight self.columns._invalidate() def quit(w, size, key): self.debugger.set_quit() end() def do_edit_config(w, size, key): self.run_edit_config() def help(w, size, key): self.message(HELP_TEXT, title="PuDB Help") self.top.listen("o", show_output) self.top.listen("!", run_shell) self.top.listen("e", show_traceback) self.top.listen("=", max_sidebar) self.top.listen("+", grow_sidebar) self.top.listen("_", min_sidebar) self.top.listen("-", shrink_sidebar) self.top.listen("V", RHColumnFocuser(0)) self.top.listen("S", RHColumnFocuser(1)) self.top.listen("B", RHColumnFocuser(2)) self.top.listen("q", quit) self.top.listen("ctrl p", do_edit_config) self.top.listen("H", help) self.top.listen("f1", help) self.top.listen("?", help) # setup --------------------------------------------------------------- import urwid.raw_display as display self.screen = display.Screen() self.setup_palette(self.screen) self.show_count = 0 self.shown_file = None self.current_line = None self.quit_event_loop = False def message(self, msg, title="Message", **kwargs): self.call_with_ui(self.dialog, urwid.ListBox([urwid.Text(msg)]), [("OK", True)], title=title, **kwargs) def run_edit_config(self): from pudb.settings import edit_config, save_config edit_config(self, CONFIG) save_config(CONFIG) def dialog(self, content, buttons_and_results, title=None, bind_enter_esc=True, focus_buttons=False, extra_bindings=[]): class ResultSetter: def __init__(subself, res): subself.res = res def __call__(subself, btn): self.quit_event_loop = [subself.res] Attr = urwid.AttrMap if bind_enter_esc: content = SignalWrap(content) def enter(w, size, key): self.quit_event_loop = [True] def esc(w, size, key): self.quit_event_loop = [False] content.listen("enter", enter) content.listen("esc", esc) button_widgets = [] for btn_descr in buttons_and_results: if btn_descr is None: button_widgets.append(urwid.Text("")) else: btn_text, btn_result = btn_descr button_widgets.append( Attr(urwid.Button(btn_text, ResultSetter(btn_result)), "button", "focused button")) w = urwid.Columns([ content, ("fixed", 15, urwid.ListBox(button_widgets)), ], dividechars=1) if focus_buttons: w.set_focus_column(1) if title is not None: w = urwid.Pile([ ("flow", urwid.AttrMap( urwid.Text(title, align="center"), "dialog title")), ("fixed", 1, urwid.SolidFill()), w]) w = SignalWrap(w) for key, binding in extra_bindings: w.listen(key, binding) w = urwid.LineBox(w) w = urwid.Overlay(w, self.top, align="center", valign="middle", width=('relative', 75), height=('relative', 75), ) w = Attr(w, "background") return self.event_loop(w)[0] @staticmethod def setup_palette(screen): from urwid.raw_display import Screen as RawScreen may_use_fancy_formats = isinstance(screen, RawScreen) and \ not hasattr(urwid.escape, "_fg_attr_xterm") from pudb.theme import get_palette screen.register_palette( get_palette(may_use_fancy_formats, CONFIG["theme"])) # UI enter/exit ----------------------------------------------------------- def show(self): if self.show_count == 0: self.screen.start() self.show_count += 1 def hide(self): self.show_count -= 1 if self.show_count == 0: self.screen.stop() def call_with_ui(self, f, *args, **kwargs): self.show() try: return f(*args, **kwargs) finally: self.hide() # interaction ------------------------------------------------------------- def event_loop(self, toplevel=None): prev_quit_loop = self.quit_event_loop try: import pygments except ImportError: if not hasattr(self, "pygments_message_shown"): self.pygments_message_shown = True self.message("Package 'pygments' not found. " "Syntax highlighting disabled.") from pudb import CONFIG WELCOME_LEVEL = "e001" if CONFIG["seen_welcome"] < WELCOME_LEVEL: CONFIG["seen_welcome"] = WELCOME_LEVEL from pudb import VERSION self.message("Welcome to PudB %s!\n\n" "PuDB is a full-screen, console-based visual debugger for Python. " " Its goal is to provide all the niceties of modern GUI-based " "debuggers in a more lightweight and keyboard-friendly package. " "PuDB allows you to debug code right where you write and test it--in " "a terminal. If you've worked with the excellent (but nowadays " "ancient) DOS-based Turbo Pascal or C tools, PuDB's UI might " "look familiar.\n\n" "If you're new here, welcome! The help screen (invoked by hitting " "'?' after this message) should get you on your way.\n" "\nNew features in version 2011.3:\n\n" "- Finer-grained string highlighting (submitted by Aaron Meurer)\n" "- Prefs tweaks, instant-apply, top-down stack (submitted by Aaron Meurer)\n" "- Size changes in sidebar boxes (submitted by Aaron Meurer)\n" "- New theme 'midnight' (submitted by Aaron Meurer)\n" "- Support for IPython 0.11 (submitted by Chris Farrow)\n" "- Suport for custom stringifiers (submitted by Aaron Meurer)\n" "\nNew features in version 2011.2:\n\n" "- Fix for post-mortem debugging (submitted by 'Sundance')\n" "\nNew features in version 2011.1:\n\n" "- Breakpoints saved between sessions\n" "- A new 'dark vim' theme\n" "(both contributed by Naveen Michaud-Agrawal)\n" "\nNew features in version 0.93:\n\n" "- Stored preferences (no more pesky IPython prompt!)\n" "- Themes\n" "- Line numbers (optional)\n" % VERSION) from pudb.settings import save_config save_config(CONFIG) self.run_edit_config() try: if toplevel is None: toplevel = self.top self.size = self.screen.get_cols_rows() self.quit_event_loop = False while not self.quit_event_loop: canvas = toplevel.render(self.size, focus=True) self.screen.draw_screen(self.size, canvas) keys = self.screen.get_input() for k in keys: if k == "window resize": self.size = self.screen.get_cols_rows() else: toplevel.keypress(self.size, k) return self.quit_event_loop finally: self.quit_event_loop = prev_quit_loop # debugger-facing interface ----------------------------------------------- def interaction(self, exc_tuple): self.current_exc_tuple = exc_tuple from pudb import VERSION caption = [(None, u"PuDB %s - ?:help n:next s:step into b:breakpoint o:output " "t:run to cursor !:python shell" % VERSION)] if self.debugger.post_mortem: from traceback import format_exception self.message( "The program has terminated abnormally because of an exception.\n\n" "A full traceback is below. You may recall this traceback at any " "time using the 'e' key. " "The debugger has entered post-mortem mode and will prevent further " "state changes.\n\n" + "".join(format_exception(*exc_tuple)), title="Program Terminated for Uncaught Exception") caption.extend([ (None, " "), ("warning", "[POST-MORTEM MODE]") ]) elif exc_tuple is not None: caption.extend([ (None, " "), ("warning", "[PROCESSING EXCEPTION - hit 'e' to examine]") ]) self.caption.set_text(caption) self.event_loop() def set_current_file(self, fname): from pudb.source_view import SourceLine, format_source fname = self.debugger.canonic(fname) if self.shown_file != fname: if fname == "<string>": self.source[:] = [SourceLine(self, fname)] else: breakpoints = self.debugger.get_file_breaks(fname) try: from linecache import getlines lines = getlines(fname) from pudb.lowlevel import detect_encoding source_enc, _ = detect_encoding(iter(lines).next) decoded_lines = [] for l in lines: if hasattr(l, "decode"): decoded_lines.append(l.decode(source_enc)) else: decoded_lines.append(l) self.source[:] = format_source(self, decoded_lines, set(breakpoints)) except: from traceback import format_exception import sys self.message("Could not load source file '%s':\n\n%s" % ( fname, "".join(format_exception(*sys.exc_info()))), title="Source Code Load Error") self.source[:] = [SourceLine(self, "Error while loading '%s'." % fname)] self.shown_file = fname self.current_line = None def show_line(self, line, fname=None): chaged_file = False if fname is not None: changed_file = self.shown_file != fname self.set_current_file(fname) line -= 1 if line >= 0 and line < len(self.source): self.source_list.set_focus(line) if changed_file: self.source_list.set_focus_valign("middle") def set_current_line(self, line, fname): if self.current_line is not None: self.current_line.set_current(False) self.show_line(line, fname) line -= 1 if line >= 0 and line < len(self.source): self.current_line = self.source[line] self.current_line.set_current(True) def update_var_view(self, locals=None, globals=None): if locals is None: locals = self.debugger.curframe.f_locals if globals is None: globals = self.debugger.curframe.f_globals from pudb.var_view import make_var_view self.locals[:] = make_var_view( self.get_frame_var_info(read_only=True), locals, globals) def _get_bp_list(self): return [bp for fn, bp_lst in self.debugger.get_all_breaks().iteritems() for lineno in bp_lst for bp in self.debugger.get_breaks(fn, lineno) if not bp.temporary] def _format_fname(self, fname): from os.path import dirname, basename name = basename(fname) if name == "__init__.py": name = "..."+dirname(fname)[-10:]+"/"+name return name def update_breakpoints(self): self.bp_walker[:] = [ BreakpointFrame(self.debugger.current_bp == (bp.file, bp.line), self._format_fname(bp.file), bp.line) for bp in self._get_bp_list()] def update_stack(self): def make_frame_ui(frame_lineno): frame, lineno = frame_lineno code = frame.f_code class_name = None if code.co_argcount and code.co_varnames[0] == "self": try: class_name = frame.f_locals["self"].__class__.__name__ except: pass return StackFrame(frame is self.debugger.curframe, code.co_name, class_name, self._format_fname(code.co_filename), lineno) from pudb import CONFIG if CONFIG["current_stack_frame"] == "top": self.stack_walker[:] = [make_frame_ui(fl) for fl in self.debugger.stack[::-1]] elif CONFIG["current_stack_frame"] == "bottom": self.stack_walker[:] = [make_frame_ui(fl) for fl in self.debugger.stack] else: raise ValueError("invalid value for 'current_stack_frame' pref") def show_exception(self, exc_type, exc_value, traceback): from traceback import format_exception self.message( "".join(format_exception( exc_type, exc_value, traceback)), title="Exception Occurred")