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
@@ -29,18 +29,21 @@ async def test_category_change_cleans_up_old_attachments_directory(
|
||||
|
||||
try:
|
||||
# 1. Create note with initial category
|
||||
logger.info(f"Creating note '{note_title}' in category '{initial_category}'")
|
||||
logger.info("Creating note '%s' in category '%s'", note_title, initial_category)
|
||||
created_note = await nc_client.notes.create_note(
|
||||
title=note_title, content="Initial content", category=initial_category
|
||||
)
|
||||
note_id = created_note["id"]
|
||||
etag1 = created_note["etag"]
|
||||
logger.info(f"Note created with ID: {note_id}, Etag: {etag1}")
|
||||
logger.info("Note created with ID: %s, Etag: %s", note_id, etag1)
|
||||
time.sleep(1)
|
||||
|
||||
# 2. Add attachment (passing initial category)
|
||||
logger.info(
|
||||
f"Adding attachment '{attachment_filename}' to note {note_id} (in {initial_category})"
|
||||
"Adding attachment '%s' to note %s (in %s)",
|
||||
attachment_filename,
|
||||
note_id,
|
||||
initial_category,
|
||||
)
|
||||
upload_response = await nc_client.webdav.add_note_attachment(
|
||||
note_id=note_id,
|
||||
@@ -55,7 +58,8 @@ async def test_category_change_cleans_up_old_attachments_directory(
|
||||
|
||||
# 3. Verify attachment retrieval from initial category
|
||||
logger.info(
|
||||
f"Verifying attachment retrieval from initial category '{initial_category}'"
|
||||
"Verifying attachment retrieval from initial category '%s'",
|
||||
initial_category,
|
||||
)
|
||||
retrieved_content1, _ = await nc_client.webdav.get_note_attachment(
|
||||
note_id=note_id, filename=attachment_filename, category=initial_category
|
||||
@@ -65,13 +69,16 @@ async def test_category_change_cleans_up_old_attachments_directory(
|
||||
|
||||
# 4. Construct and check the WebDAV path for the initial category's attachment directory
|
||||
initial_webdav_path = f"Notes/{initial_category}/.attachments.{note_id}"
|
||||
logger.info(f"Initial WebDAV path for attachments: {initial_webdav_path}")
|
||||
logger.info("Initial WebDAV path for attachments: %s", initial_webdav_path)
|
||||
# Here we would check if the directory exists, but the WebDAV client doesn't directly
|
||||
# expose directory listing functionality, so we'll infer from attachment retrieval success
|
||||
|
||||
# 5. Update note category
|
||||
logger.info(
|
||||
f"Updating note {note_id} category from '{initial_category}' to '{new_category}'"
|
||||
"Updating note %s category from '%s' to '%s'",
|
||||
note_id,
|
||||
initial_category,
|
||||
new_category,
|
||||
)
|
||||
current_note_data = await nc_client.notes.get_note(note_id=note_id)
|
||||
current_etag = current_note_data["etag"]
|
||||
@@ -84,12 +91,12 @@ async def test_category_change_cleans_up_old_attachments_directory(
|
||||
)
|
||||
etag3 = updated_note["etag"]
|
||||
assert updated_note["category"] == new_category
|
||||
logger.info(f"Note category updated successfully. New Etag: {etag3}")
|
||||
logger.info("Note category updated successfully. New Etag: %s", etag3)
|
||||
time.sleep(1)
|
||||
|
||||
# 6. Verify attachment retrieval from new category
|
||||
logger.info(
|
||||
f"Verifying attachment retrieval from new category '{new_category}'"
|
||||
"Verifying attachment retrieval from new category '%s'", new_category
|
||||
)
|
||||
retrieved_content2, _ = await nc_client.webdav.get_note_attachment(
|
||||
note_id=note_id, filename=attachment_filename, category=new_category
|
||||
@@ -99,7 +106,8 @@ async def test_category_change_cleans_up_old_attachments_directory(
|
||||
|
||||
# 7. Try to retrieve from old category - this should fail
|
||||
logger.info(
|
||||
f"Trying to retrieve attachment from old category '{initial_category}' - should fail"
|
||||
"Trying to retrieve attachment from old category '%s' - should fail",
|
||||
initial_category,
|
||||
)
|
||||
try:
|
||||
await nc_client.webdav.get_note_attachment(
|
||||
@@ -115,7 +123,8 @@ async def test_category_change_cleans_up_old_attachments_directory(
|
||||
except HTTPStatusError as e:
|
||||
# This is the expected outcome - old directory should be gone
|
||||
logger.info(
|
||||
f"Correctly got error accessing old category path: {e.response.status_code}"
|
||||
"Correctly got error accessing old category path: %s",
|
||||
e.response.status_code,
|
||||
)
|
||||
assert e.response.status_code == 404, (
|
||||
f"Expected 404, got {e.response.status_code}"
|
||||
@@ -143,14 +152,16 @@ async def test_category_change_cleans_up_old_attachments_directory(
|
||||
207,
|
||||
]: # Success codes indicate the directory exists (a problem)
|
||||
logger.error(
|
||||
f"Old attachment directory still exists! PROPFIND returned {status}"
|
||||
"Old attachment directory still exists! PROPFIND returned %s",
|
||||
status,
|
||||
)
|
||||
assert False, (
|
||||
f"Expected old attachment directory to be gone, but it still exists (PROPFIND returned {status})!"
|
||||
)
|
||||
# If we got another status code (like 404), it's also good - the directory doesn't exist
|
||||
logger.info(
|
||||
f"Verified old attachment directory does not exist (PROPFIND returned {status})"
|
||||
"Verified old attachment directory does not exist (PROPFIND returned %s)",
|
||||
status,
|
||||
)
|
||||
except HTTPStatusError as e:
|
||||
# 404 is expected - directory should not exist
|
||||
@@ -164,10 +175,10 @@ async def test_category_change_cleans_up_old_attachments_directory(
|
||||
finally:
|
||||
# 8. Cleanup: Delete the note
|
||||
if note_id:
|
||||
logger.info(f"Cleaning up note ID: {note_id}")
|
||||
logger.info("Cleaning up note ID: %s", note_id)
|
||||
try:
|
||||
await nc_client.notes.delete_note(note_id=note_id)
|
||||
logger.info(f"Note {note_id} deleted.")
|
||||
logger.info("Note %s deleted.", note_id)
|
||||
time.sleep(1)
|
||||
|
||||
# 9. Verify both old and new attachment paths are gone
|
||||
@@ -209,14 +220,16 @@ async def test_category_change_cleans_up_old_attachments_directory(
|
||||
207,
|
||||
]: # Success codes indicate the directory exists (a problem)
|
||||
logger.error(
|
||||
f"New category attachment directory still exists! PROPFIND returned {status}"
|
||||
"New category attachment directory still exists! PROPFIND returned %s",
|
||||
status,
|
||||
)
|
||||
assert False, (
|
||||
f"Expected new category attachment directory to be gone, but it still exists (PROPFIND returned {status})!"
|
||||
)
|
||||
# If we got another status code (like 404), it's also good - the directory doesn't exist
|
||||
logger.info(
|
||||
f"Verified new category attachment directory does not exist (PROPFIND returned {status})"
|
||||
"Verified new category attachment directory does not exist (PROPFIND returned %s)",
|
||||
status,
|
||||
)
|
||||
except HTTPStatusError as e:
|
||||
assert e.response.status_code == 404, (
|
||||
@@ -240,14 +253,16 @@ async def test_category_change_cleans_up_old_attachments_directory(
|
||||
207,
|
||||
]: # Success codes indicate the directory exists (a problem)
|
||||
logger.error(
|
||||
f"Old category attachment directory still exists! PROPFIND returned {status}"
|
||||
"Old category attachment directory still exists! PROPFIND returned %s",
|
||||
status,
|
||||
)
|
||||
assert False, (
|
||||
f"Expected old category attachment directory to be gone, but it still exists (PROPFIND returned {status})!"
|
||||
)
|
||||
# If we got another status code (like 404), it's also good - the directory doesn't exist
|
||||
logger.info(
|
||||
f"Verified old category attachment directory does not exist (PROPFIND returned {status})"
|
||||
"Verified old category attachment directory does not exist (PROPFIND returned %s)",
|
||||
status,
|
||||
)
|
||||
except HTTPStatusError as e:
|
||||
assert e.response.status_code == 404, (
|
||||
@@ -261,4 +276,4 @@ async def test_category_change_cleans_up_old_attachments_directory(
|
||||
"Verified all attachment directories are properly cleaned up."
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error during cleanup for note {note_id}: {e}")
|
||||
logger.error("Error during cleanup for note %s: %s", note_id, e)
|
||||
|
||||
@@ -33,7 +33,7 @@ async def test_create_and_delete_directory(
|
||||
# Create directory
|
||||
result = await nc_client.webdav.create_directory(test_dir)
|
||||
assert result["status_code"] == 201 # Created
|
||||
logger.info(f"Created directory: {test_dir}")
|
||||
logger.info("Created directory: %s", test_dir)
|
||||
|
||||
# Verify directory exists by listing parent
|
||||
parent_listing = await nc_client.webdav.list_directory(test_base_path)
|
||||
@@ -43,7 +43,7 @@ async def test_create_and_delete_directory(
|
||||
# Delete directory
|
||||
delete_result = await nc_client.webdav.delete_resource(test_dir)
|
||||
assert delete_result["status_code"] in [204, 404] # No Content or Not Found
|
||||
logger.info(f"Deleted directory: {test_dir}")
|
||||
logger.info("Deleted directory: %s", test_dir)
|
||||
|
||||
finally:
|
||||
# Cleanup: ensure directory is deleted
|
||||
@@ -67,13 +67,13 @@ async def test_write_read_delete_file(nc_client: NextcloudClient, test_base_path
|
||||
test_file, test_content.encode("utf-8"), content_type="text/plain"
|
||||
)
|
||||
assert write_result["status_code"] in [200, 201, 204] # Success codes
|
||||
logger.info(f"Wrote file: {test_file}")
|
||||
logger.info("Wrote file: %s", test_file)
|
||||
|
||||
# Read file back
|
||||
content, content_type = await nc_client.webdav.read_file(test_file)
|
||||
assert content.decode("utf-8") == test_content
|
||||
assert "text/plain" in content_type
|
||||
logger.info(f"Read file: {test_file}")
|
||||
logger.info("Read file: %s", test_file)
|
||||
|
||||
# Verify file appears in directory listing
|
||||
listing = await nc_client.webdav.list_directory(test_base_path)
|
||||
@@ -83,7 +83,7 @@ async def test_write_read_delete_file(nc_client: NextcloudClient, test_base_path
|
||||
# Delete file
|
||||
delete_result = await nc_client.webdav.delete_resource(test_file)
|
||||
assert delete_result["status_code"] in [204, 404] # No Content or Not Found
|
||||
logger.info(f"Deleted file: {test_file}")
|
||||
logger.info("Deleted file: %s", test_file)
|
||||
|
||||
finally:
|
||||
# Cleanup
|
||||
@@ -106,7 +106,7 @@ async def test_list_directory_empty_and_populated(
|
||||
empty_listing = await nc_client.webdav.list_directory(test_base_path)
|
||||
assert isinstance(empty_listing, list)
|
||||
assert len(empty_listing) == 0
|
||||
logger.info(f"Empty directory listing: {len(empty_listing)} items")
|
||||
logger.info("Empty directory listing: %s items", len(empty_listing))
|
||||
|
||||
# Add some files and directories
|
||||
await nc_client.webdav.create_directory(f"{test_base_path}/subdir1")
|
||||
@@ -140,7 +140,7 @@ async def test_list_directory_empty_and_populated(
|
||||
assert "content_type" in item
|
||||
assert "last_modified" in item
|
||||
|
||||
logger.info(f"Populated directory listing: {len(populated_listing)} items")
|
||||
logger.info("Populated directory listing: %s items", len(populated_listing))
|
||||
|
||||
finally:
|
||||
# Cleanup
|
||||
@@ -162,7 +162,7 @@ async def test_read_nonexistent_file(nc_client: NextcloudClient):
|
||||
await nc_client.webdav.read_file(nonexistent_file)
|
||||
|
||||
assert exc_info.value.response.status_code == 404
|
||||
logger.info(f"Correctly got 404 for nonexistent file: {nonexistent_file}")
|
||||
logger.info("Correctly got 404 for nonexistent file: %s", nonexistent_file)
|
||||
|
||||
|
||||
async def test_delete_nonexistent_resource(nc_client: NextcloudClient):
|
||||
@@ -171,7 +171,7 @@ async def test_delete_nonexistent_resource(nc_client: NextcloudClient):
|
||||
|
||||
result = await nc_client.webdav.delete_resource(nonexistent_resource)
|
||||
assert result["status_code"] == 404
|
||||
logger.info(f"Correctly got 404 for nonexistent resource: {nonexistent_resource}")
|
||||
logger.info("Correctly got 404 for nonexistent resource: %s", nonexistent_resource)
|
||||
|
||||
|
||||
async def test_create_nested_directories(
|
||||
@@ -200,7 +200,7 @@ async def test_create_nested_directories(
|
||||
assert level2_listing[0]["name"] == "level3"
|
||||
assert level2_listing[0]["is_directory"] is True
|
||||
|
||||
logger.info(f"Created nested directory structure: {nested_path}")
|
||||
logger.info("Created nested directory structure: %s", nested_path)
|
||||
|
||||
finally:
|
||||
# Cleanup - delete from deepest to shallowest
|
||||
@@ -241,7 +241,7 @@ async def test_overwrite_existing_file(nc_client: NextcloudClient, test_base_pat
|
||||
content, _ = await nc_client.webdav.read_file(test_file)
|
||||
assert content.decode("utf-8") == new_content
|
||||
|
||||
logger.info(f"Successfully overwrote file: {test_file}")
|
||||
logger.info("Successfully overwrote file: %s", test_file)
|
||||
|
||||
finally:
|
||||
# Cleanup
|
||||
@@ -270,4 +270,4 @@ async def test_list_root_directory(nc_client: NextcloudClient):
|
||||
assert "content_type" in item
|
||||
assert "last_modified" in item
|
||||
|
||||
logger.info(f"Root directory contains {len(root_listing)} items")
|
||||
logger.info("Root directory contains %s items", len(root_listing))
|
||||
|
||||
@@ -47,16 +47,16 @@ async def test_search_setup(nc_client: NextcloudClient):
|
||||
for file_path, content, content_type in test_files:
|
||||
await nc_client.webdav.write_file(file_path, content, content_type)
|
||||
|
||||
logger.info(f"Created test directory with {len(test_files)} files: {test_dir}")
|
||||
logger.info("Created test directory with %s files: %s", len(test_files), test_dir)
|
||||
|
||||
yield test_dir
|
||||
|
||||
# Cleanup
|
||||
try:
|
||||
await nc_client.webdav.delete_resource(test_dir)
|
||||
logger.info(f"Cleaned up test directory: {test_dir}")
|
||||
logger.info("Cleaned up test directory: %s", test_dir)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to cleanup test directory {test_dir}: {e}")
|
||||
logger.warning("Failed to cleanup test directory %s: %s", test_dir, e)
|
||||
|
||||
|
||||
async def test_find_by_name_exact(nc_client: NextcloudClient, test_search_setup: str):
|
||||
@@ -69,7 +69,7 @@ async def test_find_by_name_exact(nc_client: NextcloudClient, test_search_setup:
|
||||
readme_files = [r for r in results if r.get("name") == "readme.md"]
|
||||
assert len(readme_files) >= 1, "Should find readme.md"
|
||||
|
||||
logger.info(f"Found {len(results)} files matching 'readme.md'")
|
||||
logger.info("Found %s files matching 'readme.md'", len(results))
|
||||
|
||||
|
||||
async def test_find_by_name_wildcard_extension(
|
||||
@@ -86,7 +86,7 @@ async def test_find_by_name_wildcard_extension(
|
||||
name = result.get("name", "")
|
||||
assert name.endswith(".txt"), f"Expected .txt file, got {name}"
|
||||
|
||||
logger.info(f"Found {len(results)} .txt files")
|
||||
logger.info("Found %s .txt files", len(results))
|
||||
|
||||
|
||||
async def test_find_by_name_wildcard_prefix(
|
||||
@@ -105,7 +105,7 @@ async def test_find_by_name_wildcard_prefix(
|
||||
f"Expected name to start with 'document', got {name}"
|
||||
)
|
||||
|
||||
logger.info(f"Found {len(results)} files starting with 'document'")
|
||||
logger.info("Found %s files starting with 'document'", len(results))
|
||||
|
||||
|
||||
async def test_find_by_type_text(nc_client: NextcloudClient, test_search_setup: str):
|
||||
@@ -122,7 +122,7 @@ async def test_find_by_type_text(nc_client: NextcloudClient, test_search_setup:
|
||||
f"Expected text/* type, got {content_type}"
|
||||
)
|
||||
|
||||
logger.info(f"Found {len(results)} text files")
|
||||
logger.info("Found %s text files", len(results))
|
||||
|
||||
|
||||
async def test_find_by_type_specific(
|
||||
@@ -143,7 +143,7 @@ async def test_find_by_type_specific(
|
||||
f"Expected application/pdf, got {content_type}"
|
||||
)
|
||||
|
||||
logger.info(f"Found {len(results)} PDF files")
|
||||
logger.info("Found %s PDF files", len(results))
|
||||
|
||||
|
||||
async def test_search_with_limit(nc_client: NextcloudClient, test_search_setup: str):
|
||||
@@ -157,7 +157,7 @@ async def test_search_with_limit(nc_client: NextcloudClient, test_search_setup:
|
||||
assert len(results) <= 2, f"Should return at most 2 results, got {len(results)}"
|
||||
assert len(results) > 0, "Should return at least 1 result"
|
||||
|
||||
logger.info(f"Found {len(results)} files with limit=2")
|
||||
logger.info("Found %s files with limit=2", len(results))
|
||||
|
||||
|
||||
async def test_search_files_combined_filters(
|
||||
@@ -198,7 +198,7 @@ async def test_search_files_combined_filters(
|
||||
f"Expected name to start with 'document', got {name}"
|
||||
)
|
||||
|
||||
logger.info(f"Found {len(results)} files matching combined filters")
|
||||
logger.info("Found %s files matching combined filters", len(results))
|
||||
|
||||
|
||||
async def test_search_empty_scope(nc_client: NextcloudClient, test_search_setup: str):
|
||||
@@ -210,7 +210,7 @@ async def test_search_empty_scope(nc_client: NextcloudClient, test_search_setup:
|
||||
# Should find at least the one we created
|
||||
assert len(results) >= 1, f"Should find at least 1 file named {unique_name}"
|
||||
|
||||
logger.info(f"Found {len(results)} files in root scope")
|
||||
logger.info("Found %s files in root scope", len(results))
|
||||
|
||||
|
||||
async def test_search_subdirectory(nc_client: NextcloudClient, test_search_setup: str):
|
||||
@@ -226,7 +226,7 @@ async def test_search_subdirectory(nc_client: NextcloudClient, test_search_setup
|
||||
nested_file = results[0]
|
||||
assert "nested.txt" in nested_file.get("name", ""), "Should find nested.txt"
|
||||
|
||||
logger.info(f"Found file in subdirectory: {nested_file.get('name')}")
|
||||
logger.info("Found file in subdirectory: %s", nested_file.get("name"))
|
||||
|
||||
|
||||
async def test_search_no_results(nc_client: NextcloudClient, test_search_setup: str):
|
||||
@@ -259,10 +259,10 @@ async def test_search_properties_returned(
|
||||
|
||||
# Optional properties that may be present
|
||||
optional_props = ["size", "content_type", "last_modified", "etag"]
|
||||
logger.info(f"Result properties: {list(result.keys())}")
|
||||
logger.info("Result properties: %s", list(result.keys()))
|
||||
|
||||
# At least some optional properties should be present
|
||||
present_optional = [prop for prop in optional_props if prop in result]
|
||||
assert len(present_optional) > 0, f"Should have at least one of {optional_props}"
|
||||
|
||||
logger.info(f"Search returned properties: {list(result.keys())}")
|
||||
logger.info("Search returned properties: %s", list(result.keys()))
|
||||
|
||||
Reference in New Issue
Block a user