Skip to content

Commit bd4780a

Browse files
tobixenclaude
andcommitted
chore: log the per-object fallback loads instead of swallowing them silently
`_batch_load_objects()` and its async twin fall back to loading objects one at a time when the calendar-multiget REPORT fails. The fallback swallowed every per-object error with a bare `except Exception: pass`, so a server that failed both the batch and the individual load left one error-level line about the batch and no trace at all of what happened to each object. github-code-quality flagged both handlers - an empty except with neither a log nor an explanatory comment - on #694 The swallowing itself is the documented contract: the caller filters on is_loaded() afterwards, and one unfetchable object must not abort the rest. So the behaviour is unchanged and both handlers now say that in a comment, plus a debug line naming the URL. Debug rather than warning on purpose - the batch failure above is already logged at error level, and a large batch would otherwise emit a wall of warnings for a condition the caller is expected to handle. The docstring said "silently swallowing"; it now says what actually happens. Prompt: please fix the four findings [of the 17 bot findings on the PR, the four judged real: the two workflow permission blocks in the previous commit, and these two empty excepts] Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent ba4a66f commit bd4780a

1 file changed

Lines changed: 13 additions & 4 deletions

File tree

caldav/collection.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,8 +1434,9 @@ def _batch_load_objects(self, objects: list) -> None:
14341434
"""Load unloaded objects from the list in a single calendar-multiget REPORT.
14351435
14361436
Already-loaded objects are skipped. If the REPORT fails, falls back to
1437-
individual obj.load(only_if_unloaded=True) calls per object, silently
1438-
swallowing per-object errors so callers can filter on is_loaded() afterward.
1437+
individual obj.load(only_if_unloaded=True) calls per object; a per-object
1438+
failure is logged at debug level and otherwise swallowed, so callers can
1439+
filter on is_loaded() afterward.
14391440
"""
14401441
unloaded = [o for o in objects if not o.is_loaded()]
14411442
if not unloaded:
@@ -1448,7 +1449,13 @@ def _batch_load_objects(self, objects: list) -> None:
14481449
try:
14491450
obj.load(only_if_unloaded=True)
14501451
except Exception:
1451-
pass
1452+
## Deliberate: this method's contract is that the caller
1453+
## filters on is_loaded() afterwards, so one object that
1454+
## cannot be fetched must not abort the rest. Logged at
1455+
## debug rather than warning because the batch failure above
1456+
## is already an error-level line, and a large batch would
1457+
## otherwise produce a wall of warnings.
1458+
log.debug("Individual load failed for %s", obj.url, exc_info=True)
14521459

14531460
async def _async_batch_load_objects(self, objects: list) -> None:
14541461
"""Async version of _batch_load_objects.
@@ -1474,7 +1481,9 @@ async def _async_batch_load_objects(self, objects: list) -> None:
14741481
if inspect.isawaitable(load_result):
14751482
await load_result
14761483
except Exception:
1477-
pass
1484+
## Same contract as the sync twin above: skip and let the
1485+
## caller filter on is_loaded().
1486+
log.debug("Individual load failed for %s", obj.url, exc_info=True)
14781487

14791488
def calendar_multiget(self, *largs, **kwargs):
14801489
"""

0 commit comments

Comments
 (0)