Handle Wikidata API errors with proper HTTP responses (429, 503)

Also retry on RequestException and raise QueryError on non-200 responses.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Edward Betts 2026-05-15 15:19:02 +00:00
parent d75d617d11
commit 1442620ece
2 changed files with 15 additions and 2 deletions

View file

@ -110,13 +110,16 @@ Row = dict[str, dict[str, typing.Any]]
Hit = dict[str, str | int | None] Hit = dict[str, str | int | None]
@backoff.on_exception(backoff.expo, QueryError, max_tries=5) @backoff.on_exception(backoff.expo, (QueryError, RequestException), max_tries=5)
def wdqs(query: str) -> list[Row]: def wdqs(query: str) -> list[Row]:
"""Pass query to the Wikidata Query Service.""" """Pass query to the Wikidata Query Service."""
r = requests.post( r = requests.post(
wikidata_query_api_url, data={"query": query, "format": "json"}, headers=headers wikidata_query_api_url, data={"query": query, "format": "json"}, headers=headers
) )
if not r.ok:
raise QueryError(query, r)
try: try:
return typing.cast(list[Row], r.json()["results"]["bindings"]) return typing.cast(list[Row], r.json()["results"]["bindings"])
except requests.exceptions.JSONDecodeError: except requests.exceptions.JSONDecodeError:

View file

@ -10,6 +10,7 @@ import traceback
import typing import typing
from time import time from time import time
import requests.exceptions
import sqlalchemy.exc import sqlalchemy.exc
import werkzeug.debug.tbtools import werkzeug.debug.tbtools
from flask import Flask, jsonify, redirect, render_template, request, url_for from flask import Flask, jsonify, redirect, render_template, request, url_for
@ -375,7 +376,16 @@ def index() -> str | Response:
return jsonify(coords={"lat": lat, "lon": lon}, error=error_msg) return jsonify(coords={"lat": lat, "lon": lon}, error=error_msg)
needs_commons = request.args.get("needs_commons", "true").lower() != "false" needs_commons = request.args.get("needs_commons", "true").lower() != "false"
try:
result = lat_lon_to_wikidata(lat, lon, needs_commons=needs_commons)["result"] result = lat_lon_to_wikidata(lat, lon, needs_commons=needs_commons)["result"]
except (wikidata.QueryError, wikidata.APIResponseError) as e:
r = e.r if isinstance(e, wikidata.QueryError) else e.response
if r.status_code == 429:
extra = {"Retry-After": r.headers["Retry-After"]} if "Retry-After" in r.headers else {}
return jsonify(error="Rate limited by Wikidata, please try again later"), 429, extra
return jsonify(error=f"Wikidata query failed (HTTP {r.status_code}), please try again later"), 503
except requests.exceptions.RequestException:
return jsonify(error="Could not connect to Wikidata, please try again later"), 503
result.pop("element", None) result.pop("element", None)
result.pop("geojson", None) result.pop("geojson", None)
if logging_enabled: if logging_enabled: