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:
co-authored by
Claude Opus 4.7
parent
a4e6125d28
commit
665cb9b1eb
@@ -73,7 +73,7 @@ async def test_read_only_token_filters_write_tools(nc_mcp_login_flow_client_read
|
||||
assert len(result.tools) > 0
|
||||
|
||||
tool_names = [tool.name for tool in result.tools]
|
||||
logger.info(f"Read-only token sees {len(tool_names)} tools")
|
||||
logger.info("Read-only token sees %s tools", len(tool_names))
|
||||
|
||||
# Verify read tools are present (only for apps with :read scopes)
|
||||
# Read-only token has: notes.read, calendar.read, contacts.read,
|
||||
@@ -104,8 +104,8 @@ async def test_read_only_token_filters_write_tools(nc_mcp_login_flow_client_read
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"✅ Read-only token properly filters tools: {len(tool_names)} read tools visible, "
|
||||
f"write tools hidden"
|
||||
"✅ Read-only token properly filters tools: %s read tools visible, write tools hidden",
|
||||
len(tool_names),
|
||||
)
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ async def test_write_only_token_filters_read_tools(nc_mcp_login_flow_client_writ
|
||||
assert len(result.tools) > 0
|
||||
|
||||
tool_names = [tool.name for tool in result.tools]
|
||||
logger.info(f"Write-only token sees {len(tool_names)} tools")
|
||||
logger.info("Write-only token sees %s tools", len(tool_names))
|
||||
|
||||
# Verify write tools are present
|
||||
# Write-only token has: notes.write, calendar.write, contacts.write,
|
||||
@@ -153,8 +153,8 @@ async def test_write_only_token_filters_read_tools(nc_mcp_login_flow_client_writ
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"✅ Write-only token properly filters tools: {len(tool_names)} write tools visible, "
|
||||
f"read tools hidden"
|
||||
"✅ Write-only token properly filters tools: %s write tools visible, read tools hidden",
|
||||
len(tool_names),
|
||||
)
|
||||
|
||||
|
||||
@@ -171,8 +171,8 @@ async def test_full_access_token_shows_all_tools(nc_mcp_login_flow_client_full_a
|
||||
assert len(result.tools) > 0
|
||||
|
||||
tool_names = [tool.name for tool in result.tools]
|
||||
logger.info(f"Full access token sees {len(tool_names)} tools")
|
||||
logger.info(f"Tools: {sorted(tool_names)}")
|
||||
logger.info("Full access token sees %s tools", len(tool_names))
|
||||
logger.info("Tools: %s", sorted(tool_names))
|
||||
|
||||
# Verify both read and write tools are present
|
||||
# Full access has all *read and *write scopes
|
||||
@@ -197,7 +197,7 @@ async def test_full_access_token_shows_all_tools(nc_mcp_login_flow_client_full_a
|
||||
assert len(tool_names) >= 90
|
||||
|
||||
logger.info(
|
||||
f"✅ Full access token sees all tools: {len(tool_names)} total (read + write)"
|
||||
"✅ Full access token sees all tools: %s total (read + write)", len(tool_names)
|
||||
)
|
||||
|
||||
|
||||
@@ -415,7 +415,8 @@ async def test_jwt_with_no_custom_scopes_returns_zero_tools(
|
||||
|
||||
tool_names = [tool.name for tool in result.tools]
|
||||
logger.info(
|
||||
f"JWT token with no custom scopes sees {len(tool_names)} tools (should be 7 auth tools)"
|
||||
"JWT token with no custom scopes sees %s tools (should be 7 auth tools)",
|
||||
len(tool_names),
|
||||
)
|
||||
|
||||
# Only auth/provisioning tools should be visible (they require 'openid' scope)
|
||||
@@ -435,8 +436,8 @@ async def test_jwt_with_no_custom_scopes_returns_zero_tools(
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"✅ JWT token with only openid scope correctly shows {len(tool_names)} auth tools, "
|
||||
"resource tools filtered out"
|
||||
"✅ JWT token with only openid scope correctly shows %s auth tools, resource tools filtered out",
|
||||
len(tool_names),
|
||||
)
|
||||
|
||||
|
||||
@@ -457,7 +458,7 @@ async def test_jwt_consent_scenarios_read_only(nc_mcp_login_flow_client_read_onl
|
||||
assert len(result.tools) > 0
|
||||
|
||||
tool_names = [tool.name for tool in result.tools]
|
||||
logger.info(f"JWT with nc:read consent sees {len(tool_names)} tools")
|
||||
logger.info("JWT with nc:read consent sees %s tools", len(tool_names))
|
||||
|
||||
# Verify read tools are present
|
||||
read_tools = ["nc_notes_get_note", "nc_notes_search_notes", "nc_webdav_read_file"]
|
||||
@@ -474,7 +475,8 @@ async def test_jwt_consent_scenarios_read_only(nc_mcp_login_flow_client_read_onl
|
||||
assert tool not in tool_names, f"Write tool {tool} should be filtered out"
|
||||
|
||||
logger.info(
|
||||
f"✅ JWT with nc:read consent: {len(tool_names)} read tools visible, write tools filtered"
|
||||
"✅ JWT with nc:read consent: %s read tools visible, write tools filtered",
|
||||
len(tool_names),
|
||||
)
|
||||
|
||||
|
||||
@@ -495,7 +497,7 @@ async def test_jwt_consent_scenarios_write_only(nc_mcp_login_flow_client_write_o
|
||||
assert len(result.tools) > 0
|
||||
|
||||
tool_names = [tool.name for tool in result.tools]
|
||||
logger.info(f"JWT with nc:write consent sees {len(tool_names)} tools")
|
||||
logger.info("JWT with nc:write consent sees %s tools", len(tool_names))
|
||||
|
||||
# Verify write tools are present
|
||||
write_tools = [
|
||||
@@ -512,7 +514,8 @@ async def test_jwt_consent_scenarios_write_only(nc_mcp_login_flow_client_write_o
|
||||
assert tool not in tool_names, f"Read-only tool {tool} should be filtered out"
|
||||
|
||||
logger.info(
|
||||
f"✅ JWT with nc:write consent: {len(tool_names)} write tools visible, read-only tools filtered"
|
||||
"✅ JWT with nc:write consent: %s write tools visible, read-only tools filtered",
|
||||
len(tool_names),
|
||||
)
|
||||
|
||||
|
||||
@@ -533,7 +536,7 @@ async def test_jwt_consent_scenarios_full_access(nc_mcp_login_flow_client_full_a
|
||||
assert len(result.tools) > 0
|
||||
|
||||
tool_names = [tool.name for tool in result.tools]
|
||||
logger.info(f"JWT with full consent sees {len(tool_names)} tools")
|
||||
logger.info("JWT with full consent sees %s tools", len(tool_names))
|
||||
|
||||
# Verify both read and write tools are present
|
||||
read_tools = ["nc_notes_get_note", "nc_webdav_read_file"]
|
||||
@@ -549,7 +552,7 @@ async def test_jwt_consent_scenarios_full_access(nc_mcp_login_flow_client_full_a
|
||||
assert len(tool_names) >= 90, f"Expected 90+ tools but got {len(tool_names)}"
|
||||
|
||||
logger.info(
|
||||
f"✅ JWT with full consent: {len(tool_names)} tools visible (all read + write)"
|
||||
"✅ JWT with full consent: %s tools visible (all read + write)", len(tool_names)
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user