diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 981753eb9..3bafc4c9c 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -67,7 +67,7 @@ def test_quantize_no_dither(): def test_quantize_no_dither2(): im = Image.new("RGB", (9, 1)) - im.putdata(list((p,) * 3 for p in range(0, 36, 4))) + im.putdata([(p,) * 3 for p in range(0, 36, 4)]) palette = Image.new("P", (1, 1)) data = (0, 0, 0, 32, 32, 32) diff --git a/pyproject.toml b/pyproject.toml index 41b5aa87f..f9cea0612 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,6 +96,7 @@ test-extras = "tests" [tool.ruff] line-length = 88 select = [ + "C4", # flake8-comprehensions "E", # pycodestyle errors "EM", # flake8-errmsg "F", # pyflakes errors diff --git a/setup.py b/setup.py index f13f03713..2a364ba97 100755 --- a/setup.py +++ b/setup.py @@ -440,17 +440,17 @@ class pil_build_ext(build_ext): # # add configured kits - for root_name, lib_name in dict( - JPEG_ROOT="libjpeg", - JPEG2K_ROOT="libopenjp2", - TIFF_ROOT=("libtiff-5", "libtiff-4"), - ZLIB_ROOT="zlib", - FREETYPE_ROOT="freetype2", - HARFBUZZ_ROOT="harfbuzz", - FRIBIDI_ROOT="fribidi", - LCMS_ROOT="lcms2", - IMAGEQUANT_ROOT="libimagequant", - ).items(): + for root_name, lib_name in { + "JPEG_ROOT": "libjpeg", + "JPEG2K_ROOT": "libopenjp2", + "TIFF_ROOT": ("libtiff-5", "libtiff-4"), + "ZLIB_ROOT": "zlib", + "FREETYPE_ROOT": "freetype2", + "HARFBUZZ_ROOT": "harfbuzz", + "FRIBIDI_ROOT": "fribidi", + "LCMS_ROOT": "lcms2", + "IMAGEQUANT_ROOT": "libimagequant", + }.items(): root = globals()[root_name] if root is None and root_name in os.environ: diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index ef719e3ec..b51019c66 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -396,7 +396,7 @@ def _save(im, fp, filename, bitmap_header=True): dpi = info.get("dpi", (96, 96)) # 1 meter == 39.3701 inches - ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi)) + ppm = tuple(int(x * 39.3701 + 0.5) for x in dpi) stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3) header = 40 # or 64 for OS/2 version 2 diff --git a/src/PIL/CurImagePlugin.py b/src/PIL/CurImagePlugin.py index 94efff341..fc0dae44b 100644 --- a/src/PIL/CurImagePlugin.py +++ b/src/PIL/CurImagePlugin.py @@ -64,8 +64,6 @@ class CurImageFile(BmpImagePlugin.BmpImageFile): d, e, o, a = self.tile[0] self.tile[0] = d, (0, 0) + self.size, o, a - return - # # -------------------------------------------------------------------- diff --git a/src/PIL/FpxImagePlugin.py b/src/PIL/FpxImagePlugin.py index a878cbfd2..3027ef45b 100644 --- a/src/PIL/FpxImagePlugin.py +++ b/src/PIL/FpxImagePlugin.py @@ -227,6 +227,7 @@ class FpxImageFile(ImageFile.ImageFile): break # isn't really required self.stream = stream + self._fp = self.fp self.fp = None def load(self): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index d435b5617..2853bd596 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -40,7 +40,7 @@ from enum import IntEnum from pathlib import Path try: - import defusedxml.ElementTree as ElementTree + from defusedxml import ElementTree except ImportError: ElementTree = None @@ -1160,7 +1160,7 @@ class Image: if palette.mode != "P": msg = "bad mode for palette image" raise ValueError(msg) - if self.mode != "RGB" and self.mode != "L": + if self.mode not in {"RGB", "L"}: msg = "only RGB or L mode images can be quantized to a palette" raise ValueError(msg) im = self.im.convert("P", dither, palette.im) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index fbf320d72..6509d4c8e 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -921,7 +921,7 @@ def floodfill(image, xy, value, border=None, thresh=0): if border is None: fill = _color_diff(p, background) <= thresh else: - fill = p != value and p != border + fill = p not in (value, border) if fill: pixel[s, t] = value new_edge.add((s, t)) diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 963d6c1a3..bb0cb676a 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -334,10 +334,7 @@ def _save(im, fp, filename): if quality_layers is not None and not ( isinstance(quality_layers, (list, tuple)) and all( - [ - isinstance(quality_layer, (int, float)) - for quality_layer in quality_layers - ] + isinstance(quality_layer, (int, float)) for quality_layer in quality_layers ) ): msg = "quality_layers must be a sequence of numbers" diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 3596e0899..b8a5e7a59 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -397,7 +397,7 @@ class JpegImageFile(ImageFile.ImageFile): # self.__offset = self.fp.tell() break s = self.fp.read(1) - elif i == 0 or i == 0xFFFF: + elif i in {0, 0xFFFF}: # padded marker or junk; move on s = b"\xff" elif i == 0xFF00: # Skip extraneous data (escaped 0xFF) diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 801318930..e4154902f 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -66,6 +66,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): self._n_frames = len(self.images) self.is_animated = self._n_frames > 1 + self.__fp = self.fp self.seek(0) def seek(self, frame): @@ -87,10 +88,12 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): return self.frame def close(self): + self.__fp.close() self.ole.close() super().close() def __exit__(self, *args): + self.__fp.close() self.ole.close() super().__exit__() diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index acb9ce5a3..a2a259c89 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -123,7 +123,7 @@ class SgiImageFile(ImageFile.ImageFile): def _save(im, fp, filename): - if im.mode != "RGB" and im.mode != "RGBA" and im.mode != "L": + if im.mode not in {"RGB", "RGBA", "L"}: msg = "Unsupported SGI image mode" raise ValueError(msg) @@ -155,7 +155,7 @@ def _save(im, fp, filename): # Z Dimension: Number of channels z = len(im.mode) - if dim == 1 or dim == 2: + if dim in {1, 2}: z = 1 # assert we've got the right number of bands. diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py index edcae41e4..b02c637b6 100644 --- a/src/PIL/TiffTags.py +++ b/src/PIL/TiffTags.py @@ -427,7 +427,7 @@ def _populate(): TAGS_V2[k] = TagInfo(k, *v) - for group, tags in TAGS_V2_GROUPS.items(): + for tags in TAGS_V2_GROUPS.values(): for k, v in tags.items(): tags[k] = TagInfo(k, *v) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 0d111a8ec..c5c9441d4 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -482,7 +482,7 @@ def extract_dep(url: str, filename: str) -> None: msg = "Attempted Path Traversal in Zip File" raise RuntimeError(msg) zf.extractall(sources_dir) - elif filename.endswith(".tar.gz") or filename.endswith(".tgz"): + elif filename.endswith((".tar.gz", ".tgz")): with tarfile.open(file, "r:gz") as tgz: for member in tgz.getnames(): member_abspath = os.path.abspath(os.path.join(sources_dir, member))