Skip to content

Commit

Permalink
Merge pull request #8279 from radarhere/type_hint_init
Browse files Browse the repository at this point in the history
  • Loading branch information
hugovk authored Aug 28, 2024
2 parents 3c3b6ea + eae107c commit 1bf9fb4
Show file tree
Hide file tree
Showing 28 changed files with 122 additions and 66 deletions.
3 changes: 0 additions & 3 deletions Tests/test_color_lut.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ def test_correct_args(
self, lut_mode: str, table_channels: int, table_size: int | tuple[int, int, int]
) -> None:
im = Image.new("RGB", (10, 10), 0)
assert im.im is not None
im.im.color_lut_3d(
lut_mode,
Image.Resampling.BILINEAR,
Expand All @@ -142,7 +141,6 @@ def test_wrong_mode(
) -> None:
with pytest.raises(ValueError, match="wrong mode"):
im = Image.new(image_mode, (10, 10), 0)
assert im.im is not None
im.im.color_lut_3d(
lut_mode,
Image.Resampling.BILINEAR,
Expand All @@ -162,7 +160,6 @@ def test_correct_mode(
self, image_mode: str, lut_mode: str, table_channels: int, table_size: int
) -> None:
im = Image.new(image_mode, (10, 10), 0)
assert im.im is not None
im.im.color_lut_3d(
lut_mode,
Image.Resampling.BILINEAR,
Expand Down
1 change: 1 addition & 0 deletions Tests/test_file_webp.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ def test_roundtrip_rgba_palette(self, tmp_path: Path) -> None:
temp_file = str(tmp_path / "temp.webp")
im = Image.new("RGBA", (1, 1)).convert("P")
assert im.mode == "P"
assert im.palette is not None
assert im.palette.mode == "RGBA"
im.save(temp_file)

Expand Down
6 changes: 6 additions & 0 deletions Tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,7 @@ def _make_new(
assert new_image.size == image.size
assert new_image.info == base_image.info
if palette_result is not None:
assert new_image.palette is not None
assert new_image.palette.tobytes() == palette_result.tobytes()
else:
assert new_image.palette is None
Expand Down Expand Up @@ -1002,12 +1003,14 @@ def test_has_transparency_data(self) -> None:
# P mode with RGBA palette
im = Image.new("RGBA", (1, 1)).convert("P")
assert im.mode == "P"
assert im.palette is not None
assert im.palette.mode == "RGBA"
assert im.has_transparency_data

def test_apply_transparency(self) -> None:
im = Image.new("P", (1, 1))
im.putpalette((0, 0, 0, 1, 1, 1))
assert im.palette is not None
assert im.palette.colors == {(0, 0, 0): 0, (1, 1, 1): 1}

# Test that no transformation is applied without transparency
Expand All @@ -1025,13 +1028,16 @@ def test_apply_transparency(self) -> None:
im.putpalette((0, 0, 0, 255, 1, 1, 1, 128), "RGBA")
im.info["transparency"] = 0
im.apply_transparency()
assert im.palette is not None
assert im.palette.colors == {(0, 0, 0, 0): 0, (1, 1, 1, 128): 1}

# Test that transparency bytes are applied
with Image.open("Tests/images/pil123p.png") as im:
assert isinstance(im.info["transparency"], bytes)
assert im.palette is not None
assert im.palette.colors[(27, 35, 6)] == 24
im.apply_transparency()
assert im.palette is not None
assert im.palette.colors[(27, 35, 6, 214)] == 24

def test_constants(self) -> None:
Expand Down
1 change: 1 addition & 0 deletions Tests/test_image_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,5 @@ def test_fromarray_palette() -> None:
out = Image.fromarray(a, "P")

# Assert that the Python and C palettes match
assert out.palette is not None
assert len(out.palette.colors) == len(out.im.getpalette()) / 3
1 change: 1 addition & 0 deletions Tests/test_image_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ def test_trns_RGB(tmp_path: Path) -> None:
def test_l_macro_rounding(convert_mode: str) -> None:
for mode in ("P", "PA"):
im = Image.new(mode, (1, 1))
assert im.palette is not None
im.palette.getcolor((0, 1, 2))

converted_im = im.convert(convert_mode)
Expand Down
1 change: 1 addition & 0 deletions Tests/test_image_putpalette.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def test_rgba_palette(mode: str, palette: tuple[int, ...]) -> None:
im = Image.new("P", (1, 1))
im.putpalette(palette, mode)
assert im.getpalette() == [1, 2, 3]
assert im.palette is not None
assert im.palette.colors == {(1, 2, 3, 4): 0}


Expand Down
4 changes: 4 additions & 0 deletions Tests/test_image_quantize.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def test_quantize_no_dither() -> None:

converted = image.quantize(dither=Image.Dither.NONE, palette=palette)
assert converted.mode == "P"
assert converted.palette is not None
assert converted.palette.palette == palette.palette.palette


Expand All @@ -81,6 +82,7 @@ def test_quantize_no_dither2() -> None:
palette.putpalette(data)
quantized = im.quantize(dither=Image.Dither.NONE, palette=palette)

assert quantized.palette is not None
assert tuple(quantized.palette.palette) == data

px = quantized.load()
Expand Down Expand Up @@ -117,6 +119,7 @@ def test_colors() -> None:
im = hopper()
colors = 2
converted = im.quantize(colors)
assert converted.palette is not None
assert len(converted.palette.palette) == colors * len("RGB")


Expand Down Expand Up @@ -147,6 +150,7 @@ def test_palette(method: Image.Quantize, color: tuple[int, ...]) -> None:
converted = im.quantize(method=method)
converted_px = converted.load()
assert converted_px is not None
assert converted.palette is not None
assert converted_px[0, 0] == converted.palette.colors[color]


Expand Down
2 changes: 1 addition & 1 deletion selftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def testimage() -> None:
or you call the "load" method:
>>> im = Image.open("Tests/images/hopper.ppm")
>>> print(im.im) # internal image attribute
>>> print(im._im) # internal image attribute
None
>>> a = im.load()
>>> type(im.im) # doctest: +ELLIPSIS
Expand Down
1 change: 1 addition & 0 deletions src/PIL/BlpImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
magic = b"BLP1" if im.encoderinfo.get("blp_version") == "BLP1" else b"BLP2"
fp.write(magic)

assert im.palette is not None
fp.write(struct.pack("<i", 1)) # Uncompressed or DirectX compression
fp.write(struct.pack("<b", Encoding.UNCOMPRESSED))
fp.write(struct.pack("<b", 1 if im.palette.mode == "RGBA" else 0))
Expand Down
2 changes: 2 additions & 0 deletions src/PIL/BmpImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ def _bitmap(self, header: int = 0, offset: int = 0) -> None:

# ------------------ Special case : header is reported 40, which
# ---------------------- is shorter than real size for bpp >= 16
assert isinstance(file_info["width"], int)
assert isinstance(file_info["height"], int)
self._size = file_info["width"], file_info["height"]

# ------- If color count was not found in the header, compute from bits
Expand Down
5 changes: 2 additions & 3 deletions src/PIL/EpsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ def _open(self) -> None:
self.fp.seek(offset)

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

byte_arr = bytearray(255)
bytes_mv = memoryview(byte_arr)
Expand Down Expand Up @@ -228,7 +227,7 @@ 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 self.tile or (trailer_reached and reading_trailer_comments):
try:
# Note: The DSC spec says that BoundingBox
# fields should be integers, but some drivers
Expand Down Expand Up @@ -346,7 +345,7 @@ def _read_comment(s: str) -> bool:
trailer_reached = True
bytes_read = 0

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

Expand Down
2 changes: 2 additions & 0 deletions src/PIL/FpxImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ def _open_index(self, index: int = 1) -> None:

# size (highest resolution)

assert isinstance(prop[0x1000002], int)
assert isinstance(prop[0x1000003], int)
self._size = prop[0x1000002], prop[0x1000003]

size = max(self.size)
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/GbrImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def _open(self) -> None:
self._data_size = width * height * color_depth

def load(self) -> Image.core.PixelAccess | None:
if not self.im:
if self._im is None:
self.im = Image.core.new(self.mode, self.size)
self.frombytes(self.fp.read(self._data_size))
return Image.Image.load(self)
Expand Down
49 changes: 32 additions & 17 deletions src/PIL/GifImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def seek(self, frame: int) -> None:
if not self._seek_check(frame):
return
if frame < self.__frame:
self.im = None
self._im = None
self._seek(0)

last_frame = self.__frame
Expand Down Expand Up @@ -320,11 +320,14 @@ def _seek(self, frame: int, update_image: bool = True) -> None:
else:
self._mode = "L"

if not palette and self.global_palette:
if palette:
self.palette = palette
elif self.global_palette:
from copy import copy

palette = copy(self.global_palette)
self.palette = palette
self.palette = copy(self.global_palette)
else:
self.palette = None
else:
if self.mode == "P":
if (
Expand Down Expand Up @@ -376,7 +379,7 @@ def _rgb(color: int) -> tuple[int, int, int]:
self.dispose = Image.core.fill(dispose_mode, dispose_size, color)
else:
# replace with previous contents
if self.im is not None:
if self._im is not None:
# only dispose the extent in this frame
self.dispose = self._crop(self.im, self.dispose_extent)
elif frame_transparency is not None:
Expand Down Expand Up @@ -434,7 +437,7 @@ def load_prepare(self) -> None:
self.im = Image.core.fill("P", self.size, self._frame_transparency or 0)
self.im.putpalette("RGB", *self._frame_palette.getdata())
else:
self.im = None
self._im = None
self._mode = temp_mode
self._frame_palette = None

Expand Down Expand Up @@ -495,6 +498,7 @@ def _normalize_mode(im: Image.Image) -> Image.Image:
return im
if Image.getmodebase(im.mode) == "RGB":
im = im.convert("P", palette=Image.Palette.ADAPTIVE)
assert im.palette is not None
if im.palette.mode == "RGBA":
for rgba in im.palette.colors:
if rgba[3] == 0:
Expand Down Expand Up @@ -536,11 +540,11 @@ def _normalize_palette(
if not source_palette:
source_palette = bytearray(i // 3 for i in range(768))
im.palette = ImagePalette.ImagePalette("RGB", palette=source_palette)
assert source_palette is not None

used_palette_colors: list[int] | None
if palette:
used_palette_colors = []
assert source_palette is not None
used_palette_colors: list[int | None] = []
assert im.palette is not None
for i in range(0, len(source_palette), 3):
source_color = tuple(source_palette[i : i + 3])
index = im.palette.colors.get(source_color)
Expand All @@ -553,20 +557,25 @@ def _normalize_palette(
if j not in used_palette_colors:
used_palette_colors[i] = j
break
im = im.remap_palette(used_palette_colors)
dest_map: list[int] = []
for index in used_palette_colors:
assert index is not None
dest_map.append(index)
im = im.remap_palette(dest_map)
else:
used_palette_colors = _get_optimize(im, info)
if used_palette_colors is not None:
im = im.remap_palette(used_palette_colors, source_palette)
optimized_palette_colors = _get_optimize(im, info)
if optimized_palette_colors is not None:
im = im.remap_palette(optimized_palette_colors, source_palette)
if "transparency" in info:
try:
info["transparency"] = used_palette_colors.index(
info["transparency"] = optimized_palette_colors.index(
info["transparency"]
)
except ValueError:
del info["transparency"]
return im

assert im.palette is not None
im.palette.palette = source_palette
return im

Expand All @@ -578,7 +587,8 @@ def _write_single_frame(
) -> None:
im_out = _normalize_mode(im)
for k, v in im_out.info.items():
im.encoderinfo.setdefault(k, v)
if isinstance(k, str):
im.encoderinfo.setdefault(k, v)
im_out = _normalize_palette(im_out, palette, im.encoderinfo)

for s in _get_global_header(im_out, im.encoderinfo):
Expand Down Expand Up @@ -632,7 +642,8 @@ def _write_multiple_frames(
for k, v in im_frame.info.items():
if k == "transparency":
continue
im.encoderinfo.setdefault(k, v)
if isinstance(k, str):
im.encoderinfo.setdefault(k, v)

encoderinfo = im.encoderinfo.copy()
if "transparency" in im_frame.info:
Expand Down Expand Up @@ -662,10 +673,12 @@ def _write_multiple_frames(
)
background = _get_background(im_frame, color)
background_im = Image.new("P", im_frame.size, background)
assert im_frames[0].im.palette is not None
background_im.putpalette(im_frames[0].im.palette)
bbox = _getbbox(background_im, im_frame)[1]
elif encoderinfo.get("optimize") and im_frame.mode != "1":
if "transparency" not in encoderinfo:
assert im_frame.palette is not None
try:
encoderinfo["transparency"] = (
im_frame.palette._new_color_index(im_frame)
Expand Down Expand Up @@ -903,6 +916,7 @@ def _get_optimize(im: Image.Image, info: dict[str, Any]) -> list[int] | None:
if optimise or max(used_palette_colors) >= len(used_palette_colors):
return used_palette_colors

assert im.palette is not None
num_palette_colors = len(im.palette.palette) // Image.getmodebands(
im.palette.mode
)
Expand Down Expand Up @@ -952,7 +966,7 @@ def _get_palette_bytes(im: Image.Image) -> bytes:
:param im: Image object
:returns: Bytes, len<=768 suitable for inclusion in gif header
"""
return im.palette.palette if im.palette else b""
return bytes(im.palette.palette) if im.palette else b""


def _get_background(
Expand All @@ -965,6 +979,7 @@ def _get_background(
# WebPImagePlugin stores an RGBA value in info["background"]
# So it must be converted to the same format as GifImagePlugin's
# info["background"] - a global color table index
assert im.palette is not None
try:
background = im.palette.getcolor(info_background, im)
except ValueError as e:
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/IcnsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ def load(self) -> Image.core.PixelAccess | None:
)

px = Image.Image.load(self)
if self.im is not None and self.im.size == self.size:
if self._im is not None and self.im.size == self.size:
# Already loaded
return px
self.load_prepare()
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/IcoImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ def size(self, value: tuple[int, int]) -> None:
self._size = value

def load(self) -> Image.core.PixelAccess | None:
if self.im is not None and self.im.size == self.size:
if self._im is not None and self.im.size == self.size:
# Already loaded
return Image.Image.load(self)
im = self.ico.getimage(self.size)
Expand Down
Loading

0 comments on commit 1bf9fb4

Please sign in to comment.