From 2def20c646bb90f4173ea88977d3c4f88b467f8e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 26 Apr 2026 10:02:44 -0500 Subject: [PATCH] Use partial() for download_web_files_in_config call sites; collapse dedup loop into a dict comp - Both audio_file and speaker.media_player now use functools.partial to bind path_for=_compute_local_file_path instead of an inline lambda. - download_content_many's dedup loop becomes a dict comprehension. Last-URL wins now (dict-comp semantics) instead of first-URL; in practice duplicate paths only arise when the URL itself is duplicated, so the choice is meaningless. Test updated accordingly. --- esphome/components/audio_file/__init__.py | 3 ++- esphome/components/speaker/media_player/__init__.py | 5 ++++- esphome/external_files.py | 11 +++++------ tests/unit_tests/test_external_files.py | 6 +++--- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/esphome/components/audio_file/__init__.py b/esphome/components/audio_file/__init__.py index c2f1378f9d..88be6db168 100644 --- a/esphome/components/audio_file/__init__.py +++ b/esphome/components/audio_file/__init__.py @@ -1,4 +1,5 @@ from dataclasses import dataclass, field +from functools import partial import hashlib import logging from pathlib import Path @@ -199,7 +200,7 @@ def _validate_supported_local_file(config: list[ConfigType]) -> list[ConfigType] CONFIG_SCHEMA = cv.All( cv.only_on_esp32, cv.ensure_list(MEDIA_FILE_TYPE_SCHEMA), - lambda c: download_web_files_in_config(c, _compute_local_file_path), + partial(download_web_files_in_config, path_for=_compute_local_file_path), _validate_supported_local_file, ) diff --git a/esphome/components/speaker/media_player/__init__.py b/esphome/components/speaker/media_player/__init__.py index bbb51c03de..fbc83ef12f 100644 --- a/esphome/components/speaker/media_player/__init__.py +++ b/esphome/components/speaker/media_player/__init__.py @@ -1,5 +1,6 @@ """Speaker Media Player Setup.""" +from functools import partial import hashlib import logging from pathlib import Path @@ -277,7 +278,9 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_CODEC_SUPPORT_ENABLED): cv.Any(cv.boolean, cv.string), cv.Optional(CONF_FILES): cv.All( cv.ensure_list(MEDIA_FILE_TYPE_SCHEMA), - lambda c: download_web_files_in_config(c, _compute_local_file_path), + partial( + download_web_files_in_config, path_for=_compute_local_file_path + ), ), cv.Optional(CONF_TASK_STACK_IN_PSRAM): cv.All( cv.boolean, cv.requires_component(psram.DOMAIN) diff --git a/esphome/external_files.py b/esphome/external_files.py index 22ce617aff..132fe76a9e 100644 --- a/esphome/external_files.py +++ b/esphome/external_files.py @@ -144,13 +144,12 @@ def download_content_many( Items are de-duplicated by `path` -- two callers asking for the same cache file (e.g. the same URL referenced twice in a config) would - otherwise race on `download_content`'s non-atomic write. + otherwise race on `download_content`'s non-atomic write. When the + same `path` appears more than once, the last URL wins (standard dict + comprehension semantics); in practice duplicate paths only arise when + the URL is duplicated, so the choice doesn't matter. """ - seen: dict[Path, str] = {} - for url, path in items: - if path in seen: - continue - seen[path] = url + seen: dict[Path, str] = {path: url for url, path in items} if not seen: return if len(seen) == 1: diff --git a/tests/unit_tests/test_external_files.py b/tests/unit_tests/test_external_files.py index fd27f70315..8965c04b30 100644 --- a/tests/unit_tests/test_external_files.py +++ b/tests/unit_tests/test_external_files.py @@ -368,7 +368,9 @@ def test_download_content_many_dedupes_by_path( mock_download_content: MagicMock, setup_core: Path ) -> None: """Two items pointing at the same cache path must collapse to one - download -- otherwise concurrent writes race on the same file. + download -- otherwise concurrent writes race on the same file. Which + URL wins doesn't matter (in practice duplicate paths only arise when + the URL is duplicated), so we only assert the call count and path. """ path = setup_core / "shared" items = [ @@ -378,9 +380,7 @@ def test_download_content_many_dedupes_by_path( ] external_files.download_content_many(items) assert mock_download_content.call_count == 1 - # First-seen URL wins, matching dict-insertion order. args, _ = mock_download_content.call_args - assert args[0] == "https://example.com/a" assert args[1] == path