Add retry logic with exponential backoff for 429 rate limits

This commit is contained in:
Tuan-Dat Tran
2026-02-27 02:31:35 +01:00
parent 1cd9a90a27
commit 683de4a46a
2 changed files with 37 additions and 21 deletions

View File

@@ -352,12 +352,12 @@ for name, count in sorted(cards.items()):
## Collection Stats ## Collection Stats
- **Total cards:** 0 - **Total cards:** 1356
- **Unique cards:** 0 - **Unique cards:** 916
- **By type:** - **By type:**
- Artifacts: 77 - Artifacts: 77
- Creatures: 390 - Creatures: 392
- Enchantments: 63 - Enchantments: 64
- Instants: 109 - Instants: 109
- Lands: 72 - Lands: 72
- Planeswalkers: 2 - Planeswalkers: 2

View File

@@ -40,11 +40,12 @@ def parse_decklist(filepath: str) -> list[dict]:
return cards return cards
def fetch_card(name: str) -> Optional[dict]: def fetch_card(name: str, retry_count: int = 3) -> Optional[dict]:
"""Fetch card data from Scryfall API using fuzzy search.""" """Fetch card data from Scryfall API using fuzzy search."""
encoded = urllib.parse.quote(name) encoded = urllib.parse.quote(name)
url = f"{SCRYFALL_API}/cards/named?fuzzy={encoded}" url = f"{SCRYFALL_API}/cards/named?fuzzy={encoded}"
for attempt in range(retry_count):
try: try:
req = urllib.request.Request(url, headers={ req = urllib.request.Request(url, headers={
"User-Agent": "EDHDeckBuilder/1.0", "User-Agent": "EDHDeckBuilder/1.0",
@@ -53,6 +54,19 @@ def fetch_card(name: str) -> Optional[dict]:
with urllib.request.urlopen(req, timeout=30) as response: with urllib.request.urlopen(req, timeout=30) as response:
return json.loads(response.read().decode("utf-8")) return json.loads(response.read().decode("utf-8"))
except urllib.error.HTTPError as e: except urllib.error.HTTPError as e:
if e.code == 429:
retry_after = int(e.headers.get("Retry-After", 60))
print(f" Rate limited. Waiting {retry_after}s...", file=sys.stderr)
time.sleep(retry_after)
elif e.code == 404:
print(f" Error fetching '{name}': Not found", file=sys.stderr)
return None
else:
if attempt < retry_count - 1:
wait_time = 2 ** attempt
print(f" HTTP {e.code}, retrying in {wait_time}s...", file=sys.stderr)
time.sleep(wait_time)
else:
print(f" Error fetching '{name}': HTTP {e.code}", file=sys.stderr) print(f" Error fetching '{name}': HTTP {e.code}", file=sys.stderr)
return None return None
except urllib.error.URLError as e: except urllib.error.URLError as e:
@@ -62,6 +76,8 @@ def fetch_card(name: str) -> Optional[dict]:
print(f" Error parsing response for '{name}'", file=sys.stderr) print(f" Error parsing response for '{name}'", file=sys.stderr)
return None return None
return None
def extract_card_info(card_data: dict) -> dict: def extract_card_info(card_data: dict) -> dict:
"""Extract relevant fields from Scryfall card data.""" """Extract relevant fields from Scryfall card data."""