Skip to content

Commit

Permalink
removed python3.8 support
Browse files Browse the repository at this point in the history
Signed-off-by: Alexander Piskun <[email protected]>
  • Loading branch information
bigcat88 committed Oct 5, 2024
1 parent 115e9a3 commit acdd866
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 56 deletions.
2 changes: 1 addition & 1 deletion pillow_heif/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Version of pillow_heif/pi_heif."""

__version__ = "0.19.0.dev0"
__version__ = "0.20.0.dev0"
24 changes: 13 additions & 11 deletions pillow_heif/as_plugin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""Plugins for the Pillow library."""

from __future__ import annotations

from itertools import chain
from typing import Union
from typing import IO
from warnings import warn

from PIL import Image, ImageFile, ImageSequence
Expand Down Expand Up @@ -32,7 +34,7 @@
class _LibHeifImageFile(ImageFile.ImageFile):
"""Base class with all functionality for ``HeifImageFile`` and ``AvifImageFile`` classes."""

_heif_file: Union[HeifFile, None] = None
_heif_file: HeifFile | None = None
_close_exclusive_fp_after_loading = True
_mode: str # only for Pillow 10.1+

Expand Down Expand Up @@ -85,7 +87,7 @@ def getxmp(self) -> dict:
return self._getxmp(xmp_data[0]) # pylint: disable=no-member
return {}

def seek(self, frame):
def seek(self, frame: int):
if not self._seek_check(frame):
return
self.__frame = frame
Expand All @@ -94,7 +96,7 @@ def seek(self, frame):
if _exif is not None and getattr(_exif, "_loaded", None):
_exif._loaded = False # pylint: disable=protected-access

def tell(self):
def tell(self) -> int:
return self.__frame

def verify(self) -> None:
Expand All @@ -113,7 +115,7 @@ def is_animated(self) -> bool:
"""Returns ``True`` if this image contains more than one frame, or ``False`` otherwise."""
return self.n_frames > 1

def _seek_check(self, frame):
def _seek_check(self, frame: int):
if frame < 0 or frame >= self.n_frames:
raise EOFError("attempt to seek outside sequence")
return self.tell() != frame
Expand All @@ -140,11 +142,11 @@ def _is_supported_heif(fp) -> bool:
return magic[8:12] in (b"heic", b"heix", b"heim", b"heis", b"hevc", b"hevx", b"hevm", b"hevs", b"mif1", b"msf1")


def _save_heif(im, fp, _filename):
def _save_heif(im: Image.Image, fp: IO[bytes], _filename: str | bytes):
__save_one(im, fp, HeifCompressionFormat.HEVC)


def _save_all_heif(im, fp, _filename):
def _save_all_heif(im: Image.Image, fp: IO[bytes], _filename: str | bytes):
__save_all(im, fp, HeifCompressionFormat.HEVC)


Expand Down Expand Up @@ -183,11 +185,11 @@ def _is_supported_avif(fp) -> bool:
# return False


def _save_avif(im, fp, _filename):
def _save_avif(im: Image.Image, fp: IO[bytes], _filename: str | bytes):
__save_one(im, fp, HeifCompressionFormat.AV1)


def _save_all_avif(im, fp, _filename):
def _save_all_avif(im: Image.Image, fp: IO[bytes], _filename: str | bytes):
__save_all(im, fp, HeifCompressionFormat.AV1)


Expand Down Expand Up @@ -234,13 +236,13 @@ def __options_update(**kwargs):
warn(f"Unknown option: {k}", stacklevel=1)


def __save_one(im, fp, compression_format: HeifCompressionFormat):
def __save_one(im: Image.Image, fp: IO[bytes], compression_format: HeifCompressionFormat):
ctx_write = CtxEncode(compression_format, **im.encoderinfo)
_pil_encode_image(ctx_write, im, True, **im.encoderinfo)
ctx_write.save(fp)


def __save_all(im, fp, compression_format: HeifCompressionFormat):
def __save_all(im: Image.Image, fp: IO[bytes], compression_format: HeifCompressionFormat):
ctx_write = CtxEncode(compression_format, **im.encoderinfo)
current_frame = im.tell() if hasattr(im, "tell") else None
append_images = im.encoderinfo.get("append_images", [])
Expand Down
30 changes: 15 additions & 15 deletions pillow_heif/heif.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Functions and classes for heif images to read and write."""

from __future__ import annotations

from copy import copy, deepcopy
from io import SEEK_SET
from typing import Any, Dict, List, Optional, Tuple
from typing import Any

from PIL import Image

Expand Down Expand Up @@ -38,7 +40,7 @@
class BaseImage:
"""Base class for :py:class:`HeifImage` and :py:class:`HeifDepthImage`."""

size: tuple
size: tuple[int, int]
"""Width and height of the image."""

mode: str
Expand Down Expand Up @@ -82,7 +84,7 @@ def __array_interface__(self):
else:
width = int(width / 2)
typestr = "<u2"
shape: Tuple[Any, ...] = (self.size[1], width)
shape: tuple[Any, ...] = (self.size[1], width)
if MODE_INFO[self.mode][0] > 1:
shape += (MODE_INFO[self.mode][0],)
return {"shape": shape, "typestr": typestr, "version": 3, "data": self.data}
Expand Down Expand Up @@ -143,13 +145,11 @@ class HeifImage(BaseImage):

def __init__(self, c_image):
super().__init__(c_image)
_metadata: List[dict] = c_image.metadata
_metadata: list[dict] = c_image.metadata
_exif = _retrieve_exif(_metadata)
_xmp = _retrieve_xmp(_metadata)
_thumbnails: List[Optional[int]] = (
[i for i in c_image.thumbnails if i is not None] if options.THUMBNAILS else []
)
_depth_images: List[Optional[HeifDepthImage]] = (
_thumbnails: list[int | None] = [i for i in c_image.thumbnails if i is not None] if options.THUMBNAILS else []
_depth_images: list[HeifDepthImage | None] = (
[HeifDepthImage(i) for i in c_image.depth_image_list if i is not None] if options.DEPTH_IMAGES else []
)
_heif_meta = _get_heif_meta(c_image)
Expand All @@ -166,7 +166,7 @@ def __init__(self, c_image):
if _heif_meta:
self.info["heif"] = _heif_meta
save_colorspace_chroma(c_image, self.info)
_color_profile: Dict[str, Any] = c_image.color_profile
_color_profile: dict[str, Any] = c_image.color_profile
if _color_profile:
if _color_profile["type"] in ("rICC", "prof"):
self.info["icc_profile"] = _color_profile["data"]
Expand Down Expand Up @@ -248,7 +248,7 @@ def __init__(self, fp=None, convert_hdr_to_8bit=True, bgr_mode=False, **kwargs):
preferred_decoder,
)
self.mimetype = mimetype
self._images: List[HeifImage] = [HeifImage(i) for i in images if i is not None]
self._images: list[HeifImage] = [HeifImage(i) for i in images if i is not None]
self.primary_index = 0
for index, _ in enumerate(self._images):
if _.info.get("primary", False):
Expand Down Expand Up @@ -385,7 +385,7 @@ def __delitem__(self, key):
raise IndexError(f"invalid image index: {key}")
del self._images[key]

def add_frombytes(self, mode: str, size: tuple, data, **kwargs):
def add_frombytes(self, mode: str, size: tuple[int, int], data, **kwargs):
"""Adds image from bytes to container.
.. note:: Supports ``stride`` value if needed.
Expand Down Expand Up @@ -549,7 +549,7 @@ def read_heif(fp, convert_hdr_to_8bit=True, bgr_mode=False, **kwargs) -> HeifFil
return ret


def encode(mode: str, size: tuple, data, fp, **kwargs) -> None:
def encode(mode: str, size: tuple[int, int], data, fp, **kwargs) -> None:
"""Encodes data in a ``fp``.
:param mode: `BGR(A);16`, `RGB(A);16`, LA;16`, `L;16`, `I;16L`, `BGR(A)`, `RGB(A)`, `LA`, `L`
Expand All @@ -560,12 +560,12 @@ def encode(mode: str, size: tuple, data, fp, **kwargs) -> None:
_encode_images([HeifImage(MimCImage(mode, size, data, **kwargs))], fp, **kwargs)


def _encode_images(images: List[HeifImage], fp, **kwargs) -> None:
def _encode_images(images: list[HeifImage], fp, **kwargs) -> None:
compression = kwargs.get("format", "HEIF")
compression_format = HeifCompressionFormat.AV1 if compression == "AVIF" else HeifCompressionFormat.HEVC
if not _pillow_heif.get_lib_info()[compression]:
raise RuntimeError(f"No {compression} encoder found.")
images_to_save: List[HeifImage] = images + kwargs.get("append_images", [])
images_to_save: list[HeifImage] = images + kwargs.get("append_images", [])
if not kwargs.get("save_all", True):
images_to_save = images_to_save[:1]
if not images_to_save:
Expand Down Expand Up @@ -603,7 +603,7 @@ def from_pillow(pil_image: Image.Image) -> HeifFile:
return _


def from_bytes(mode: str, size: tuple, data, **kwargs) -> HeifFile:
def from_bytes(mode: str, size: tuple[int, int], data, **kwargs) -> HeifFile:
"""Creates :py:class:`~pillow_heif.HeifFile` from bytes.
.. note:: Supports ``stride`` value if needed.
Expand Down
35 changes: 18 additions & 17 deletions pillow_heif/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
Mostly for internal use, so prototypes can change between versions.
"""

from __future__ import annotations

import builtins
import re
from dataclasses import dataclass
from enum import IntEnum
from math import ceil
from pathlib import Path
from struct import pack, unpack
from typing import List, Optional, Union

from PIL import Image

Expand Down Expand Up @@ -93,7 +94,7 @@ def save_colorspace_chroma(c_image, info: dict) -> None:
info["chroma"] = chroma


def set_orientation(info: dict) -> Optional[int]:
def set_orientation(info: dict) -> int | None:
"""Reset orientation in ``EXIF`` to ``1`` if any orientation present.
Removes ``XMP`` orientation tag if it is present.
Expand All @@ -116,7 +117,7 @@ def _get_orientation_for_encoder(info: dict) -> int:
return 1 if image_orientation is None else image_orientation


def _get_orientation_xmp(info: dict, exif_orientation: Optional[int], reset: bool = False) -> Optional[int]:
def _get_orientation_xmp(info: dict, exif_orientation: int | None, reset: bool = False) -> int | None:
xmp_orientation = 1
if info.get("xmp"):
xmp_data = info["xmp"].rsplit(b"\x00", 1)
Expand All @@ -141,7 +142,7 @@ def _get_orientation_xmp(info: dict, exif_orientation: Optional[int], reset: boo
return xmp_orientation if exif_orientation is None and xmp_orientation != 1 else None


def _get_orientation(info: dict, reset: bool = False) -> Optional[int]:
def _get_orientation(info: dict, reset: bool = False) -> int | None:
original_orientation = None
if info.get("exif"):
try:
Expand Down Expand Up @@ -215,7 +216,7 @@ def _get_bytes(fp, length=None) -> bytes:
return bytes(fp)[:length]


def _retrieve_exif(metadata: List[dict]) -> Optional[bytes]:
def _retrieve_exif(metadata: list[dict]) -> bytes | None:
_result = None
_purge = []
for i, md_block in enumerate(metadata):
Expand All @@ -235,7 +236,7 @@ def _retrieve_exif(metadata: List[dict]) -> Optional[bytes]:
return _result


def _retrieve_xmp(metadata: List[dict]) -> Optional[bytes]:
def _retrieve_xmp(metadata: list[dict]) -> bytes | None:
_result = None
_purge = []
for i, md_block in enumerate(metadata):
Expand All @@ -248,7 +249,7 @@ def _retrieve_xmp(metadata: List[dict]) -> Optional[bytes]:
return _result


def _exif_from_pillow(img: Image.Image) -> Optional[bytes]:
def _exif_from_pillow(img: Image.Image) -> bytes | None:
if "exif" in img.info:
return img.info["exif"]
if hasattr(img, "getexif"): # noqa
Expand All @@ -258,7 +259,7 @@ def _exif_from_pillow(img: Image.Image) -> Optional[bytes]:
return None


def _xmp_from_pillow(img: Image.Image) -> Optional[bytes]:
def _xmp_from_pillow(img: Image.Image) -> bytes | None:
_xmp = None
if "xmp" in img.info:
_xmp = img.info["xmp"]
Expand Down Expand Up @@ -324,7 +325,7 @@ def _rotate_pil(img: Image.Image, orientation: int) -> Image.Image:
return img


def _get_primary_index(some_iterator, primary_index: Optional[int]) -> int:
def _get_primary_index(some_iterator, primary_index: int | None) -> int:
primary_attrs = [_.info.get("primary", False) for _ in some_iterator]
if primary_index is None:
primary_index = 0
Expand All @@ -336,7 +337,7 @@ def _get_primary_index(some_iterator, primary_index: Optional[int]) -> int:
return primary_index


def __get_camera_intrinsic_matrix(values: Optional[tuple]):
def __get_camera_intrinsic_matrix(values: tuple | None):
return (
{
"focal_length_x": values[0],
Expand Down Expand Up @@ -383,7 +384,7 @@ def __init__(self, compression_format: HeifCompressionFormat, **kwargs):
_value = value if isinstance(value, str) else str(value)
self.ctx_write.set_parameter(key, _value)

def add_image(self, size: tuple, mode: str, data, **kwargs) -> None:
def add_image(self, size: tuple[int, int], mode: str, data, **kwargs) -> None:
"""Adds image to the encoder."""
if size[0] <= 0 or size[1] <= 0:
raise ValueError("Empty images are not supported.")
Expand Down Expand Up @@ -412,7 +413,7 @@ def add_image_ycbcr(self, img: Image.Image, **kwargs) -> None:
im_out.add_plane_l(img.size, 8, 8, bytes(img.getdata(i)), kwargs.get("stride", 0), i)
self._finish_add_image(im_out, img.size, **kwargs)

def _finish_add_image(self, im_out, size: tuple, **kwargs):
def _finish_add_image(self, im_out, size: tuple[int, int], **kwargs):
# set ICC color profile
__icc_profile = kwargs.get("icc_profile")
if __icc_profile is not None:
Expand Down Expand Up @@ -468,15 +469,15 @@ def save(self, fp) -> None:
class MimCImage:
"""Mimicry of the HeifImage class."""

def __init__(self, mode: str, size: tuple, data: bytes, **kwargs):
def __init__(self, mode: str, size: tuple[int, int], data: bytes, **kwargs):
self.mode = mode
self.size = size
self.stride: int = kwargs.get("stride", size[0] * MODE_INFO[mode][0] * ceil(MODE_INFO[mode][1] / 8))
self.data = data
self.metadata: List[dict] = []
self.metadata: list[dict] = []
self.color_profile = None
self.thumbnails: List[int] = []
self.depth_image_list: List = []
self.thumbnails: list[int] = []
self.depth_image_list: list = []
self.primary = False
self.chroma = HeifChroma.UNDEFINED.value
self.colorspace = HeifColorspace.UNDEFINED.value
Expand All @@ -494,6 +495,6 @@ def bit_depth(self) -> int:
return MODE_INFO[self.mode][1]


def load_libheif_plugin(plugin_path: Union[str, Path]) -> None:
def load_libheif_plugin(plugin_path: str | Path) -> None:
"""Load specified LibHeif plugin."""
_pillow_heif.load_plugin(plugin_path)
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ requires = [
[tool.cibuildwheel]
build-verbosity = "2"
build = [
"cp38-* cp39-* cp310-* cp311-* cp312-* cp313-* pp39-* pp310-*",
"cp39-* cp310-* cp311-* cp312-* cp313-* pp39-* pp310-*",
]
skip = [
"cp36-* cp37-* pp37-* pp38-* cp38-macosx_arm64",
"cp36-* cp37-* cp38-* pp37-* pp38-* cp38-macosx_arm64",
]
test-extras = "tests-min"
test-command = "pytest {project}"
Expand Down Expand Up @@ -40,11 +40,11 @@ before-build = [

[tool.black]
line-length = 120
target-version = [ "py38" ]
target-version = [ "py39" ]
preview = true

[tool.ruff]
target-version = "py38"
target-version = "py39"
line-length = 120
preview = true
lint.select = [
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ classifiers =
Topic :: Multimedia :: Graphics
Topic :: Multimedia :: Graphics :: Graphics Conversion
Programming Language :: Python :: 3
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
Programming Language :: Python :: 3.13
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy
License :: OSI Approved :: GNU General Public License v2 (GPLv2)
Expand Down
Loading

0 comments on commit acdd866

Please sign in to comment.