fix: add timeout and error handling to Codeforces and LeetCode API calls#219
fix: add timeout and error handling to Codeforces and LeetCode API calls#219francisofficial01 wants to merge 2 commits into
Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 43 minutes and 11 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughThe PR adds timeout parameters (10 seconds) and exception handling to all external API calls in the leaderboard views, replacing simple status-code checks with try/except blocks that return sensible fallback values on network failures or malformed responses. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
api/leaderboard/views.py (2)
487-513:⚠️ Potential issue | 🔴 CriticalDon't change
get_leetcode_datato return truthy sentinels without updating callers.This method now returns
"NOT_FOUND"/"TRANSIENT_ERROR", but Line 524 still does a truthiness check and Lines 525-530 indexuser_dataas a dict. For any outdated user on a 404 or transient upstream error,get()will now crash with aTypeError.One safe option: keep the old falsy contract
try: response = requests.get(url, timeout=10) if response.status_code == 404: - return "NOT_FOUND" + return None if response.status_code != 200: logger.error(f"LeetCode server error for {username}: Status {response.status_code}") - return "TRANSIENT_ERROR" + return None @@ except requests.exceptions.Timeout: logger.error(f"LeetCode timeout for {username}") - return "TRANSIENT_ERROR" + return None except Exception as e: logger.error(f"Error fetching LeetCode data for {username}: {e}") - return "TRANSIENT_ERROR" + return None🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@api/leaderboard/views.py` around lines 487 - 513, get_leetcode_data now returns string sentinels ("NOT_FOUND"/"TRANSIENT_ERROR") which breaks existing callers that expect a falsy value (e.g., None/False) and then index user_data as a dict; revert the sentinel returns to the previous falsy contract (return None) for 404 and transient errors, keep logging the specific condition (use logger.error with the status or exception) and ensure the success path still returns the dict with keys like "ranking", "easy_solved", etc.; update only get_leetcode_data (the function) so callers that do a truthiness check and then index user_data continue to work without changes.
348-409:⚠️ Potential issue | 🔴 CriticalImport
BeautifulSoupbefore using it at line 358.The code uses
BeautifulSoup(response.text, "html.parser")but the file does not import it. This will cause aNameErroron any successful CodeChef fetch. Whilebeautifulsoup4is declared as a project dependency, the import statement is missing from this file.Required import
+from bs4 import BeautifulSoup🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@api/leaderboard/views.py` around lines 348 - 409, The file uses BeautifulSoup in the CodeChef fetch logic (BeautifulSoup(response.text, "html.parser") inside the function handling CodeChef responses) but never imports it; add the missing import "from bs4 import BeautifulSoup" near the top import block (alongside other third-party imports) so BeautifulSoup is defined when the code reaches that line, then run linters/tests to confirm no unused-import warnings or import-order issues.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@api/leaderboard/views.py`:
- Around line 292-314: The get_codeforces_data function has misindented blocks:
align the except requests.RequestException as e: block with its corresponding
try in get_codeforces_data so the except, response.json parsing, status checks,
and return statements are inside the function body correctly (ensure
response.raise_for_status() is inside try and any early returns remain correct).
After fixing indentation, move the CodeforcesUserViewSet class out of the
get_codeforces_data function (dedent it to module level) so it is defined at
top-level and available to DRF routing; reference the get_codeforces_data
function name and CodeforcesUserViewSet class name when locating the blocks to
fix. Ensure no leftover nested indentation or unreachable code remains.
---
Outside diff comments:
In `@api/leaderboard/views.py`:
- Around line 487-513: get_leetcode_data now returns string sentinels
("NOT_FOUND"/"TRANSIENT_ERROR") which breaks existing callers that expect a
falsy value (e.g., None/False) and then index user_data as a dict; revert the
sentinel returns to the previous falsy contract (return None) for 404 and
transient errors, keep logging the specific condition (use logger.error with the
status or exception) and ensure the success path still returns the dict with
keys like "ranking", "easy_solved", etc.; update only get_leetcode_data (the
function) so callers that do a truthiness check and then index user_data
continue to work without changes.
- Around line 348-409: The file uses BeautifulSoup in the CodeChef fetch logic
(BeautifulSoup(response.text, "html.parser") inside the function handling
CodeChef responses) but never imports it; add the missing import "from bs4
import BeautifulSoup" near the top import block (alongside other third-party
imports) so BeautifulSoup is defined when the code reaches that line, then run
linters/tests to confirm no unused-import warnings or import-order issues.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3cedef02-7399-4212-a8ce-bc89cc976d38
📒 Files selected for processing (1)
api/leaderboard/views.py
| try: | ||
| response = requests.get(url, timeout=10) | ||
| response.raise_for_status() | ||
| except requests.RequestException as e: | ||
| print(f"Request failed: {e}") | ||
| return None | ||
|
|
||
| if response.status_code == 200: | ||
| data = response.json() | ||
| if data.get("status") == "OK" and data.get("result"): | ||
| user_data = data["result"][0] | ||
| return { | ||
| "rating": user_data.get("rating", 0), | ||
| "max_rating": user_data.get("maxRating", 0), | ||
| "last_activity": user_data.get( | ||
| "lastOnlineTimeSeconds", MAX_DATE_TIMESTAMP | ||
| ), | ||
| "last_activity": user_data.get("lastOnlineTimeSeconds", MAX_DATE_TIMESTAMP), | ||
| "avatar": user_data.get("titlePhoto", ""), | ||
| } | ||
|
|
||
| queryset = codeforcesUser.objects.all() | ||
| serializer_class = CF_Serializer | ||
| return None # Explicit fallback | ||
|
|
||
|
|
||
| class CodeforcesUserViewSet(viewsets.ModelViewSet): # or wherever these belong | ||
| queryset = codeforcesUser.objects.all() | ||
| serializer_class = CF_Serializer |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '290,314p' api/leaderboard/views.py | nl -ba
python - <<'PY'
from pathlib import Path
import ast
path = Path("api/leaderboard/views.py")
source = path.read_text()
try:
tree = ast.parse(source)
print("AST parse: OK")
except SyntaxError as e:
print(f"SyntaxError: line={e.lineno}, offset={e.offset}, msg={e.msg}")
PYRepository: OpenLake/Leaderboard-Pro
Length of output: 110
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read the relevant section with line numbers
echo "=== Lines 290-320 of api/leaderboard/views.py ==="
head -320 api/leaderboard/views.py | tail -31 | cat -n
# Check for syntax errors
echo ""
echo "=== AST Parse Check ==="
python3 <<'PY'
from pathlib import Path
import ast
path = Path("api/leaderboard/views.py")
source = path.read_text()
try:
tree = ast.parse(source)
print("AST parse: OK - No syntax errors")
except SyntaxError as e:
print(f"SyntaxError found:")
print(f" Line: {e.lineno}")
print(f" Offset: {e.offset}")
print(f" Message: {e.msg}")
print(f" Text: {e.text}")
PYRepository: OpenLake/Leaderboard-Pro
Length of output: 1762
Fix indentation and class scope in get_codeforces_data.
The code has critical syntax errors: the except block (line 295 onwards) is misindented and does not match the try block, causing an AST parse failure. Additionally, CodeforcesUserViewSet (line 312) is incorrectly nested inside the get_codeforces_data method rather than defined at the module level, making it unreachable to DRF routing even after fixing the indentation.
Proposed fix
def get_codeforces_data(self, username):
url = f"https://codeforces.com/api/user.info?handles={username}"
- try:
- response = requests.get(url, timeout=10)
- response.raise_for_status()
- except requests.RequestException as e:
- print(f"Request failed: {e}")
- return None
-
- data = response.json()
- if data.get("status") == "OK" and data.get("result"):
- user_data = data["result"][0]
- return {
- "rating": user_data.get("rating", 0),
- "max_rating": user_data.get("maxRating", 0),
- "last_activity": user_data.get("lastOnlineTimeSeconds", MAX_DATE_TIMESTAMP),
- "avatar": user_data.get("titlePhoto", ""),
- }
-
- return None # Explicit fallback
-
- class CodeforcesUserViewSet(viewsets.ModelViewSet): # or wherever these belong
- queryset = codeforcesUser.objects.all()
- serializer_class = CF_Serializer
+ try:
+ response = requests.get(url, timeout=10)
+ response.raise_for_status()
+ except requests.RequestException as e:
+ logger.warning("Codeforces request failed for %s: %s", username, e)
+ return None
+
+ data = response.json()
+ if data.get("status") == "OK" and data.get("result"):
+ user_data = data["result"][0]
+ return {
+ "rating": user_data.get("rating", 0),
+ "max_rating": user_data.get("maxRating", 0),
+ "last_activity": user_data.get("lastOnlineTimeSeconds", MAX_DATE_TIMESTAMP),
+ "avatar": user_data.get("titlePhoto", ""),
+ }
+
+ return NoneMove CodeforcesUserViewSet to module level after fixing indentation.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@api/leaderboard/views.py` around lines 292 - 314, The get_codeforces_data
function has misindented blocks: align the except requests.RequestException as
e: block with its corresponding try in get_codeforces_data so the except,
response.json parsing, status checks, and return statements are inside the
function body correctly (ensure response.raise_for_status() is inside try and
any early returns remain correct). After fixing indentation, move the
CodeforcesUserViewSet class out of the get_codeforces_data function (dedent it
to module level) so it is defined at top-level and available to DRF routing;
reference the get_codeforces_data function name and CodeforcesUserViewSet class
name when locating the blocks to fix. Ensure no leftover nested indentation or
unreachable code remains.
Fixes #208
Changes
Why
Without a timeout, the server hangs indefinitely if Codeforces/LeetCode is slow or down.
The LeetCode proxy (onrender.com free tier) frequently returns non-200 responses,
causing an unhandled KeyError crash in the caller.
Summary by CodeRabbit