diff --git a/.ci/install.sh b/.ci/install.sh index 52b821417..aeb5e6514 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -27,14 +27,13 @@ python3 -m pip install --upgrade wheel python3 -m pip install coverage python3 -m pip install defusedxml python3 -m pip install ipython -python3 -m pip install numpy python3 -m pip install olefile python3 -m pip install -U pytest python3 -m pip install -U pytest-cov python3 -m pip install -U pytest-timeout python3 -m pip install pyroma -# optional test dependency, only install if there's a binary package. -# fails on beta 3.14 and PyPy +# optional test dependencies, only install if there's a binary package. +python3 -m pip install --only-binary=:all: numpy || true python3 -m pip install --only-binary=:all: pyarrow || true # PyQt6 doesn't support PyPy3 diff --git a/.github/workflows/macos-install.sh b/.github/workflows/macos-install.sh index b114d4a23..7c768af48 100755 --- a/.github/workflows/macos-install.sh +++ b/.github/workflows/macos-install.sh @@ -26,9 +26,8 @@ python3 -m pip install -U pytest python3 -m pip install -U pytest-cov python3 -m pip install -U pytest-timeout python3 -m pip install pyroma -python3 -m pip install numpy -# optional test dependency, only install if there's a binary package. -# fails on beta 3.14 and PyPy +# optional test dependencies, only install if there's a binary package. +python3 -m pip install --only-binary=:all: numpy || true python3 -m pip install --only-binary=:all: pyarrow || true # libavif diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index c4d0fa046..3450de355 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -35,7 +35,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["pypy3.11", "3.11", "3.12", "3.13", "3.14"] + python-version: ["pypy3.11", "3.11", "3.12", "3.13", "3.14", "3.15"] architecture: ["x64"] include: # Test the oldest Python on 32-bit diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 167faa239..da3eea066 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,6 +42,8 @@ jobs: ] python-version: [ "pypy3.11", + "3.15t", + "3.15", "3.14t", "3.14", "3.13t", @@ -54,6 +56,7 @@ jobs: - { python-version: "3.12", PYTHONOPTIMIZE: 1, REVERSE: "--reverse" } - { python-version: "3.11", PYTHONOPTIMIZE: 2 } # Free-threaded + - { python-version: "3.15t", disable-gil: true } - { python-version: "3.14t", disable-gil: true } - { python-version: "3.13t", disable-gil: true } # Intel diff --git a/.github/workflows/wheels-dependencies.sh b/.github/workflows/wheels-dependencies.sh index 07ea75a75..4201c335f 100755 --- a/.github/workflows/wheels-dependencies.sh +++ b/.github/workflows/wheels-dependencies.sh @@ -96,7 +96,7 @@ else FREETYPE_VERSION=2.14.1 fi HARFBUZZ_VERSION=12.2.0 -LIBPNG_VERSION=1.6.51 +LIBPNG_VERSION=1.6.53 JPEGTURBO_VERSION=3.1.2 OPENJPEG_VERSION=2.5.4 XZ_VERSION=5.8.1 diff --git a/.github/zizmor.yml b/.github/zizmor.yml index b56709781..e60c79441 100644 --- a/.github/zizmor.yml +++ b/.github/zizmor.yml @@ -1,6 +1,8 @@ # Configuration for the zizmor static analysis tool, run via pre-commit in CI # https://docs.zizmor.sh/configuration/ rules: + obfuscation: + disable: true unpinned-uses: config: policies: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 564206ce1..8477729e6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,17 +1,17 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.3 + rev: v0.14.7 hooks: - id: ruff-check args: [--exit-non-zero-on-fix] - repo: https://github.com/psf/black-pre-commit-mirror - rev: 25.9.0 + rev: 25.11.0 hooks: - id: black - repo: https://github.com/PyCQA/bandit - rev: 1.8.6 + rev: 1.9.2 hooks: - id: bandit args: [--severity-level=high] @@ -24,7 +24,7 @@ repos: exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$) - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v21.1.2 + rev: v21.1.6 hooks: - id: clang-format types: [c] @@ -51,24 +51,24 @@ repos: exclude: ^\.github/.*TEMPLATE|^Tests/(fonts|images)/ - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.34.1 + rev: 0.35.0 hooks: - id: check-github-workflows - id: check-readthedocs - id: check-renovate - repo: https://github.com/zizmorcore/zizmor-pre-commit - rev: v1.16.2 + rev: v1.18.0 hooks: - id: zizmor - repo: https://github.com/sphinx-contrib/sphinx-lint - rev: v1.0.1 + rev: v1.0.2 hooks: - id: sphinx-lint - repo: https://github.com/tox-dev/pyproject-fmt - rev: v2.11.0 + rev: v2.11.1 hooks: - id: pyproject-fmt diff --git a/Tests/images/bmp/html/bkgd.png b/Tests/images/bmp/html/bkgd.png deleted file mode 100644 index d66ca9d65..000000000 Binary files a/Tests/images/bmp/html/bkgd.png and /dev/null differ diff --git a/Tests/images/bmp/html/bmpsuite.html b/Tests/images/bmp/html/bmpsuite.html deleted file mode 100644 index b8e327ed9..000000000 --- a/Tests/images/bmp/html/bmpsuite.html +++ /dev/null @@ -1,578 +0,0 @@ - - - - - -BMP Suite Image List - - - - - - - -

BMP Suite Image List

- -

For BMP Suite -version 2.3

- -

This document describes the images in BMP Suite, and shows what -I allege to be the correct way to interpret them. PNG and JPEG images are -used for reference. -

- -

It also shows how your web browser displays the BMP images, -but that’s not its main purpose. -BMP is poor image format to use on web pages, so a web browser’s -level of support for it is arguably not important.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileVer.Correct displayIn your browserNotes
g/pal1.bmp31 bit/pixel paletted image, in which black is the first color in - the palette.
g/pal1wb.bmp31 bit/pixel paletted image, in which white is the first color in - the palette.
g/pal1bg.bmp31 bit/pixel paletted image, with colors other than black and white.
q/pal1p1.bmp31 bit/pixel paletted image, with only one color in the palette. - The documentation says that 1-bpp images have a palette size of 2 - (not “up to 2”), but it would be silly for a viewer not to - support a size of 1.
q/pal2.bmp3A paletted image with 2 bits/pixel. Usually only 1, 4, - and 8 are allowed, but 2 is legal on Windows CE.
g/pal4.bmp3Paletted image with 12 palette colors, and 4 bits/pixel.
g/pal4rle.bmp34-bit image that uses RLE compression.
q/pal4rletrns.bmp3
- or

- or
An RLE-compressed image that used “delta” - codes to skip over some pixels, leaving them undefined. Some viewers - make undefined pixels transparent, others make them black, and - others assign them palette color 0 (purple, in this case).
g/pal8.bmp3Our standard paletted image, with 252 palette colors, and 8 - bits/pixel.
g/pal8-0.bmp3Every field that can be set to 0 is set to 0: pixels/meter=0; - colors used=0 (meaning the default 256); size-of-image=0.
g/pal8rle.bmp38-bit image that uses RLE compression.
q/pal8rletrns.bmp3
- or

- or
8-bit version of q/pal4rletrns.bmp.
g/pal8w126.bmp3Images with different widths and heights. - In BMP format, rows are padded to a multiple of four bytes, so we - test all four possibilities.
g/pal8w125.bmp3
g/pal8w124.bmp3
g/pal8topdown.bmp3BMP images are normally stored from the bottom up, but - there is a way to store them from the top down.
q/pal8offs.bmp3A file with some unused bytes between the palette and the - image. This is probably valid, but I’m not 100% sure.
q/pal8oversizepal.bmp3An 8-bit image with 300 palette colors. This may be invalid, - because the documentation could - be interpreted to imply that 8-bit images aren’t allowed - to have more than 256 colors.
g/pal8nonsquare.bmp3 -
- or
- -
An image with non-square pixels: the X pixels/meter is twice - the Y pixels/meter. Image editors can be expected to - leave the image “squashed”; image viewers should - consider stretching it to its correct proportions.
g/pal8os2.bmpOS/2v1An OS/2-style bitmap.
q/pal8os2sp.bmpOS/2v1An OS/2v1 with a less-than-full-sized palette. - Probably not valid, but such files have been seen in the wild.
q/pal8os2v2.bmpOS/2v2My attempt to make an OS/2v2 bitmap.
q/pal8os2v2-16.bmpOS/2v2An OS/2v2 bitmap whose header has only 16 bytes, instead of the full 64.
g/pal8v4.bmp4A v4 bitmap. I’m not sure that the gamma and chromaticity values in - this file are sensible, because I can’t find any detailed documentation - of them.
g/pal8v5.bmp5A v5 bitmap. Version 5 has additional colorspace options over v4, so it - is easier to create, and ought to be more portable.
g/rgb16.bmp3A 16-bit image with the default color format: 5 bits each for red, - green, and blue, and 1 unused bit. - The whitest colors should (I assume) be displayed as pure white: - (255,255,255), not - (248,248,248).
g/rgb16-565.bmp3A 16-bit image with a BITFIELDS segment indicating 5 red, 6 green, - and 5 blue bits. This is a standard 16-bit format, even supported by - old versions of Windows that don’t support any other non-default 16-bit - formats. - The whitest colors should be displayed as pure white: - (255,255,255), not - (248,252,248).
g/rgb16-565pal.bmp3A 16-bit image with both a BITFIELDS segment and a palette.
q/rgb16-231.bmp3An unusual and silly 16-bit image, with 2 red bits, 3 green bits, and 1 - blue bit. Most viewers do support this image, but the colors may be darkened - with a yellow-green shadow. That’s because they’re doing simple - bit-shifting (possibly including one round of bit replication), instead of - proper scaling.
q/rgba16-4444.bmp5A 16-bit image with an alpha channel. There are 4 bits for each color - channel, and 4 bits for the alpha channel. - It’s not clear if this is valid, but I can’t find anything that - suggests it isn’t. -
g/rgb24.bmp3A perfectly ordinary 24-bit (truecolor) image.
g/rgb24pal.bmp3A 24-bit image, with a palette containing 256 colors. There is little if - any reason for a truecolor image to contain a palette, but it is legal.
q/rgb24largepal.bmp3A 24-bit image, with a palette containing 300 colors. - The fact that the palette has more than 256 colors may cause some viewers - to complain, but the documentation does not mention a size limit.
q/rgb24prof.bmp5My attempt to make a BMP file with an embedded color profile.
q/rgb24lprof.bmp5My attempt to make a BMP file with a linked color profile.
q/rgb24jpeg.bmp5My attempt to make BMP files with embedded JPEG and PNG images. - These are not likely to be supported by much of anything (they’re - intended for printers).
q/rgb24png.bmp5
g/rgb32.bmp3A 32-bit image using the default color format for 32-bit images (no - BITFIELDS segment). There are 8 bits per color channel, and 8 unused - bits. The unused bits are set to 0.
g/rgb32bf.bmp3A 32-bit image with a BITFIELDS segment. As usual, there are 8 bits per - color channel, and 8 unused bits. But the color channels are in an unusual - order, so the viewer must read the BITFIELDS, and not just guess.
q/rgb32fakealpha.bmp3
- or
- -
Same as g/rgb32.bmp, except that the unused bits are set to something - other than 0. - If the image becomes transparent toward the bottom, it probably means - the viewer uses heuristics to guess whether the undefined - data represents transparency.
q/rgb32-111110.bmp3A 32 bits/pixel image, with all 32 bits used: 11 each for red and - green, and 10 for blue. As far as I know, this is perfectly valid, but it - is unusual.
q/rgba32.bmp5A BMP with an alpha channel. Transparency is barely documented, - so it’s possible that this file is not correctly formed. - The color channels are in an unusual order, to prevent viewers from - passing this test by making a lucky guess.
q/rgba32abf.bmp3An image of type BI_ALHPABITFIELDS. Supposedly, this was used on - Windows CE. I don’t know whether it is constructed correctly.
b/badbitcount.bmp3N/AHeader indicates an absurdly large number of bits/pixel.
b/badbitssize.bmp3N/AHeader incorrectly indicates that the bitmap is several GB in size.
b/baddens1.bmp3N/ADensity (pixels per meter) suggests the image is much - larger in one dimension than the other.
b/baddens2.bmp3N/A
b/badfilesize.bmp3N/AHeader incorrectly indicates that the file is several GB in size.
b/badheadersize.bmp?N/AHeader size is 66 bytes, which is not a valid size for any known BMP - version.
b/badpalettesize.bmp3N/AHeader incorrectly indicates that the palette contains an absurdly large - number of colors.
b/badplanes.bmp3N/AThe “planes” setting, which is required to be 1, is not 1.
b/badrle.bmp3N/AAn invalid RLE-compressed image that tries to cause buffer overruns.
b/badwidth.bmp3N/AThe image claims to be a negative number of pixels in width.
b/pal8badindex.bmp3N/AMany of the palette indices used in the image are not present in the - palette.
b/reallybig.bmp3N/AAn image with a very large reported width and height.
b/rletopdown.bmp3N/AAn RLE-compressed image that tries to use top-down orientation, - which isn’t allowed.
b/shortfile.bmp3N/AA file that has been truncated in the middle of the bitmap.
- - - - diff --git a/Tests/images/bmp/html/fakealpha.png b/Tests/images/bmp/html/fakealpha.png deleted file mode 100644 index 89292bcbb..000000000 Binary files a/Tests/images/bmp/html/fakealpha.png and /dev/null differ diff --git a/Tests/images/bmp/html/pal1p1.png b/Tests/images/bmp/html/pal1p1.png deleted file mode 100644 index 92fc0f945..000000000 Binary files a/Tests/images/bmp/html/pal1p1.png and /dev/null differ diff --git a/Tests/images/bmp/html/pal2.png b/Tests/images/bmp/html/pal2.png deleted file mode 100644 index 1bbfe175f..000000000 Binary files a/Tests/images/bmp/html/pal2.png and /dev/null differ diff --git a/Tests/images/bmp/html/pal4rletrns-0.png b/Tests/images/bmp/html/pal4rletrns-0.png deleted file mode 100644 index b689c842a..000000000 Binary files a/Tests/images/bmp/html/pal4rletrns-0.png and /dev/null differ diff --git a/Tests/images/bmp/html/pal4rletrns-b.png b/Tests/images/bmp/html/pal4rletrns-b.png deleted file mode 100644 index 9befa575f..000000000 Binary files a/Tests/images/bmp/html/pal4rletrns-b.png and /dev/null differ diff --git a/Tests/images/bmp/html/pal4rletrns.png b/Tests/images/bmp/html/pal4rletrns.png deleted file mode 100644 index 9b0c04436..000000000 Binary files a/Tests/images/bmp/html/pal4rletrns.png and /dev/null differ diff --git a/Tests/images/bmp/html/pal8nonsquare-v.png b/Tests/images/bmp/html/pal8nonsquare-v.png deleted file mode 100644 index a1cd1ab18..000000000 Binary files a/Tests/images/bmp/html/pal8nonsquare-v.png and /dev/null differ diff --git a/Tests/images/bmp/html/pal8rletrns-0.png b/Tests/images/bmp/html/pal8rletrns-0.png deleted file mode 100644 index a1c1fda50..000000000 Binary files a/Tests/images/bmp/html/pal8rletrns-0.png and /dev/null differ diff --git a/Tests/images/bmp/html/pal8rletrns-b.png b/Tests/images/bmp/html/pal8rletrns-b.png deleted file mode 100644 index 1ede504d4..000000000 Binary files a/Tests/images/bmp/html/pal8rletrns-b.png and /dev/null differ diff --git a/Tests/images/bmp/html/pal8rletrns.png b/Tests/images/bmp/html/pal8rletrns.png deleted file mode 100644 index 2d8e957f1..000000000 Binary files a/Tests/images/bmp/html/pal8rletrns.png and /dev/null differ diff --git a/Tests/images/bmp/html/rgb16-231.png b/Tests/images/bmp/html/rgb16-231.png deleted file mode 100644 index 76efe526e..000000000 Binary files a/Tests/images/bmp/html/rgb16-231.png and /dev/null differ diff --git a/Tests/images/bmp/html/rgb24.jpg b/Tests/images/bmp/html/rgb24.jpg deleted file mode 100644 index c43698c9b..000000000 Binary files a/Tests/images/bmp/html/rgb24.jpg and /dev/null differ diff --git a/Tests/images/bmp/html/rgba16-4444.png b/Tests/images/bmp/html/rgba16-4444.png deleted file mode 100644 index bfeda6fae..000000000 Binary files a/Tests/images/bmp/html/rgba16-4444.png and /dev/null differ diff --git a/Tests/images/bmp/html/rgba32.png b/Tests/images/bmp/html/rgba32.png deleted file mode 100644 index 25e542a65..000000000 Binary files a/Tests/images/bmp/html/rgba32.png and /dev/null differ diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py index 82cab39c6..3cd0fbb2d 100644 --- a/Tests/test_bmp_reference.py +++ b/Tests/test_bmp_reference.py @@ -72,7 +72,7 @@ def test_good() -> None: "pal8-0.bmp": "pal8.png", "pal8rle.bmp": "pal8.png", "pal8topdown.bmp": "pal8.png", - "pal8nonsquare.bmp": "pal8nonsquare-v.png", + "pal8nonsquare.bmp": "pal8nonsquare-e.png", "pal8os2.bmp": "pal8.png", "pal8os2sp.bmp": "pal8.png", "pal8os2v2.bmp": "pal8.png", @@ -103,7 +103,7 @@ def test_good() -> None: # with paletized image, since the palette might # be differently ordered for an equivalent image. im = im.convert("RGBA") - compare = im.convert("RGBA") + compare = compare.convert("RGBA") assert_image_similar(im, compare, 5) except Exception as msg: diff --git a/Tests/test_file_gribstub.py b/Tests/test_file_gribstub.py index 960e5f4be..4dbed6b31 100644 --- a/Tests/test_file_gribstub.py +++ b/Tests/test_file_gribstub.py @@ -59,7 +59,7 @@ def test_handler(tmp_path: Path) -> None: def open(self, im: Image.Image) -> None: self.opened = True - def load(self, im: Image.Image) -> Image.Image: + def load(self, im: ImageFile.ImageFile) -> Image.Image: self.loaded = True im.fp.close() return Image.new("RGB", (1, 1)) diff --git a/Tests/test_file_hdf5stub.py b/Tests/test_file_hdf5stub.py index e4f09a09c..1e48597d3 100644 --- a/Tests/test_file_hdf5stub.py +++ b/Tests/test_file_hdf5stub.py @@ -61,7 +61,7 @@ def test_handler(tmp_path: Path) -> None: def open(self, im: Image.Image) -> None: self.opened = True - def load(self, im: Image.Image) -> Image.Image: + def load(self, im: ImageFile.ImageFile) -> Image.Image: self.loaded = True im.fp.close() return Image.new("RGB", (1, 1)) diff --git a/Tests/test_file_iptc.py b/Tests/test_file_iptc.py index 0376b9997..9e2d8c06d 100644 --- a/Tests/test_file_iptc.py +++ b/Tests/test_file_iptc.py @@ -143,6 +143,7 @@ def test_getiptcinfo_tiff() -> None: # Test with LONG tag type with Image.open("Tests/images/hopper.Lab.tif") as im: + assert isinstance(im, TiffImagePlugin.TiffImageFile) im.tag_v2.tagtype[TiffImagePlugin.IPTC_NAA_CHUNK] = TiffTags.LONG iptc = IptcImagePlugin.getiptcinfo(im) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 7cb3ea8e4..e36b5f39e 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -11,7 +11,15 @@ from typing import Any, NamedTuple import pytest -from PIL import Image, ImageFilter, ImageOps, TiffImagePlugin, TiffTags, features +from PIL import ( + Image, + ImageFile, + ImageFilter, + ImageOps, + TiffImagePlugin, + TiffTags, + features, +) from PIL.TiffImagePlugin import OSUBFILETYPE, SAMPLEFORMAT, STRIPOFFSETS, SUBIFD from .helper import ( @@ -27,7 +35,7 @@ from .helper import ( @skip_unless_feature("libtiff") class LibTiffTestCase: - def _assert_noerr(self, tmp_path: Path, im: TiffImagePlugin.TiffImageFile) -> None: + def _assert_noerr(self, tmp_path: Path, im: ImageFile.ImageFile) -> None: """Helper tests that assert basic sanity about the g4 tiff reading""" # 1 bit assert im.mode == "1" @@ -370,6 +378,7 @@ class TestFileLibTiff(LibTiffTestCase): im.save(out, tiffinfo=ifd) with Image.open(out) as reloaded: + assert isinstance(reloaded, TiffImagePlugin.TiffImageFile) assert reloaded.tag_v2[37000] == 100 def test_inknames_tag( @@ -1146,9 +1155,9 @@ class TestFileLibTiff(LibTiffTestCase): with Image.open("Tests/images/g4_orientation_1.tif") as base_im: for i in range(2, 9): with Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") as im: - im = ImageOps.exif_transpose(im) + im_transposed = ImageOps.exif_transpose(im) - assert_image_similar(base_im, im, 0.7) + assert_image_similar(base_im, im_transposed, 0.7) @pytest.mark.parametrize( "test_file", diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 9875fe096..7f163a4d6 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -787,7 +787,9 @@ class TestFilePng: im.save(test_file, exif=im.getexif()) with Image.open(test_file) as reloaded: + assert isinstance(reloaded, PngImagePlugin.PngImageFile) exif = reloaded._getexif() + assert exif is not None assert exif[305] == "Adobe Photoshop CS Macintosh" def test_exif_argument(self, tmp_path: Path) -> None: diff --git a/docs/releasenotes/12.1.0.rst b/docs/releasenotes/12.1.0.rst new file mode 100644 index 000000000..b6e1810c6 --- /dev/null +++ b/docs/releasenotes/12.1.0.rst @@ -0,0 +1,59 @@ +12.1.0 +------ + +Security +======== + +TODO +^^^^ + +TODO + +:cve:`YYYY-XXXXX`: TODO +^^^^^^^^^^^^^^^^^^^^^^^ + +TODO + +Backwards incompatible changes +============================== + +TODO +^^^^ + +TODO + +Deprecations +============ + +TODO +^^^^ + +TODO + +API changes +=========== + +TODO +^^^^ + +TODO + +API additions +============= + +Specify window in ImageGrab on macOS +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When using :py:meth:`~PIL.ImageGrab.grab`, a specific window can now be selected on +macOS in addition to Windows. On macOS, this is a CGWindowID:: + + from PIL import ImageGrab + ImageGrab.grab(window=cgwindowid) + +Other changes +============= + +TODO +^^^^ + +TODO diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index f66240c89..b097770a3 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -14,6 +14,7 @@ expected to be backported to earlier versions. .. toctree:: :maxdepth: 2 + 12.1.0 12.0.0 11.3.0 11.2.1 diff --git a/selftest.py b/selftest.py index e9b5689a0..c484d4e2d 100755 --- a/selftest.py +++ b/selftest.py @@ -40,12 +40,14 @@ def testimage() -> None: >>> with Image.open("Tests/images/hopper.gif") as im: ... _info(im) ('GIF', 'P', (128, 128)) - >>> _info(Image.open("Tests/images/hopper.ppm")) + >>> with Image.open("Tests/images/hopper.ppm") as im: + ... _info(im) ('PPM', 'RGB', (128, 128)) >>> try: - ... _info(Image.open("Tests/images/hopper.jpg")) + ... with Image.open("Tests/images/hopper.jpg") as im: + ... _info(im) ... except OSError as v: - ... print(v) + ... print(v) ('JPEG', 'RGB', (128, 128)) PIL doesn't actually load the image data until it's needed, diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 58c460ef3..0560a5a7d 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -116,8 +116,8 @@ class GifImageFile(ImageFile.ImageFile): # check if palette contains colour indices p = self.fp.read(3 << bits) if self._is_palette_needed(p): - p = ImagePalette.raw("RGB", p) - self.global_palette = self.palette = p + palette = ImagePalette.raw("RGB", p) + self.global_palette = self.palette = palette self._fp = self.fp # FIXME: hack self.__rewind = self.fp.tell() @@ -256,7 +256,7 @@ class GifImageFile(ImageFile.ImageFile): info["comment"] += b"\n" + comment else: info["comment"] = comment - s = None + s = b"" continue elif s[0] == 255 and frame == 0 and block is not None: # @@ -299,7 +299,7 @@ class GifImageFile(ImageFile.ImageFile): bits = self.fp.read(1)[0] self.__offset = self.fp.tell() break - s = None + s = b"" if interlace is None: msg = "image not found in GIF frame" diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 9d50812eb..b71395c62 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1519,6 +1519,9 @@ class Image: "".join(self.info["Raw profile type exif"].split("\n")[3:]) ) elif hasattr(self, "tag_v2"): + from . import TiffImagePlugin + + assert isinstance(self, TiffImagePlugin.TiffImageFile) self._exif.bigtiff = self.tag_v2._bigtiff self._exif.endian = self.tag_v2._endian self._exif.load_from_fp(self.fp, self.tag_v2._offset) diff --git a/src/PIL/XVThumbImagePlugin.py b/src/PIL/XVThumbImagePlugin.py index cde28388f..192c041d9 100644 --- a/src/PIL/XVThumbImagePlugin.py +++ b/src/PIL/XVThumbImagePlugin.py @@ -66,10 +66,10 @@ class XVThumbImageFile(ImageFile.ImageFile): break # parse header line (already read) - s = s.strip().split() + w, h = s.strip().split(maxsplit=2)[:2] self._mode = "P" - self._size = int(s[0]), int(s[1]) + self._size = int(w), int(h) self.palette = ImagePalette.raw("RGB", PALETTE) diff --git a/tox.ini b/tox.ini index d58fd67b6..7f116c6e7 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ requires = tox>=4.2 env_list = lint - py{py3, 314, 313, 312, 311, 310} + py{py3, 315, 314, 313, 312, 311, 310} [testenv] deps = diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index cd2ef13c1..87cfef7d0 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -121,7 +121,7 @@ V = { "LCMS2": "2.17", "LIBAVIF": "1.3.0", "LIBIMAGEQUANT": "4.4.1", - "LIBPNG": "1.6.51", + "LIBPNG": "1.6.53", "LIBWEBP": "1.6.0", "OPENJPEG": "2.5.4", "TIFF": "4.7.1",