fix(metadata): guard get_ai_metadata PIL open against non-OSError

get_ai_metadata opened the file with PIL unguarded, so a HEIC (or any
format PIL can't open without optional plugins) raised
UnidentifiedImageError instead of falling through to the binary scan --
unlike has_ai_metadata, which already guards. Wrap the open in
except Exception and continue to the C2PA/IPTC path. Regression test
feeds an unopenable .heic shell and asserts no raise.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
test-user
2026-05-24 16:19:15 -07:00
parent af787fd8d6
commit f36320ff39
2 changed files with 22 additions and 9 deletions
+15 -9
View File
@@ -198,15 +198,21 @@ def get_ai_metadata(image_path: Path) -> dict[str, str]:
result: dict[str, str] = {}
with Image.open(image_path) as img:
for key, value in img.info.items():
if _is_ai_key(key):
if isinstance(value, bytes):
result[key] = f"<binary {len(value)} bytes>"
elif isinstance(value, str) and len(value) > 200:
result[key] = value[:200] + ""
else:
result[key] = str(value)
# PIL may not open AVIF/HEIF/JPEG-XL without optional plugins (and
# ultralytics' Image.open patch can raise ModuleNotFoundError); fall through
# to the C2PA/binary path on any open failure. See CLAUDE.md.
try:
with Image.open(image_path) as img:
for key, value in img.info.items():
if _is_ai_key(key):
if isinstance(value, bytes):
result[key] = f"<binary {len(value)} bytes>"
elif isinstance(value, str) and len(value) > 200:
result[key] = value[:200] + ""
else:
result[key] = str(value)
except Exception as exc:
logger.debug("PIL could not open %s for AI-metadata scan: %s", image_path, exc)
# C2PA manifest fields from the single canonical parser (noai/c2pa.py).
c2pa = extract_c2pa_info(image_path)
+7
View File
@@ -159,6 +159,13 @@ class TestGetAiMetadata:
assert meta["parameters"].endswith("")
assert len(meta["parameters"]) <= 205
def test_unopenable_file_does_not_raise(self, tmp_path: Path):
# PIL can't open HEIC without pillow-heif; get_ai_metadata must fall
# through to the binary scan, not propagate UnidentifiedImageError.
path = tmp_path / "iphone.heic"
path.write_bytes(b"\x00\x00\x00\x18ftypheic" + b"\x00" * 64)
assert get_ai_metadata(path) == {}
@pytest.mark.skipif(not SAMPLES_DIR.exists(), reason="data/samples not present")
class TestGetAiMetadataRealSample: