Repository navigation
Add revert buttons to key properties to overwrite action defaults - #54
Merged
Merged
Conversation
nazbert
added a commit
to nazbert/Deckard
that referenced
this pull request
Jul 12, 2026
remove_old_cache_files had three startup hazards:
- a legacy index entry with no "date" popped the entry and then still
fell into `time.time() - None` -- TypeError out of StoreCache.__init__,
taking the whole store backend down at startup
- an entry whose cache file was already gone was skipped but never
dropped, so it survived every future pass (immortal index entry)
- os.remove was unguarded against the exists()/remove() race and EPERM
Legacy dateless entries now fall back to the content clocks ("fetched",
then file mtime -- the same order get_fetched_date uses), missing-path
entries are purged from the index, and a failed remove is logged and
retried on the next pass instead of raising.
Red-checked by tests/scenario_store_cache_eviction.py (the dateless case
reproduced the startup TypeError verbatim before the fix).
Refs StreamController#54
nazbert
added a commit
to nazbert/Deckard
that referenced
this pull request
Jul 12, 2026
…out of get_web_image get_remote_file never forwarded data_type to the StoreCache calls, so a binary fetch (data_type="content", e.g. every thumbnail) was indexed under the same "text" cache string as a text fetch of that path -- one cache file shared by both, opened "r" or "rb" depending on who asked first. The key now carries the data type end to end (is_cached / open_cache_file / get_fetched_date / the write), including the failed-fetch stale fallback. get_web_image also wrapped its fetch and decode in bare `except:` clauses, which swallow asyncio.CancelledError -- a cancelled store load's image tasks completed with "no image" instead of cancelling. Both are now `except Exception` with the failure logged; cancellation propagates. Red-checked by tests/scenario_store_remote_datatype.py. Refs StreamController#54
nazbert
added a commit
to nazbert/Deckard
that referenced
this pull request
Jul 12, 2026
…four copies check_required_version existed as four near-identical copies (StorePage, PluginPage, StorePreview, PluginPreview), each comparing with a strict `min_version < app_version`: an asset requiring EXACTLY the running app version was flagged incompatible (red border). Only StorePreview's copy had any callers -- the StorePage/PluginPage variants (is_min_app_version gated, returning None otherwise) and PluginPreview's shadow were dead weight waiting to diverge. There is now one implementation, StoreData.is_min_app_version_satisfied (inclusive `<=`, fails open with a warning on an unparseable remote version string instead of raising out of the preview build). StorePreview delegates to it; the other three copies are deleted, with PluginPreview inheriting StorePreview's method. Red-checked by tests/scenario_store_version_gate.py (equality case returned False before the fix). Refs StreamController#54
nazbert
added a commit
to nazbert/Deckard
that referenced
this pull request
Jul 12, 2026
Dead code that could only crash if ever reached: - make_api_call read/wrote self.api_cache, which no __init__ ever created -- its sole caller was the unreachable tail of get_stargazers (behind an unconditional `return 0`). Both the method and the dead tail are gone; the deactivated get_stargazers stub stays. - remove_old_manifest_cache / remove_old_attribution_cache iterated self.manifest_cache / self.attribution_cache, attributes that have never existed, and had zero callers. - the unused async_lru (alru_cache) import, the now-orphaned datetime import, and clone_repo's commented make_api_call block. Refs StreamController#54
nazbert
added a commit
to nazbert/Deckard
that referenced
this pull request
Jul 12, 2026
uninstall_plugin iterated gl.page_manager.pages directly (transferred from StreamController#56): the list() copies didn't make it safe -- load_page, discard_controller and clear_old_cached_pages mutate that dict under _pages_lock from other threads, so the unlocked iteration could race a concurrent insert/evict mid-teardown. PageManagerBackend grows all_cached_pages(), the all-controllers sibling of pages_for_controller(): a snapshot of every cached Page taken under _pages_lock, iterated lock-free by the caller (page teardown may invoke plugin hooks, so the lock must not be held across it). uninstall_plugin now uses it. Refs StreamController#54
nazbert
added a commit
to nazbert/Deckard
that referenced
this pull request
Jul 12, 2026
DynamicFlowBox.show_range set recycled children VISIBLE synchronously (and it is also called from chooser build threads) while binding each child's new asset via a separate GLib.idle_add. A click in that gap activated the PREVIOUS page's asset -- or a fresh placeholder's None asset, a TypeError in on_child_activated -- and a child selected on the old page kept its GTK selection while already showing a different asset (phantom selection across pages/filters). The whole rebind now runs as ONE main-loop callback (_apply_range): unselect_all, then bind-THEN-show per child, then hide leftovers and set nav sensitivity. Input events are dispatched by the same main loop, so a half-rebound pool is never clickable. get_items_to_show moved inside the callback too -- filter/sort funcs read GTK state (search entry, toggles) and now always do so on the main thread. With the factory running on the main loop, the preview setters (AssetPreview.set_asset and the wallpaper/SD+/icon pack equivalents) bind text/image directly instead of deferring through idle_add, which left a just-shown child displaying the previous item's name/thumbnail for a frame. Red-checked by tests/scenario_assetui_recycler.py (lands with the build_finished fix in the next commit; its gap assertions reproduced the visible-with-None-asset state verbatim against this code). Refs StreamController#54
nazbert
added a commit
to nazbert/Deckard
that referenced
this pull request
Jul 12, 2026
CustomAssetChooser.build() flipped build_finished=True BEFORE draining build_task_finished_tasks, with no lock against show_for_path: a caller that read the flag as False could append its deferred task AFTER the (only) drain had already snapshotted the queue. The task sat in the list forever -- the Asset Manager opened without preselecting the requested path. Flag flip + queue snapshot now happen atomically under _build_tasks_lock (_finish_build); show_for_path checks the flag and appends under the same lock, so a task either makes the snapshot or sees build_finished True and dispatches directly -- never stranded in between. Tasks still run outside the lock. tests/scenario_assetui_recycler.py deterministically replays the loser interleaving (append gated mid-flight while the build finishes) plus an enqueue-storm stress, and carries the red-checked assertions for the previous commit's recycler bind gap. Refs StreamController#54
nazbert
added a commit
to nazbert/Deckard
that referenced
this pull request
Jul 12, 2026
…e runtime loader is_min_app_version_satisfied compared raw parsed versions, while the runtime plugin loader (PluginBase.is_minimum_version_ok via _get_parsed_base_version) strips to base_version. On a pre-release build these diverged: running 1.5.0-beta.15, a store asset requiring the release 1.5.0 displayed as incompatible (1.5.0b15 < 1.5.0) while the loader would load it fine (base 1.5.0 == base 1.5.0) -- the badge lied about what installing would actually do. Strip both sides to base_version before the inclusive compare, matching the runtime gate. Replicated locally (two-line base_version strip) rather than importing PluginBase: StoreData is a leaf module that already defers `import globals` to stay cycle-free, and pulling in the plugin-manager import graph would reintroduce that risk. scenario_store_version_gate gains an assertion that the store verdict equals the runtime gate's across suffixed (pre/post) and plain-release min versions; red-checked against the raw compare (diverged on min=1.5.0 under the 1.5.0-beta.15 build). Refs StreamController#54
nazbert
added a commit
to nazbert/Deckard
that referenced
this pull request
Jul 12, 2026
…ueue race IconPackChooserStack carried the same unlocked flag-vs-queue race that was just fixed for the custom-asset chooser (!30 / _finish_build), in its subtler two-flag form: the pack chooser and the icon chooser each build on their own worker thread, set their own build_finished, and call on_load_finished. show_for_path checked get_is_build_finished() (both flags) then appended unlocked -- a caller that read a flag as False could append its deferred task after the draining thread snapshotted the queue, stranding it (the Asset Manager opened without preselecting the requested icon). And because on_load_finished runs from BOTH threads, the old copy()-and-remove() drain could double-run or double-remove a task across a concurrent entry. Apply the same pattern: flag-check + append and snapshot + clear are now serialized under one lock (_loads_lock); tasks run OUTSIDE the lock (they recurse into show_for_path, which takes the same lock). Extends tests/scenario_assetui_recycler.py with the icon-pack race (Refs StreamController#54): the raced-defer interleave (gate-based, no sleep) and a deterministic two-thread concurrent-drain that forces the double-run. Both red-checked against the pre-fix Stack.py; full harness 35/35.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add revert buttons to key properties to overwrite action defaults.
Implementation of #49