diff --git a/Tests/test_box_blur.py b/Tests/test_box_blur.py index 1f6ed6127..dd5fe0290 100644 --- a/Tests/test_box_blur.py +++ b/Tests/test_box_blur.py @@ -24,6 +24,7 @@ def test_imageops_box_blur() -> None: def box_blur(image: Image.Image, radius: float = 1, n: int = 1) -> Image.Image: + assert image.im is not None return image._new(image.im.box_blur((radius, radius), n)) diff --git a/Tests/test_color_lut.py b/Tests/test_color_lut.py index 0d9c0b419..574ccd124 100644 --- a/Tests/test_color_lut.py +++ b/Tests/test_color_lut.py @@ -47,6 +47,7 @@ class TestColorLut3DCoreAPI: def test_wrong_args(self) -> None: im = Image.new("RGB", (10, 10), 0) + assert im.im is not None with pytest.raises(ValueError, match="filter"): im.im.color_lut_3d( @@ -107,6 +108,7 @@ class TestColorLut3DCoreAPI: def test_correct_args(self) -> None: im = Image.new("RGB", (10, 10), 0) + assert im.im is not None im.im.color_lut_3d( "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3) @@ -143,51 +145,60 @@ class TestColorLut3DCoreAPI: def test_wrong_mode(self) -> None: with pytest.raises(ValueError, match="wrong mode"): im = Image.new("L", (10, 10), 0) + assert im.im is not None im.im.color_lut_3d( "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3) ) with pytest.raises(ValueError, match="wrong mode"): im = Image.new("RGB", (10, 10), 0) + assert im.im is not None im.im.color_lut_3d( "L", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3) ) with pytest.raises(ValueError, match="wrong mode"): im = Image.new("L", (10, 10), 0) + assert im.im is not None im.im.color_lut_3d( "L", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3) ) with pytest.raises(ValueError, match="wrong mode"): im = Image.new("RGB", (10, 10), 0) + assert im.im is not None im.im.color_lut_3d( "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3) ) with pytest.raises(ValueError, match="wrong mode"): im = Image.new("RGB", (10, 10), 0) + assert im.im is not None im.im.color_lut_3d( "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3) ) def test_correct_mode(self) -> None: im = Image.new("RGBA", (10, 10), 0) + assert im.im is not None im.im.color_lut_3d( "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3) ) im = Image.new("RGBA", (10, 10), 0) + assert im.im is not None im.im.color_lut_3d( "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3) ) im = Image.new("RGB", (10, 10), 0) + assert im.im is not None im.im.color_lut_3d( "HSV", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3) ) im = Image.new("RGB", (10, 10), 0) + assert im.im is not None im.im.color_lut_3d( "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3) ) @@ -202,6 +213,7 @@ class TestColorLut3DCoreAPI: g.transpose(Image.Transpose.ROTATE_180), ], ) + assert im.im is not None # Fast test with small cubes for size in [2, 3, 5, 7, 11, 16, 17]: @@ -238,6 +250,7 @@ class TestColorLut3DCoreAPI: g.transpose(Image.Transpose.ROTATE_180), ], ) + assert im.im is not None # Red channel copied to alpha assert_image_equal( @@ -262,6 +275,7 @@ class TestColorLut3DCoreAPI: g.transpose(Image.Transpose.ROTATE_270), ], ) + assert im.im is not None assert_image_equal( im, @@ -284,6 +298,7 @@ class TestColorLut3DCoreAPI: g.transpose(Image.Transpose.ROTATE_180), ], ) + assert im.im is not None # Reverse channels by splitting and using table # fmt: off @@ -309,6 +324,7 @@ class TestColorLut3DCoreAPI: g.transpose(Image.Transpose.ROTATE_180), ], ) + assert im.im is not None # fmt: off transformed = im._new(im.im.color_lut_3d('RGB', Image.Resampling.BILINEAR, diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index cbc905de4..3fb416ffe 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -250,6 +250,7 @@ class TestFileWebp: 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) diff --git a/Tests/test_image.py b/Tests/test_image.py index d8372789b..d4ab8ad38 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -695,11 +695,13 @@ class TestImage: image: Image.Image, palette_result: ImagePalette.ImagePalette | None = None, ) -> None: + assert image.im is not None new_image = base_image._new(image.im) assert new_image.mode == image.mode 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 @@ -990,12 +992,14 @@ class TestImage: # 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 @@ -1013,13 +1017,16 @@ class TestImage: 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: diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py index bb6064882..4d8ffbf4a 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -113,4 +113,6 @@ 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 out.im is not None assert len(out.palette.colors) == len(out.im.getpalette()) / 3 diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index ebb7f2822..6a925975e 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -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) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 412ab44c3..ea5438f3d 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -137,7 +137,9 @@ def test_builtinfilter_p() -> None: builtin_filter = ImageFilter.BuiltinFilter() with pytest.raises(ValueError): - builtin_filter.filter(hopper("P").im) + im = hopper("P").im + assert im is not None + builtin_filter.filter(im) def test_kernel_not_enough_coefficients() -> None: diff --git a/Tests/test_image_getim.py b/Tests/test_image_getim.py index 9afa02b0a..e62d6e0ec 100644 --- a/Tests/test_image_getim.py +++ b/Tests/test_image_getim.py @@ -8,4 +8,5 @@ def test_sanity() -> None: type_repr = repr(type(im.getim())) assert "PyCapsule" in type_repr + assert im.im is not None assert isinstance(im.im.id, int) diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py index 75d9d2fc1..f2c447f71 100644 --- a/Tests/test_image_putpalette.py +++ b/Tests/test_image_putpalette.py @@ -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} diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 903cd8550..7c564d967 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -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 @@ -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() @@ -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") @@ -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] diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index ce6209c0d..4a5901d47 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -27,6 +27,7 @@ class TestImagingResampleVulnerability: ): with pytest.raises(MemoryError): # any resampling filter will do here + assert im.im is not None im.im.resize((xsize, ysize), Image.Resampling.BILINEAR) def test_invalid_size(self) -> None: diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index c9e304512..5700b3e06 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -27,6 +27,7 @@ class TestImagingCoreResize: ) -> Image.Image: # Image class independent version of resize. im.load() + assert im.im is not None return im._new(im.im.resize(size, f)) @pytest.mark.parametrize( @@ -37,6 +38,8 @@ class TestImagingCoreResize: r = self.resize(im, (15, 12), Image.Resampling.NEAREST) assert r.mode == mode assert r.size == (15, 12) + assert r.im is not None + assert im.im is not None assert r.im.bands == im.im.bands def test_convolution_modes(self) -> None: @@ -51,6 +54,8 @@ class TestImagingCoreResize: r = self.resize(im, (15, 12), Image.Resampling.BILINEAR) assert r.mode == mode assert r.size == (15, 12) + assert r.im is not None + assert im.im is not None assert r.im.bands == im.im.bands @pytest.mark.parametrize( diff --git a/Tests/test_imagemorph.py b/Tests/test_imagemorph.py index 4363f456e..b40de7f98 100644 --- a/Tests/test_imagemorph.py +++ b/Tests/test_imagemorph.py @@ -325,6 +325,8 @@ def test_wrong_mode() -> None: lut = ImageMorph.LutBuilder(op_name="corner").build_lut() imrgb = Image.new("RGB", (10, 10)) iml = Image.new("L", (10, 10)) + assert imrgb.im is not None + assert iml.im is not None with pytest.raises(RuntimeError): _imagingmorph.apply(bytes(lut), imrgb.im.id, iml.im.id) diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index 920012d86..64116bf70 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -98,10 +98,12 @@ def test_blur_accuracy(test_images: dict[str, ImageFile.ImageFile]) -> None: (4, 3, 2), (4, 2, 2), ]: + assert i.im is not None assert i.im.getpixel((x, y))[c] >= 250 # Fuzzy match. def gp(x: int, y: int) -> tuple[int, ...]: + assert i.im is not None return i.im.getpixel((x, y)) assert 236 <= gp(7, 4)[0] <= 239 diff --git a/Tests/test_lib_image.py b/Tests/test_lib_image.py index 31548bbc9..0a36af8b6 100644 --- a/Tests/test_lib_image.py +++ b/Tests/test_lib_image.py @@ -7,18 +7,21 @@ from PIL import Image def test_setmode() -> None: im = Image.new("L", (1, 1), 255) + assert im.im is not None im.im.setmode("1") assert im.im.getpixel((0, 0)) == 255 im.im.setmode("L") assert im.im.getpixel((0, 0)) == 255 im = Image.new("1", (1, 1), 1) + assert im.im is not None im.im.setmode("L") assert im.im.getpixel((0, 0)) == 255 im.im.setmode("1") assert im.im.getpixel((0, 0)) == 255 im = Image.new("RGB", (1, 1), (1, 2, 3)) + assert im.im is not None im.im.setmode("RGB") assert im.im.getpixel((0, 0)) == (1, 2, 3) im.im.setmode("RGBA") diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 6d71049a9..dc7192b99 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -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("= 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 @@ -443,6 +445,7 @@ def _save( elif im.mode == "L": palette = b"".join(o8(i) * 4 for i in range(256)) elif im.mode == "P": + assert im.im is not None palette = im.im.getpalette("RGB", "BGRX") colors = len(palette) // 4 else: diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 35915e652..c5ca8dadc 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -71,7 +71,7 @@ def Ghostscript( fp: IO[bytes], scale: int = 1, transparency: bool = False, -) -> Image.Image: +) -> Image.core.ImagingCore: """Render an image using Ghostscript""" global gs_binary if not has_ghostscript(): @@ -161,6 +161,7 @@ def Ghostscript( except OSError: pass + assert out_im.im is not None im = out_im.im.copy() out_im.close() return im @@ -190,7 +191,6 @@ class EpsImageFile(ImageFile.ImageFile): self.fp.seek(offset) self._mode = "RGB" - self._size = None byte_arr = bytearray(255) bytes_mv = memoryview(byte_arr) @@ -228,7 +228,7 @@ class EpsImageFile(ImageFile.ImageFile): 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 @@ -346,7 +346,7 @@ class EpsImageFile(ImageFile.ImageFile): trailer_reached = True bytes_read = 0 - if not self._size: + if not self.tile: msg = "cannot determine EPS bounding box" raise OSError(msg) diff --git a/src/PIL/FpxImagePlugin.py b/src/PIL/FpxImagePlugin.py index 386e37233..64d48635e 100644 --- a/src/PIL/FpxImagePlugin.py +++ b/src/PIL/FpxImagePlugin.py @@ -81,6 +81,8 @@ class FpxImageFile(ImageFile.ImageFile): # size (highest resolution) + assert isinstance(prop[0x1000002], int) + assert isinstance(prop[0x1000003], int) self._size = prop[0x1000002], prop[0x1000003] size = max(self.size) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index bf74f9356..55f927d40 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -307,6 +307,7 @@ class GifImageFile(ImageFile.ImageFile): self.tile = [] if self.dispose: + assert self.im is not None self.im.paste(self.dispose, self.dispose_extent) self._frame_palette = palette if palette is not None else self.global_palette @@ -320,17 +321,21 @@ class GifImageFile(ImageFile.ImageFile): 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 ( LOADING_STRATEGY != LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY or palette ): + assert self.im is not None if "transparency" in self.info: self.im.putpalettealpha(self.info["transparency"], 0) self.im = self.im.convert("RGBA", Image.Dither.FLOYDSTEINBERG) @@ -432,6 +437,7 @@ class GifImageFile(ImageFile.ImageFile): self._prev_im = self.im if self._frame_palette: self.im = Image.core.fill("P", self.size, self._frame_transparency or 0) + assert self.im is not None self.im.putpalette("RGB", *self._frame_palette.getdata()) else: self.im = None @@ -441,6 +447,7 @@ class GifImageFile(ImageFile.ImageFile): super().load_prepare() def load_end(self) -> None: + assert self.im is not None if self.__frame == 0: if self.mode == "P" and LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS: if self._frame_transparency is not None: @@ -495,6 +502,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: @@ -531,16 +539,17 @@ def _normalize_palette( if im.mode == "P": if not source_palette: + assert im.im is not None source_palette = im.im.getpalette("RGB")[:768] else: # L-mode 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) @@ -553,20 +562,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 @@ -578,7 +592,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): @@ -630,7 +645,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: @@ -660,10 +676,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) @@ -901,6 +919,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 ) @@ -950,7 +969,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( @@ -963,6 +982,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: diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 8729f7643..58d4a90a0 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -92,6 +92,7 @@ def read_32( msg = f"Error reading channel [{repr(bytesleft)} left]" raise SyntaxError(msg) band = Image.frombuffer("L", pixel_size, b"".join(data), "raw", "L", 0, 1) + assert im.im is not None im.im.putband(band.im, band_ix) return {"RGB": im} diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 2fb7ecd52..9d60011be 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -353,6 +353,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: fp.write(b"Lut: 1\r\n") fp.write(b"\000" * (511 - fp.tell()) + b"\032") if im.mode in ["P", "PA"]: + assert im.im is not None im_palette = im.im.getpalette("RGB", "RGB;L") colors = len(im_palette) // 3 palette = b"" diff --git a/src/PIL/Image.py b/src/PIL/Image.py index ebf4f46c4..7532a49a2 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -532,16 +532,16 @@ class Image: format_description: str | None = None _close_exclusive_fp_after_loading = True - def __init__(self): + def __init__(self) -> None: # FIXME: take "new" parameters / other image? # FIXME: turn mode and size into delegating properties? - self.im = None + self.im: core.ImagingCore | None = None self._mode = "" self._size = (0, 0) - self.palette = None - self.info = {} + self.palette: ImagePalette.ImagePalette | None = None + self.info: dict[str | tuple[int, int], Any] = {} self.readonly = 0 - self._exif = None + self._exif: Exif | None = None @property def width(self) -> int: @@ -617,11 +617,12 @@ class Image: # Instead of simply setting to None, we're setting up a # deferred error that will better explain that the core image # object is gone. - self.im = DeferredError(ValueError("Operation on closed image")) + self.im = DeferredError(ValueError("Operation on closed image")) # type: ignore[assignment] def _copy(self) -> None: self.load() - self.im = self.im.copy() + if self.im: + self.im = self.im.copy() self.readonly = 0 def _ensure_mutable(self) -> None: @@ -648,6 +649,7 @@ class Image: self.load() if not format or format == "PPM": + assert self.im is not None self.im.save_ppm(filename) else: self.save(filename, format, **options) @@ -796,6 +798,7 @@ class Image: # unpack data e = _getencoder(self.mode, encoder_name, encoder_args) + assert self.im is not None e.setimage(self.im) bufsize = max(65536, self.size[0] * 4) # see RawEncode.c @@ -862,6 +865,7 @@ class Image: # unpack data d = _getdecoder(self.mode, decoder_name, decoder_args) + assert self.im is not None d.setimage(self.im) s = d.decode(data) @@ -975,6 +979,7 @@ class Image: deprecate(mode, 12) self.load() + assert self.im is not None has_transparency = "transparency" in self.info if not mode and self.mode == "P": @@ -1045,9 +1050,11 @@ class Image: # use existing conversions trns_im = new(self.mode, (1, 1)) if self.mode == "P": + assert self.palette is not None trns_im.putpalette(self.palette) if isinstance(t, tuple): err = "Couldn't allocate a palette color for transparency" + assert trns_im.palette is not None try: t = trns_im.palette.getcolor(t, self) except ValueError as e: @@ -1087,6 +1094,7 @@ class Image: new_im = self._new(im) from . import ImagePalette + assert new_im.im is not None new_im.palette = ImagePalette.ImagePalette( "RGB", new_im.im.getpalette("RGB") ) @@ -1149,7 +1157,9 @@ class Image: if trns is not None: if new_im.mode == "P" and new_im.palette: try: - new_im.info["transparency"] = new_im.palette.getcolor(trns, new_im) + new_im.info["transparency"] = new_im.palette.getcolor( + cast(tuple[int, ...], trns), new_im # trns was converted to RGB + ) except ValueError as e: del new_im.info["transparency"] if str(e) != "cannot allocate more than 256 colors": @@ -1216,6 +1226,7 @@ class Image: ) raise ValueError(msg) + assert self.im is not None if palette: # use palette from reference image palette.load() @@ -1227,6 +1238,7 @@ class Image: raise ValueError(msg) im = self.im.convert("P", dither, palette.im) new_im = self._new(im) + assert palette.palette is not None new_im.palette = palette.palette.copy() return new_im @@ -1253,6 +1265,7 @@ class Image: :returns: An :py:class:`~PIL.Image.Image` object. """ self.load() + assert self.im is not None return self._new(self.im.copy()) __copy__ = copy @@ -1281,6 +1294,7 @@ class Image: raise ValueError(msg) self.load() + assert self.im is not None return self._new(self._crop(self.im, box)) def _crop( @@ -1334,6 +1348,7 @@ class Image: if ymargin is None: ymargin = xmargin self.load() + assert self.im is not None return self._new(self.im.expand(xmargin, ymargin)) if TYPE_CHECKING: @@ -1358,6 +1373,7 @@ class Image: raise TypeError(msg) multiband = isinstance(filter, ImageFilter.MultibandFilter) + assert self.im is not None if self.im.bands == 1 or multiband: return self._new(filter.filter(self.im)) @@ -1393,6 +1409,7 @@ class Image: """ self.load() + assert self.im is not None return self.im.getbbox(alpha_only) def getcolors( @@ -1412,6 +1429,7 @@ class Image: """ self.load() + assert self.im is not None if self.mode in ("1", "L", "P"): h = self.im.histogram() out: list[tuple[int, float]] = [(h[i], i) for i in range(256) if h[i]] @@ -1439,6 +1457,7 @@ class Image: """ self.load() + assert self.im is not None if band is not None: return self.im.getband(band) return self.im # could be abused @@ -1454,6 +1473,7 @@ class Image: """ self.load() + assert self.im is not None if self.im.bands > 1: return tuple(self.im.getband(i).getextrema() for i in range(self.im.bands)) return self.im.getextrema() @@ -1592,6 +1612,7 @@ class Image: """ self.load() + assert self.im is not None return self.im.ptr def getpalette(self, rawmode: str | None = "RGB") -> list[int] | None: @@ -1608,6 +1629,7 @@ class Image: """ self.load() + assert self.im is not None try: mode = self.im.getpalettemode() except ValueError: @@ -1628,11 +1650,15 @@ class Image: :returns: A boolean. """ - return ( + if ( self.mode in ("LA", "La", "PA", "RGBA", "RGBa") - or (self.mode == "P" and self.palette.mode.endswith("A")) or "transparency" in self.info - ) + ): + return True + if self.mode == "P": + assert self.palette is not None + return self.palette.mode.endswith("A") + return False def apply_transparency(self) -> None: """ @@ -1671,6 +1697,7 @@ class Image: """ self.load() + assert self.im is not None return self.im.getpixel(tuple(xy)) def getprojection(self) -> tuple[list[int], list[int]]: @@ -1682,6 +1709,7 @@ class Image: """ self.load() + assert self.im is not None x, y = self.im.getprojection() return list(x), list(y) @@ -1709,6 +1737,7 @@ class Image: :returns: A list containing pixel counts. """ self.load() + assert self.im is not None if mask: mask.load() return self.im.histogram((0, 0), mask.im) @@ -1737,6 +1766,7 @@ class Image: :returns: A float value representing the image entropy """ self.load() + assert self.im is not None if mask: mask.load() return self.im.entropy((0, 0), mask.im) @@ -1815,26 +1845,32 @@ class Image: raise ValueError(msg) box += (box[0] + size[0], box[1] + size[1]) + source: core.ImagingCore | str | float | tuple[float, ...] if isinstance(im, str): from . import ImageColor - im = ImageColor.getcolor(im, self.mode) - + source = ImageColor.getcolor(im, self.mode) elif isImageType(im): im.load() if self.mode != im.mode: if self.mode != "RGB" or im.mode not in ("LA", "RGBA", "RGBa"): # should use an adapter for this! im = im.convert(self.mode) - im = im.im + assert im.im is not None + source = im.im + elif isinstance(im, tuple): + source = im + else: + source = cast(float, im) self._ensure_mutable() + assert self.im is not None if mask: mask.load() - self.im.paste(im, box, mask.im) + self.im.paste(source, box, mask.im) else: - self.im.paste(im, box) + self.im.paste(source, box) def alpha_composite( self, im: Image, dest: Sequence[int] = (0, 0), source: Sequence[int] = (0, 0) @@ -1924,6 +1960,7 @@ class Image: if isinstance(lut, ImagePointHandler): return lut.point(self) + assert self.im is not None if callable(lut): # if it isn't a list, it should be a function if self.mode in ("I", "I;16", "F"): @@ -1960,17 +1997,20 @@ class Image: if self.mode not in ("LA", "PA", "RGBA"): # attempt to promote self to a matching alpha mode + assert self.im is not None try: mode = getmodebase(self.mode) + "A" try: self.im.setmode(mode) except (AttributeError, ValueError) as e: # do things the hard way + assert self.im is not None im = self.im.convert(mode) if im.mode not in ("LA", "PA", "RGBA"): msg = "alpha channel could not be added" raise ValueError(msg) from e # sanity check self.im = im + assert self.im is not None self._mode = self.im.mode except KeyError as e: msg = "illegal image mode" @@ -1981,6 +2021,7 @@ class Image: else: band = 3 + assert self.im is not None if isImageType(alpha): # alpha layer if alpha.mode not in ("1", "L"): @@ -2023,6 +2064,7 @@ class Image: self._ensure_mutable() + assert self.im is not None self.im.putdata(data, scale, offset) def putpalette( @@ -2105,8 +2147,10 @@ class Image: if self.mode == "PA": alpha = value[3] if len(value) == 4 else 255 value = value[:3] - palette_index = self.palette.getcolor(value, self) + assert self.palette is not None + palette_index = self.palette.getcolor(tuple(value), self) value = (palette_index, alpha) if self.mode == "PA" else palette_index + assert self.im is not None return self.im.putpixel(xy, value) def remap_palette( @@ -2133,6 +2177,7 @@ class Image: if source_palette is None: if self.mode == "P": self.load() + assert self.im is not None palette_mode = self.im.getpalettemode() if palette_mode == "RGBA": bands = 4 @@ -2180,6 +2225,7 @@ class Image: # m_im.putpalette(mapping_palette, 'L') # converts to 'P' # or just force it. # UNDONE -- this is part of the general issue with palettes + assert m_im.im is not None m_im.im.putpalette(palette_mode, palette_mode + ";L", m_im.palette.tobytes()) m_im = m_im.convert("L") @@ -2325,6 +2371,7 @@ class Image: (box[3] - reduce_box[1]) / factor_y, ) + assert self.im is not None return self._new(self.im.resize(size, resample, box)) def reduce( @@ -2359,7 +2406,7 @@ class Image: return im.convert(self.mode) self.load() - + assert self.im is not None return self._new(self.im.reduce(factor, box)) def rotate( @@ -2637,6 +2684,7 @@ class Image: """ self.load() + assert self.im is not None if self.im.bands == 1: return (self.copy(),) return tuple(map(self._new, self.im.split())) @@ -2661,6 +2709,7 @@ class Image: msg = f'The image has no channel "{channel}"' raise ValueError(msg) from e + assert self.im is not None return self._new(self.im.getband(channel)) def tell(self) -> int: @@ -2765,6 +2814,7 @@ class Image: self.im = im.im self._size = final_size + assert self.im is not None self._mode = self.im.mode self.readonly = 0 @@ -2949,6 +2999,7 @@ class Image: if image.mode in ("1", "P"): resample = Resampling.NEAREST + assert self.im is not None self.im.transform(box, image.im, method, data, resample, fill) def transpose(self, method: Transpose) -> Image: @@ -2963,6 +3014,7 @@ class Image: """ self.load() + assert self.im is not None return self._new(self.im.transpose(method)) def effect_spread(self, distance: int) -> Image: @@ -2972,6 +3024,7 @@ class Image: :param distance: Distance to spread pixels. """ self.load() + assert self.im is not None return self._new(self.im.effect_spread(distance)) def toqimage(self): @@ -3205,6 +3258,7 @@ def frombuffer( if mode == "P": from . import ImagePalette + assert im.im is not None im.palette = ImagePalette.ImagePalette("RGB", im.im.getpalette("RGB")) im.readonly = 1 return im diff --git a/src/PIL/ImageChops.py b/src/PIL/ImageChops.py index 29a5c995f..b1b616d5c 100644 --- a/src/PIL/ImageChops.py +++ b/src/PIL/ImageChops.py @@ -48,6 +48,7 @@ def invert(image: Image.Image) -> Image.Image: """ image.load() + assert image.im is not None return image._new(image.im.chop_invert()) @@ -63,6 +64,7 @@ def lighter(image1: Image.Image, image2: Image.Image) -> Image.Image: image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_lighter(image2.im)) @@ -78,6 +80,7 @@ def darker(image1: Image.Image, image2: Image.Image) -> Image.Image: image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_darker(image2.im)) @@ -93,6 +96,7 @@ def difference(image1: Image.Image, image2: Image.Image) -> Image.Image: image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_difference(image2.im)) @@ -110,6 +114,7 @@ def multiply(image1: Image.Image, image2: Image.Image) -> Image.Image: image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_multiply(image2.im)) @@ -124,6 +129,7 @@ def screen(image1: Image.Image, image2: Image.Image) -> Image.Image: image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_screen(image2.im)) @@ -136,6 +142,7 @@ def soft_light(image1: Image.Image, image2: Image.Image) -> Image.Image: image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_soft_light(image2.im)) @@ -148,6 +155,7 @@ def hard_light(image1: Image.Image, image2: Image.Image) -> Image.Image: image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_hard_light(image2.im)) @@ -160,6 +168,7 @@ def overlay(image1: Image.Image, image2: Image.Image) -> Image.Image: image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_overlay(image2.im)) @@ -177,6 +186,7 @@ def add( image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_add(image2.im, scale, offset)) @@ -194,6 +204,7 @@ def subtract( image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_subtract(image2.im, scale, offset)) @@ -207,6 +218,7 @@ def add_modulo(image1: Image.Image, image2: Image.Image) -> Image.Image: image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_add_modulo(image2.im)) @@ -220,6 +232,7 @@ def subtract_modulo(image1: Image.Image, image2: Image.Image) -> Image.Image: image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_subtract_modulo(image2.im)) @@ -238,6 +251,7 @@ def logical_and(image1: Image.Image, image2: Image.Image) -> Image.Image: image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_and(image2.im)) @@ -253,6 +267,7 @@ def logical_or(image1: Image.Image, image2: Image.Image) -> Image.Image: image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_or(image2.im)) @@ -268,6 +283,7 @@ def logical_xor(image1: Image.Image, image2: Image.Image) -> Image.Image: image1.load() image2.load() + assert image1.im is not None return image1._new(image1.im.chop_xor(image2.im)) @@ -308,4 +324,5 @@ def offset(image: Image.Image, xoffset: int, yoffset: int | None = None) -> Imag if yoffset is None: yoffset = xoffset image.load() + assert image.im is not None return image._new(image.im.offset(xoffset, yoffset)) diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index ec10230f1..3a6d52949 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -352,6 +352,8 @@ class ImageCmsTransform(Image.ImagePointHandler): im.load() if imOut is None: imOut = Image.new(self.output_mode, im.size, None) + assert im.im is not None + assert imOut.im is not None self.transform.apply(im.im.id, imOut.im.id) imOut.info["icc_profile"] = self.output_profile.tobytes() return imOut @@ -361,6 +363,7 @@ class ImageCmsTransform(Image.ImagePointHandler): if im.mode != self.output_mode: msg = "mode mismatch" raise ValueError(msg) # wrong output mode + assert im.im is not None self.transform.apply(im.im.id, im.im.id) im.info["icc_profile"] = self.output_profile.tobytes() return im diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 6f56d0236..988e3d7c5 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -32,7 +32,6 @@ from __future__ import annotations import math -import numbers import struct from collections.abc import Sequence from types import ModuleType @@ -160,13 +159,13 @@ class ImageDraw: if ink is not None: if isinstance(ink, str): ink = ImageColor.getcolor(ink, self.mode) - if self.palette and not isinstance(ink, numbers.Number): + if self.palette and isinstance(ink, tuple): ink = self.palette.getcolor(ink, self._image) result_ink = self.draw.draw_ink(ink) if fill is not None: if isinstance(fill, str): fill = ImageColor.getcolor(fill, self.mode) - if self.palette and not isinstance(fill, numbers.Number): + if self.palette and isinstance(fill, tuple): fill = self.palette.getcolor(fill, self._image) result_fill = self.draw.draw_ink(fill) return result_ink, result_fill diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 2a8846c1a..babf453cb 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -475,6 +475,7 @@ class Parser: d, e, o, a = im.tile[0] im.tile = [] self.decoder = Image._getdecoder(im.mode, d, a, im.decoderconfig) + assert im.im is not None self.decoder.setimage(im.im, e) # calculate decoder offset @@ -566,6 +567,7 @@ def _encode_tile( fp.seek(offset) encoder = Image._getencoder(im.mode, encoder_name, args, im.encoderconfig) try: + assert im.im is not None encoder.setimage(im.im, extents) if encoder.pushes_fd: encoder.setfd(fp) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 2ab65bfef..2d71b4df4 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -142,6 +142,7 @@ class ImageFont: raise TypeError(msg) image.load() + assert image.im is not None self.font = Image.core.font(image.im, data) diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py index 191cc2a5f..c8f0406ed 100644 --- a/src/PIL/ImageMath.py +++ b/src/PIL/ImageMath.py @@ -65,6 +65,8 @@ class _Operand: except AttributeError as e: msg = f"bad operand type for '{op}'" raise TypeError(msg) from e + assert out.im is not None + assert im_1.im is not None _imagingmath.unop(op, out.im.id, im_1.im.id) else: # binary operation @@ -93,6 +95,9 @@ class _Operand: except AttributeError as e: msg = f"bad operand type for '{op}'" raise TypeError(msg) from e + assert out.im is not None + assert im_1.im is not None + assert im_2.im is not None _imagingmath.binop(op, out.im.id, im_1.im.id, im_2.im.id) return _Operand(out) diff --git a/src/PIL/ImageMorph.py b/src/PIL/ImageMorph.py index 6a43983d3..cd4fa3cfe 100644 --- a/src/PIL/ImageMorph.py +++ b/src/PIL/ImageMorph.py @@ -213,6 +213,8 @@ class MorphOp: msg = "Image mode must be L" raise ValueError(msg) outimage = Image.new(image.mode, image.size, None) + assert image.im is not None + assert outimage.im is not None count = _imagingmorph.apply(bytes(self.lut), image.im.id, outimage.im.id) return count, outimage @@ -229,6 +231,7 @@ class MorphOp: if image.mode != "L": msg = "Image mode must be L" raise ValueError(msg) + assert image.im is not None return _imagingmorph.match(bytes(self.lut), image.im.id) def get_on_pixels(self, image: Image.Image) -> list[tuple[int, int]]: @@ -240,6 +243,7 @@ class MorphOp: if image.mode != "L": msg = "Image mode must be L" raise ValueError(msg) + assert image.im is not None return _imagingmorph.get_on_pixels(image.im.id) def load_lut(self, filename: str) -> None: diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index 6b13e57a0..6344220ef 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -43,6 +43,7 @@ def _pilbitmap_check() -> int: if _pilbitmap_ok is None: try: im = Image.new("1", (1, 1)) + assert im.im is not None tkinter.BitmapImage(data=f"PIL:{im.im.id}") _pilbitmap_ok = 1 except tkinter.TclError: @@ -127,10 +128,7 @@ class PhotoImage: # palette mapped data image.apply_transparency() image.load() - try: - mode = image.palette.mode - except AttributeError: - mode = "RGB" # default + mode = image.palette.mode if image.palette else "RGB" size = image.size kw["width"], kw["height"] = size @@ -190,6 +188,7 @@ class PhotoImage: # convert to blittable im.load() image = im.im + assert image is not None if image.isblock() and im.mode == self.__mode: block = image else: @@ -231,6 +230,7 @@ class BitmapImage: if _pilbitmap_check(): # fast way (requires the pilbitmap booster patch) image.load() + assert image.im is not None kw["data"] = f"PIL:{image.im.id}" self.__im = image # must keep a reference else: @@ -277,6 +277,7 @@ def getimage(photo: PhotoImage) -> Image.Image: """Copies the contents of a PhotoImage to a PIL image memory.""" im = Image.new("RGBA", (photo.width(), photo.height())) block = im.im + assert block is not None _pyimagingtkcall("PyImagingPhotoGet", photo, block.id) diff --git a/src/PIL/IptcImagePlugin.py b/src/PIL/IptcImagePlugin.py index 17243e705..03712d24c 100644 --- a/src/PIL/IptcImagePlugin.py +++ b/src/PIL/IptcImagePlugin.py @@ -199,9 +199,13 @@ def getiptcinfo( data = None + info: dict[tuple[int, int], bytes | list[bytes]] = {} if isinstance(im, IptcImageFile): # return info dictionary right away - return im.info + for k, v in im.info.items(): + if isinstance(k, tuple): + info[k] = v + return info elif isinstance(im, JpegImagePlugin.JpegImageFile): # extract the IPTC/NAA resource @@ -237,4 +241,7 @@ def getiptcinfo( except (IndexError, KeyError): pass # expected failure - return iptc_im.info + for k, v in iptc_im.info.items(): + if isinstance(k, tuple): + info[k] = v + return info diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index fc897e2b9..04fe8ba85 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -473,6 +473,7 @@ class JpegImageFile(ImageFile.ImageFile): except OSError: pass + assert self.im is not None self._mode = self.im.mode self._size = self.im.size diff --git a/src/PIL/PalmImagePlugin.py b/src/PIL/PalmImagePlugin.py index 1735070f8..9ca93c531 100644 --- a/src/PIL/PalmImagePlugin.py +++ b/src/PIL/PalmImagePlugin.py @@ -173,6 +173,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: flags = 0 if im.mode == "P" and "custom-colormap" in im.info: + assert im.palette is not None flags = flags & _FLAGS["custom-colormap"] colormapsize = 4 * 256 + 2 colormapmode = im.palette.mode diff --git a/src/PIL/PcdImagePlugin.py b/src/PIL/PcdImagePlugin.py index 1cd5c4a9d..64898e235 100644 --- a/src/PIL/PcdImagePlugin.py +++ b/src/PIL/PcdImagePlugin.py @@ -55,6 +55,7 @@ class PcdImageFile(ImageFile.ImageFile): assert self.im is not None self.im = self.im.rotate(self.tile_post_rotate) + assert self.im is not None self._size = self.im.size diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 910fa9755..59b5d0a8b 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -863,6 +863,7 @@ class PngImageFile(ImageFile.ImageFile): assert self.png is not None self.dispose: _imaging.ImagingCore | None + dispose_extent = None if frame == 0: if rewind: self._fp.seek(self.__rewind) @@ -877,7 +878,7 @@ class PngImageFile(ImageFile.ImageFile): self.default_image = self.info.get("default_image", False) self.dispose_op = self.info.get("disposal") self.blend_op = self.info.get("blend") - self.dispose_extent = self.info.get("bbox") + dispose_extent = self.info.get("bbox") self.__frame = 0 else: if frame != self.__frame + 1: @@ -886,6 +887,7 @@ class PngImageFile(ImageFile.ImageFile): # ensure previous frame was loaded self.load() + assert self.im is not None if self.dispose: self.im.paste(self.dispose, self.dispose_extent) @@ -935,11 +937,13 @@ class PngImageFile(ImageFile.ImageFile): self.tile = self.png.im_tile self.dispose_op = self.info.get("disposal") self.blend_op = self.info.get("blend") - self.dispose_extent = self.info.get("bbox") + dispose_extent = self.info.get("bbox") if not self.tile: msg = "image not found in APNG frame" raise EOFError(msg) + if dispose_extent: + self.dispose_extent: tuple[float, float, float, float] = dispose_extent # setup frame disposal (actual disposal done when needed in the next _seek()) if self._prev_im is None and self.dispose_op == Disposal.OP_PREVIOUS: @@ -1046,6 +1050,7 @@ class PngImageFile(ImageFile.ImageFile): self.png = None else: if self._prev_im and self.blend_op == Blend.OP_OVER: + assert self.im is not None updated = self._crop(self.im, self.dispose_extent) if self.im.mode == "RGB" and "transparency" in self.info: mask = updated.convert_transparent( @@ -1400,6 +1405,7 @@ def _save( chunk(fp, cid, data) if im.mode == "P": + assert im.im is not None palette_byte_number = colors * 3 palette_bytes = im.im.getpalette("RGB")[:palette_byte_number] while len(palette_bytes) < palette_byte_number: @@ -1430,8 +1436,9 @@ def _save( # and it's in the info dict. It's probably just stale. msg = "cannot use transparency for this mode" raise OSError(msg) - else: - if im.mode == "P" and im.im.getpalettemode() == "RGBA": + elif im.mode == "P": + assert im.im is not None + if im.im.getpalettemode() == "RGBA": alpha = im.im.getpalette("RGBA", "A") alpha_bytes = colors chunk(fp, b"tRNS", alpha[:alpha_bytes]) diff --git a/src/PIL/QoiImagePlugin.py b/src/PIL/QoiImagePlugin.py index 202ef52d0..c81d381fb 100644 --- a/src/PIL/QoiImagePlugin.py +++ b/src/PIL/QoiImagePlugin.py @@ -26,7 +26,7 @@ class QoiImageFile(ImageFile.ImageFile): 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" diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index d9d1bab5a..2b53c7dfa 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1313,6 +1313,7 @@ class TiffImageFile(ImageFile.ImageFile): decoder = Image._getdecoder( self.mode, "libtiff", tuple(args), self.decoderconfig ) + assert self.im is not None try: decoder.setimage(self.im, extents) except ValueError as e: