lektor_ng.imagetools._compat

src/lektor_ng/imagetools/_compat.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
"""Compatibility with various versions of Pillow."""

from __future__ import annotations

from collections.abc import Iterable, Mapping
from enum import IntEnum
from types import ModuleType, SimpleNamespace

import PIL.ExifTags
import PIL.Image

__all__ = ["ExifTags", "Transpose", "UnidentifiedImageError"]

PILLOW_VERSION_INFO = tuple(map(int, PIL.__version__.split(".")))

if PILLOW_VERSION_INFO >= (9, 4):
    ExifTags: ModuleType | SimpleNamespace = PIL.ExifTags
else:
    # Pillow < 9.4 does not provide the PIL.ExifTags.{Base,GPS,IFD} enums. Here we
    # provide and ExifTags namespace which has them.

    def _reverse_map(mapping: Mapping[int, str]) -> dict[str, int]:
        return dict(map(reversed, mapping.items()))  # type: ignore[arg-type]

    ExifTags = SimpleNamespace(
        Base=IntEnum("Base", _reverse_map(PIL.ExifTags.TAGS)),
        GPS=IntEnum("GPS", _reverse_map(PIL.ExifTags.GPSTAGS)),
        IFD=IntEnum("IFD", [("Exif", 34665), ("GPSInfo", 34853)]),
        TAGS=PIL.ExifTags.TAGS,
        GPSTAGS=PIL.ExifTags.GPSTAGS,
    )


if hasattr(PIL.Image, "Transpose"):
    # pillow >= 9.1
    Transpose = PIL.Image.Transpose
else:

    def _make_enum(name: str, members: Iterable[str]) -> IntEnum:
        items = ((member, getattr(PIL.Image, member)) for member in members)
        return IntEnum(name, items)

    Transpose = _make_enum(  # type: ignore[misc, assignment]
        "Transpose",
        (
            "FLIP_LEFT_RIGHT",
            "FLIP_TOP_BOTTOM",
            "ROTATE_90",
            "ROTATE_180",
            "ROTATE_270",
            "TRANSPOSE",
            "TRANSVERSE",
        ),
    )


# UnidentifiedImageError only exists in Pillow >= 7.0.0
UnidentifiedImageError = getattr(PIL, "UnidentifiedImageError", OSError)