fix: stop save_options truncating config.json #10
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/config-save-crash"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Saving from the options or scripts tab replaced
config.jsonwith only thekeys the caller happened to pass, then crashed on the first key that was no
longer there.
cfgupdatereplacesconfig.jsonwholesale, sosave_optionsmerged itsargument onto
self.config_dictfirst. Nothing ever assignsconfig_dict--it is the empty class attribute throughout -- so the merge was a no-op and the
caller's partial dict became the entire file.
save_scriptspasses a single key, so setting up a provider manually reducedthe config to
{"_scripts": {}}(the provider combobox is empty at that point,hence the empty prefix).
save_optionsthen readtemp_config["ping"]andraised
KeyError, which PyQt5 turns into a process abort.Fix
Merge onto
config.settings, which is what the no-op merge was reaching for:load_configalready backfills it fromdefault_settings, so it is alwayscomplete.
Callers keep passing partial dicts, which
read_option_changeneeds anyway --a tristate checkbox reports neither 0 nor 2 and is deliberately omitted so it
keeps its stored value.
Verification
config.jsonwent from 311bytes to 16 (
{"_scripts": {}}) before the abort, confirming the write atline 1350 lands ahead of the
KeyErrorat 1365.config_dictacross the whole GUI module: one assignment (the emptyclass attribute) and one read (the merge). Nothing else touches it.
load_configbackfills every key indefault_settings, so the newmerge base cannot itself be partial.
Not covered by an automated test:
save_optionsis a method on the main Qtwindow and testing it would mean standing up the whole GUI. The behaviour is
exercised by the manual retest of provider setup.
Related fixes (found along the way, technically out of scope)
print(self.config_list)left inread_option_change.update_cmdin theCalledProcessErrorhandler is undefined and would raiseNameError. The handler is unreachable --dbus_callswallowsDBusExceptionand never raisesCalledProcessError-- so this was latent,but a dangling name in an error path is worth removing while here.
Saving anything from the options or scripts tab replaced config.json with just the handful of keys the caller happened to touch, then crashed on the first key that was no longer there. cfgupdate replaces config.json wholesale, so save_options merged its argument onto self.config_dict first. Nothing ever assigns config_dict -- it is the empty class attribute throughout -- so the merge was a no-op and whatever the caller passed became the entire file. save_scripts passes a single key, so setting up a provider manually reduced config.json to {"_scripts": {}}. save_options then read temp_config["ping"] and raised KeyError, which PyQt5 turns into an abort. Merging onto config.settings instead is what the no-op merge was reaching for: load_config already backfills it from default_settings, so it is always complete. Callers keep passing partial dicts, which is what read_option_change needs anyway -- a tristate checkbox reports neither 0 nor 2 and is deliberately left out so it keeps its stored value. Also drops a debug print of config_list and an undefined name in the CalledProcessError handler, which would have raised NameError had that handler been reachable -- dbus_call swallows DBusException and never raises CalledProcessError. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>