Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persistent watch expressions (continuation of #150) #661

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

Kevin-Prichard
Copy link

@Kevin-Prichard Kevin-Prichard commented Sep 29, 2024

UPDATE

There was a problem with determinism in the watch pane, caused by there being two containers for WatchExpressions: FrameVarInfo.watches (watches specific to one stack frame), and the file (~/.config/pudb/saved-watches-*) maintained by save_watches() -a list of global watches. This combination would occasionally cause all watches to be deleted from the file, because the prior rev sometimes read the watches from the FrameVarInfo.watches which almost never contained the same set of expressions as the file.

This behavior prompted me to consolidate all WatchExpression related code into one class (Watches in var_view.py) to provide a central manager of Watches, rather that behavior being dispersed to various points in the UI code. Easier to maintain, and control the behavior regardless of what code is invoking it.

Summary

Persistence of watch expressions. This pull incorporates and continues with #150's fine work. Mentioned in discussion #660 - Plugins (and persistent Watch expressions)

Details

  • merged lechat:save_load_watches into inducer:main, and it works almost perfectly
  • one tiny mod to var_view.py: to import pudb.debugger.CONFIG into local scope, to fix bug discovered after the merge
  • another tiny mod to debugger.py: call save_watches() after a watch expression is deleted by the user, making that item become de-persisted from disk. This fixes an issue where the delete key tends to do nothing in the variables Watch expression subpane, following merge of lechat:save_load_watches into inducer:main
  • the issue occurs because load_watches() is called every time make_var_view() is invoked by update_var_view(), which itself is invoked frequently -whenever the UI needs redrawing. So, the removal of an expression by the delete key lasts for only approximately two shakes of a lamb's tail
  • consolidated previous watch expression code from DebuggerUI, and save_watches & load_watches, into class Watches, centralizing pretty much all watches behavior into one API. Simplifies watch code, eliminated redundancies, cures the non-deterministic behavior caused by attaching WatcheExpressions to stack frames, making it easier to maintain
  • added tests for new code
  • changed config option "persist_watches" to default to True, because why not

Tests

  • added WatchExpressionTests and WatchesTests to pudb/test/test_var_view.py, to cover new watch expression code
  • I would like to add tests for pudb that exercise it through urwid's interfaces, so that the watches pane can be tested from top-to-bottom; I am learning how to do this and will likely submit it in a future pull

@lucasgonze
Copy link

Given that the checks have been in an "expected - waiting" state for two weeks, It seems like the CI is hung.

@inducer
Copy link
Owner

inducer commented Oct 18, 2024

No, sorry, that was me. I needed to click "Approve".

@inducer
Copy link
Owner

inducer commented Oct 18, 2024

(But I've been a bit preoccupied with the whole thing being quite broken on 3.13: #664)

lechat and others added 13 commits October 18, 2024 13:52
…covered bug after merging lechat:save_load_watches into inducer:main
…d by the user, making that item become de-persisted. This fixes an issue where the delete key tends to do nothing in the variables Watch expression subpane, following merge of lechat:save_load_watches into inducer:main. The issue occurs because load_watches() is called every time make_var_view() is invoked by update_var_view(), which itself is invoked frequently -whenever the UI needs redrawing. So, the removal of an expression by the delete key lasts for only approximately two shakes of a lamb's tail.
…code into new class Watches, acting as global container

  - this resolves the previous dual, competing watch sources: the list FrameVarInfo.watches, plus the file maintained by save_watches() and load_watches().  FrameVarInfo.watches caused apparent non-determinism in the Watches pane, because it was associated with a particular stack frame, so whatever was in .watches on a particular frame would get displayed while the debugger was in that state, but would disappear from the pane when the execution point caused that frame to be pushed or popped.  So, in removing FrameVarInfo.watches and consolidating all WatchExpression storage into the single, global container Watches, the watches pane remains consistent no matter the current frame.  Caveat that there can be expressions which won't evaluate in the current frame context, of course.  That's the trade-off, and I hope that makes sense to other developers.
…and has methods; add some type checking; use dict.get for CONFIG instead of direct item ref.

- pudb/test/test_var_view.py: add tests for classes WatchExpression and Watches
@Kevin-Prichard
Copy link
Author

@inducer I have fixed the ruff and pylint issues within my PR branch, ready for another go

@Kevin-Prichard
Copy link
Author

What can I do to help move this to acceptance, @inducer ? I think the workflow needs approval, for one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants