Skip to content

Commit eee0ffc

Browse files
authored
fix(ci): silence ty unresolved-import for optional PIL in image_loader [SDK-307] (#4193)
## What & why The **Code Quality** CI job (`uv run ty check .`) is currently **red on every PR**, including already-merged ones — the check is broken repo-wide, not by any individual PR. **Root cause:** `cognee/infrastructure/loaders/core/image_loader.py` imports Pillow (`PIL`) for its optional EXIF-metadata and perceptual-hash features. The imports are guarded at runtime with `try/except ImportError` (graceful degradation), so Pillow is genuinely optional — but it is **not** installed by the Code Quality job (which runs only `uv sync --extra dev`; Pillow reaches the env only transitively via the `unstructured` extra). Because `[tool.ty.src].include` covers `cognee/infrastructure/loaders`, ty type-checks that file and emits **5 `unresolved-import` diagnostics** for `PIL` / `PIL.ExifTags`. **Evidence it's pre-existing and repo-wide:** Code Quality = FAILURE on #4155, #4156, #4134 and the already-merged #4153; `image_loader.py` and the ty config are byte-identical across the last ~34 dev commits. ## Change Add an inline `# ty: ignore[unresolved-import]` at each of the 5 optional `PIL` import sites — matching the existing convention in the repo (e.g. `litellm_instructor/.../azure_openai/adapter.py`). These imports are intentionally optional and already runtime-guarded, so suppressing the *static* unresolved-import there is the correct semantics. **No dependency or lockfile change**, and the `try/except` guards are untouched. Alternative considered: add Pillow to the `dev` extra so ty can actually resolve it (real type coverage of the PIL path) — heavier (lockfile change) and inconsistent with how other optional imports are handled; left as a possible later improvement. ## Verification (local, ty resolving against a full dev env with Pillow absent) - `ty check .`: **5 diagnostics → 0** ("All checks passed!"). - `ruff check` and `ruff format --check`: clean. Linear: **SDK-307**. Unblocks the Code Quality gate for all open PRs. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
2 parents a717756 + e71fc4b commit eee0ffc

1 file changed

Lines changed: 5 additions & 5 deletions

File tree

cognee/infrastructure/loaders/core/image_loader.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ def _extract_exif_metadata(file_path: str) -> Optional[str]:
171171
coordinates when available, or None if the image has no EXIF data.
172172
"""
173173
try:
174-
from PIL import Image
175-
from PIL.ExifTags import TAGS
174+
from PIL import Image # ty: ignore[unresolved-import]
175+
from PIL.ExifTags import TAGS # ty: ignore[unresolved-import]
176176
except ImportError:
177177
return None
178178

@@ -234,7 +234,7 @@ def _compute_perceptual_hash(file_path: str) -> Optional[str]:
234234
Returns the hash as a hex string, or None on failure.
235235
"""
236236
try:
237-
from PIL import Image
237+
from PIL import Image # ty: ignore[unresolved-import]
238238
except ImportError:
239239
return None
240240

@@ -270,7 +270,7 @@ def _dhash(image, hash_size: int = 8) -> str:
270270
Difference hash: resize to (hash_size+1 x hash_size), convert to
271271
grayscale, compare adjacent columns, and pack bits into a hex string.
272272
"""
273-
from PIL import Image
273+
from PIL import Image # ty: ignore[unresolved-import]
274274

275275
image = image.convert("L").resize((hash_size + 1, hash_size), Image.LANCZOS)
276276
pixels = list(image.getdata())
@@ -293,7 +293,7 @@ def _dhash(image, hash_size: int = 8) -> str:
293293
def _format_gps_info(gps_dict: dict) -> Optional[str]:
294294
"""Format GPSInfo dict (tag 34853) into human-readable coordinates."""
295295
try:
296-
from PIL.ExifTags import GPSTAGS
296+
from PIL.ExifTags import GPSTAGS # ty: ignore[unresolved-import]
297297
except ImportError:
298298
return None
299299

0 commit comments

Comments
 (0)