mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
Merge branch 'esp8266-native-library-backend' into esp8266-native-build-spec
This commit is contained in:
@@ -15,6 +15,7 @@ regardless of which toolchain consumes the result.
|
||||
from collections import deque
|
||||
from collections.abc import Callable, Iterable
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import contextlib
|
||||
from dataclasses import dataclass, field
|
||||
import glob
|
||||
import hashlib
|
||||
@@ -108,14 +109,7 @@ class URLSource(Source):
|
||||
def __init__(self, url: str):
|
||||
self.url = url
|
||||
|
||||
def download(
|
||||
self,
|
||||
dir_suffix: str,
|
||||
force: bool = False,
|
||||
salt: str = "",
|
||||
namespace: str = "",
|
||||
progress: Callable[[int], None] | None = None,
|
||||
) -> Path:
|
||||
def _cache_dir(self, dir_suffix: str, salt: str, namespace: str) -> Path:
|
||||
# Namespace the cache per backend (e.g. pio_components/idf, .../zephyr) so
|
||||
# the build files each backend writes into the library dir can't collide.
|
||||
base_dir = Path(CORE.data_dir) / DOMAIN
|
||||
@@ -125,7 +119,23 @@ class URLSource(Source):
|
||||
h.update(self.url.encode())
|
||||
if salt:
|
||||
h.update(salt.encode())
|
||||
path = base_dir / h.hexdigest()[:8] / dir_suffix
|
||||
return base_dir / h.hexdigest()[:8] / dir_suffix
|
||||
|
||||
def is_cached(self, dir_suffix: str, salt: str = "", namespace: str = "") -> bool:
|
||||
"""Whether a completed extraction already exists for this source."""
|
||||
return (
|
||||
self._cache_dir(dir_suffix, salt, namespace) / ".esphome_extracted"
|
||||
).is_file()
|
||||
|
||||
def download(
|
||||
self,
|
||||
dir_suffix: str,
|
||||
force: bool = False,
|
||||
salt: str = "",
|
||||
namespace: str = "",
|
||||
progress: Callable[[int], None] | None = None,
|
||||
) -> Path:
|
||||
path = self._cache_dir(dir_suffix, salt, namespace)
|
||||
# Marker file written last to signal a complete extraction. Using a
|
||||
# marker (instead of just `path.is_dir()`) means an interrupted
|
||||
# extraction is correctly detected and re-run on the next invocation,
|
||||
@@ -964,6 +974,13 @@ def _prefetch_wave(
|
||||
if component.source.url in seen:
|
||||
continue
|
||||
seen.add(component.source.url)
|
||||
with contextlib.suppress(Exception):
|
||||
if component.source.is_cached(
|
||||
component.get_sanitized_name(), salt=salt, namespace=namespace
|
||||
):
|
||||
# A completed extraction downloads nothing; a warm build
|
||||
# must stay silent
|
||||
continue
|
||||
components.append(component)
|
||||
if len(components) < 2:
|
||||
return
|
||||
|
||||
@@ -678,6 +678,50 @@ def test_content_lengths_head_requests(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
assert lib._content_lengths(["https://x/a", "https://x/bad"]) == [123, 0]
|
||||
|
||||
|
||||
def test_prefetch_wave_cache_probe_failure_still_prefetches(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""The cache probe is best-effort; a failing probe prefetches anyway."""
|
||||
calls: list[str] = []
|
||||
monkeypatch.setattr(
|
||||
ConvertedLibrary,
|
||||
"download",
|
||||
lambda self, **kw: calls.append(self.source.url),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
URLSource,
|
||||
"is_cached",
|
||||
lambda self, *a, **kw: (_ for _ in ()).throw(RuntimeError("no core")),
|
||||
)
|
||||
wave = [
|
||||
("a", ConvertedLibrary("a", "1.0", URLSource("https://x/a.tar.gz"))),
|
||||
("b", ConvertedLibrary("b", "1.0", URLSource("https://x/b.tar.gz"))),
|
||||
]
|
||||
lib._prefetch_wave(wave, "", "idf")
|
||||
assert sorted(calls) == ["https://x/a.tar.gz", "https://x/b.tar.gz"]
|
||||
|
||||
|
||||
def test_prefetch_wave_warm_cache_is_silent(
|
||||
setup_core, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""Already-extracted archives download nothing; a warm build must not
|
||||
print a Downloading line or draw a bar."""
|
||||
monkeypatch.setattr(
|
||||
ConvertedLibrary,
|
||||
"download",
|
||||
lambda self, **kw: (_ for _ in ()).throw(AssertionError("downloaded")),
|
||||
)
|
||||
wave = []
|
||||
for name in ("a", "b", "c"):
|
||||
comp = ConvertedLibrary(name, "1.0", URLSource(f"https://x/{name}.tar.gz"))
|
||||
marker_dir = comp.source._cache_dir(comp.get_sanitized_name(), "", "idf")
|
||||
marker_dir.mkdir(parents=True)
|
||||
(marker_dir / ".esphome_extracted").touch()
|
||||
wave.append((name, comp))
|
||||
lib._prefetch_wave(wave, "", "idf")
|
||||
assert "Downloading" not in caplog.text
|
||||
|
||||
|
||||
def test_prefetch_wave_single_archive_skips_the_pool(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user