fix: remove progress task on skipped jobs (v0.5.12)
progress.add_task() fires unconditionally at the top of the job loop; both continue paths (ValueError from _safe_person_dir and the symlink TOCTOU guard) skipped remove_task(), leaving orphaned 0% rows in the terminal for the rest of the run.
This commit is contained in:
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.5.12] - 2026-06-15
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Progress task leak on skipped jobs**: `progress.add_task()` is called unconditionally at the top of the job loop, but both early-exit `continue` paths — the `ValueError` skip from `_safe_person_dir` and the symlink-TOCTOU skip added in v0.5.11 — bypassed `progress.remove_task()`, leaving orphaned 0% rows in the terminal display for the rest of the run. Both `continue` paths now call `progress.remove_task(job_task)` before continuing.
|
||||||
|
|
||||||
## [0.5.11] - 2026-06-15
|
## [0.5.11] - 2026-06-15
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.5.11"
|
version = "0.5.12"
|
||||||
description = "Selects diverse, high-quality photos from Immich as training data for Frigate face recognition."
|
description = "Selects diverse, high-quality photos from Immich as training data for Frigate face recognition."
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
|
|||||||
@@ -105,12 +105,14 @@ def execute_jobs(jobs: list[dict]) -> None:
|
|||||||
person_dir = _safe_person_dir(Config.OUTPUT_DIR, name)
|
person_dir = _safe_person_dir(Config.OUTPUT_DIR, name)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
|
progress.remove_task(job_task)
|
||||||
continue
|
continue
|
||||||
# Face crops are transient (uploaded then discarded); wipe before each run.
|
# Face crops are transient (uploaded then discarded); wipe before each run.
|
||||||
# A symlink could appear here via a TOCTOU race after _safe_person_dir
|
# A symlink could appear here via a TOCTOU race after _safe_person_dir
|
||||||
# returned — writing through it would land crops outside output_dir.
|
# returned — writing through it would land crops outside output_dir.
|
||||||
if os.path.islink(person_dir):
|
if os.path.islink(person_dir):
|
||||||
logger.error("person_dir %s became a symlink after path check — skipping job", person_dir)
|
logger.error("person_dir %s became a symlink after path check — skipping job", person_dir)
|
||||||
|
progress.remove_task(job_task)
|
||||||
continue
|
continue
|
||||||
if os.path.isdir(person_dir):
|
if os.path.isdir(person_dir):
|
||||||
shutil.rmtree(person_dir)
|
shutil.rmtree(person_dir)
|
||||||
|
|||||||
Reference in New Issue
Block a user