From 2e18a774f2f9918da504d3c9567f9f73da3499f3 Mon Sep 17 00:00:00 2001 From: Maxim Shulga Date: Sun, 2 Aug 2026 17:33:45 -0700 Subject: [PATCH] Fail the same way on JSON of an unexpected shape json.loads succeeding does not mean the result is a list of objects with an id. A dict, an entry without an id, or a scalar raised TypeError or KeyError, which escaped unhandled and defeated the point of the change. Both cases now raise DaemonError, and the original exception is chained so the cause is visible. --- dsc/client.py | 13 +++++++++++-- tests/test_commands.py | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/dsc/client.py b/dsc/client.py index 2965f74..62c56f0 100644 --- a/dsc/client.py +++ b/dsc/client.py @@ -281,9 +281,18 @@ class SeafileClient: try: libraries = json.loads(out.decode()) except (UnicodeDecodeError, ValueError) as err: - raise DaemonError(f"Cannot read the library list from seaf-cli: {err}") + raise DaemonError( + f"Cannot read the library list from seaf-cli: {err}" + ) from err - return {lib["id"] for lib in libraries} + # Valid JSON is not necessarily the expected shape, and an unexpected + # one must fail the same way rather than as a bare TypeError. + try: + return {lib["id"] for lib in libraries} + except (KeyError, TypeError) as err: + raise DaemonError( + f"Unexpected library list from seaf-cli: {err}" + ) from err def configure(self, args: argparse.Namespace, check_for_daemon: bool = True): need_restart = False diff --git a/tests/test_commands.py b/tests/test_commands.py index 48edf8f..082f16f 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -213,3 +213,25 @@ def test_local_libraries_reports_a_failed_command(client, monkeypatch): run_returning(monkeypatch, b"", returncode=1) with pytest.raises(DaemonError): client.get_local_libraries() + + +@pytest.mark.parametrize("payload", [ + b'{"name": "Docs"}', # an object where a list is expected + b'[{"name": "Docs"}]', # an entry with no id + b'["Docs"]', # entries that are not objects + b'[null]', + b'null', + b'42', +]) +def test_local_libraries_reports_unexpected_json_shapes(client, monkeypatch, payload): + """Valid JSON of the wrong shape must not escape as a raw TypeError.""" + run_returning(monkeypatch, payload) + with pytest.raises(DaemonError): + client.get_local_libraries() + + +def test_local_libraries_error_keeps_the_original_cause(client, monkeypatch): + run_returning(monkeypatch, b"not json at all") + with pytest.raises(DaemonError) as err: + client.get_local_libraries() + assert err.value.__cause__ is not None