fix(vision): degrade image validation gracefully when Pillow is missing

Pillow is an optional dependency in this codebase (every other PIL use in
tools/vision_tools.py imports lazily and falls back). Both salvaged
validators now distinguish 'PIL missing' (pass through, header-only
sniff) from 'decode failed' (reject), so a Pillow-less install keeps
working instead of rejecting every PNG.

Follow-up to salvaged #53307 (@CannibalKush) and #76896 (@HaiyiMei).
This commit is contained in:
Teknium
2026-08-28 03:56:41 -07:00
parent 765142d94f
commit 4029a24f0c
+12 -2
View File
@@ -254,9 +254,15 @@ def _detect_image_mime_type_from_bytes(data: bytes) -> Optional[str]:
if header.startswith(b"\x89PNG\r\n\x1a\n"):
# Magic bytes alone are insufficient: native vision history is
# immutable, so reject corrupt PNGs before they can be embedded.
# Pillow is an optional dependency — when it is missing we fall back
# to header-only sniffing (the full-decode gate in
# _validate_raster_image_decodable is likewise skipped without PIL);
# only an actual failed verify() rejects the bytes.
try:
from PIL import Image
except ImportError:
return "image/png"
try:
with Image.open(BytesIO(data)) as image:
image.verify()
except Exception:
@@ -410,7 +416,11 @@ def _validate_raster_image_decodable(image_path: Path) -> Optional[str]:
try:
from PIL import Image as _PILImage
from PIL import ImageSequence as _PILImageSequence
except ImportError:
# Pillow is optional — without it we cannot decode-validate, so pass
# the image through unvalidated rather than rejecting everything.
return None
try:
with _PILImage.open(image_path) as image:
image.verify()
with _PILImage.open(image_path) as image: