From 06c2c4a5842c41fa544b06bdbcfb880026b206a5 Mon Sep 17 00:00:00 2001 From: Holden Date: Mon, 15 Jun 2026 02:20:34 +0000 Subject: [PATCH] fix: strip empty env vars in _getenv_num/_getenv_bool; unify FORCE_CPU - _getenv_num: add raw.strip() + empty-string guard so numeric vars set to "" (common Compose pattern for "use default") return the default silently instead of warning "not a valid int/float" - _getenv_bool: same guard so True-defaulted flags set to "" return the configured default instead of silently returning False - embeddings.py: replace inline FORCE_CPU bool parse with _getenv_bool --- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- winnow/config.py | 6 ++++++ winnow/embeddings.py | 3 ++- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4e1156..324283b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.5.17] - 2026-06-15 + +### Fixed + +- **`_getenv_num` and `_getenv_bool` now treat an explicitly-empty env var as unset**: previously, `YEARS_FILTER=` (blank) in a `.env` or Compose file caused `int("")` to raise `ValueError`, logging a spurious "not a valid int" warning and returning the default. Both helpers now strip whitespace and treat an empty string the same as an absent variable, returning the typed default silently. This affects all numeric config vars (`YEARS_FILTER`, `MIN_FACE_WIDTH`, `MIN_FACE_COUNT`, `MAX_AUTO_IMAGES`, `BLUR_THRESHOLD`, `MIN_CONFIDENCE`, `FACE_MARGIN`) and all boolean config vars. `_getenv_optional_float` already handled this correctly. + +- **`FORCE_CPU` now uses `_getenv_bool`**: `embeddings.py` retained the old inline `os.getenv("FORCE_CPU", "").lower() in ("true", "1", "yes")` pattern after v0.5.16 introduced `_getenv_bool`. The inline copy is now replaced so the canonical truthy-string set is defined in one place. + ## [0.5.16] - 2026-06-15 ### Changed diff --git a/pyproject.toml b/pyproject.toml index f5ed11f..ba8d203 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "winnow" -version = "0.5.16" +version = "0.5.17" description = "Selects diverse, high-quality photos from Immich as training data for Frigate face recognition." license = "AGPL-3.0-or-later" requires-python = ">=3.13" diff --git a/winnow/config.py b/winnow/config.py index 1606026..b516325 100644 --- a/winnow/config.py +++ b/winnow/config.py @@ -16,6 +16,9 @@ def _getenv_num(name: str, default, cast): raw = os.getenv(name) if raw is None: return default + raw = raw.strip() + if not raw: + return default try: return cast(raw) except ValueError: @@ -46,6 +49,9 @@ def _getenv_bool(name: str, default: bool) -> bool: raw = os.getenv(name) if raw is None: return default + raw = raw.strip() + if not raw: + return default return raw.lower() in ("true", "1", "yes") diff --git a/winnow/embeddings.py b/winnow/embeddings.py index 09bae77..5f8f7b2 100644 --- a/winnow/embeddings.py +++ b/winnow/embeddings.py @@ -18,6 +18,7 @@ import numpy as np from PIL import Image from .cache import get_cache +from .config import _getenv_bool logger = logging.getLogger(__name__) @@ -50,7 +51,7 @@ _insightface_loaded = False def _is_force_cpu() -> bool: """Check if CPU mode is forced via environment variable.""" - return os.getenv("FORCE_CPU", "").lower() in ("true", "1", "yes") + return _getenv_bool("FORCE_CPU", False) def _preload_cuda_libs() -> None: