imgutil.py 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. import io
  2. from pathlib import Path
  3. from PIL import Image
  4. try:
  5. import imghdr as _imghdr
  6. except ModuleNotFoundError: # Python 3.13+
  7. _imghdr = None
  8. def what(file: str | Path | None = None, h: bytes | None = None) -> str | None:
  9. """
  10. Compatibility wrapper for the removed stdlib `imghdr` module (Python 3.13+).
  11. Behaves like `imghdr.what(file, h)` and returns a lowercase format string
  12. like "jpeg", "png", "gif", or None if unknown.
  13. """
  14. if _imghdr is not None:
  15. return _imghdr.what(file, h)
  16. if h is not None:
  17. return _what_from_bytes(h)
  18. if file is None:
  19. return None
  20. try:
  21. with Image.open(str(file)) as im:
  22. fmt = im.format
  23. except Exception:
  24. return None
  25. return fmt.lower() if fmt else None
  26. def _what_from_bytes(data: bytes) -> str | None:
  27. if data.startswith(b"\xff\xd8\xff"):
  28. return "jpeg"
  29. if data.startswith(b"\x89PNG\r\n\x1a\n"):
  30. return "png"
  31. if data[:6] in (b"GIF87a", b"GIF89a"):
  32. return "gif"
  33. if data.startswith(b"BM"):
  34. return "bmp"
  35. if len(data) >= 12 and data.startswith(b"RIFF") and data[8:12] == b"WEBP":
  36. return "webp"
  37. if data.startswith(b"II*\x00") or data.startswith(b"MM\x00*"):
  38. return "tiff"
  39. try:
  40. with Image.open(io.BytesIO(data)) as im:
  41. fmt = im.format
  42. except Exception:
  43. return None
  44. return fmt.lower() if fmt else None