refactor: convert f-string logging to lazy %-style format (G004)

Sweep all 1676 G004 violations across 112 files, converting
`logger.<level>(f"…{x}…")` to `logger.<level>("…%s…", x)`.

Why: ruff rule G004 was added to pyproject.toml to enforce lazy
%-style logging — defers formatting until the log level is enabled
and lets structured log tooling match the unformatted template.

Conversion preserves rendered output byte-for-byte:
- `{x}` → `%s` + `x`
- `{x!r}` / `{x!s}` / `{x!a}` → `%r` / `%s` / `%a`
- Format specs (`{x:.2f}`, `{x:>10}`) → `%s` + `format(x, 'spec')`
  (printf-style specs aren't 1:1 with Python format specs, so we
  delegate to `format()` to keep identical output)
- Literal `%` → `%%`
- Concatenated f-strings (`f"a {x} " "b"`) flattened
- Trailing kwargs (`exc_info=True`) preserved

Verified:
- `uv run ruff check --select G004` → 0 violations
- `uv run ty check -- nextcloud_mcp_server` → passes
- `uv run pytest tests/unit/` → 1010 passed

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-05-13 01:12:17 +02:00
co-authored by Claude Opus 4.7
parent a4e6125d28
commit 665cb9b1eb
112 changed files with 2534 additions and 1859 deletions
+102 -55
View File
@@ -155,7 +155,7 @@ async def get_last_indexed_timestamp(user_id: str) -> int | None:
)
num_points = len(points)
logger.info(f"Found {num_points} indexed notes in Qdrant for user {user_id}")
logger.info("Found %s indexed notes in Qdrant for user %s", num_points, user_id)
if points:
timestamps = [
@@ -165,14 +165,16 @@ async def get_last_indexed_timestamp(user_id: str) -> int | None:
]
max_timestamp = max(timestamps) if timestamps else 0
logger.info(
f"Max indexed_at: {max_timestamp}, timestamps sample: {timestamps[:3]}"
"Max indexed_at: %s, timestamps sample: %s",
max_timestamp,
timestamps[:3],
)
return int(max_timestamp) if max_timestamp > 0 else None
logger.info(f"No indexed notes found for user {user_id}")
logger.info("No indexed notes found for user %s", user_id)
return None
except Exception as e:
logger.warning(f"Failed to get last indexed timestamp: {e}", exc_info=True)
logger.warning("Failed to get last indexed timestamp: %s", e, exc_info=True)
return None
@@ -198,7 +200,7 @@ async def scanner_task(
user_id: User to scan
task_status: Status object for signaling task readiness
"""
logger.info(f"Scanner task started for user: {user_id}")
logger.info("Scanner task started for user: %s", user_id)
settings = get_settings()
# Signal that the task has started and is ready
@@ -215,7 +217,7 @@ async def scanner_task(
)
except Exception as e:
logger.error(f"Scanner error: {e}", exc_info=True)
logger.error("Scanner error: %s", e, exc_info=True)
# Sleep until next interval or wake event
try:
@@ -247,7 +249,10 @@ async def scan_user_documents(
scan_id = random.randint(1000, 9999)
logger.info(
f"[SCAN-{scan_id}] Starting scan for user: {user_id}, initial_sync={initial_sync}"
"[SCAN-%s] Starting scan for user: %s, initial_sync=%s",
scan_id,
user_id,
initial_sync,
)
with trace_operation(
@@ -266,7 +271,9 @@ async def scan_user_documents(
)
if prune_before:
logger.info(
f"[SCAN-{scan_id}] Using pruneBefore={prune_before} to optimize data transfer"
"[SCAN-%s] Using pruneBefore=%s to optimize data transfer",
scan_id,
prune_before,
)
# For deletion tracking, get all doc_ids in Qdrant (for incremental sync)
@@ -303,7 +310,7 @@ async def scan_user_documents(
if point.payload is not None and "doc_id" in point.payload
}
logger.debug(f"Found {len(indexed_doc_ids)} indexed documents in Qdrant")
logger.debug("Found %s indexed documents in Qdrant", len(indexed_doc_ids))
# Stream notes from Nextcloud and process immediately
note_count = 0
@@ -341,7 +348,8 @@ async def scan_user_documents(
doc_key = (user_id, doc_id)
if doc_key in _potentially_deleted:
logger.debug(
f"Document {doc_id} reappeared, removing from deletion grace period"
"Document %s reappeared, removing from deletion grace period",
doc_id,
)
del _potentially_deleted[doc_key]
@@ -368,14 +376,17 @@ async def scan_user_documents(
stale_threshold = get_settings().vector_sync_scan_interval * 5
if placeholder_age > stale_threshold:
logger.debug(
f"Found stale placeholder for note {doc_id} "
f"(age={placeholder_age:.1f}s), requeuing"
"Found stale placeholder for note %s (age=%ss), requeuing",
doc_id,
format(placeholder_age, ".1f"),
)
needs_indexing = True
else:
logger.debug(
f"Skipping note {doc_id} with recent placeholder "
f"(age={placeholder_age:.1f}s < {stale_threshold:.1f}s)"
"Skipping note %s with recent placeholder (age=%ss < %ss)",
doc_id,
format(placeholder_age, ".1f"),
format(stale_threshold, ".1f"),
)
if needs_indexing:
@@ -399,11 +410,11 @@ async def scan_user_documents(
queued += 1
# Log and record metrics after streaming
logger.info(f"[SCAN-{scan_id}] Found {note_count} notes for {user_id}")
logger.info("[SCAN-%s] Found %s notes for %s", scan_id, note_count, user_id)
record_vector_sync_scan(note_count)
if initial_sync:
logger.info(f"Sent {queued} documents for initial sync: {user_id}")
logger.info("Sent %s documents for initial sync: %s", queued, user_id)
return
# Check for deleted documents (in Qdrant but not in Nextcloud)
@@ -426,8 +437,10 @@ async def scan_user_documents(
if time_missing >= grace_period:
# Grace period elapsed, send for deletion
logger.info(
f"Document {doc_id} missing for {time_missing:.1f}s "
f"(>{grace_period:.1f}s grace period), sending deletion"
"Document %s missing for %ss (>%ss grace period), sending deletion",
doc_id,
format(time_missing, ".1f"),
format(grace_period, ".1f"),
)
await send_stream.send(
DocumentTask(
@@ -443,13 +456,16 @@ async def scan_user_documents(
del _potentially_deleted[doc_key]
else:
logger.debug(
f"Document {doc_id} still missing "
f"({time_missing:.1f}s/{grace_period:.1f}s grace period)"
"Document %s still missing (%ss/%ss grace period)",
doc_id,
format(time_missing, ".1f"),
format(grace_period, ".1f"),
)
else:
# First time missing, add to grace period tracking
logger.debug(
f"Document {doc_id} missing for first time, starting grace period"
"Document %s missing for first time, starting grace period",
doc_id,
)
_potentially_deleted[doc_key] = current_time
@@ -476,7 +492,7 @@ async def scan_user_documents(
if point.payload is not None and "doc_id" in point.payload
}
logger.debug(f"Found {len(indexed_file_ids)} indexed files in Qdrant")
logger.debug("Found %s indexed files in Qdrant", len(indexed_file_ids))
# Scan for tagged PDF files
file_count = 0
@@ -568,7 +584,9 @@ async def scan_user_documents(
file_key = (user_id, file_id)
if file_key in _potentially_deleted:
logger.debug(
f"File {file_path} (ID: {file_id}) reappeared, removing from deletion grace period"
"File %s (ID: %s) reappeared, removing from deletion grace period",
file_path,
file_id,
)
del _potentially_deleted[file_key]
@@ -595,14 +613,19 @@ async def scan_user_documents(
stale_threshold = get_settings().vector_sync_scan_interval * 5
if placeholder_age > stale_threshold:
logger.debug(
f"Found stale placeholder for file {file_path} (ID: {file_id}) "
f"(age={placeholder_age:.1f}s), requeuing"
"Found stale placeholder for file %s (ID: %s) (age=%ss), requeuing",
file_path,
file_id,
format(placeholder_age, ".1f"),
)
needs_indexing = True
else:
logger.debug(
f"Skipping file {file_path} (ID: {file_id}) with recent placeholder "
f"(age={placeholder_age:.1f}s < {stale_threshold:.1f}s)"
"Skipping file %s (ID: %s) with recent placeholder (age=%ss < %ss)",
file_path,
file_id,
format(placeholder_age, ".1f"),
format(stale_threshold, ".1f"),
)
if needs_indexing:
@@ -627,7 +650,7 @@ async def scan_user_documents(
file_queued += 1
logger.info(
f"[SCAN-{scan_id}] Found {file_count} tagged PDFs for {user_id}"
"[SCAN-%s] Found %s tagged PDFs for %s", scan_id, file_count, user_id
)
record_vector_sync_scan(file_count)
@@ -645,8 +668,10 @@ async def scan_user_documents(
if time_missing >= grace_period:
# Grace period elapsed, send for deletion
logger.info(
f"File ID {file_id} missing for {time_missing:.1f}s "
f"(>{grace_period:.1f}s grace period), sending deletion"
"File ID %s missing for %ss (>%ss grace period), sending deletion",
file_id,
format(time_missing, ".1f"),
format(grace_period, ".1f"),
)
await send_stream.send(
DocumentTask(
@@ -662,12 +687,13 @@ async def scan_user_documents(
else:
# First time missing, add to grace period tracking
logger.debug(
f"File ID {file_id} missing for first time, starting grace period"
"File ID %s missing for first time, starting grace period",
file_id,
)
_potentially_deleted[file_key] = current_time
except Exception as e:
logger.warning(f"Failed to scan tagged files for {user_id}: {e}")
logger.warning("Failed to scan tagged files for %s: %s", user_id, e)
queued += file_queued
@@ -683,7 +709,7 @@ async def scan_user_documents(
)
queued += news_queued
except Exception as e:
logger.warning(f"Failed to scan news items for {user_id}: {e}")
logger.warning("Failed to scan news items for %s: %s", user_id, e)
# Scan Deck cards
deck_queued = 0
@@ -697,14 +723,19 @@ async def scan_user_documents(
)
queued += deck_queued
except Exception as e:
logger.warning(f"Failed to scan deck cards for {user_id}: {e}")
logger.warning("Failed to scan deck cards for %s: %s", user_id, e)
if queued > 0:
logger.info(
f"Sent {queued} documents ({file_queued} files, {news_queued} news items, {deck_queued} deck cards) for incremental sync: {user_id}"
"Sent %s documents (%s files, %s news items, %s deck cards) for incremental sync: %s",
queued,
file_queued,
news_queued,
deck_queued,
user_id,
)
else:
logger.debug(f"No changes detected for {user_id}")
logger.debug("No changes detected for %s", user_id)
async def scan_news_items(
@@ -754,7 +785,7 @@ async def scan_news_items(
for point in points
if point.payload is not None and "doc_id" in point.payload
}
logger.debug(f"Found {len(indexed_item_ids)} indexed news items in Qdrant")
logger.debug("Found %s indexed news items in Qdrant", len(indexed_item_ids))
# Fetch all items (News app caps at ~200 per feed via auto-purge)
all_items = await nc_client.news.get_items(
@@ -762,7 +793,7 @@ async def scan_news_items(
type_=NewsItemType.ALL,
get_read=True,
)
logger.debug(f"[SCAN-{scan_id}] Found {len(all_items)} news items")
logger.debug("[SCAN-%s] Found %s news items", scan_id, len(all_items))
item_count = len(all_items)
nextcloud_item_ids: set[str] = set()
@@ -800,7 +831,8 @@ async def scan_news_items(
doc_key = (user_id, doc_id)
if doc_key in _potentially_deleted:
logger.debug(
f"News item {doc_id} reappeared, removing from deletion grace period"
"News item %s reappeared, removing from deletion grace period",
doc_id,
)
del _potentially_deleted[doc_key]
@@ -820,8 +852,9 @@ async def scan_news_items(
stale_threshold = settings.vector_sync_scan_interval * 5
if placeholder_age > stale_threshold:
logger.debug(
f"Found stale placeholder for news item {doc_id} "
f"(age={placeholder_age:.1f}s), requeuing"
"Found stale placeholder for news item %s (age=%ss), requeuing",
doc_id,
format(placeholder_age, ".1f"),
)
needs_indexing = True
@@ -844,7 +877,10 @@ async def scan_news_items(
queued += 1
logger.info(
f"[SCAN-{scan_id}] Found {item_count} news items (starred+unread) for {user_id}"
"[SCAN-%s] Found %s news items (starred+unread) for %s",
scan_id,
item_count,
user_id,
)
record_vector_sync_scan(item_count)
@@ -864,8 +900,10 @@ async def scan_news_items(
if time_missing >= grace_period:
logger.info(
f"News item {doc_id} missing for {time_missing:.1f}s "
f"(>{grace_period:.1f}s grace period), sending deletion"
"News item %s missing for %ss (>%ss grace period), sending deletion",
doc_id,
format(time_missing, ".1f"),
format(grace_period, ".1f"),
)
await send_stream.send(
DocumentTask(
@@ -880,7 +918,8 @@ async def scan_news_items(
del _potentially_deleted[doc_key]
else:
logger.debug(
f"News item {doc_id} missing for first time, starting grace period"
"News item %s missing for first time, starting grace period",
doc_id,
)
_potentially_deleted[doc_key] = current_time
@@ -932,11 +971,11 @@ async def scan_deck_cards(
for point in points
if point.payload is not None and "doc_id" in point.payload
}
logger.debug(f"Found {len(indexed_card_ids)} indexed deck cards in Qdrant")
logger.debug("Found %s indexed deck cards in Qdrant", len(indexed_card_ids))
# Fetch all boards
boards = await nc_client.deck.get_boards()
logger.debug(f"[SCAN-{scan_id}] Found {len(boards)} deck boards")
logger.debug("[SCAN-%s] Found %s deck boards", scan_id, len(boards))
card_count = 0
nextcloud_card_ids: set[str] = set()
@@ -949,7 +988,7 @@ async def scan_deck_cards(
# Skip deleted boards (soft delete: deletedAt > 0)
if board.deletedAt > 0:
logger.debug(f"[SCAN-{scan_id}] Skipping deleted board {board.id}")
logger.debug("[SCAN-%s] Skipping deleted board %s", scan_id, board.id)
continue
# Get stacks for this board
@@ -998,7 +1037,8 @@ async def scan_deck_cards(
doc_key = (user_id, doc_id)
if doc_key in _potentially_deleted:
logger.debug(
f"Deck card {doc_id} reappeared, removing from deletion grace period"
"Deck card %s reappeared, removing from deletion grace period",
doc_id,
)
del _potentially_deleted[doc_key]
@@ -1018,8 +1058,9 @@ async def scan_deck_cards(
stale_threshold = settings.vector_sync_scan_interval * 5
if placeholder_age > stale_threshold:
logger.debug(
f"Found stale placeholder for deck card {doc_id} "
f"(age={placeholder_age:.1f}s), requeuing"
"Found stale placeholder for deck card %s (age=%ss), requeuing",
doc_id,
format(placeholder_age, ".1f"),
)
needs_indexing = True
@@ -1043,7 +1084,10 @@ async def scan_deck_cards(
queued += 1
logger.info(
f"[SCAN-{scan_id}] Found {card_count} deck cards (non-archived) for {user_id}"
"[SCAN-%s] Found %s deck cards (non-archived) for %s",
scan_id,
card_count,
user_id,
)
record_vector_sync_scan(card_count)
@@ -1062,8 +1106,10 @@ async def scan_deck_cards(
if time_missing >= grace_period:
logger.info(
f"Deck card {doc_id} missing for {time_missing:.1f}s "
f"(>{grace_period:.1f}s grace period), sending deletion"
"Deck card %s missing for %ss (>%ss grace period), sending deletion",
doc_id,
format(time_missing, ".1f"),
format(grace_period, ".1f"),
)
await send_stream.send(
DocumentTask(
@@ -1078,7 +1124,8 @@ async def scan_deck_cards(
del _potentially_deleted[doc_key]
else:
logger.debug(
f"Deck card {doc_id} missing for first time, starting grace period"
"Deck card %s missing for first time, starting grace period",
doc_id,
)
_potentially_deleted[doc_key] = current_time