Skip to content

Conversation

@dhirukumar
Copy link

✨ Proposed Change

Resolves: #2650

This Pull Request introduces complete type hints across all backend utility functions, improving code clarity, static analysis, and maintainability.

🔧 Changes Implemented

  • Added parameter type hints
  • Added return type hints
  • Added proper typing for optional parameters using None
  • Followed existing project-wide typing conventions
  • Ensured no functional logic was modified — typing-only improvements
  • Updated all relevant utility files:

📁 Updated Files

  • backend/apps/common/utils.py
  • backend/apps/github/utils.py
  • backend/apps/slack/utils.py

📌 Example Followed

def format_date(date: datetime | None, format_str: str = "%Y-%m-%d") -> str | None:
    """Format date to string.""" 

✅ Checklist

  • I have read and followed the contributing guidelines-(https://github.com/OWASP/Nest/blob/main/CONTRIBUTING.md)
  • All utility functions now include complete type hints.
  • Optional parameters use correct None handling.
  • Type hints follow existing project conventions.
  • I ran make check-test locally and all checks/tests passed.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 17, 2025

Summary by CodeRabbit

  • Chores
    • Enhanced internal utility functions with improved type safety and parameter handling.
    • Updated error handling behavior for better consistency across backend operations.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Added and refined type hints across multiple backend utility modules, made several parameters optional, adjusted a few return types (including returning empty string on certain failures), and updated docstrings to match the new signatures.

Changes

Cohort / File(s) Change Summary
Common utilities
backend/apps/common/utils.py
Introduced TYPE_CHECKING-guarded HttpRequest import; added/expanded type hints and Optionals; updated signatures to `clean_url(url: str
GitHub utilities
backend/apps/github/utils.py
Adjusted signatures and return annotations to allow Optionals and widened timeout typing: `check_funding_policy_compliance(platform: str, target: str
Slack utilities
backend/apps/slack/utils.py
Added explicit parameter and return type hints: escape(content: str) -> str, `get_news_data(limit: int = 10, timeout: int

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Mixed annotation updates across three modules; mostly straightforward but includes a few behavioral-return changes.
  • Review attention points:
    • get_repository_file_content now returns empty string on failure — verify callers handle "" vs previously None.
    • Optional returns for get_repository_path, normalize_url, and clean_url — check upstream usage for None handling.
    • get_user_ip_address now types HttpRequest via TYPE_CHECKING import — confirm runtime imports unaffected.
    • Timeout parameter widenings (float/tuple vs int) — ensure call sites pass compatible types.

Suggested labels

backend

Suggested reviewers

  • arkid15r
  • kasya

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add type hints to utils.py files' clearly and directly summarizes the main change of adding type hints across utility files.
Description check ✅ Passed The description thoroughly explains the proposed changes, lists all modified files, provides examples, and references the linked issue #2650.
Linked Issues check ✅ Passed The PR successfully implements all requirements from issue #2650: complete type hints added to parameters and returns, optional parameters properly handled with None, and conventions followed consistently across common/github/slack utils.py files.
Out of Scope Changes check ✅ Passed All changes are scoped to adding type hints to utility functions in backend/apps/common/utils.py, backend/apps/github/utils.py, and backend/apps/slack/utils.py, with no unrelated modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6bb2d83 and 064c982.

📒 Files selected for processing (1)
  • backend/apps/github/utils.py (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/apps/github/utils.py

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
backend/apps/slack/utils.py (2)

81-114: Fix docstring type mismatch for timeout parameter.

The docstring at line 87 states timeout (int, optional), but the actual parameter type is float | None. This inconsistency should be corrected.

Apply this diff to fix the docstring:

 @lru_cache
 def get_news_data(limit: int = 10, timeout: float | None = 30) -> list[dict[str, str]]:
     """Get news data.

     Args:
         limit (int, optional): The maximum number of news items to fetch.

-        timeout (int, optional): The request timeout in seconds.
+        timeout (float | None, optional): The request timeout in seconds.

     Returns:
         list: A list of dictionaries containing news data (author, title, and URL).

     """

118-141: Fix docstring type mismatch for timeout parameter.

Similar to get_news_data, the docstring at line 122 states timeout (int, optional), but the actual parameter type is float | None.

Apply this diff to fix the docstring:

 @lru_cache
 def get_staff_data(timeout: float | None = 30) -> list[dict[str, Any]] | None:
     """Get staff data.

     Args:
-        timeout (int, optional): The request timeout in seconds.
+        timeout (float | None, optional): The request timeout in seconds.

     Returns:
         list or None: A sorted list of staff data dictionaries, or None if an error occurs.

     """
🧹 Nitpick comments (2)
backend/apps/github/utils.py (2)

60-82: Consider removing the else clause for consistency.

The else clause after except is valid but unnecessary here. The idiomatic pattern would be to return directly after the try block since the except already returns.

Apply this diff for a more conventional structure:

 def get_repository_file_content(
     url: str,
     *,
     timeout: float | None = 30,
 ) -> str:
     """Get the content of a file from a repository.

     Args:
         url (str): The URL of the file.
         timeout (float | None, optional): The request timeout in seconds.

     Returns:
         str: The content of the file, or empty string if the request fails.

     """
     try:
         response: requests.Response = requests.get(url, timeout=timeout)
     except RequestException as e:
         logger.exception("Failed to fetch file", extra={"URL": url, "error": str(e)})
         return ""
-    else:
-        return response.text
+    return response.text

114-121: Minor: Consider extracting prefix constants to module level.

The http_prefix and https_prefix variables are constants used within this function. While local annotations are fine, these could be module-level constants for reusability and clarity.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28ba425 and cb23fee.

📒 Files selected for processing (3)
  • backend/apps/common/utils.py (9 hunks)
  • backend/apps/github/utils.py (5 hunks)
  • backend/apps/slack/utils.py (11 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-06T12:57:58.021Z
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2223
File: backend/apps/owasp/models/common.py:0-0
Timestamp: 2025-09-06T12:57:58.021Z
Learning: When filtering URLs by domain in the OWASP project, use `urlparse(url).netloc == domain` instead of `domain in url` to avoid security vulnerabilities where malicious subdomains could bypass filtering (e.g., `testowasp.org` would match when filtering for `owasp.org`).

Applied to files:

  • backend/apps/github/utils.py
🔇 Additional comments (8)
backend/apps/github/utils.py (1)

36-57: LGTM! Proper security-conscious domain checking.

The type hints are correct, and the use of urlparse(target).netloc for domain extraction follows security best practices to avoid subdomain bypasses.

backend/apps/slack/utils.py (2)

25-35: LGTM! Type hint correctly added.

The return type annotation is accurate and the function logic remains unchanged.


183-233: LGTM! Type hints properly added.

The parameter type tuple[dict[str, Any], ...] and local variable text: list[str] annotations are accurate.

backend/apps/common/utils.py (5)

17-19: LGTM! Proper use of TYPE_CHECKING.

Using TYPE_CHECKING to conditionally import HttpRequest is the correct pattern to avoid circular imports while maintaining type safety.


21-38: LGTM! Local variable type annotations added correctly.

The explicit type annotations for parts, offset, head, and segments improve code clarity without changing logic.


143-160: LGTM! Type hints correctly widen the parameter type.

The union type int | str | datetime properly reflects the function's capability to handle multiple input types, and the local dt: datetime annotation improves clarity.


220-238: LGTM! Type hints correctly handle None inputs.

The parameter type str | None properly reflects that the function can accept None and will return False in that case.


93-109: Review comment is accurate; no issues found after verification.

Based on verification of the codebase:

  1. Intentional behavior change: The function signature declares -> str (not str | None), making the empty string return intentional and consistent with the type contract.

  2. Downstream handling is safe: The only downstream usage is in backend/apps/core/api/internal/algolia.py where ip_address is:

    • Interpolated into a cache key string (line 57) — empty string is harmless
    • Passed to get_search_results() and ultimately IndexBase.get_client(ip_address=None) — both accept optional parameters safely
  3. Minor test coverage gap (not a blocker): Current tests cover the HTTP_X_FORWARDED_FOR and REMOTE_ADDR present cases, plus the local environment case, but lack an explicit test for both headers missing in a non-local environment. This is the scenario that triggers the empty string return. Adding a test case would improve coverage but is not critical since empty string is handled gracefully throughout the codebase.

@dhirukumar
Copy link
Author

hii @arkid15r can you plese review this PR

Copy link
Collaborator

@rudransh-shrivastava rudransh-shrivastava left a comment

Choose a reason for hiding this comment

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

Hello, thank you for working on this.

Add complete type hints to all utility functions:

Parameter types
Return types
Optional parameters with proper None handling

You can remove the type annotations in the function code, and only modify it for parameters, return types, and docstrings.

Please use a consistent format for docstrings. I have left a few examples of what needs to be changed below:

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/apps/slack/utils.py (1)

82-99: Minor docstring/type mismatch on timeout in get_news_data

The signature uses timeout: float | None = 30, but the docstring still documents timeout (int, optional). Consider aligning the docstring with the annotation (e.g., float | None) so the documented API matches the typed one.

♻️ Duplicate comments (1)
backend/apps/slack/utils.py (1)

163-172: get_posts_data return docstring still mentions sponsors

The function retrieves posts (Post.recent_posts()), but the Returns: line now says “A queryset of sponsors, or None if an error occurs.” This is misleading and matches a concern already raised in past review comments; it should refer to posts, not sponsors.

Suggested fix:

-    Returns:
-        QuerySet or None: A queryset of sponsors, or None if an error occurs.
+    Returns:
+        QuerySet or None: A queryset of posts, or None if an error occurs.
🧹 Nitpick comments (3)
backend/apps/slack/utils.py (3)

56-77: Tightened return type for get_gsoc_projects is good; consider updating docstring

get_gsoc_projects(year: int) -> list[dict[str, Any]] better reflects reality than a bare list. The docstring Returns: section still says just list:; you may want to update it to list[dict[str, Any]] (or similar wording) for consistency with the new type hint.


118-141: get_staff_data typing is accurate; docstring could mirror types more closely

The return type list[dict[str, Any]] | None and timeout: float | None = 30 look reasonable and match the usage. As with get_news_data, the docstring still says timeout (int, optional) and list or None:; if you want full consistency, you could update these to reflect float | None and list[dict[str, Any]] | None.


183-194: get_text blocks type is correct but could be generalized

Annotating blocks as tuple[dict[str, Any], ...] and text as list[str] is type-safe for current usage. However, Slack blocks are commonly passed around as lists; if call sites use lists instead of tuples, a more general type such as Sequence[dict[str, Any]] (or Iterable[dict[str, Any]]) would better match real usage and avoid static-type noise.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb23fee and d009270.

📒 Files selected for processing (3)
  • backend/apps/common/utils.py (9 hunks)
  • backend/apps/github/utils.py (5 hunks)
  • backend/apps/slack/utils.py (11 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/apps/github/utils.py
  • backend/apps/common/utils.py
🔇 Additional comments (4)
backend/apps/slack/utils.py (4)

9-14: Typing imports and TYPE_CHECKING block look correct

Using TYPE_CHECKING for QuerySet and importing Any is consistent with typical Django typing patterns; no issues here.


25-35: escape() annotation aligns with implementation

escape(content: str) -> str correctly matches the escape_html usage and docstring; nothing to change here.


148-152: Docstring wording tweak is fine, but keep an eye on consistency

The updated Returns: text (“A queryset of sponsors, or None if an error occurs.”) remains accurate for get_sponsors_data(limit: int = 10) -> QuerySet | None. No functional or typing concerns here.


236-247: strip_markdown typing and pattern annotation look good

strip_markdown(text: str) -> str and slack_link_pattern: re.Pattern[str] accurately describe the function behavior and improve clarity; no issues noted.

Copy link
Collaborator

@rudransh-shrivastava rudransh-shrivastava left a comment

Choose a reason for hiding this comment

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

Hi, I have suggested changes for backend/apps/common/utils.py only, please apply similar changes to the other two files too.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (9)
backend/apps/slack/utils.py (4)

93-93: Remove local variable type hints per previous review feedback.

Based on the past review discussion, the project convention is to limit type hints to function boundaries (parameters and return types) rather than annotating obvious local variables. Please remove the type annotations for response, items_total, and items.

Apply this diff:

-    response: requests.Response = requests.get(OWASP_NEWS_URL, timeout=timeout)
+    response = requests.get(OWASP_NEWS_URL, timeout=timeout)
     tree = html.fromstring(response.content)
     h2_tags = tree.xpath("//h2")
 
-    items_total: int = 0
-    items: list[dict[str, str]] = []
+    items_total = 0
+    items = []

Also applies to: 97-98


128-128: Remove local variable type hint per project convention.

The type annotation for file_path is unnecessary since it's obviously a string literal. Per previous feedback, please limit type hints to function signatures.

Apply this diff:

-    file_path: str = "https://raw.githubusercontent.com/OWASP/owasp.github.io/main/_data/staff.yml"
+    file_path = "https://raw.githubusercontent.com/OWASP/owasp.github.io/main/_data/staff.yml"

193-193: Remove local variable type hint per project convention.

Please remove the type annotation for text to align with the project's approach of annotating only function boundaries.

Apply this diff:

-    text: list[str] = []
+    text = []

246-246: Remove local variable type hint per project convention.

While the re.Pattern[str] annotation is more explicit than simpler cases, the project convention is to avoid annotating local variables where the type is clear from context.

Apply this diff:

-    slack_link_pattern: re.Pattern[str] = re.compile(r"<(https?://[^|]+)\|([^>]+)>")
+    slack_link_pattern = re.compile(r"<(https?://[^|]+)\|([^>]+)>")
backend/apps/common/utils.py (5)

58-58: Verify docstring convention for optional parameters.

Based on past review discussions, there's an ongoing question about whether to use (str | None) or (str, optional) in docstrings. The reviewer previously requested consistency with existing project code, suggesting (str, optional) format. Please verify the established project convention.

#!/bin/bash
# Check existing docstring conventions for optional parameters across the project
rg -n -C2 -g '*.py' '^\s+\w+\s+\([^)]*,\s*optional\)' backend/apps/ | head -40

106-109: Remove local variable type hint per project convention.

The type annotation for x_forwarded_for should be removed to align with project conventions. However, the logic refactoring itself is fine.

Apply this diff:

-    x_forwarded_for: str | None = request.META.get("HTTP_X_FORWARDED_FOR")
-    if x_forwarded_for:
-        return x_forwarded_for.split(",")[0]
-    return request.META.get("REMOTE_ADDR", "")
+    x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
+    if x_forwarded_for:
+        return x_forwarded_for.split(",")[0]
+    return request.META.get("REMOTE_ADDR", "")

154-154: Remove local variable type hint per project convention.

Please remove the type annotation for dt to align with project standards.

Apply this diff:

-        dt: datetime = datetime.strptime(value, "%Y-%m-%d").replace(tzinfo=UTC)
+        dt = datetime.strptime(value, "%Y-%m-%d").replace(tzinfo=UTC)

224-224: Verify docstring convention for optional parameters.

Similar to the clean_url function, please verify whether the project convention is (str | None) or (str, optional) for consistency.


168-168: Fix spacing in docstring parameter.

Missing space after comma in the type specification.

Apply this diff:

-        unit (str,optional): The unit to append.
+        unit (str, optional): The unit to append.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d009270 and e232859.

📒 Files selected for processing (2)
  • backend/apps/common/utils.py (9 hunks)
  • backend/apps/slack/utils.py (10 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-28T14:51:14.736Z
Learnt from: adithya-naik
Repo: OWASP/Nest PR: 1894
File: frontend/src/components/TopContributorsList.tsx:74-74
Timestamp: 2025-07-28T14:51:14.736Z
Learning: In the OWASP/Nest project, the maintainer adithya-naik prefers not to create separate components for code that's only used in two specific cases, following the YAGNI principle to avoid over-engineering when the duplication is limited and manageable.

Applied to files:

  • backend/apps/common/utils.py
🔇 Additional comments (6)
backend/apps/slack/utils.py (2)

9-9: LGTM! Type hints added correctly.

The Any import and function signature for escape are properly typed.

Also applies to: 25-25


183-183: LGTM! Proper type hint for blocks parameter.

The tuple type hint correctly captures that blocks is a tuple of dictionaries.

backend/apps/common/utils.py (4)

8-8: LGTM! Proper use of TYPE_CHECKING.

Using TYPE_CHECKING to import HttpRequest is the correct pattern to avoid runtime circular import issues while maintaining type safety.

Also applies to: 17-19


93-93: LGTM! Proper type hint for HttpRequest parameter.

The addition of HttpRequest type hint improves type safety and IDE support for this function.


143-143: LGTM! Expanded parameter types correctly.

Adding datetime to the accepted parameter types is a good enhancement, and the docstring correctly reflects this.

Also applies to: 147-147


163-163: LGTM! Optional parameter typed correctly.

The unit parameter properly uses str | None to indicate it's optional.

Copy link
Collaborator

@arkid15r arkid15r left a comment

Choose a reason for hiding this comment

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

Let's keep it simple and handle function/method level hints only in this PR. Please use optional -- we can later migrate it to something like str | None -- could be your next PR @dhirukumar

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
backend/apps/slack/utils.py (3)

81-114: Fix docstring to match type hint.

The type hint specifies timeout: float | None = 30, but the docstring at line 87 states timeout (int, optional). These should be consistent.

Apply this diff to fix the docstring:

-        timeout (int, optional): The request timeout in seconds.
+        timeout (float, optional): The request timeout in seconds.

The local variable type annotations are correct.


118-141: Fix docstring to match type hint.

Similar to get_news_data, the type hint specifies timeout: float | None = 30, but the docstring at line 122 states timeout (int, optional).

Apply this diff to fix the docstring:

-        timeout (int, optional): The request timeout in seconds.
+        timeout (float, optional): The request timeout in seconds.

The file_path type annotation and function return type are correct.


236-247: Fix docstring return format for consistency.

The return section of the docstring is missing the type prefix. For consistency with other functions in the file, it should include the return type.

Apply this diff to fix the docstring:

     Returns:
-        The text with markdown formatting removed.
+        str: The text with markdown formatting removed.

The type annotation for slack_link_pattern: re.Pattern[str] is correct.

🧹 Nitpick comments (1)
backend/apps/github/utils.py (1)

60-82: Good improvements to error handling structure.

The addition of the else: block is good practice, ensuring the return only happens when no exception occurs. Type annotations and docstring updates accurately reflect the function's behavior.

Minor note: The default value 30 is an int, while the type hint is float | None. This works due to Python's int/float compatibility but could be more explicit as 30.0 for consistency.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e232859 and 6a68656.

📒 Files selected for processing (3)
  • backend/apps/common/utils.py (8 hunks)
  • backend/apps/github/utils.py (4 hunks)
  • backend/apps/slack/utils.py (10 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/apps/common/utils.py
🔇 Additional comments (6)
backend/apps/github/utils.py (2)

36-36: LGTM! Type hint and docstring properly aligned.

The optional parameter type hint str | None correctly reflects the nullable target parameter, and the docstring format matches the project conventions.

Also applies to: 41-41


84-96: LGTM! Docstring formatting updates are appropriate.

The docstring return type formatting has been updated consistently across both functions.

Also applies to: 99-123

backend/apps/slack/utils.py (4)

9-9: LGTM! Correct type hints added.

The addition of Any to typing imports supports the type annotations used throughout the file. The escape function's type hints are accurate.

Also applies to: 25-35


56-56: LGTM! Appropriate use of Any for external API response.

The return type list[dict[str, Any]] correctly reflects the structure of GSoC project data from the search API.


144-161: LGTM! Function signature and docstring are clear.

The simplified docstring return description is acceptable, as the type information is captured in the function signature.


183-233: LGTM! Type hints accurately represent the data structures.

The parameter type tuple[dict[str, Any], ...] correctly represents Slack block elements, and the local variable annotation text: list[str] is accurate.

@dhirukumar
Copy link
Author

Hi @rudransh-shrivastava,
I’ve made all the changes and kept everything simple, just like @arkid15r suggested.
Thank you for your review and guidance — really appreciate it!

@dhirukumar dhirukumar requested a review from arkid15r November 19, 2025 17:45
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/apps/common/utils.py (1)

93-108: <!-- [scratchpad_start]
[task verification_1/1]
[observations]

  • Function: get_user_ip_address(request: HttpRequest) -> str (lines 93-107)
  • Return annotation: str (but actual return can be None)
  • Return statement: x_forwarded_for.split(",")[0] if x_forwarded_for else request.META.get("REMOTE_ADDR")
  • Tests found:
    • test_get_user_ip_address: provides mock_request with HTTP_X_FORWARDED_FOR or REMOTE_ADDR keys present
    • test_get_user_ip_address_local: provides IS_LOCAL_ENVIRONMENT=True case
    • CRITICAL: Tests do NOT cover the case where BOTH keys are missing (where None would be returned)
  • Usage in algolia.py:
    • Line 50: assigns result to ip_address variable
    • Line 57: uses ip_address in f-string cache key: f"{cache_key}:{ip_address}"
    • Line 114: passes to IndexBase.get_client(ip_address=ip_address)
    • search function parameter (line 87): ip_address=None defaults to None
    • search function docstring (line 97): says "ip_address (str, optional)" - acknowledges optional

[analysis]

  • Review comment is CORRECT and verified
  • Type annotation -> str is violated: request.META.get() returns None if key not found
  • No test covers the edge case where both keys are absent
  • Usage in algolia.py line 57 would produce "{cache_key}:None" string if None is returned
  • search function already accepts None (line 87), so inconsistency is real

[pending]
None - verification complete

[actions]
Generate rewritten review comment confirming the issue with categorization tag

[done]
Issue confirmed: function returns None in uncovered edge case, violating str type annotation
</scratchpad_end] -->

Fix return type inconsistency in get_user_ip_address

The function annotation promises -> str, but request.META.get("REMOTE_ADDR") can return None when neither HTTP_X_FORWARDED_FOR nor REMOTE_ADDR keys exist in the request metadata. Current tests don't cover this case.

To resolve, either:

  • Change return type to str | None and update the docstring, or
  • Provide a safe default (e.g., "" or "0.0.0.0") to guarantee a string is always returned.

Note: Line 57 in backend/apps/core/api/internal/algolia.py includes ip_address in an f-string cache key, which would produce "...cache_key:None" if the value is None—likely unintended behavior.

🧹 Nitpick comments (2)
backend/apps/github/utils.py (1)

60-79: Clarify semantics of timeout when None is passed

The timeout parameter is now typed as float | None and documented as optional, but the docstring doesn’t explain what None means (effectively “no timeout” for requests.get). Consider documenting this behavior so callers know when to pass None vs relying on the default.

backend/apps/common/utils.py (1)

141-159: natural/round helpers’ type hints and docstrings match behavior

  • natural_date(value: int | str | datetime) -> str correctly describes the supported types and the existing branching logic.
  • natural_number(value: int, unit: str | None = None) -> str plus the (str, optional) docstring for unit are consistent with how the unit is appended and pluralized.
  • Updated param type details in the round_down docstring match the existing function signature and behavior.

These helpers’ public contracts now read clearly without altering runtime logic.

Also applies to: 161-173, 176-187

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a68656 and 51012fa.

📒 Files selected for processing (3)
  • backend/apps/common/utils.py (8 hunks)
  • backend/apps/github/utils.py (4 hunks)
  • backend/apps/slack/utils.py (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/apps/slack/utils.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-28T14:51:14.736Z
Learnt from: adithya-naik
Repo: OWASP/Nest PR: 1894
File: frontend/src/components/TopContributorsList.tsx:74-74
Timestamp: 2025-07-28T14:51:14.736Z
Learning: In the OWASP/Nest project, the maintainer adithya-naik prefers not to create separate components for code that's only used in two specific cases, following the YAGNI principle to avoid over-engineering when the duplication is limited and manageable.

Applied to files:

  • backend/apps/common/utils.py
🔇 Additional comments (4)
backend/apps/github/utils.py (2)

36-56: check_funding_policy_compliance: type hint and docstring are consistent

target being annotated as str | None and documented as (str, optional) matches the early return on falsy target and doesn’t change logic. This looks correct and aligned with prior review feedback on docstring style.


82-95: Return type unions str | None accurately reflect failure cases

Exposing str | None for both get_repository_path and normalize_url matches the implementation (they can fail and return no usable string), and the docstrings clearly document the None cases. From this file’s perspective, these annotations look sound; just ensure call sites are prepared to handle None if they weren’t already.

Also applies to: 97-121

backend/apps/common/utils.py (2)

8-9: Good use of TYPE_CHECKING and forward-referenced HttpRequest

Importing HttpRequest only under TYPE_CHECKING together with from __future__ import annotations keeps runtime imports minimal while giving static checkers full type info. This is a clean pattern and fits the project’s typing goals.

Also applies to: 17-19


54-68: Optional URL handling in clean_url and validate_url is consistent

Allowing url: str | None in both clean_url and validate_url and documenting as (str, optional) aligns their signatures and behavior. clean_url returning None for falsy/empty input, and validate_url returning False when url is falsy or parsing fails, form a coherent pair of helpers with clear, predictable semantics.

Also applies to: 218-236

Copy link
Collaborator

@rudransh-shrivastava rudransh-shrivastava left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
backend/apps/common/utils.py (1)

93-107: get_user_ip_address(): annotation is clear; consider ensuring a non-None fallback

Typing the parameter as HttpRequest and the return as str improves clarity. One minor point: request.META.get("REMOTE_ADDR") can technically be None, which would violate the -> str guarantee in edge cases. You might consider a default, e.g.:

-    x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
-    return x_forwarded_for.split(",")[0] if x_forwarded_for else request.META.get("REMOTE_ADDR")
+    x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
+    return (
+        x_forwarded_for.split(",")[0]
+        if x_forwarded_for
+        else request.META.get("REMOTE_ADDR", "")
+    )

to always return a string.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51012fa and 4738646.

📒 Files selected for processing (3)
  • backend/apps/common/utils.py (8 hunks)
  • backend/apps/github/utils.py (4 hunks)
  • backend/apps/slack/utils.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/apps/github/utils.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-28T14:51:14.736Z
Learnt from: adithya-naik
Repo: OWASP/Nest PR: 1894
File: frontend/src/components/TopContributorsList.tsx:74-74
Timestamp: 2025-07-28T14:51:14.736Z
Learning: In the OWASP/Nest project, the maintainer adithya-naik prefers not to create separate components for code that's only used in two specific cases, following the YAGNI principle to avoid over-engineering when the duplication is limited and manageable.

Applied to files:

  • backend/apps/common/utils.py
🧬 Code graph analysis (1)
backend/apps/common/utils.py (2)
backend/apps/github/models/release.py (1)
  • url (64-66)
backend/apps/github/models/repository.py (1)
  • url (192-194)
🔇 Additional comments (10)
backend/apps/slack/utils.py (3)

25-35: escape(): type signature is precise and consistent

Annotating escape(content: str) -> str matches the existing behavior and clarifies the expected input and output without changing semantics.


80-92: get_news_data(): timeout docstring matches annotated intent

The timeout (float, optional) docstring is now consistent with the timeout: float | None = 30 annotation and the “optional” style used elsewhere in the project.


117-137: get_staff_data(): timeout documentation aligns with typing

The updated timeout (float, optional) docstring aligns with the annotated timeout: float | None = 30 and mirrors the style used in other utilities.

backend/apps/common/utils.py (7)

8-19: TYPE_CHECKING import and HttpRequest guard are idiomatic

Using TYPE_CHECKING with a guarded HttpRequest import works well with from __future__ import annotations, keeping runtime imports minimal while giving type checkers full context.


54-68: clean_url(): annotations now match actual None semantics

The signature clean_url(url: str | None) -> str | None and the updated docstring correctly express that falsy/empty inputs and fully stripped values yield None, aligning types with existing behavior.


83-90: get_nest_user_agent(): return typing and docstring are aligned

Explicitly annotating -> str and documenting the return type clarifies the contract without changing behavior.


141-158: natural_date(): union typing and docstring accurately describe supported inputs

The value: int | str | datetime annotation plus the updated docstring match the branching logic (string parse, timestamp, or datetime passthrough) and make the API surface clearer.


161-173: natural_number(): optional unit typing matches behavior

The signature natural_number(value: int, unit: str | None = None) -> str and the unit (str, optional) docstring correctly reflect that the unit is purely additive and omitted when None.


176-187: round_down(): docstring now fully specifies parameter types

Adding (int) types for value and base in the docstring matches the function signature and clarifies the expected arguments.


218-236: validate_url(): optional URL typing lines up with existing checks

The updated signature validate_url(url: str | None) -> bool and url (str, optional) docstring align with the early if not url: return False logic and downstream parsing, accurately documenting the function’s contract.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4738646 and 6bb2d83.

📒 Files selected for processing (2)
  • backend/apps/github/utils.py (5 hunks)
  • backend/apps/slack/utils.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/apps/slack/utils.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: CodeQL (javascript-typescript)
  • GitHub Check: CodeQL (python)
  • GitHub Check: Run frontend checks
🔇 Additional comments (4)
backend/apps/github/utils.py (4)

36-36: LGTM: Optional target parameter correctly typed.

The type hint target: str | None and docstring format (str, optional) correctly reflect that the target parameter can be None, and the function logic properly handles this case.

Also applies to: 41-41


72-72: LGTM: Return type correctly reflects non-optional string.

The docstring correctly states that an empty string is returned on failure, which aligns with the -> str return type annotation and the actual implementation.


82-82: LGTM: Optional return type correctly annotated.

The return type str | None and updated docstring accurately reflect that the function can return None when URL parsing fails, matching the implementation at line 94.

Also applies to: 89-90


97-97: LGTM: Optional return type correctly annotated.

The return type str | None and docstring properly indicate that None is returned for invalid URLs, which aligns with the implementation.

Also applies to: 105-105

auto-merge was automatically disabled November 22, 2025 05:42

Head branch was pushed to by a user without write access

@sonarqubecloud
Copy link

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Type Hints to Utility Functions

3 participants