diff --git a/pudb/debugger.py b/pudb/debugger.py index 01f19bb3..706abafc 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -1006,14 +1006,59 @@ def change_var_state(w, size, key): elif key == "m": iinfo.show_methods = not iinfo.show_methods elif key == "delete": - fvi = self.get_frame_var_info(read_only=False) - try: - fvi.watches.remove(var.watch_expr) - except ValueError: - pass + self.delete_watch(var.watch_expr) self.update_var_view(focus_index=focus_index) + def _watch_editors(watch_expr): + """ + Create widgets for editing the given expression. + """ + def set_watch_scope(radio_button, new_state, user_data): + if new_state: + watch_expr.set_scope(user_data) + + def set_watch_method(radio_button, new_state, user_data): + if new_state: + watch_expr.set_method(user_data) + + watch_edit = urwid.Edit([("label", "Watch expression: ")], + watch_expr.expression) + + scope_rbs = [] + urwid.RadioButton( + group=scope_rbs, + label="Local: watch in current frame only", + state=watch_expr.scope == "local", + on_state_change=set_watch_scope, + user_data="local", + ) + urwid.RadioButton( + group=scope_rbs, + label="Global: watch in all frames", + state=watch_expr.scope == "global", + on_state_change=set_watch_scope, + user_data="global", + ) + + method_rbs = [] + urwid.RadioButton( + group=method_rbs, + label="Expression: always re-evaluate the expression", + state=watch_expr.method == "expression", + on_state_change=set_watch_method, + user_data="expression", + ) + urwid.RadioButton( + group=method_rbs, + label="Reference: evaluate once, watch the resulting value", + state=watch_expr.method == "reference", + on_state_change=set_watch_method, + user_data="reference", + ) + + return watch_edit, scope_rbs, method_rbs + def edit_inspector_detail(w, size, key): var, pos = self.var_list._w.get_focus() @@ -1029,13 +1074,17 @@ def edit_inspector_detail(w, size, key): ] if var.watch_expr is not None: - watch_edit = urwid.Edit([ - ("label", "Watch expression: ") - ], var.watch_expr.expression) + watch_edit, scope_rbs, method_rbs = _watch_editors(var.watch_expr) id_segment = [ - urwid.AttrMap(watch_edit, "input", "focused input"), - urwid.Text(""), - ] + urwid.AttrMap(watch_edit, "input", "focused input"), + urwid.Text(""), + urwid.Text("Scope:"), + ] + scope_rbs + [ + urwid.Text(""), + urwid.Text("Method:"), + ] + method_rbs + [ + urwid.Text("") + ] buttons.extend([None, ("Delete", "del")]) @@ -1123,63 +1172,18 @@ def edit_inspector_detail(w, size, key): iinfo.access_level = "all" if var.watch_expr is not None: - new_expression = watch_edit.get_edit_text() - if new_expression != var.watch_expr.expression: - var.watch_expr.set_expression(new_expression) + var.watch_expr.set_expression(watch_edit.get_edit_text()) + self.change_watch_scope(var.watch_expr, fvi) elif result == "del": - try: - fvi.watches.remove(var.watch_expr) - except ValueError: - pass + self.delete_watch(var.watch_expr, fvi) self.update_var_view() def insert_watch(w, size, key): from pudb.var_view import WatchExpression watch_expr = WatchExpression() - - def set_watch_scope(radio_button, new_state, user_data): - if new_state == True: - watch_expr.scope = user_data - - def set_watch_method(radio_button, new_state, user_data): - if new_state == True: - watch_expr.method = user_data - - watch_edit = urwid.Edit([("label", "Watch expression: ")]) - - scope_rbs = [] - urwid.RadioButton( - group=scope_rbs, - label="Local: watch in current frame only", - state=True, - on_state_change=set_watch_scope, - user_data="local", - ) - urwid.RadioButton( - group=scope_rbs, - label="Global: watch in all frames", - state=False, - on_state_change=set_watch_scope, - user_data="global", - ) - - method_rbs = [] - urwid.RadioButton( - group=method_rbs, - label="Expression: always re-evaluate the expression", - state=True, - on_state_change=set_watch_method, - user_data="expression", - ) - urwid.RadioButton( - group=method_rbs, - label="Reference: evaluate once, watch the resulting value", - state=False, - on_state_change=set_watch_method, - user_data="reference", - ) + watch_edit, scope_rbs, method_rbs = _watch_editors(watch_expr) if self.dialog( urwid.ListBox(urwid.SimpleListWalker([ diff --git a/pudb/var_view.py b/pudb/var_view.py index 623c0889..26c06e39 100644 --- a/pudb/var_view.py +++ b/pudb/var_view.py @@ -216,9 +216,17 @@ def eval(self, frame_globals, frame_locals): return self._value def set_expression(self, expression): - self.expression = expression + if expression != self.expression: + self.expression = expression + self._value = self.NOT_EVALUATED + + def set_method(self, method): + self.method = method self._value = self.NOT_EVALUATED + def set_scope(self, scope): + self.scope = scope + class WatchEvalError: def __str__(self): @@ -803,13 +811,31 @@ def get_frame_var_info(self, read_only, ssid=None): else: return self.frame_var_info.setdefault(ssid, FrameVarInfo()) - def add_watch(self, watch_expr: WatchExpression): + def add_watch(self, watch_expr: WatchExpression, fvi=None): if watch_expr.scope == "local": - fvi = self.get_frame_var_info(read_only=False) + if fvi is None: + fvi = self.get_frame_var_info(read_only=False) fvi.watches.append(watch_expr) elif watch_expr.scope == "global": self.global_watches.append(watch_expr) + def delete_watch(self, watch_expr: WatchExpression, fvi=None): + if fvi is None: + fvi = self.get_frame_var_info(read_only=False) + # Need to delete both locally and globally- could be in either! + # (The watch_expr.scope attribute may have changed) + try: + fvi.watches.remove(watch_expr) + except ValueError: + pass + try: + self.global_watches.remove(watch_expr) + except ValueError: + pass + + def change_watch_scope(self, watch_expr, fvi=None): + self.delete_watch(watch_expr, fvi) + self.add_watch(watch_expr, fvi) # }}}