Skip to content

Commit 92e8ab5

Browse files
fix(utils): narrow bare except Exception in merge_json_files (#4189)
merge_json_files's read of the existing JSON file caught bare `Exception` around `json5.load`, so a real bug there (e.g. a `TypeError`/`AttributeError`) was silently treated the same as a normal parse failure -- `None` returned, existing settings preserved untouched, nothing surfaced unless `verbose`. Only `OSError` (inaccessible file) and `ValueError` (malformed JSON5 -- json5's decode error is a `ValueError` subclass) are expected outcomes here; anything else should propagate. Same bug, same fix shape, as the caller `handle_vscode_settings`, whose own bare `except Exception` was just narrowed to `(OSError, ValueError, KeyError)` in commit 16f4577 (PR #3844) with the same rationale ("let programming errors like TypeError or AttributeError propagate"). That PR's own regression test monkeypatched `merge_json_files` to prove the caller's narrowing works; this fixes and tests the callee itself, which still had the original bare-except bug. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 7eee05d commit 92e8ab5

2 files changed

Lines changed: 27 additions & 1 deletion

File tree

‎src/specify_cli/_utils.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ def merge_json_files(existing_path: Path, new_content: Any, verbose: bool = Fals
249249
except FileNotFoundError:
250250
# Handle race condition where file is deleted after exists() check
251251
exists = False
252-
except Exception as e:
252+
except (OSError, ValueError) as e:
253253
if verbose:
254254
console.print(f"[yellow]Warning: Could not read or parse existing JSON in {existing_path.name} ({e}).[/yellow]")
255255
# Skip merge to preserve existing file if unparseable or inaccessible (e.g. PermissionError)

‎tests/test_merge.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,29 @@ def test_handle_vscode_settings_propagates_programming_errors(tmp_path):
212212
)
213213
finally:
214214
utils_mod.merge_json_files = original_merge
215+
216+
217+
def test_merge_json_files_propagates_programming_errors(tmp_path, monkeypatch):
218+
"""Unexpected programming errors reading the existing file must propagate.
219+
220+
``merge_json_files``'s own read of the existing JSON file caught bare
221+
``Exception`` around ``json5.load``, so a real bug there (e.g. a
222+
``TypeError``) was silently treated the same as a normal parse failure --
223+
``None`` returned, existing settings preserved, nothing logged unless
224+
``verbose``. Only ``OSError`` (inaccessible file) and ``ValueError``
225+
(malformed JSON5 -- json5's decode error is a ``ValueError`` subclass)
226+
are expected outcomes here; anything else must propagate, matching the
227+
narrowing already applied to the caller, ``handle_vscode_settings``.
228+
"""
229+
existing_file = tmp_path / "settings.json"
230+
existing_file.write_text('{"a": 1}\n', encoding="utf-8")
231+
232+
import specify_cli._utils as utils_mod
233+
234+
def _boom(*_a, **_kw):
235+
raise TypeError("boom")
236+
237+
monkeypatch.setattr(utils_mod.json5, "load", _boom)
238+
239+
with pytest.raises(TypeError):
240+
merge_json_files(existing_file, {"b": 2})

0 commit comments

Comments
 (0)