Skip to content

enh: implement new ruff standards - #967

Open
taylorwilsdon wants to merge 3 commits into
mainfrom
new_ruff
Open

enh: implement new ruff standards #967
taylorwilsdon wants to merge 3 commits into
mainfrom
new_ruff

Conversation

@taylorwilsdon

@taylorwilsdon taylorwilsdon commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • Bug Fixes
    • Improved attachment expiry handling using consistent UTC timestamps.
    • Hardened OAuth callback and minimal stdio callback handling with clearer error diagnostics.
    • Improved Google Docs tab response compatibility and validation, including safer “missing reply” handling.
    • Preserved Gmail exported attachment/content integrity during async file operations.
  • Compatibility
    • Updated tool schemas to consistently support nullable/optional parameters across Google Docs formatting and document operations.
  • Tests
    • Added regression coverage for Docs tab response extraction (including backward-compatible reply formats).
  • Chores
    • Standardized formatting/lint checks and pinned tooling versions for more consistent release behavior.

@taylorwilsdon taylorwilsdon self-assigned this Jul 26, 2026
@taylorwilsdon taylorwilsdon added the enhancement New feature or request label Jul 26, 2026
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c34daf51-f14a-48d4-bbd5-0874f3601f60

📥 Commits

Reviewing files that changed from the base of the PR and between c49a79f and fe02fef.

📒 Files selected for processing (5)
  • gmail/gmail_helpers.py
  • gmail/gmail_tools.py
  • main.py
  • pyproject.toml
  • tests/gmail/test_thread_ownership_helpers.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/gmail/test_thread_ownership_helpers.py
  • pyproject.toml
  • gmail/gmail_helpers.py
  • gmail/gmail_tools.py

📝 Walkthrough

Walkthrough

This PR modernizes Python type annotations across authentication, core utilities, Google service integrations, and tool interfaces. It also pins Ruff, standardizes exception logging, adds timezone-aware attachment expiry handling, tightens selected validation paths, and updates related tests and schemas.

Changes

Repository modernization

Layer / File(s) Summary
Tooling and startup handling
.github/workflows/ruff.yml, pyproject.toml, main.py, tests/test_cwe77_ruff_workflow.py
Ruff is pinned to 0.16.0, bounded in development dependencies, and configured with repository-specific exclusions and ignores. Startup import organization and exception logging are updated.
Authentication and OAuth contracts
auth/*.py, core/config.py, core/context.py
Authentication, OAuth session, credential-store, permission, and decorator APIs adopt built-in generics and union annotations. Several OAuth and callback-server error paths use logger.exception.
Core utilities and service infrastructure
core/*.py
Attachment metadata uses timezone-aware UTC timestamps, HTTP streaming uses a shared timeout constant, OAuth discovery paths are centralized, and tool filtering/type declarations are modernized.
Google service tool interfaces
gappsscript/*, gcalendar/*, gchat/*, gcontacts/*, gdocs/*, gdrive/*, gforms/*, gmail/*, gsearch/*, gsheets/*, gslides/*, gtasks/*
Public tool signatures, helper annotations, schemas, and local data structures migrate from typing aliases to built-in generics and `
Tests and generated schemas
tests/auth/*, tests/core/*, tests/gdocs/*, tests/gmail/*, tests/gdrive/*, tests/gcontacts/*, tests/gsheets/*
Tests remove obsolete Ruff suppressions, reorganize imports, use timezone-aware fixture metadata, validate nullable Docs schemas, and read exported Gmail files asynchronously.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested reviewers: drfaust92

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning No description was provided, so none of the required template sections are filled out. Add the template sections for Description, Type of Change, Testing, Checklist, and Additional Notes, and confirm Allow edits from maintainers.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and matches the PR's main Ruff workflow and configuration updates.
Docstring Coverage ✅ Passed Docstring coverage is 89.65% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch new_ruff

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread auth/google_auth.py
f"Failed to start the OAuth flow for {user_display_name}: {e}",
exc_info=True,
error_text = f"Could not initiate authentication for {user_display_name} due to an unexpected error: {e!s}"
logger.exception(
Comment thread auth/google_auth.py
async def handle_auth_callback(
scopes: List[str],
scopes: list[str],
authorization_response: str,

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@auth/google_auth.py`:
- Around line 618-627: Update the exception handlers around the OAuth
authentication flow and the corresponding handling near the later referenced
path so neither logs nor raised user-facing errors interpolate the raw
exception. Log only sanitized, contextual messages without traceback details,
raise a generic actionable error, and preserve the original exception solely
through exception chaining.

In `@auth/oauth21_session_store.py`:
- Around line 111-120: Update SessionContext.metadata to use a default
consistent with its declared type, preferably by assigning
field(default_factory=dict) and removing the None normalization from
__post_init__; otherwise annotate it as dict[str, Any] | None while preserving
the existing initialization behavior.

In `@tests/auth/test_auth_info_middleware.py`:
- Line 13: Fix the unused method-argument warnings in set_state at
tests/auth/test_auth_info_middleware.py:13 and the corresponding method at
tests/auth/test_oauth21_session_store.py:210 by renaming or removing unused
arguments, or adding targeted per-line suppressions; do not rely on a global
ARG002 ignore.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c9833478-bc71-4cb0-a31b-4a7077658638

📥 Commits

Reviewing files that changed from the base of the PR and between bad7f9c and c49a79f.

📒 Files selected for processing (109)
  • .github/workflows/ruff.yml
  • auth/auth_info_middleware.py
  • auth/credential_store.py
  • auth/external_oauth_provider.py
  • auth/google_auth.py
  • auth/mcp_session_middleware.py
  • auth/oauth21_session_store.py
  • auth/oauth_callback_server.py
  • auth/oauth_config.py
  • auth/oauth_responses.py
  • auth/oauth_types.py
  • auth/permissions.py
  • auth/port_resolver.py
  • auth/scopes.py
  • auth/service_decorator.py
  • core/api_enablement.py
  • core/attachment_storage.py
  • core/comments.py
  • core/config.py
  • core/context.py
  • core/http_utils.py
  • core/log_formatter.py
  • core/server.py
  • core/tool_registry.py
  • core/tool_tier_loader.py
  • core/utils.py
  • gappsscript/apps_script_tools.py
  • gcalendar/calendar_helpers.py
  • gcalendar/calendar_tools.py
  • gchat/chat_tools.py
  • gcontacts/contacts_helpers.py
  • gcontacts/contacts_tools.py
  • gdocs/docs_helpers.py
  • gdocs/docs_markdown_writer.py
  • gdocs/docs_structure.py
  • gdocs/docs_tables.py
  • gdocs/docs_tools.py
  • gdocs/managers/__init__.py
  • gdocs/managers/batch_operation_manager.py
  • gdocs/managers/header_footer_manager.py
  • gdocs/managers/table_operation_manager.py
  • gdocs/managers/validation_manager.py
  • gdocs/operation_schemas.py
  • gdrive/drive_helpers.py
  • gdrive/drive_tools.py
  • gforms/forms_tools.py
  • gmail/gmail_helpers.py
  • gmail/gmail_tools.py
  • gsearch/search_tools.py
  • gsheets/__init__.py
  • gsheets/sheets_helpers.py
  • gsheets/sheets_tools.py
  • gslides/slides_helpers.py
  • gslides/slides_tools.py
  • gtasks/tasks_tools.py
  • main.py
  • pyproject.toml
  • tests/auth/test_auth_info_middleware.py
  • tests/auth/test_google_auth_callback_refresh_token.py
  • tests/auth/test_google_auth_pkce.py
  • tests/auth/test_google_auth_prompt_selection.py
  • tests/auth/test_google_auth_refresh_persistence.py
  • tests/auth/test_google_auth_stdio_preflight.py
  • tests/auth/test_httplib2_timeout.py
  • tests/auth/test_oauth21_session_store.py
  • tests/auth/test_oauth_callback_server.py
  • tests/core/test_comments.py
  • tests/core/test_credentials_dir_permissions.py
  • tests/core/test_pdf_image_utils.py
  • tests/core/test_service_decorator_cleanup.py
  • tests/core/test_start_google_auth.py
  • tests/core/test_storage.py
  • tests/core/test_user_google_email_defaults.py
  • tests/core/test_validate_file_path.py
  • tests/gappsscript/manual_test.py
  • tests/gappsscript/test_apps_script_tools.py
  • tests/gcalendar/test_focus_time.py
  • tests/gcalendar/test_out_of_office.py
  • tests/gchat/test_chat_tools.py
  • tests/gcontacts/test_contacts_integration.py
  • tests/gcontacts/test_contacts_tools.py
  • tests/gcontacts/test_contacts_tools_v2.py
  • tests/gcontacts/test_contacts_tools_v3.py
  • tests/gdocs/golden/docs_tool_schemas.json
  • tests/gdocs/test_docs_markdown.py
  • tests/gdocs/test_insert_doc_tab_response.py
  • tests/gdocs/test_paragraph_style.py
  • tests/gdocs/test_strikethrough.py
  • tests/gdocs/test_suggestions_view_mode.py
  • tests/gdocs/test_table_column_operations.py
  • tests/gdocs/test_table_row_operations.py
  • tests/gdrive/test_drive_file_content.py
  • tests/gdrive/test_drive_tools.py
  • tests/gdrive/test_url_download_limits.py
  • tests/gforms/test_forms_tools.py
  • tests/gmail/test_body_format.py
  • tests/gmail/test_draft_gmail_message.py
  • tests/gmail/test_forward_gmail_message.py
  • tests/gmail/test_get_gmail_attachment_content.py
  • tests/gmail/test_inline_image_attachments.py
  • tests/gmail/test_modify_gmail_message_labels_schema.py
  • tests/gmail/test_thread_ownership_helpers.py
  • tests/gsheets/test_format_sheet_range.py
  • tests/gsheets/test_resize_sheet_dimensions.py
  • tests/helpers.py
  • tests/test_cwe77_ruff_workflow.py
  • tests/test_main_permissions_tier.py
  • tests/test_permissions.py
  • tests/test_scopes.py
💤 Files with no reviewable changes (3)
  • tests/core/test_storage.py
  • tests/auth/test_google_auth_prompt_selection.py
  • tests/gdocs/test_insert_doc_tab_response.py

Comment thread auth/google_auth.py
Comment on lines 618 to 627
except FileNotFoundError as e:
error_text = f"OAuth client credentials not found: {e}. Please either:\n1. Set environment variables: GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET\n2. Ensure '{CONFIG_CLIENT_SECRETS_PATH}' file exists"
logger.error(error_text, exc_info=True)
logger.exception(error_text)
raise Exception(error_text)
except Exception as e:
error_text = f"Could not initiate authentication for {user_display_name} due to an unexpected error: {str(e)}"
logger.error(
f"Failed to start the OAuth flow for {user_display_name}: {e}",
exc_info=True,
error_text = f"Could not initiate authentication for {user_display_name} due to an unexpected error: {e!s}"
logger.exception(
f"Failed to start the OAuth flow for {user_display_name}",
)
raise Exception(error_text)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not expose raw OAuth exceptions in logs or user-facing errors.

error_text/error_msg include {e!s}, and logger.exception records the exception traceback as well. OAuth and client-configuration exceptions can contain credential or request data; CodeQL flags these paths. Log a sanitized message and raise a generic actionable error, preserving the original exception only through exception chaining.

Proposed fix
 except FileNotFoundError as e:
-    error_text = f"OAuth client credentials not found: {e}. Please either:\n1. Set environment variables: GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET\n2. Ensure '{CONFIG_CLIENT_SECRETS_PATH}' file exists"
-    logger.exception(error_text)
-    raise Exception(error_text)
+    logger.error("OAuth client credentials were not found", exc_info=False)
+    raise Exception(
+        "OAuth client credentials were not found. Configure the OAuth environment "
+        "variables or provide the client secrets file."
+    ) from e
 except Exception as e:
-    error_text = f"Could not initiate authentication for {user_display_name} due to an unexpected error: {e!s}"
-    logger.exception(f"Failed to start the OAuth flow for {user_display_name}")
-    raise Exception(error_text)
+    logger.error(
+        "Failed to start OAuth flow for %s (%s)",
+        user_display_name,
+        type(e).__name__,
+    )
+    raise Exception(
+        f"Could not initiate authentication for {user_display_name}."
+    ) from e

Also applies to: 1372-1375

🧰 Tools
🪛 GitHub Check: CodeQL

[failure] 620-620: Clear-text logging of sensitive information
This expression logs sensitive data (secret) as clear text.


[failure] 625-625: Clear-text logging of sensitive information
This expression logs sensitive data (password) as clear text.
This expression logs sensitive data (password) as clear text.
This expression logs sensitive data (password) as clear text.
This expression logs sensitive data (password) as clear text.
This expression logs sensitive data (password) as clear text.
This expression logs sensitive data (password) as clear text.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@auth/google_auth.py` around lines 618 - 627, Update the exception handlers
around the OAuth authentication flow and the corresponding handling near the
later referenced path so neither logs nor raised user-facing errors interpolate
the raw exception. Log only sanitized, contextual messages without traceback
details, raise a generic actionable error, and preserve the original exception
solely through exception chaining.

Source: Linters/SAST tools

Comment on lines +111 to 120
session_id: str | None = None
user_id: str | None = None
auth_context: Any | None = None
request: Any | None = None
metadata: dict[str, Any] = None
issuer: str | None = None

def __post_init__(self):
if self.metadata is None:
self.metadata = {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Keep SessionContext.metadata consistent with its default.

metadata is annotated as dict[str, Any] but defaults to None and is explicitly normalized in __post_init__. Use dict[str, Any] | None or field(default_factory=dict).

Proposed fix
-    metadata: dict[str, Any] = None
+    metadata: dict[str, Any] | None = None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
session_id: str | None = None
user_id: str | None = None
auth_context: Any | None = None
request: Any | None = None
metadata: dict[str, Any] = None
issuer: str | None = None
def __post_init__(self):
if self.metadata is None:
self.metadata = {}
session_id: str | None = None
user_id: str | None = None
auth_context: Any | None = None
request: Any | None = None
metadata: dict[str, Any] | None = None
issuer: str | None = None
def __post_init__(self):
if self.metadata is None:
self.metadata = {}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@auth/oauth21_session_store.py` around lines 111 - 120, Update
SessionContext.metadata to use a default consistent with its declared type,
preferably by assigning field(default_factory=dict) and removing the None
normalization from __post_init__; otherwise annotate it as dict[str, Any] | None
while preserving the existing initialization behavior.

self.session_id = "session-123"

async def set_state(self, key, value, serializable=True): # noqa: ARG002
async def set_state(self, key, value, serializable=True):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

rg -n 'ARG002|select|extend-select|ignore' pyproject.toml
uvx ruff@0.16.0 check tests/auth/test_auth_info_middleware.py tests/auth/test_oauth21_session_store.py --select ARG002

Repository: taylorwilsdon/google_workspace_mcp

Length of output: 572


🏁 Script executed:

#!/bin/bash
set -e

echo "===== pyproject ruff refs ====="
sed -n '1,190p' pyproject.toml | rg -n 'ARG002|ruff|select|extend-select|ignore|per-file-ignores' -C 2 || true

echo
echo "===== relevant files lines ====="
sed -n '1,35p' tests/auth/test_auth_info_middleware.py
sed -n '190,225p' tests/auth/test_oauth21_session_store.py

echo
echo "===== ruff availability/search for pinned version ====="
(command -v ruff && ruff --version || true)
rg -n "ruff|ARG002" pyproject.toml uv.lock README.md .github -S 2>/dev/null || true

Repository: taylorwilsdon/google_workspace_mcp

Length of output: 11518


🏁 Script executed:

#!/bin/bash
set -e

echo "===== test files exact content around target methods ====="
awk 'NR>=1 && NR<=30 {print NR": "$0}' tests/auth/test_auth_info_middleware.py
awk 'NR>=190 && NR<=222 {print NR": "$0}' tests/auth/test_oauth21_session_store.py

echo
echo "===== current local Ruff ARG002 behavior ====="
ruff check tests/auth/test_auth_info_middleware.py tests/auth/test_oauth21_session_store.py --select ARG002 --output-format=full || true

echo
echo "===== pyproject ignores summary ====="
awk '/^\[tool\.ruff\.lint\]|^\[tool\.ruff\.lint\.per-file-ignores\]|^ignore\s*=\s*\[|ARG002/' pyproject.toml || true

Repository: taylorwilsdon/google_workspace_mcp

Length of output: 3563


Fix the stale ARG002 suppressions

Ruff still reports these unused method arguments, and the Ruff config has no ARG002 ignore. Replace the removed noqa with either per-line suppressions or rename/remove the unused args:

  • tests/auth/test_auth_info_middleware.py#L13
  • tests/auth/test_oauth21_session_store.py#L210
📍 Affects 2 files
  • tests/auth/test_auth_info_middleware.py#L13-L13 (this comment)
  • tests/auth/test_oauth21_session_store.py#L210-L210
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/auth/test_auth_info_middleware.py` at line 13, Fix the unused
method-argument warnings in set_state at
tests/auth/test_auth_info_middleware.py:13 and the corresponding method at
tests/auth/test_oauth21_session_store.py:210 by renaming or removing unused
arguments, or adding targeted per-line suppressions; do not rely on a global
ARG002 ignore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants