Skip to content

Implementation of Dependency.release() and Depedency.reopen() - #490

Open
superhellth wants to merge 9 commits into
pydoit:masterfrom
superhellth:feat/dep-release
Open

superhellth wants to merge 9 commits into
pydoit:masterfrom
superhellth:feat/dep-release

Conversation

@superhellth

Copy link
Copy Markdown

Motivation:
Needed for the planned TUI plugin. Ensure TUI can stay open in one terminal, while doit jobs run in another. Generic interface provided for plugins to use.

Provided interface [new]:

  • has_changes() -> bool: True if the backend holds changes not yet written to the file. It is always False once the DB is closed.
  • release(discard=False): closes the handle without saving. It raises UnsavedChangesError if has_changes() is true and discard is not set. When it raises, the DB stays open and close() still saves.
  • reopen(discard=False): calls release(discard), then reopens the file.

Main Cost:
To make that check reliable, all three backends (JsonDB, DbmDB, SqliteDB) now buffer every mutation in memory and touch the file only in dump(). A read-only user of the DB never writes to it or takes a write lock.

Changes:

  • has_changes() on every backend, backed by a sticky _changed flag. set() and remove_all() set it. remove() sets it only if the id is in the file or in the dirty set. Reads never set it.
  • UnsavedChangesError subclasses DatabaseException, so existing except DatabaseException handlers keep working. When it raises, the DB stays open and close() still saves.
  • Deferred removals in DbmDB and SqliteDB. remove() and remove_all() record _removed / _truncate and apply them in dump(). Before, they deleted from the file immediately, so release() could
    not undo them. DbmDB.dump() tolerates keys another process already removed.
  • SqliteDB.get() fix. It used to cache a missing row, so in_() then reported an id as present just because it had been read. It now caches only real rows, and in_() checks the file with a
    read-only query. This also fixes a misleading get_value() error for unknown task ids.
  • Plugin backends without has_changes() or release() still work. Dependency uses getattr and treats a missing method as "no changes" or "keep handle".

Testing:

  • New backend-parametrized suites cover has_changes(), the release guard, deferred removals, in_() after a read, and a sqlite3 no-write-lock check. They run on json, sqlite3, dbm.gnu,
    dbm.ndbm and dbm.dumb.
  • test_release_does_not_save now uses release(discard=True).
  • A get_status() regression test covers the checker_changed path. It fails if release(discard=True) is swapped for close().

superhellth and others added 9 commits September 20, 2026 11:29
Allow releasing the DB file handle without saving and re-reading the DB
afterwards. Needed by commands (e.g. plugins) that watch task state live
without persisting anything. Backends without release() keep working.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.

1 participant