From b7ba24b567b21138bccff179fa8500d3f699ad1e Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Sat, 22 Aug 2026 21:36:03 +0100 Subject: [PATCH] Handle Wikipedia category API failures --- main.py | 16 +++++++ templates/category.html | 2 +- tests/test_category_search.py | 85 +++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 tests/test_category_search.py diff --git a/main.py b/main.py index 111c7bc..ee6fee4 100755 --- a/main.py +++ b/main.py @@ -354,6 +354,7 @@ class CategoryResult: articles: list[ArticleWithoutImage] gcmcontinue: str | None + error: str | None = None # Common non-content images to ignore when checking if an article has images @@ -435,6 +436,7 @@ def get_articles_without_images( articles_without_images: list[ArticleWithoutImage] = [] seen_pageids: set[int] = set() next_gcmcontinue: str | None = None + error: str | None = None # Build initial continue params from the external pagination token continue_params: dict[str, str] = {} @@ -451,8 +453,20 @@ def get_articles_without_images( ) response.raise_for_status() data = response.json() + if api_error := data.get("error"): + raise requests.RequestException( + f"{api_error.get('code', 'unknown')}: " + f"{api_error.get('info', 'unknown Wikipedia API error')}" + ) except (requests.RequestException, json.JSONDecodeError) as e: print(f"Wikipedia API error: {e}") + if articles_without_images: + error = ( + "Wikipedia stopped responding before the whole category could " + "be checked. Showing partial results; please try again." + ) + else: + error = "Unable to load articles from Wikipedia. Please try again." break pages = data.get("query", {}).get("pages", {}) @@ -491,6 +505,7 @@ def get_articles_without_images( return CategoryResult( articles=articles_without_images, gcmcontinue=next_gcmcontinue, + error=error, ) @@ -891,6 +906,7 @@ def category_search() -> str: category_name=category_name, articles=result.articles, gcmcontinue=result.gcmcontinue, + error=result.error, ) diff --git a/templates/category.html b/templates/category.html index 62fcb1a..2fe8df8 100644 --- a/templates/category.html +++ b/templates/category.html @@ -50,7 +50,7 @@ {% endif %} - {% else %} + {% elif not error %}
All articles in this category have images!
diff --git a/tests/test_category_search.py b/tests/test_category_search.py new file mode 100644 index 0000000..8a22e1f --- /dev/null +++ b/tests/test_category_search.py @@ -0,0 +1,85 @@ +"""Tests for Wikipedia category searches.""" + +from unittest.mock import patch + +import requests + +from main import CategoryResult, app, get_articles_without_images + + +def test_api_failure_is_not_reported_as_an_empty_success(): + with patch("main.requests.get", side_effect=requests.ConnectionError("offline")): + result = get_articles_without_images("Category:Example") + + assert result.articles == [] + assert result.error == "Unable to load articles from Wikipedia. Please try again." + + +def test_api_error_response_is_not_reported_as_an_empty_success(): + error_response = type( + "Response", + (), + { + "raise_for_status": lambda self: None, + "json": lambda self: { + "error": {"code": "readonly", "info": "Wikipedia is read-only"} + }, + }, + )() + + with patch("main.requests.get", return_value=error_response): + result = get_articles_without_images("Category:Example") + + assert result.articles == [] + assert result.error == "Unable to load articles from Wikipedia. Please try again." + + +def test_api_failure_after_results_marks_them_as_partial(): + first_response = type( + "Response", + (), + { + "raise_for_status": lambda self: None, + "json": lambda self: { + "query": { + "pages": { + "1": {"pageid": 1, "title": "No image", "images": []} + } + }, + "continue": { + "gcmcontinue": "next-page", + "continue": "gcmcontinue||", + }, + }, + }, + )() + + with patch( + "main.requests.get", + side_effect=[first_response, requests.ConnectionError("offline")], + ): + result = get_articles_without_images("Category:Example") + + assert [article.title for article in result.articles] == ["No image"] + assert result.error is not None + assert "partial results" in result.error + + +def test_category_page_does_not_claim_success_after_api_failure(): + result = CategoryResult( + articles=[], + gcmcontinue=None, + error="Unable to load articles from Wikipedia. Please try again.", + ) + + with ( + patch("main.get_articles_without_images", return_value=result), + patch("main.log_interaction"), + app.test_client() as client, + ): + response = client.get("/category?cat=Category:Example") + + page = response.get_data(as_text=True) + assert response.status_code == 200 + assert "Unable to load articles from Wikipedia" in page + assert "All articles in this category have images" not in page