mirror of
https://github.com/esphome/esphome.git
synced 2026-08-30 01:26:43 +00:00
Name the operation in retry warnings, share the attempts constant
This commit is contained in:
@@ -161,12 +161,17 @@ def has_remote_file_changed(
|
||||
# Retried even though a failure degrades gracefully to the
|
||||
# cached copy below: allow_stale=False consumers reject an
|
||||
# unverified copy, so for them a healed flake avoids a hard
|
||||
# failure in download_content.
|
||||
# failure in download_content. Only connection-level failures
|
||||
# are retryable here; HEAD never raises on an HTTP status
|
||||
# (deliberately: servers that reject HEAD with 405/501 fall
|
||||
# through to "changed" and the GET below handles them), so a
|
||||
# 5xx lands in the != 304 branch and the GET's retry covers it.
|
||||
response = fetch_with_retry(
|
||||
url,
|
||||
lambda: requests.head(
|
||||
url, headers=headers, timeout=timeout, allow_redirects=True
|
||||
),
|
||||
what="Revalidation",
|
||||
)
|
||||
|
||||
_LOGGER.debug(
|
||||
|
||||
@@ -15,7 +15,7 @@ from typing import IO, TYPE_CHECKING
|
||||
|
||||
from esphome.happy_eyeballs import ensure_happy_eyeballs
|
||||
from esphome.helpers import ProgressBar, rmtree
|
||||
from esphome.net_retry import is_transient_download_error
|
||||
from esphome.net_retry import NETWORK_MAX_ATTEMPTS, is_transient_download_error
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import requests
|
||||
@@ -30,8 +30,9 @@ _LOGGER = logging.getLogger(__name__)
|
||||
_MIRROR_ATTEMPTS = 3
|
||||
|
||||
# Passes over the whole mirror list when a transient network error is in
|
||||
# the mix; matches git.py's _NETWORK_MAX_ATTEMPTS (3 tries, 2s/4s backoff).
|
||||
_MIRROR_SWEEP_ATTEMPTS = 3
|
||||
# the mix; shares net_retry's policy (3 tries, 2s/4s backoff), which in
|
||||
# turn matches git.py's _NETWORK_MAX_ATTEMPTS.
|
||||
_MIRROR_SWEEP_ATTEMPTS = NETWORK_MAX_ATTEMPTS
|
||||
|
||||
|
||||
def get_project_link_flags() -> list[str]:
|
||||
|
||||
@@ -38,6 +38,11 @@ def _is_permanent_dns_failure(e: BaseException) -> bool:
|
||||
if id(exc) in seen:
|
||||
continue
|
||||
if isinstance(exc, socket.gaierror):
|
||||
# Everything except EAI_AGAIN is treated as permanent. Codes
|
||||
# like EAI_NONAME can occur during a brief network outage too,
|
||||
# but a 6s backoff rarely outlives one, and permanent means
|
||||
# callers fall back to their cache immediately instead of
|
||||
# sleeping first (the offline-build case).
|
||||
return exc.errno != socket.EAI_AGAIN
|
||||
seen.add(id(exc))
|
||||
stack.extend(
|
||||
@@ -83,10 +88,11 @@ def is_transient_download_error(e: Exception) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def fetch_with_retry[T](url: str, fetch: Callable[[], T]) -> T:
|
||||
def fetch_with_retry[T](url: str, fetch: Callable[[], T], what: str = "Download") -> T:
|
||||
"""Run ``fetch``, retrying failures that ``is_transient_download_error``
|
||||
classifies as transient with 2s/4s backoff. Other failures and the
|
||||
final attempt's failure propagate to the caller's fallback handling.
|
||||
``what`` names the operation in the retry warning.
|
||||
"""
|
||||
import requests
|
||||
|
||||
@@ -98,7 +104,8 @@ def fetch_with_retry[T](url: str, fetch: Callable[[], T]) -> T:
|
||||
raise
|
||||
delay = 2**attempt
|
||||
_LOGGER.warning(
|
||||
"Download of %s failed: %s. Retrying in %d seconds... (attempt %d/%d)",
|
||||
"%s of %s failed: %s. Retrying in %d seconds... (attempt %d/%d)",
|
||||
what,
|
||||
url,
|
||||
e,
|
||||
delay,
|
||||
|
||||
@@ -647,9 +647,10 @@ def test_has_remote_file_changed_retries_transient_error(
|
||||
mock_requests_head: MagicMock,
|
||||
mock_retry_sleep: MagicMock,
|
||||
setup_core: Path,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""A HEAD revalidation that fails transiently then returns 304 does not
|
||||
mark the cached copy stale."""
|
||||
mark the cached copy stale, and the retry warning names the operation."""
|
||||
test_file = setup_core / "cached.txt"
|
||||
test_file.write_bytes(b"cached content")
|
||||
|
||||
@@ -669,6 +670,7 @@ def test_has_remote_file_changed_retries_transient_error(
|
||||
assert test_file not in external_files._run_data().stale_paths
|
||||
assert mock_requests_head.call_count == 2
|
||||
assert mock_retry_sleep.call_args_list == [call(2)]
|
||||
assert "Revalidation of" in caplog.text
|
||||
|
||||
|
||||
def test_download_content_skip_external_update_uses_cache(
|
||||
|
||||
Reference in New Issue
Block a user