Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Handle asyncio.TimeoutError for link validation code (#1051)
Browse files Browse the repository at this point in the history
Handle asyncio.TimeoutError
  • Loading branch information
sarayourfriend authored Dec 16, 2022
1 parent 9b3059f commit 6898481
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 4 deletions.
7 changes: 4 additions & 3 deletions api/catalog/api/utils/validate_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ def _get_expiry(status, default):

async def _head(url: str, session: aiohttp.ClientSession) -> tuple[str, int]:
try:
async with session.head(url, timeout=2, allow_redirects=False) as response:
async with session.head(url, allow_redirects=False) as response:
return url, response.status
except aiohttp.ClientError as exception:
except (aiohttp.ClientError, asyncio.TimeoutError) as exception:
_log_validation_failure(exception)
return url, -1

Expand All @@ -44,7 +44,8 @@ async def _head(url: str, session: aiohttp.ClientSession) -> tuple[str, int]:
@async_to_sync
async def _make_head_requests(urls: list[str]) -> list[tuple[str, int]]:
tasks = []
async with aiohttp.ClientSession(headers=HEADERS) as session:
timeout = aiohttp.ClientTimeout(total=2)
async with aiohttp.ClientSession(headers=HEADERS, timeout=timeout) as session:
tasks = [asyncio.ensure_future(_head(url, session)) for url in urls]
responses = asyncio.gather(*tasks)
await responses
Expand Down
26 changes: 25 additions & 1 deletion api/test/unit/utils/validate_images_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
from unittest import mock

import aiohttp
Expand Down Expand Up @@ -43,4 +44,27 @@ def test_sends_user_agent(wrapped_client_session: mock.AsyncMock):
for url in image_urls:
assert url in requested_urls

wrapped_client_session.assert_called_once_with(headers=HEADERS)
wrapped_client_session.assert_called_once_with(headers=HEADERS, timeout=mock.ANY)


def test_handles_timeout():
"""
Note: This test takes just over 3 seconds to run as it simulates network delay of 3 seconds.
"""
query_hash = "test_handles_timeout"
results = [{"identifier": i} for i in range(1)]
image_urls = [f"https://example.org/{i}" for i in range(len(results))]
start_slice = 0

def raise_timeout_error(*args, **kwargs):
raise asyncio.TimeoutError()

with mock.patch(
"aiohttp.client.ClientSession._request", side_effect=raise_timeout_error
):
validate_images(query_hash, start_slice, results, image_urls)

# `validate_images` directly modifies the results list
# if the results are timing out then they're considered dead and discarded
# so should not appear in the final list of results.
assert len(results) == 0

0 comments on commit 6898481

Please sign in to comment.