Compare commits
2
Commits
v1.1.0
...
b009c0cf50
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b009c0cf50 | ||
|
|
e6eddf5bc3 |
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "aioweb"
|
name = "aioweb"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
description = "Async HTTP session wrapper over aiohttp — proxies, header overwrites, retries, previews. Config-free, installable."
|
description = "Async HTTP session wrapper over aiohttp — proxies, header overwrites, retries, previews. Config-free, installable."
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|||||||
+21
-9
@@ -470,10 +470,12 @@ class ExtendedSession:
|
|||||||
# get a typed failure and request_with_retries can label it a timeout. now also
|
# get a typed failure and request_with_retries can label it a timeout. now also
|
||||||
# catches the hang-guard firing (asyncio.wait_for raises asyncio.TimeoutError).
|
# catches the hang-guard firing (asyncio.wait_for raises asyncio.TimeoutError).
|
||||||
raise aiohttp.ServerTimeoutError(f"timeout for {url}: {error}") from error
|
raise aiohttp.ServerTimeoutError(f"timeout for {url}: {error}") from error
|
||||||
except aiohttp.ClientError as error:
|
except aiohttp.ClientError:
|
||||||
# re-raise the original subtype (not flattened) - request_with_retries still
|
# re-raise the original subtype (not flattened) - request_with_retries still
|
||||||
# catches the base aiohttp.ClientError below and is unaffected
|
# catches the base aiohttp.ClientError below and is unaffected. no log here:
|
||||||
log.error("client error for %s: %s", url, error)
|
# this path RAISES, so the exception carries the failure (raise XOR log); on the
|
||||||
|
# retrying path commons.aretry emits the per-attempt WARNING and the terminal
|
||||||
|
# branch logs on exhaustion - logging here would double-report the same failure.
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def request_with_retries(
|
async def request_with_retries(
|
||||||
@@ -507,7 +509,11 @@ class ExtendedSession:
|
|||||||
headers=headers, proxies=proxies, timeout=timeout, debug=debug,
|
headers=headers, proxies=proxies, timeout=timeout, debug=debug,
|
||||||
)
|
)
|
||||||
if response.status_code in retry_statuses:
|
if response.status_code in retry_statuses:
|
||||||
log.warning("retryable status %s for %s", response.status_code, url)
|
# debug, not warning: this RAISES _RetryStatus to trigger a retry, so it must
|
||||||
|
# not also warn/error the same failure (raise XOR log). commons.aretry emits
|
||||||
|
# the per-attempt WARNING with the attempt count when it catches and retries;
|
||||||
|
# the terminal branch below logs on exhaustion.
|
||||||
|
log.debug("retryable status %s for %s", response.status_code, url)
|
||||||
raise _RetryStatus(response)
|
raise _RetryStatus(response)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@@ -516,19 +522,25 @@ class ExtendedSession:
|
|||||||
attempt, attempts=attempts, backoff=1.0, factor=backoff_base,
|
attempt, attempts=attempts, backoff=1.0, factor=backoff_base,
|
||||||
jitter=False, on=(Exception,),
|
jitter=False, on=(Exception,),
|
||||||
)
|
)
|
||||||
|
# terminal path: retries are exhausted and the failure is SWALLOWED into a falsy
|
||||||
|
# FailureResponse the caller branches on (the documented contract). rule 2 mandates a
|
||||||
|
# log on a swallow; level is WARNING because the lib recovered cleanly into a
|
||||||
|
# branchable value - the failing op is the caller's to escalate once it sees the falsy
|
||||||
|
# result, so the lib does not claim ERROR on the caller's behalf. all four branches are
|
||||||
|
# the same event class (exhausted retries) and stay at one uniform level.
|
||||||
except _RetryStatus as exhausted:
|
except _RetryStatus as exhausted:
|
||||||
log.error("all %d attempts failed for %s (last status %s)",
|
log.warning("all %d attempts failed for %s (last status %s)",
|
||||||
attempts, url, exhausted.response.status_code)
|
attempts, url, exhausted.response.status_code)
|
||||||
return exhausted.response
|
return exhausted.response
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
# catch before ClientError so a timeout is labeled as such, not generic
|
# catch before ClientError so a timeout is labeled as such, not generic
|
||||||
log.error("all %d attempts timed out for %s", attempts, url)
|
log.warning("all %d attempts timed out for %s", attempts, url)
|
||||||
return FailureResponse(reason="timeout", url=url)
|
return FailureResponse(reason="timeout", url=url)
|
||||||
except aiohttp.ClientError as error:
|
except aiohttp.ClientError as error:
|
||||||
log.error("all %d attempts failed for %s (client error: %s)", attempts, url, error)
|
log.warning("all %d attempts failed for %s (client error: %s)", attempts, url, error)
|
||||||
return FailureResponse(reason=f"client error: {error}", url=url)
|
return FailureResponse(reason=f"client error: {error}", url=url)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
log.error("all %d attempts failed for %s (unexpected: %s)", attempts, url, error)
|
log.warning("all %d attempts failed for %s (unexpected: %s)", attempts, url, error)
|
||||||
return FailureResponse(reason=f"unexpected error: {error}", url=url)
|
return FailureResponse(reason=f"unexpected error: {error}", url=url)
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user