diff --git a/src/wikibots/flickr.py b/src/wikibots/flickr.py index 7127b25..bed606e 100644 --- a/src/wikibots/flickr.py +++ b/src/wikibots/flickr.py @@ -20,6 +20,17 @@ RATE_LIMIT_DELAYS = (60, 180, 300) +class _PatchedFlickrApi(FlickrApi): + # Temporary fix until flickr-photos-api handles missing attributes + def parse_single_photo_info(self, info_resp, *, photo_id): + usage_elem = info_resp.find("photo/usage") + if usage_elem is not None: + for attr in ("candownload", "canblog", "canprint", "canshare"): + if attr not in usage_elem.attrib: + usage_elem.set(attr, "0") + return super().parse_single_photo_info(info_resp, photo_id=photo_id) + + class FlickrBot(BaseBot): redis_prefix = "xQ6cz5J84Viw/K6FIcOH1kxJjfiS8jO56AoSmhBgO/A=" summary = "add [[Commons:Structured data|SDC]] based on metadata from Flickr" @@ -32,7 +43,7 @@ class FlickrBot(BaseBot): def __init__(self) -> None: super().__init__() - self.flickr_api = FlickrApi.with_api_key( + self.flickr_api = _PatchedFlickrApi.with_api_key( api_key=os.getenv("FLICKR_API_KEY", ""), user_agent=self.user_agent, ) diff --git a/tests/test_flickr_rate_limit.py b/tests/test_flickr_rate_limit.py index 58548d8..fc9bc37 100644 --- a/tests/test_flickr_rate_limit.py +++ b/tests/test_flickr_rate_limit.py @@ -1,10 +1,12 @@ from typing import cast from unittest.mock import MagicMock, call, patch +from xml.etree import ElementTree as ET import httpx import pytest +from flickr_api import FlickrApi -from wikibots.flickr import FlickrBot +from wikibots.flickr import FlickrBot, _PatchedFlickrApi from wikibots.lib.bot import RateLimitExhausted @@ -13,7 +15,7 @@ def make_bot() -> FlickrBot: with ( patch("wikibots.lib.bot.Redis") as mock_redis_cls, patch("wikibots.lib.bot.requests.Session"), - patch("wikibots.flickr.FlickrApi"), + patch("wikibots.flickr._PatchedFlickrApi"), ): mock_redis_cls.return_value.ping.return_value = True bot = FlickrBot() @@ -29,6 +31,35 @@ def make_429_error() -> httpx.HTTPStatusError: ) +def test_patched_flickr_api_fills_missing_usage_attrs(): + info_resp = ET.fromstring( + '' + ) + with patch.object(FlickrApi, "parse_single_photo_info", return_value={}): + api = _PatchedFlickrApi.__new__(_PatchedFlickrApi) + api.parse_single_photo_info(info_resp, photo_id="55315578212") + + usage = info_resp.find("photo/usage") + assert usage is not None + assert usage.attrib["canprint"] == "0" + assert usage.attrib["candownload"] == "1" + assert usage.attrib["canblog"] == "0" + assert usage.attrib["canshare"] == "1" + + +def test_patched_flickr_api_does_not_overwrite_existing_usage_attrs(): + info_resp = ET.fromstring( + '' + ) + with patch.object(FlickrApi, "parse_single_photo_info", return_value={}): + api = _PatchedFlickrApi.__new__(_PatchedFlickrApi) + api.parse_single_photo_info(info_resp, photo_id="12345") + + usage = info_resp.find("photo/usage") + assert usage is not None + assert usage.attrib["canprint"] == "1" + + def test_rate_limit_retries_with_delays_then_raises(mocker): bot = make_bot() redis_mock = cast(MagicMock, bot.redis)