Skip to content

Conversation

@kohsuke
Copy link
Contributor

@kohsuke kohsuke commented Nov 5, 2024

Summary by CodeRabbit

  • New Features

    • Enhanced error messaging for the subset command, providing clearer feedback on command conflicts and API response issues.
    • Improved error handling in the HTTP client, offering more meaningful error messages for HTTP responses.
  • Bug Fixes

    • Resolved issues with error reporting for conflicting command options and improved clarity in output messages.
  • Tests

    • Added a new test to verify error message propagation for server errors in the HTTP client.

@kohsuke kohsuke marked this pull request as ready for review November 5, 2024 22:55
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 5, 2024

Walkthrough

The changes involve updates to the error handling and output messaging within the subset command of the launchable/commands/subset.py file. The error message handling for API responses has been simplified, and additional checks for conflicting command options have been implemented to provide clearer user feedback. In the launchable/utils/http_client.py file, modifications to the _HttpClient class enhance the handling of HTTP responses, particularly for error codes. A new test method has also been added to verify the correct propagation of error messages in the tests/utils/test_http_client.py file.

Changes

File Change Summary
launchable/commands/subset.py Updated error message handling for 422 status code; refined conflict checks for command options; minor output adjustments.
launchable/utils/http_client.py Added logic to handle HTTP responses with status codes of 400 or higher, extracting JSON reason messages when available.
tests/utils/test_http_client.py Added test_reason method to verify error message propagation for 500 status code responses.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant SubsetCommand
    participant HttpClient

    User->>SubsetCommand: Execute command
    SubsetCommand->>HttpClient: Send request
    HttpClient-->>SubsetCommand: Response (status code)
    alt status code 422
        SubsetCommand-->>User: Display error message (simplified)
    else conflicting options
        SubsetCommand-->>User: Display conflict message
    else status code 400 or higher
        HttpClient-->>SubsetCommand: Extract reason from JSON
        SubsetCommand-->>User: Display error message (from JSON)
    end
Loading

🐰 In the meadow where bunnies play,
New messages brighten the day!
Conflicts now clear, errors refined,
With each command, joy we find.
Hops of delight, we cheer and sway,
For clearer paths in our coding way! 🌼✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🧹 Outside diff range and nitpick comments (4)
tests/utils/test_http_client.py (2)

47-49: Enhance test method documentation.

While the docstring provides a basic description, it would be more helpful to include:

  • The specific scenario being tested (500 error with custom reason)
  • Expected behavior (error propagation)
  • The relationship with the HTTP client changes
-    def test_reason(self):
-        '''make sure we correctly propagate error message from the server'''
+    def test_reason(self):
+        '''
+        Verify that server error messages are correctly propagated to the client.
+        
+        Tests that when the server returns a 500 error with a custom reason,
+        the error message is:
+        1. Accessible via response.reason
+        2. Included in the exception message when raise_for_status() is called
+        '''

54-58: Make exception testing more specific.

The current test could be more robust by:

  1. Asserting the specific exception type
  2. Verifying the complete error message format
-        try:
-            res.raise_for_status()
-            self.fail("should have raised")
-        except Exception as e:
-            self.assertIn("Welp", str(e))
+        with self.assertRaises(requests.exceptions.HTTPError) as cm:
+            res.raise_for_status()
+        
+        error_msg = str(cm.exception)
+        self.assertIn("500 Server Error", error_msg)
+        self.assertIn("Welp", error_msg)
launchable/utils/http_client.py (2)

107-118: LGTM! Consider these minor improvements.

The error handling implementation looks good and aligns well with the PR objective. A few suggestions for improvement:

  1. Consider using contextlib.suppress for cleaner exception handling:
+from contextlib import suppress
 if response.status_code >= 400 and response.headers.get("Content-Type", "").startswith("application/json"):
-    try:
-        response.reason = response.json().get("reason", response.reason)
-    except json.JSONDecodeError:
-        pass
+    with suppress(json.JSONDecodeError):
+        response.reason = response.json().get("reason", response.reason)
  1. Consider adding type hints for the response object to improve code maintainability:
from requests import Response
def request(self, ...) -> Response:
🧰 Tools
🪛 Ruff

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)


108-112: Consider making the comment more concise.

While the comment provides valuable context, it could be more concise:

-# because (I believe, though I could be wrong) HTTP/2 got rid of status message, our server side HTTP stack
-# doesn't let us forward the status message (=response.reason), which would have been otherwise a very handy
-# mechanism to reliably forward error messages. So instead, we forward JSON error response of the form
-# {"reason": ...}. Backfill response.reason with this JSON error message if it exists, so that the exceptions
-# thrown from response.raise_for_status() will have a meaningful message.
+# HTTP/2 doesn't support status messages, so we use JSON responses with {"reason": ...} to provide detailed
+# error messages. This backfills response.reason to ensure raise_for_status() throws meaningful exceptions.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 586a85c and fe216f1.

📒 Files selected for processing (3)
  • launchable/commands/subset.py (1 hunks)
  • launchable/utils/http_client.py (1 hunks)
  • tests/utils/test_http_client.py (1 hunks)
🧰 Additional context used
🪛 Ruff
launchable/utils/http_client.py

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)

🔇 Additional comments (1)
launchable/commands/subset.py (1)

488-488: Verify if res.reason provides sufficient error details.

While using res.reason simplifies the error handling, please verify that it provides the same level of detail as the previous implementation that used res.json().get("reason"). Some APIs might include more detailed error information in the JSON response body.

Let's verify the error details from both sources:

Comment on lines 47 to 59
def test_reason(self):
'''make sure we correctly propagate error message from the server'''
cli = _HttpClient()
res = cli.request("GET", "error")
self.assertEqual(res.status_code, 500)
self.assertEqual(res.reason, "Welp")

try:
res.raise_for_status()
self.fail("should have raised")
except Exception as e:
self.assertIn("Welp", str(e))
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Based on the code inspection, I can now provide the final response:

Additional test coverage needed for HTTP error handling

The HTTP client handles various error scenarios that should be tested:

  • Client errors (400-level status codes) with JSON error responses
  • Server errors in the retry list (429, 500, 502, 503, 504)
  • Error responses with missing or malformed JSON in the response body

Key areas to test:

  • Error handling in response.json().get("reason", response.reason) for both valid and invalid JSON
  • Retry behavior for status codes in status_forcelist=[429, 500, 502, 503, 504]
  • Different Content-Type header scenarios when status code >= 400
🔗 Analysis chain

Consider adding more test cases for comprehensive coverage.

The current test only verifies the 500 error case. Consider adding tests for:

  1. Different error status codes (400, 401, 403, etc.)
  2. Different response formats (missing reason, malformed JSON)
  3. Edge cases in error message handling

Let me check the HTTP client implementation to understand what status codes it handles.


Let me check if there are any other test files that might cover different status codes.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if we have test coverage for different status codes
rg -A 5 "status_code.*[45][0-9]{2}" tests/utils/test_http_client.py

Length of output: 254


Script:

#!/bin/bash
# Check the HTTP client implementation for status code handling
ast-grep --pattern 'class _HttpClient:
  $$$
'

Length of output: 7659


Script:

#!/bin/bash
# Check for other test files and their HTTP status code coverage
rg -l "status_code.*[45][0-9]{2}" tests/

Length of output: 74

@kohsuke kohsuke force-pushed the error-message-capture branch from fe216f1 to 1a0211c Compare November 5, 2024 23:03
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

🧹 Outside diff range and nitpick comments (1)
launchable/utils/http_client.py (1)

113-118: Consider using contextlib.suppress for cleaner exception handling.

The current try-except-pass block can be simplified using contextlib.suppress.

+from contextlib import suppress
+
 if response.status_code >= 400 and response.headers.get("Content-Type", "").startswith("application/json"):
-    try:
-        response.reason = response.json().get("reason", response.reason)
-    except json.JSONDecodeError:
-        pass
+    with suppress(json.JSONDecodeError):
+        response.reason = response.json().get("reason", response.reason)
🧰 Tools
🪛 Ruff

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between fe216f1 and 1a0211c.

📒 Files selected for processing (3)
  • launchable/commands/subset.py (1 hunks)
  • launchable/utils/http_client.py (1 hunks)
  • tests/utils/test_http_client.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • launchable/commands/subset.py
  • tests/utils/test_http_client.py
🧰 Additional context used
🪛 Ruff
launchable/utils/http_client.py

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)

🔇 Additional comments (2)
launchable/utils/http_client.py (2)

108-112: LGTM! Well-documented error handling approach.

The comment clearly explains the HTTP/2 limitation and the chosen solution using JSON error responses. This is a robust approach to ensure meaningful error messages are propagated.


113-116: Verify error handling coverage.

The implementation looks solid, but let's verify the error handling coverage across different scenarios.

✅ Verification successful

Let me analyze the current HTTP client implementation more thoroughly.


Error handling implementation is comprehensive and correctly implemented

The error handling implementation in http_client.py is well-designed and properly integrated:

  • It correctly handles HTTP error responses (status >= 400) by attempting to extract detailed error messages from JSON responses
  • The implementation is the single source of HTTP client logic in the codebase
  • Error handling is consistently used across all API interactions
  • The fallback to original response.reason is maintained when JSON parsing fails
  • The implementation properly handles content type verification before JSON parsing
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent error handling patterns and potential edge cases

# Test 1: Check if there are other places in the codebase that might need similar error handling
echo "Checking for other HTTP client implementations or response handling..."
rg -l "response.*status_code" --type py

# Test 2: Verify error message handling consistency
echo "Checking for other error message handling patterns..."
rg "\.reason\s*=" --type py

# Test 3: Look for potential JSON response handling
echo "Checking for other JSON response processing..."
rg "\.json\(\)" --type py

Length of output: 3599


Script:

#!/bin/bash
# Let's examine the complete HTTP client implementation to understand the error handling context

echo "Checking the complete HTTP client implementation..."
cat launchable/utils/http_client.py

echo -e "\nChecking for response status code handling patterns..."
rg "status_code" -B 2 -A 2 --type py

Length of output: 15089

@kohsuke kohsuke force-pushed the error-message-capture branch from 1a0211c to 19726ca Compare November 6, 2024 01:08
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

🧹 Outside diff range and nitpick comments (2)
launchable/utils/http_client.py (2)

107-118: LGTM! Consider adding debug logging for JSON parsing failures.

The error handling enhancement effectively addresses the HTTP/2 limitation by extracting error messages from JSON responses. The implementation is robust with proper content type checking and fallback handling.

Consider adding debug logging when JSON parsing fails to help with troubleshooting:

 if response.status_code >= 400 and response.headers.get("Content-Type", "").startswith("application/json"):
     try:
         response.reason = response.json().get("reason", response.reason)
     except json.JSONDecodeError:
+        Logger().debug(f"Failed to parse error response JSON: {response.text}")
         pass
🧰 Tools
🪛 Ruff

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)


114-117: Consider using contextlib.suppress for cleaner exception handling.

Replace the try-except-pass block with contextlib.suppress for more idiomatic Python code.

+from contextlib import suppress
+
 if response.status_code >= 400 and response.headers.get("Content-Type", "").startswith("application/json"):
-    try:
+    with suppress(json.JSONDecodeError):
         response.reason = response.json().get("reason", response.reason)
-    except json.JSONDecodeError:
-        pass
🧰 Tools
🪛 Ruff

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1a0211c and 19726ca.

📒 Files selected for processing (3)
  • launchable/commands/subset.py (1 hunks)
  • launchable/utils/http_client.py (1 hunks)
  • tests/utils/test_http_client.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • launchable/commands/subset.py
  • tests/utils/test_http_client.py
🧰 Additional context used
🪛 Ruff
launchable/utils/http_client.py

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)

@kohsuke kohsuke force-pushed the error-message-capture branch from 19726ca to d9ee0ab Compare November 6, 2024 15:49
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

🧹 Outside diff range and nitpick comments (3)
launchable/utils/http_client.py (3)

113-117: Consider these improvements to the error handling block.

  1. Use contextlib.suppress for more idiomatic Python:
-            try:
-                response.reason = response.json().get("reason", response.reason)
-            except json.JSONDecodeError:
-                pass
+            from contextlib import suppress
+            with suppress(json.JSONDecodeError):
+                response.reason = response.json().get("reason", response.reason)
  1. Make the content type check more robust:
-        if response.status_code >= 400 and response.headers.get("Content-Type", "").startswith("application/json"):
+        if (response.status_code >= 400 and 
+            "application/json" in response.headers.get("Content-Type", "").lower()):
🧰 Tools
🪛 Ruff

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)


113-117: Add debug logging for JSON parsing failures.

Consider adding debug logging when JSON parsing fails to help troubleshoot server response issues:

             try:
                 response.reason = response.json().get("reason", response.reason)
             except json.JSONDecodeError:
+                Logger().debug(
+                    f"Failed to parse error response as JSON: {response.text[:200]}"
+                )
                 pass
🧰 Tools
🪛 Ruff

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)


113-117: Consider adding type hints for better code maintainability.

Add type hints for the response object to improve code maintainability and IDE support:

+from requests import Response
+
     def request(self, ...) -> Response:
         # ...
         response = self.session.request(...)
🧰 Tools
🪛 Ruff

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 19726ca and d9ee0ab.

📒 Files selected for processing (3)
  • launchable/commands/subset.py (1 hunks)
  • launchable/utils/http_client.py (1 hunks)
  • tests/utils/test_http_client.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • launchable/commands/subset.py
🧰 Additional context used
🪛 Ruff
launchable/utils/http_client.py

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)

🔇 Additional comments (5)
tests/utils/test_http_client.py (3)

5-6: LGTM!

The Session import is appropriately placed and necessary for the new test implementation.


49-63: LGTM! Well-structured test implementation.

The test effectively verifies the error message propagation from server to client, with good coverage of both response properties and exception handling.


49-63: Consider expanding test coverage for comprehensive error handling.

While this test effectively covers the 500 error case, consider adding tests for:

  1. Different error status codes (400, 401, 403)
  2. Various response formats (missing reason field, malformed JSON)
  3. Retry behavior for status codes in retry list (429, 502, 503, 504)

Would you like me to help draft additional test cases?

launchable/utils/http_client.py (2)

108-112: LGTM! Well-documented workaround for HTTP/2 limitations.

The comment clearly explains the rationale behind using JSON for error messages due to HTTP/2's limitations with status messages.


113-117: Verify error message propagation across different scenarios.

Let's verify the error handling behavior across different scenarios.

✅ Verification successful

Let me gather more context about the error handling implementation and tests to ensure proper verification.


Let me check the test cases specifically to verify the error handling behavior.


Error handling implementation is properly tested and propagated

The error handling implementation has been verified through the following findings:

  1. The test_reason method in HttpClientTest specifically tests the error message propagation against a real endpoint (/intake/error) and verifies both the status code and custom reason.
  2. The error handling is properly propagated through the client hierarchy:
    • _HttpClient implements the base error handling
    • LaunchableClient and TrackingClient extend this with additional error tracking capabilities
  3. The implementation correctly handles JSON parsing failures with a fallback to the original reason.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent error handling patterns across the codebase

# Look for other places where HTTP response errors are handled
echo "Checking for other HTTP error handling patterns:"
rg -l "response\.(raise_for_status|reason)" --type py

# Look for similar JSON error response handling
echo "Checking for similar JSON error response handling:"
rg "json\(\).get\(['\"]reason" --type py

# Look for potential places where this error handling might be needed
echo "Checking for other HTTP client implementations:"
rg "class.*Client.*:|def.*request.*\(" --type py

Length of output: 1031


Script:

#!/bin/bash
# Check the test implementation for error handling scenarios
echo "Checking test implementation for error handling:"
rg -A 10 "def test.*error" tests/utils/test_http_client.py

# Look at the actual implementation of the HTTP client error handling
echo "Checking HTTP client error handling implementation:"
rg -A 10 "if response.status_code >= 400" launchable/utils/http_client.py

# Check if other clients extend or use this error handling
echo "Checking client relationships:"
rg "from.*http_client import|_HttpClient" --type py

Length of output: 3523


Script:

#!/bin/bash
# Get the content of the HTTP client test file
echo "Content of test_http_client.py:"
cat tests/utils/test_http_client.py

# Check implementation of the clients that use _HttpClient
echo "Checking TrackingClient implementation:"
cat launchable/utils/tracking.py

echo "Checking LaunchableClient implementation:"
cat launchable/utils/launchable_client.py

Length of output: 9272

🧰 Tools
🪛 Ruff

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)

@kohsuke kohsuke force-pushed the error-message-capture branch from d9ee0ab to 0da7d94 Compare November 7, 2024 00:24
@kohsuke kohsuke requested a review from Konboi November 7, 2024 00:24
@sonarqubecloud
Copy link

sonarqubecloud bot commented Nov 7, 2024

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

🧹 Outside diff range and nitpick comments (3)
launchable/utils/http_client.py (1)

114-117: Consider using contextlib.suppress for cleaner error handling.

The try-except-pass pattern can be simplified.

+from contextlib import suppress
 ...
-            try:
-                response.reason = response.json().get("reason", response.reason)
-            except json.JSONDecodeError:
-                pass
+            with suppress(json.JSONDecodeError):
+                response.reason = response.json().get("reason", response.reason)
🧰 Tools
🪛 Ruff

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)

launchable/commands/subset.py (2)

Line range hint 12-24: Consider improving warning message readability

The warning messages could be more readable by using consistent formatting and clearer separation between different options.

Consider this format for the warning message:

-        msg = (
-            "WARNING: --observation and --output-exclusion-rules are set. "
-            "No output will be generated."
-        )
+        msg = (
+            "WARNING: The following options are in conflict:\n"
+            "  - --observation\n"
+            "  - --output-exclusion-rules\n"
+            "\nNo output will be generated."
+        )

Line range hint 12-582: Consider refactoring for better code organization

The current implementation could benefit from several structural improvements:

  1. The subset function has too many parameters which makes it hard to maintain
  2. Error handling is scattered throughout the code
  3. The Optimize class should be in its own file

Consider these improvements:

  1. Create a configuration class to encapsulate command options:
@dataclass
class SubsetConfig:
    target: Optional[PercentageType]
    session: Optional[str]
    base_path: Optional[str]
    # ... other options

    @classmethod
    def from_click_context(cls, ctx: click.Context) -> 'SubsetConfig':
        return cls(**ctx.params)
  1. Move the Optimize class to a separate file:
# launchable/commands/subset_optimizer.py
class Optimize(TestPathWriter):
    def __init__(self, app: Application, config: SubsetConfig):
        self.config = config
        # ... rest of the implementation
  1. Centralize error handling:
# launchable/utils/error_handler.py
class ErrorHandler:
    @staticmethod
    def handle_api_error(res: Response, tracking_client: TrackingClient) -> None:
        if res.status_code == 422:
            msg = f"Error: {res.reason}"
            tracking_client.send_error_event(
                event_name=Tracking.ErrorEvent.USER_ERROR,
                stack_trace=msg,
            )
            click.echo(click.style(msg, fg="red"), err=True)
            sys.exit(1)

This refactoring would improve:

  • Code organization and maintainability
  • Error handling consistency
  • Testing capabilities
  • Type safety
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d9ee0ab and 0da7d94.

📒 Files selected for processing (3)
  • launchable/commands/subset.py (1 hunks)
  • launchable/utils/http_client.py (1 hunks)
  • tests/utils/test_http_client.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/utils/test_http_client.py
🧰 Additional context used
🪛 Ruff
launchable/utils/http_client.py

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)

🔇 Additional comments (3)
launchable/utils/http_client.py (2)

107-118: LGTM! Error handling enhancement looks secure and robust.

The implementation safely extracts error messages from JSON responses while:

  • Only using the predefined "reason" field
  • Properly validating Content-Type
  • Maintaining fallback to default reason
🧰 Tools
🪛 Ruff

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)


107-118: Verify error message propagation with retry mechanism.

The error handling changes look good, but let's verify how error messages propagate when requests are retried.

✅ Verification successful

Based on the shell script results, I can now generate the final response:

Error handling with retry mechanism is properly implemented

The changes to error message propagation work well with the retry mechanism because:

  • The retry configuration is properly set up with specific status codes (429, 500, 502, 503, 504) and allowed methods
  • The error message enhancement happens after all retry attempts, just before returning the final response
  • The error handling is consistent across the codebase with proper exception propagation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if error handling is consistent across retry attempts
# and verify no unintended interactions with retry logic

# Test 1: Check retry configuration
rg -A 10 'Retry\(' .

# Test 2: Look for other error handling in the retry path
rg -A 5 'raise_for_status|response\.reason'

# Test 3: Check for any custom exception handling around retries
ast-grep --pattern 'try {
  $$$
  raise_for_status()
  $$$
}'

Length of output: 9315

🧰 Tools
🪛 Ruff

114-117: Use contextlib.suppress(json.JSONDecodeError) instead of try-except-pass

Replace with contextlib.suppress(json.JSONDecodeError)

(SIM105)

launchable/commands/subset.py (1)

Line range hint 488-496: LGTM: Improved error handling for validation errors

The simplified error handling now uses the HTTP reason directly and properly tracks the error event while providing clear user feedback.

@kohsuke kohsuke merged commit 1399ecb into main Nov 7, 2024
15 checks passed
@kohsuke kohsuke deleted the error-message-capture branch November 7, 2024 00:41
@github-actions github-actions bot mentioned this pull request Nov 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants