From e8cddfbc6a2a6167e395fc8cfad9ea29a38f108b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 May 2024 08:45:39 +1000 Subject: [PATCH 1/7] Updated codecov/codecov-action to v4 --- .github/workflows/test-cygwin.yml | 3 ++- .github/workflows/test-docker.yml | 3 ++- .github/workflows/test-mingw.yml | 3 ++- .github/workflows/test-windows.yml | 3 ++- .github/workflows/test.yml | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 9674a4665..7972730ca 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -132,11 +132,12 @@ jobs: bash.exe .ci/after_success.sh - name: Upload coverage - uses: codecov/codecov-action@v3.1.5 + uses: codecov/codecov-action@v4 with: file: ./coverage.xml flags: GHA_Cygwin name: Cygwin Python 3.${{ matrix.python-minor-version }} + token: ${{ secrets.CODECOV_ORG_TOKEN }} success: permissions: diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index c53f23a9f..6afed74db 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -100,11 +100,12 @@ jobs: MATRIX_DOCKER: ${{ matrix.docker }} - name: Upload coverage - uses: codecov/codecov-action@v3.1.5 + uses: codecov/codecov-action@v4 with: flags: GHA_Docker name: ${{ matrix.docker }} gcov: true + token: ${{ secrets.CODECOV_ORG_TOKEN }} success: permissions: diff --git a/.github/workflows/test-mingw.yml b/.github/workflows/test-mingw.yml index a07a27c46..a773ca453 100644 --- a/.github/workflows/test-mingw.yml +++ b/.github/workflows/test-mingw.yml @@ -85,8 +85,9 @@ jobs: python3 -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests - name: Upload coverage - uses: codecov/codecov-action@v3.1.5 + uses: codecov/codecov-action@v4 with: file: ./coverage.xml flags: GHA_Windows name: "MSYS2 MinGW" + token: ${{ secrets.CODECOV_ORG_TOKEN }} diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 40994c60a..9edc15173 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -213,11 +213,12 @@ jobs: shell: pwsh - name: Upload coverage - uses: codecov/codecov-action@v3.1.5 + uses: codecov/codecov-action@v4 with: file: ./coverage.xml flags: GHA_Windows name: ${{ runner.os }} Python ${{ matrix.python-version }} + token: ${{ secrets.CODECOV_ORG_TOKEN }} success: permissions: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4573fde90..aa5646caf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -150,11 +150,12 @@ jobs: .ci/after_success.sh - name: Upload coverage - uses: codecov/codecov-action@v3.1.5 + uses: codecov/codecov-action@v4 with: flags: ${{ matrix.os == 'ubuntu-latest' && 'GHA_Ubuntu' || 'GHA_macOS' }} name: ${{ matrix.os }} Python ${{ matrix.python-version }} gcov: true + token: ${{ secrets.CODECOV_ORG_TOKEN }} success: permissions: From ac1eb57c03e182752e1207cd477300650fce4dc0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 May 2024 09:43:50 +1000 Subject: [PATCH 2/7] Install git --- .github/workflows/test-cygwin.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 7972730ca..1269ef8cb 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -55,6 +55,7 @@ jobs: packages: > gcc-g++ ghostscript + git ImageMagick jpeg libfreetype-devel From 6036d81d973e7b0a4613bb06d4a2d79310eef799 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 May 2024 20:51:54 +1000 Subject: [PATCH 3/7] Added type hints --- src/PIL/BlpImagePlugin.py | 6 +++--- src/PIL/BmpImagePlugin.py | 4 ++-- src/PIL/DcxImagePlugin.py | 4 ++-- src/PIL/DdsImagePlugin.py | 2 +- src/PIL/EpsImagePlugin.py | 4 ++-- src/PIL/FliImagePlugin.py | 4 ++-- src/PIL/GifImagePlugin.py | 12 ++++++------ src/PIL/Hdf5StubImagePlugin.py | 2 +- src/PIL/IcnsImagePlugin.py | 2 +- src/PIL/IcoImagePlugin.py | 2 +- src/PIL/ImImagePlugin.py | 4 ++-- src/PIL/Image.py | 7 +++++-- src/PIL/ImageFile.py | 10 +++++----- src/PIL/ImageFilter.py | 5 ++++- src/PIL/ImageWin.py | 4 ++-- src/PIL/MicImagePlugin.py | 4 ++-- src/PIL/MpoImagePlugin.py | 4 ++-- src/PIL/PngImagePlugin.py | 8 ++++---- src/PIL/PsdImagePlugin.py | 9 ++++----- src/PIL/SpiderImagePlugin.py | 6 +++--- src/PIL/TiffImagePlugin.py | 20 ++++++++++---------- src/PIL/WalImageFile.py | 2 +- src/PIL/WebPImagePlugin.py | 4 ++-- 23 files changed, 67 insertions(+), 62 deletions(-) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 8d351ce91..bdf54baae 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -253,7 +253,7 @@ class BlpImageFile(ImageFile.ImageFile): format = "BLP" format_description = "Blizzard Mipmap Format" - def _open(self): + def _open(self) -> None: self.magic = self.fp.read(4) self.fp.seek(5, os.SEEK_CUR) @@ -333,7 +333,7 @@ class _BLPBaseDecoder(ImageFile.PyDecoder): class BLP1Decoder(_BLPBaseDecoder): - def _load(self): + def _load(self) -> None: if self._blp_compression == Format.JPEG: self._decode_jpeg_stream() @@ -418,7 +418,7 @@ class BLP2Decoder(_BLPBaseDecoder): class BLPEncoder(ImageFile.PyEncoder): _pushes_fd = True - def _write_palette(self): + def _write_palette(self) -> bytes: data = b"" palette = self.im.getpalette("RGBA", "RGBA") for i in range(len(palette) // 4): diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 9ce0fed88..c5d1cd40d 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -283,7 +283,7 @@ class BmpImageFile(ImageFile.ImageFile): ) ] - def _open(self): + def _open(self) -> None: """Open file, check magic number and read header""" # read 14 bytes: magic number, filesize, reserved, header final offset head_data = self.fp.read(14) @@ -376,7 +376,7 @@ class DibImageFile(BmpImageFile): format = "DIB" format_description = "Windows Bitmap" - def _open(self): + def _open(self) -> None: self._bitmap() diff --git a/src/PIL/DcxImagePlugin.py b/src/PIL/DcxImagePlugin.py index b24c16329..1c455b032 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -63,7 +63,7 @@ class DcxImageFile(PcxImageFile): self.is_animated = self.n_frames > 1 self.seek(0) - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return self.frame = frame @@ -71,7 +71,7 @@ class DcxImageFile(PcxImageFile): self.fp.seek(self._offset[frame]) PcxImageFile._open(self) - def tell(self): + def tell(self) -> int: return self.frame diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 3032e4aec..59ee0f8a0 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -331,7 +331,7 @@ class DdsImageFile(ImageFile.ImageFile): format = "DDS" format_description = "DirectDraw Surface" - def _open(self): + def _open(self) -> None: if not _accept(self.fp.read(4)): msg = "not a DDS file" raise SyntaxError(msg) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index ec6705742..b57daca56 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -178,7 +178,7 @@ class PSFile: self.char = None self.fp.seek(offset, whence) - def readline(self): + def readline(self) -> str: s = [self.char or b""] self.char = None @@ -212,7 +212,7 @@ class EpsImageFile(ImageFile.ImageFile): mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"} - def _open(self): + def _open(self) -> None: (length, offset) = self._find_offset(self.fp) # go to offset - start of "%!PS" diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index 7a233d015..eea2c0c95 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -123,7 +123,7 @@ class FliImageFile(ImageFile.ImageFile): palette[i] = (r, g, b) i += 1 - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return if frame < self.__frame: @@ -162,7 +162,7 @@ class FliImageFile(ImageFile.ImageFile): self.__offset += framesize - def tell(self): + def tell(self) -> int: return self.__frame diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 93be7fefb..26e595819 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -76,7 +76,7 @@ class GifImageFile(ImageFile.ImageFile): global_palette = None - def data(self): + def data(self) -> bytes | None: s = self.fp.read(1) if s and s[0]: return self.fp.read(s[0]) @@ -88,7 +88,7 @@ class GifImageFile(ImageFile.ImageFile): return True return False - def _open(self): + def _open(self) -> None: # Screen s = self.fp.read(13) if not _accept(s): @@ -147,7 +147,7 @@ class GifImageFile(ImageFile.ImageFile): self.seek(current) return self._is_animated - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return if frame < self.__frame: @@ -417,7 +417,7 @@ class GifImageFile(ImageFile.ImageFile): elif k in self.info: del self.info[k] - def load_prepare(self): + def load_prepare(self) -> None: temp_mode = "P" if self._frame_palette else "L" self._prev_im = None if self.__frame == 0: @@ -437,7 +437,7 @@ class GifImageFile(ImageFile.ImageFile): super().load_prepare() - def load_end(self): + def load_end(self) -> None: if self.__frame == 0: if self.mode == "P" and LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS: if self._frame_transparency is not None: @@ -463,7 +463,7 @@ class GifImageFile(ImageFile.ImageFile): else: self.im.paste(frame_im, self.dispose_extent) - def tell(self): + def tell(self) -> int: return self.__frame diff --git a/src/PIL/Hdf5StubImagePlugin.py b/src/PIL/Hdf5StubImagePlugin.py index f50e6bf16..afbfd1639 100644 --- a/src/PIL/Hdf5StubImagePlugin.py +++ b/src/PIL/Hdf5StubImagePlugin.py @@ -37,7 +37,7 @@ class HDF5StubImageFile(ImageFile.StubImageFile): format = "HDF5" format_description = "HDF5" - def _open(self): + def _open(self) -> None: offset = self.fp.tell() if not _accept(self.fp.read(8)): diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index c2c950863..0a86ba883 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -252,7 +252,7 @@ class IcnsImageFile(ImageFile.ImageFile): format = "ICNS" format_description = "Mac OS icns resource" - def _open(self): + def _open(self) -> None: self.icns = IcnsFile(self.fp) self._mode = "RGBA" self.info["sizes"] = self.icns.itersizes() diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py index 82b190eb8..eacffbae6 100644 --- a/src/PIL/IcoImagePlugin.py +++ b/src/PIL/IcoImagePlugin.py @@ -302,7 +302,7 @@ class IcoImageFile(ImageFile.ImageFile): format = "ICO" format_description = "Windows Icon" - def _open(self): + def _open(self) -> None: self.ico = IcoFile(self.fp) self.info["sizes"] = self.ico.sizes() self.size = self.ico.entry[0]["dim"] diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 4613e40b6..0de7d6492 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -278,7 +278,7 @@ class ImImageFile(ImageFile.ImageFile): def is_animated(self): return self.info[FRAMES] > 1 - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return @@ -296,7 +296,7 @@ class ImImageFile(ImageFile.ImageFile): self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))] - def tell(self): + def tell(self) -> int: return self.frame diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 33b3da9a6..0f2e146bb 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1298,7 +1298,10 @@ class Image: self.load() return self._new(self.im.expand(xmargin, ymargin)) - def filter(self, filter): + if TYPE_CHECKING: + from . import ImageFilter + + def filter(self, filter: ImageFilter.Filter | type[ImageFilter.Filter]) -> Image: """ Filters this image using the given filter. For a list of available filters, see the :py:mod:`~PIL.ImageFilter` module. @@ -1310,7 +1313,7 @@ class Image: self.load() - if isinstance(filter, Callable): + if callable(filter): filter = filter() if not hasattr(filter, "filter"): msg = "filter argument should be ImageFilter.Filter instance or class" diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 27885e654..b93e2ad2c 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -311,7 +311,7 @@ class ImageFile(Image.Image): return Image.Image.load(self) - def load_prepare(self): + def load_prepare(self) -> None: # create image memory if necessary if not self.im or self.im.mode != self.mode or self.im.size != self.size: self.im = Image.core.new(self.mode, self.size) @@ -319,7 +319,7 @@ class ImageFile(Image.Image): if self.mode == "P": Image.Image.load(self) - def load_end(self): + def load_end(self) -> None: # may be overridden pass @@ -390,7 +390,7 @@ class Parser: offset = 0 finished = 0 - def reset(self): + def reset(self) -> None: """ (Consumer) Reset the parser. Note that you can only call this method immediately after you've created a parser; parser @@ -605,7 +605,7 @@ def _safe_read(fp, size): class PyCodecState: - def __init__(self): + def __init__(self) -> None: self.xsize = 0 self.ysize = 0 self.xoff = 0 @@ -634,7 +634,7 @@ class PyCodec: """ self.args = args - def cleanup(self): + def cleanup(self) -> None: """ Override to perform codec specific cleanup diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index b2c4950d6..fa9ebd9de 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -16,11 +16,14 @@ # from __future__ import annotations +import abc import functools class Filter: - pass + @abc.abstractmethod + def filter(self, image): + pass class MultibandFilter(Filter): diff --git a/src/PIL/ImageWin.py b/src/PIL/ImageWin.py index 75910d2d9..2c439038d 100644 --- a/src/PIL/ImageWin.py +++ b/src/PIL/ImageWin.py @@ -204,7 +204,7 @@ class Window: def ui_handle_damage(self, x0, y0, x1, y1): pass - def ui_handle_destroy(self): + def ui_handle_destroy(self) -> None: pass def ui_handle_repair(self, dc, x0, y0, x1, y1): @@ -213,7 +213,7 @@ class Window: def ui_handle_resize(self, width, height): pass - def mainloop(self): + def mainloop(self) -> None: Image.core.eventloop() diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 96de386a8..5aef94dfb 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -38,7 +38,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): format_description = "Microsoft Image Composer" _close_exclusive_fp_after_loading = False - def _open(self): + def _open(self) -> None: # read the OLE directory and see if this is a likely # to be a Microsoft Image Composer file @@ -88,7 +88,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): def tell(self): return self.frame - def close(self): + def close(self) -> None: self.__fp.close() self.ole.close() super().close() diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index ac9820bbf..fb6620e75 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -127,7 +127,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): def load_seek(self, pos): self._fp.seek(pos) - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return self.fp = self._fp @@ -149,7 +149,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): self.tile = [("jpeg", (0, 0) + self.size, self.offset, self.tile[0][-1])] self.__frame = frame - def tell(self): + def tell(self) -> int: return self.__frame @staticmethod diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 012e0b61b..39faa0f78 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -800,7 +800,7 @@ class PngImageFile(ImageFile.ImageFile): self.fp.close() self.fp = None - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return if frame < self.__frame: @@ -909,10 +909,10 @@ class PngImageFile(ImageFile.ImageFile): else: self.dispose = None - def tell(self): + def tell(self) -> int: return self.__frame - def load_prepare(self): + def load_prepare(self) -> None: """internal: prepare to read PNG file""" if self.info.get("interlace"): @@ -954,7 +954,7 @@ class PngImageFile(ImageFile.ImageFile): return self.fp.read(read_bytes) - def load_end(self): + def load_end(self) -> None: """internal: finished reading image data""" if self.__idat != 0: self.fp.read(self.__idat) diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index b15918313..86c1a6763 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -57,7 +57,7 @@ class PsdImageFile(ImageFile.ImageFile): format_description = "Adobe Photoshop" _close_exclusive_fp_after_loading = False - def _open(self): + def _open(self) -> None: read = self.fp.read # @@ -141,23 +141,22 @@ class PsdImageFile(ImageFile.ImageFile): self.frame = 1 self._min_frame = 1 - def seek(self, layer): + def seek(self, layer: int) -> None: if not self._seek_check(layer): return # seek to given layer (1..max) try: - name, mode, bbox, tile = self.layers[layer - 1] + _, mode, _, tile = self.layers[layer - 1] self._mode = mode self.tile = tile self.frame = layer self.fp = self._fp - return name, bbox except IndexError as e: msg = "no such layer" raise EOFError(msg) from e - def tell(self): + def tell(self) -> int: # return layer number (0=image, 1..max=layers) return self.frame diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 86582fb12..01a39e97c 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -97,7 +97,7 @@ class SpiderImageFile(ImageFile.ImageFile): format_description = "Spider 2D image" _close_exclusive_fp_after_loading = False - def _open(self): + def _open(self) -> None: # check header n = 27 * 4 # read 27 float values f = self.fp.read(n) @@ -165,13 +165,13 @@ class SpiderImageFile(ImageFile.ImageFile): return self._nimages > 1 # 1st image index is zero (although SPIDER imgnumber starts at 1) - def tell(self): + def tell(self) -> int: if self.imgnumber < 1: return 0 else: return self.imgnumber - 1 - def seek(self, frame): + def seek(self, frame: int) -> None: if self.istack == 0: msg = "attempt to seek in a non-stack file" raise EOFError(msg) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index c78c223b3..1be717de1 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1143,7 +1143,7 @@ class TiffImageFile(ImageFile.ImageFile): self.seek(current) return self._n_frames - def seek(self, frame): + def seek(self, frame: int) -> None: """Select a given frame as current image""" if not self._seek_check(frame): return @@ -1198,7 +1198,7 @@ class TiffImageFile(ImageFile.ImageFile): self.__frame = frame self._setup() - def tell(self): + def tell(self) -> int: """Return the current frame number""" return self.__frame @@ -1237,7 +1237,7 @@ class TiffImageFile(ImageFile.ImageFile): return self._load_libtiff() return super().load() - def load_end(self): + def load_end(self) -> None: # allow closing if we're on the first frame, there's no next # This is the ImageFile.load path only, libtiff specific below. if not self.is_animated: @@ -1942,7 +1942,7 @@ class AppendingTiffWriter: self.beginning = self.f.tell() self.setup() - def setup(self): + def setup(self) -> None: # Reset everything. self.f.seek(self.beginning, os.SEEK_SET) @@ -1967,7 +1967,7 @@ class AppendingTiffWriter: self.skipIFDs() self.goToEnd() - def finalize(self): + def finalize(self) -> None: if self.isFirst: return @@ -1990,7 +1990,7 @@ class AppendingTiffWriter: self.f.seek(ifd_offset) self.fixIFD() - def newFrame(self): + def newFrame(self) -> None: # Call this to finish a frame. self.finalize() self.setup() @@ -2013,7 +2013,7 @@ class AppendingTiffWriter: self.f.seek(offset, whence) return self.tell() - def goToEnd(self): + def goToEnd(self) -> None: self.f.seek(0, os.SEEK_END) pos = self.f.tell() @@ -2029,7 +2029,7 @@ class AppendingTiffWriter: self.shortFmt = self.endian + "H" self.tagFormat = self.endian + "HHL" - def skipIFDs(self): + def skipIFDs(self) -> None: while True: ifd_offset = self.readLong() if ifd_offset == 0: @@ -2084,11 +2084,11 @@ class AppendingTiffWriter: msg = f"wrote only {bytes_written} bytes but wanted 4" raise RuntimeError(msg) - def close(self): + def close(self) -> None: self.finalize() self.f.close() - def fixIFD(self): + def fixIFD(self) -> None: num_tags = self.readShort() for i in range(num_tags): diff --git a/src/PIL/WalImageFile.py b/src/PIL/WalImageFile.py index c5bf3e04c..fbd7be6ed 100644 --- a/src/PIL/WalImageFile.py +++ b/src/PIL/WalImageFile.py @@ -32,7 +32,7 @@ class WalImageFile(ImageFile.ImageFile): format = "WAL" format_description = "Quake2 Texture" - def _open(self): + def _open(self) -> None: self._mode = "P" # read header fields diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 9c8d53336..61ae9eae5 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -109,7 +109,7 @@ class WebPImageFile(ImageFile.ImageFile): """ return self._getxmp(self.info["xmp"]) if "xmp" in self.info else {} - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return @@ -174,7 +174,7 @@ class WebPImageFile(ImageFile.ImageFile): def load_seek(self, pos): pass - def tell(self): + def tell(self) -> int: if not _webp.HAVE_WEBPANIM: return super().tell() From b17f1e507b1e44246b89938e5e4b5d53716751f0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 7 May 2024 14:01:08 +1000 Subject: [PATCH 4/7] Use f-strings --- Tests/test_file_eps.py | 4 +--- selftest.py | 4 ++-- src/PIL/IptcImagePlugin.py | 2 +- src/PIL/PdfParser.py | 5 +++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index d01884f96..1c21aa8ca 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -336,9 +336,7 @@ def test_readline_psfile(tmp_path: Path) -> None: strings = ["something", "else", "baz", "bif"] def _test_readline(t: EpsImagePlugin.PSFile, ending: str) -> None: - ending = "Failure with line ending: %s" % ( - "".join("%s" % ord(s) for s in ending) - ) + ending = f"Failure with line ending: {''.join(str(ord(s)) for s in ending)}" assert t.readline().strip("\r\n") == "something", ending assert t.readline().strip("\r\n") == "else", ending assert t.readline().strip("\r\n") == "baz", ending diff --git a/selftest.py b/selftest.py index 661abcddb..9e049367e 100755 --- a/selftest.py +++ b/selftest.py @@ -165,9 +165,9 @@ if __name__ == "__main__": print("Running selftest:") status = doctest.testmod(sys.modules[__name__]) if status[0]: - print("*** %s tests of %d failed." % status) + print(f"*** {status[0]} tests of {status[1]} failed.") exit_status = 1 else: - print("--- %s tests passed." % status[1]) + print(f"--- {status[1]} tests passed.") sys.exit(exit_status) diff --git a/src/PIL/IptcImagePlugin.py b/src/PIL/IptcImagePlugin.py index 409609434..73df83bfb 100644 --- a/src/PIL/IptcImagePlugin.py +++ b/src/PIL/IptcImagePlugin.py @@ -57,7 +57,7 @@ def dump(c: Sequence[int | bytes]) -> None: """.. deprecated:: 10.2.0""" deprecate("IptcImagePlugin.dump", 12) for i in c: - print("%02x" % _i8(i), end=" ") + print(f"{_i8(i):02x}", end=" ") print() diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index c1ed78797..65db70e13 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -825,8 +825,9 @@ class PdfParser: try: stream_len = int(result[b"Length"]) except (TypeError, KeyError, ValueError) as e: - msg = "bad or missing Length in stream dict (%r)" % result.get( - b"Length", None + msg = ( + "bad or missing Length in stream dict " + f"({result.get(b'Length')})" ) raise PdfFormatError(msg) from e stream_data = data[m.end() : m.end() + stream_len] From 7d81cbd0ede0dd9e516f7c3b5e2a42988dd105b5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 7 May 2024 13:59:30 +1000 Subject: [PATCH 5/7] Do not use percent format --- Tests/test_image_access.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 02c75073a..f37ae6096 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -415,7 +415,9 @@ class TestEmbeddable: int main(int argc, char* argv[]) { - char *home = "%s"; + char *home = \"""" + + sys.prefix.replace("\\", "\\\\") + + """\"; wchar_t *whome = Py_DecodeLocale(home, NULL); Py_SetPythonHome(whome); @@ -432,7 +434,6 @@ int main(int argc, char* argv[]) return 0; } """ - % sys.prefix.replace("\\", "\\\\") ) compiler = getattr(build_ext, "new_compiler")() From ed0867abecd7f4ce8eb300c4896229fcedeee6c4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 8 May 2024 06:30:43 +1000 Subject: [PATCH 6/7] Set stream length for later use --- src/PIL/PdfParser.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index 65db70e13..c43f2da7b 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -823,12 +823,10 @@ class PdfParser: m = cls.re_stream_start.match(data, offset) if m: try: - stream_len = int(result[b"Length"]) - except (TypeError, KeyError, ValueError) as e: - msg = ( - "bad or missing Length in stream dict " - f"({result.get(b'Length')})" - ) + stream_len_str = result.get(b"Length") + stream_len = int(stream_len_str) + except (TypeError, ValueError) as e: + msg = f"bad or missing Length in stream dict ({stream_len_str})" raise PdfFormatError(msg) from e stream_data = data[m.end() : m.end() + stream_len] m = cls.re_stream_end.match(data, m.end() + stream_len) From a3356879fd5b0685b3741d6f43802626a3f5d2e0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 8 May 2024 17:57:36 +1000 Subject: [PATCH 7/7] Use f-string --- Tests/test_image_access.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index f37ae6096..e55a4d9c1 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -409,15 +409,14 @@ class TestEmbeddable: from setuptools.command import build_ext with open("embed_pil.c", "w", encoding="utf-8") as fh: + home = sys.prefix.replace("\\", "\\\\") fh.write( - """ + f""" #include "Python.h" int main(int argc, char* argv[]) -{ - char *home = \"""" - + sys.prefix.replace("\\", "\\\\") - + """\"; +{{ + char *home = "{home}"; wchar_t *whome = Py_DecodeLocale(home, NULL); Py_SetPythonHome(whome); @@ -432,7 +431,7 @@ int main(int argc, char* argv[]) PyMem_RawFree(whome); return 0; -} +}} """ )