Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions src/PIL/BlpImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,19 @@ def unpack_565(i: int) -> tuple[int, int, int]:
return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3


def decode_dxt1(data, alpha=False):
def decode_dxt1(
data: bytes, alpha: bool = False
) -> tuple[bytearray, bytearray, bytearray, bytearray]:
"""
input: one "row" of data (i.e. will produce 4*width pixels)
"""

blocks = len(data) // 8 # number of blocks in row
ret = (bytearray(), bytearray(), bytearray(), bytearray())

for block in range(blocks):
for block_index in range(blocks):
# Decode next 8-byte block.
idx = block * 8
idx = block_index * 8
color0, color1, bits = struct.unpack_from("<HHI", data, idx)

r0, g0, b0 = unpack_565(color0)
Expand Down Expand Up @@ -116,16 +118,16 @@ def decode_dxt1(data, alpha=False):
return ret


def decode_dxt3(data):
def decode_dxt3(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]:
"""
input: one "row" of data (i.e. will produce 4*width pixels)
"""

blocks = len(data) // 16 # number of blocks in row
ret = (bytearray(), bytearray(), bytearray(), bytearray())

for block in range(blocks):
idx = block * 16
for block_index in range(blocks):
idx = block_index * 16
block = data[idx : idx + 16]
# Decode next 16-byte block.
bits = struct.unpack_from("<8B", block)
Expand Down Expand Up @@ -169,16 +171,16 @@ def decode_dxt3(data):
return ret


def decode_dxt5(data):
def decode_dxt5(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]:
"""
input: one "row" of data (i.e. will produce 4 * width pixels)
"""

blocks = len(data) // 16 # number of blocks in row
ret = (bytearray(), bytearray(), bytearray(), bytearray())

for block in range(blocks):
idx = block * 16
for block_index in range(blocks):
idx = block_index * 16
block = data[idx : idx + 16]
# Decode next 16-byte block.
a0, a1 = struct.unpack_from("<BB", block)
Expand Down
3 changes: 2 additions & 1 deletion src/PIL/BmpImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,8 @@ def _open(self) -> None:
class BmpRleDecoder(ImageFile.PyDecoder):
_pulls_fd = True

def decode(self, buffer):
def decode(self, buffer: bytes) -> tuple[int, int]:
assert self.fd is not None
rle4 = self.args[1]
data = bytearray()
x = 0
Expand Down
3 changes: 2 additions & 1 deletion src/PIL/DdsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,8 @@ def load_seek(self, pos: int) -> None:
class DdsRgbDecoder(ImageFile.PyDecoder):
_pulls_fd = True

def decode(self, buffer):
def decode(self, buffer: bytes) -> tuple[int, int]:
assert self.fd is not None
bitcount, masks = self.args

# Some masks will be padded with zeros, e.g. R 0b11 G 0b1100
Expand Down
45 changes: 22 additions & 23 deletions src/PIL/EpsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import subprocess
import sys
import tempfile
from typing import IO

from . import Image, ImageFile
from ._binary import i32le as i32
Expand Down Expand Up @@ -236,35 +237,33 @@ def check_required_header_comments() -> None:
msg = 'EPS header missing "%%BoundingBox" comment'
raise SyntaxError(msg)

def _read_comment(s):
def _read_comment(s: str) -> bool:
nonlocal reading_trailer_comments
try:
m = split.match(s)
except re.error as e:
msg = "not an EPS file"
raise SyntaxError(msg) from e

if m:
k, v = m.group(1, 2)
self.info[k] = v
if k == "BoundingBox":
if v == "(atend)":
reading_trailer_comments = True
elif not self._size or (
trailer_reached and reading_trailer_comments
):
try:
# Note: The DSC spec says that BoundingBox
# fields should be integers, but some drivers
# put floating point values there anyway.
box = [int(float(i)) for i in v.split()]
self._size = box[2] - box[0], box[3] - box[1]
self.tile = [
("eps", (0, 0) + self.size, offset, (length, box))
]
except Exception:
pass
return True
if not m:
return False

k, v = m.group(1, 2)
self.info[k] = v
if k == "BoundingBox":
if v == "(atend)":
reading_trailer_comments = True
elif not self._size or (trailer_reached and reading_trailer_comments):
try:
# Note: The DSC spec says that BoundingBox
# fields should be integers, but some drivers
# put floating point values there anyway.
box = [int(float(i)) for i in v.split()]
self._size = box[2] - box[0], box[3] - box[1]
self.tile = [("eps", (0, 0) + self.size, offset, (length, box))]
except Exception:
pass
return True

while True:
byte = self.fp.read(1)
Expand Down Expand Up @@ -413,7 +412,7 @@ def load_seek(self, pos: int) -> None:
# --------------------------------------------------------------------


def _save(im, fp, filename, eps=1):
def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes, eps: int = 1) -> None:
"""EPS Writer for the Python Imaging Library."""

# make sure image data is available
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/FitsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def _parse_headers(
class FitsGzipDecoder(ImageFile.PyDecoder):
_pulls_fd = True

def decode(self, buffer):
def decode(self, buffer: bytes) -> tuple[int, int]:
assert self.fd is not None
value = gzip.decompress(self.fd.read())

Expand Down
2 changes: 1 addition & 1 deletion src/PIL/FpxImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def close(self) -> None:
self.ole.close()
super().close()

def __exit__(self, *args):
def __exit__(self, *args: object) -> None:
self.ole.close()
super().__exit__()

Expand Down
2 changes: 1 addition & 1 deletion src/PIL/ImageFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ def feed(self, data):
def __enter__(self):
return self

def __exit__(self, *args):
def __exit__(self, *args: object) -> None:
self.close()

def close(self):
Expand Down
15 changes: 8 additions & 7 deletions src/PIL/Jpeg2KImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import io
import os
import struct
from typing import IO
from typing import IO, Tuple, cast

from . import Image, ImageFile, ImagePalette, _binary

Expand Down Expand Up @@ -59,7 +59,7 @@ def _read_bytes(self, num_bytes: int) -> bytes:
self.remaining_in_box -= num_bytes
return data

def read_fields(self, field_format):
def read_fields(self, field_format: str) -> tuple[int | bytes, ...]:
size = struct.calcsize(field_format)
data = self._read_bytes(size)
return struct.unpack(field_format, data)
Expand All @@ -82,9 +82,9 @@ def next_box_type(self) -> bytes:
self.remaining_in_box = -1

# Read the length and type of the next box
lbox, tbox = self.read_fields(">I4s")
lbox, tbox = cast(Tuple[int, bytes], self.read_fields(">I4s"))
if lbox == 1:
lbox = self.read_fields(">Q")[0]
lbox = cast(int, self.read_fields(">Q")[0])
hlen = 16
else:
hlen = 8
Expand Down Expand Up @@ -127,12 +127,13 @@ def _parse_codestream(fp):
return size, mode


def _res_to_dpi(num, denom, exp):
def _res_to_dpi(num: int, denom: int, exp: int) -> float | None:
"""Convert JPEG2000's (numerator, denominator, exponent-base-10) resolution,
calculated as (num / denom) * 10^exp and stored in dots per meter,
to floating-point dots per inch."""
if denom != 0:
return (254 * num * (10**exp)) / (10000 * denom)
if denom == 0:
return None
return (254 * num * (10**exp)) / (10000 * denom)


def _parse_jp2_header(fp):
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/MicImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def close(self) -> None:
self.ole.close()
super().close()

def __exit__(self, *args):
def __exit__(self, *args: object) -> None:
self.__fp.close()
self.ole.close()
super().__exit__()
Expand Down
15 changes: 5 additions & 10 deletions src/PIL/MpoImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,14 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
JpegImagePlugin._save(im, fp, filename)


def _save_all(im, fp, filename):
def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
append_images = im.encoderinfo.get("append_images", [])
if not append_images:
try:
animated = im.is_animated
except AttributeError:
animated = False
if not animated:
_save(im, fp, filename)
return
if not append_images and not getattr(im, "is_animated", False):
_save(im, fp, filename)
return

mpf_offset = 28
offsets = []
offsets: list[int] = []
for imSequence in itertools.chain([im], append_images):
for im_frame in ImageSequence.Iterator(imSequence):
if not offsets:
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/PSDraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def image(
sx = x / im.size[0]
sy = y / im.size[1]
self.fp.write(b"%f %f scale\n" % (sx, sy))
EpsImagePlugin._save(im, self.fp, None, 0)
EpsImagePlugin._save(im, self.fp, "", 0)
self.fp.write(b"\ngrestore\n")


Expand Down
10 changes: 6 additions & 4 deletions src/PIL/PalmImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
##
from __future__ import annotations

from typing import IO

from . import Image, ImageFile
from ._binary import o8
from ._binary import o16be as o16b
Expand Down Expand Up @@ -82,10 +84,10 @@


# so build a prototype image to be used for palette resampling
def build_prototype_image():
def build_prototype_image() -> Image.Image:
image = Image.new("L", (1, len(_Palm8BitColormapValues)))
image.putdata(list(range(len(_Palm8BitColormapValues))))
palettedata = ()
palettedata: tuple[int, ...] = ()
for colormapValue in _Palm8BitColormapValues:
palettedata += colormapValue
palettedata += (0, 0, 0) * (256 - len(_Palm8BitColormapValues))
Expand All @@ -112,7 +114,7 @@ def build_prototype_image():
# (Internal) Image save plugin for the Palm format.


def _save(im, fp, filename):
def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
if im.mode == "P":
# we assume this is a color Palm image with the standard colormap,
# unless the "info" dict has a "custom-colormap" field
Expand Down Expand Up @@ -141,7 +143,7 @@ def _save(im, fp, filename):
raise OSError(msg)

# we ignore the palette here
im.mode = "P"
im._mode = "P"
rawmode = f"P;{bpp}"
version = 1

Expand Down
3 changes: 1 addition & 2 deletions src/PIL/PdfParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,8 @@ def __init__(self, filename=None, f=None, buf=None, start_offset=0, mode="rb"):
def __enter__(self) -> PdfParser:
return self

def __exit__(self, exc_type, exc_value, traceback):
def __exit__(self, *args: object) -> None:
self.close()
return False # do not suppress exceptions
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://python.readthedocs.io/en/latest/reference/datamodel.html#object.__exit__

If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method.

So False and None will have the same result.


def start_writing(self) -> None:
self.close_buf()
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/PngImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def read(self) -> tuple[bytes, int, int]:
def __enter__(self) -> ChunkStream:
return self

def __exit__(self, *args):
def __exit__(self, *args: object) -> None:
self.close()

def close(self) -> None:
Expand Down
8 changes: 1 addition & 7 deletions src/PIL/TarIO.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from __future__ import annotations

import io
from types import TracebackType

from . import ContainerIO

Expand Down Expand Up @@ -61,12 +60,7 @@ def __init__(self, tarfile: str, file: str) -> None:
def __enter__(self) -> TarIO:
return self

def __exit__(
self,
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: TracebackType | None,
) -> None:
def __exit__(self, *args: object) -> None:
self.close()

def close(self) -> None:
Expand Down
Loading