Skip to content

Commit

Permalink
add typing for Image mode and size
Browse files Browse the repository at this point in the history
  • Loading branch information
Yay295 committed Jun 15, 2024
1 parent 0bb6772 commit b4c1e67
Show file tree
Hide file tree
Showing 8 changed files with 38 additions and 31 deletions.
16 changes: 9 additions & 7 deletions src/PIL/EpsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ def _open(self) -> None:
self.fp.seek(offset)

self._mode = "RGB"
self._size = None
image_size = None

byte_arr = bytearray(255)
bytes_mv = memoryview(byte_arr)
Expand All @@ -238,7 +238,7 @@ def check_required_header_comments() -> None:
raise SyntaxError(msg)

def _read_comment(s: str) -> bool:
nonlocal reading_trailer_comments
nonlocal image_size, reading_trailer_comments
try:
m = split.match(s)
except re.error as e:
Expand All @@ -253,14 +253,14 @@ def _read_comment(s: str) -> bool:
if k == "BoundingBox":
if v == "(atend)":
reading_trailer_comments = True
elif not self.size or (trailer_reached and reading_trailer_comments):
elif not image_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))]
image_size = box[2] - box[0], box[3] - box[1]
self.tile = [("eps", (0, 0) + image_size, offset, (length, box))]
except Exception:
pass
return True
Expand Down Expand Up @@ -353,7 +353,7 @@ def _read_comment(s: str) -> bool:
else:
break

self._size = columns, rows
image_size = columns, rows
return
elif bytes_mv[:5] == b"%%EOF":
break
Expand All @@ -367,7 +367,9 @@ def _read_comment(s: str) -> bool:

check_required_header_comments()

if not self.size:
if image_size:
self._size == image_size
else:
msg = "cannot determine EPS bounding box"
raise OSError(msg)

Expand Down
4 changes: 3 additions & 1 deletion src/PIL/FitsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ def _parse_headers(
elif number_of_bits in (-32, -64):
self._mode = "F"

args = (self.mode, 0, -1) if decoder_name == "raw" else (number_of_bits,)
args: tuple[str, int, int] | tuple[int] = (
(self.mode, 0, -1) if decoder_name == "raw" else (number_of_bits,)
)
return decoder_name, offset, args


Expand Down
7 changes: 4 additions & 3 deletions src/PIL/IcnsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,10 @@ def _open(self) -> None:
self.best_size[1] * self.best_size[2],
)

@Image.Image.size.setter
def size(self, value):
info_size = value
# https://github.com/python/mypy/issues/1465
@Image.Image._size.setter # type: ignore[attr-defined]
def size(self, value: tuple[int, int]) -> None:
info_size: tuple[int, ...] = value
if info_size not in self.info["sizes"] and len(info_size) == 2:
info_size = (info_size[0], info_size[1], 1)
if (
Expand Down
5 changes: 3 additions & 2 deletions src/PIL/IcoImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,9 @@ def _open(self) -> None:
self.size = self.ico.entry[0]["dim"]
self.load()

@Image.Image.size.setter
def size(self, value):
# https://github.com/python/mypy/issues/1465
@Image.Image._size.setter # type: ignore[attr-defined]
def size(self, value: tuple[int, int]) -> None:
if value not in self.info["sizes"]:
msg = "This is not one of the allowed sizes of this image"
raise ValueError(msg)
Expand Down
24 changes: 14 additions & 10 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ def __init__(self):
self.pyaccess = None
self._exif = None

def _use_im_values(self):
def _use_im_values(self) -> bool:
"""
Whether or not to try using values from self.im
in addition to the values in this class.
Expand All @@ -559,27 +559,31 @@ def size(self) -> tuple[int, int]:
return self.im.size
return self.__size

def _set_size(self, value):
def _set_size(self, value: tuple[int, int]) -> None:
# set im.size first in case it raises an exception
if self._use_im_values():
self.im.size = value
self.__size = value

_size = property(fset=_set_size)
# MyPy doesn't support "x = property(...)"
# https://github.com/python/mypy/issues/8083
_size: tuple[int, int] = cast(tuple[int, int], property(fset=_set_size))

@property
def mode(self):
def mode(self) -> str:
if self._use_im_values():
return self.im.mode
return self.__mode

def _set_mode(self, value):
def _set_mode(self, value: str) -> None:
# set im.mode first in case it raises an exception
if self._use_im_values():
self.im.mode = value
self.__mode = value

_mode = property(fset=_set_mode)
# MyPy doesn't support "x = property(...)"
# https://github.com/python/mypy/issues/8083
_mode: str = cast(str, property(fset=_set_mode))

def _new(self, im) -> Image:
new = Image()
Expand Down Expand Up @@ -782,7 +786,7 @@ def __setstate__(self, state) -> None:
self.putpalette(palette)
self.frombytes(data)

def tobytes(self, encoder_name: str = "raw", *args) -> bytes:
def tobytes(self, encoder_name: str = "raw", *args: Any) -> bytes:
"""
Return image as a bytes object.
Expand All @@ -809,7 +813,7 @@ def tobytes(self, encoder_name: str = "raw", *args) -> bytes:
args = args[0]

if encoder_name == "raw" and args == ():
args = self.mode
args = (self.mode,)

self.load()

Expand Down Expand Up @@ -860,7 +864,7 @@ def tobitmap(self, name: str = "image") -> bytes:
]
)

def frombytes(self, data: bytes, decoder_name: str = "raw", *args) -> None:
def frombytes(self, data: bytes, decoder_name: str = "raw", *args: Any) -> None:
"""
Loads this image with pixel data from a bytes object.
Expand All @@ -877,7 +881,7 @@ def frombytes(self, data: bytes, decoder_name: str = "raw", *args) -> None:

# default format
if decoder_name == "raw" and args == ():
args = self.mode
args = (self.mode,)

# unpack data
d = _getdecoder(self.mode, decoder_name, args)
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/ImageFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def get_format_mimetype(self):
if self.format is not None:
return Image.MIME.get(self.format.upper())

def _use_im_values(self):
def _use_im_values(self) -> bool:
return self.tile is None and self.im is not None

def __setstate__(self, state):
Expand Down
9 changes: 3 additions & 6 deletions src/PIL/Jpeg2KImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def next_box_type(self) -> bytes:
return tbox


def _parse_codestream(fp):
def _parse_codestream(fp) -> tuple[tuple[int, int], str]:
"""Parse the JPEG 2000 codestream to extract the size and component
count from the SIZ marker segment, returning a PIL (size, mode) tuple."""

Expand All @@ -122,7 +122,8 @@ def _parse_codestream(fp):
elif csiz == 4:
mode = "RGBA"
else:
mode = None
msg = "unable to determine JP2 image mode"
raise SyntaxError(msg)

return size, mode

Expand Down Expand Up @@ -237,10 +238,6 @@ def _open(self) -> None:
msg = "not a JPEG 2000 file"
raise SyntaxError(msg)

if self.size is None or self.mode is None:
msg = "unable to determine size/mode"
raise SyntaxError(msg)

self._reduce = 0
self.layers = 0

Expand Down
2 changes: 1 addition & 1 deletion src/PIL/QoiImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def _open(self) -> None:
msg = "not a QOI file"
raise SyntaxError(msg)

self._size = tuple(i32(self.fp.read(4)) for i in range(2))
self._size = (i32(self.fp.read(4)), i32(self.fp.read(4)))

channels = self.fp.read(1)[0]
self._mode = "RGB" if channels == 3 else "RGBA"
Expand Down

0 comments on commit b4c1e67

Please sign in to comment.