Let ProgressBar own the mid-row interrupt, share the part-file naming, fold duplicate except arms

This commit is contained in:
J. Nick Koston
2026-08-23 16:54:33 -05:00
parent cb10904fa3
commit 04a44d6e63
3 changed files with 31 additions and 19 deletions
+15 -17
View File
@@ -773,11 +773,10 @@ def run_batch_downloads(
try:
fetch(checked)
except _BatchDownloadCancelled as err:
# Reported like a failure: an abandoned job must never read as
# a completed download if a caller sees the list after Ctrl-C
failure = (name, err)
except Exception as err: # noqa: BLE001 # pylint: disable=broad-exception-caught
except (_BatchDownloadCancelled, Exception) as err: # noqa: BLE001 # pylint: disable=broad-exception-caught
# A cancelled job reports like a failure: an abandoned download
# must never read as completed if a caller sees the list after
# Ctrl-C
failure = (name, err)
else:
return None
@@ -824,12 +823,12 @@ class _BatchDownloadProgress:
self._lock = threading.Lock()
def tracker(self) -> Callable[[int], None]:
if self._bar is None:
return lambda _: None
last = 0
def update(done: int) -> None:
nonlocal last
if self._bar is None:
return
with self._lock:
self._sum += done - last
last = done
@@ -859,13 +858,7 @@ class _BatchDownloadProgress:
class _EndRow(logging.Filter):
def filter(self, record: logging.LogRecord) -> bool:
with lock:
if (
the_bar.last_progress is not None
and the_bar.last_progress != 100
):
the_bar.done()
# Force the next tick to redraw the frame
the_bar.last_progress = None
the_bar.interrupt()
return True
end_row = _EndRow()
@@ -879,6 +872,11 @@ class _BatchDownloadProgress:
handler.removeFilter(end_row)
def _part_path(dest: Path) -> Path:
"""The in-progress sidecar ``download_with_resume`` streams into."""
return dest.with_name(dest.name + ".part")
def _cancellable_sleep(
delay: float, progress: Callable[[int], None] | None, done: int
) -> None:
@@ -939,7 +937,7 @@ def download_with_resume(
ensure_happy_eyeballs()
dest = Path(dest)
part = dest.with_name(dest.name + ".part")
part = _part_path(dest)
meta = part.with_name(part.name + ".meta")
dest.parent.mkdir(parents=True, exist_ok=True)
last_error: Exception | None = None
@@ -1062,7 +1060,7 @@ def download_with_resume(
) from last_error
def _failure_reason(e: Exception) -> str:
def _failure_reason(e: BaseException) -> str:
"""Format a download exception for the aggregated error message.
``requests`` appends " for url: <url>" to HTTP errors; the URL is already
@@ -1338,7 +1336,7 @@ def download_from_mirrors(
if f is not None:
done = f.tell()
else:
part = path_target.with_name(path_target.name + ".part")
part = _part_path(path_target)
done = part.stat().st_size if part.is_file() else 0
_cancellable_sleep(delay, progress, done)
+10
View File
@@ -735,6 +735,16 @@ class ProgressBar:
sys.stderr.write("\n")
sys.stderr.flush()
def interrupt(self) -> None:
"""End a mid-row frame so the next write starts on its own row.
The next ``update()`` redraws the bar; a finished bar stays done.
"""
if self.last_progress == 100:
return
self.done()
self.last_progress = None
def docs_url(path: str) -> str:
"""Return the URL to the documentation for a given path."""
+6 -2
View File
@@ -77,6 +77,10 @@ SRC_FILE_EXTENSIONS = list(SOURCE_KIND_FOR_SUFFIX)
DOMAIN = "pio_components"
# Marks a cache dir whose archive finished extracting; a missing marker
# means a torn extraction that must be redone
_EXTRACTED_MARKER = ".esphome_extracted"
ESPHOME_DATA_KEY = "ESPHOME"
ESPHOME_DATA_EXTRA_CMAKE_KEY = "EXTRA_CMAKE"
@@ -118,7 +122,7 @@ class URLSource(Source):
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"
self._cache_dir(dir_suffix, salt, namespace) / _EXTRACTED_MARKER
).is_file()
def download(
@@ -135,7 +139,7 @@ class URLSource(Source):
# extraction is correctly detected and re-run on the next invocation,
# and lets us extract directly into ``path`` — avoiding a
# post-extraction rename that races with antivirus on Windows.
extracted_marker = path / ".esphome_extracted"
extracted_marker = path / _EXTRACTED_MARKER
if not extracted_marker.is_file() or force:
rmdir(path, msg=f"Clean up library directory {path}")