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
This commit is contained in:
2026-06-15 02:20:34 +00:00
parent de6804226d
commit 06c2c4a584
4 changed files with 17 additions and 2 deletions
+8
View File
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [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 ## [0.5.16] - 2026-06-15
### Changed ### Changed
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "winnow" 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." 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"
+6
View File
@@ -16,6 +16,9 @@ def _getenv_num(name: str, default, cast):
raw = os.getenv(name) raw = os.getenv(name)
if raw is None: if raw is None:
return default return default
raw = raw.strip()
if not raw:
return default
try: try:
return cast(raw) return cast(raw)
except ValueError: except ValueError:
@@ -46,6 +49,9 @@ def _getenv_bool(name: str, default: bool) -> bool:
raw = os.getenv(name) raw = os.getenv(name)
if raw is None: if raw is None:
return default return default
raw = raw.strip()
if not raw:
return default
return raw.lower() in ("true", "1", "yes") return raw.lower() in ("true", "1", "yes")
+2 -1
View File
@@ -18,6 +18,7 @@ import numpy as np
from PIL import Image from PIL import Image
from .cache import get_cache from .cache import get_cache
from .config import _getenv_bool
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -50,7 +51,7 @@ _insightface_loaded = False
def _is_force_cpu() -> bool: def _is_force_cpu() -> bool:
"""Check if CPU mode is forced via environment variable.""" """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: def _preload_cuda_libs() -> None: