From 59c9d87f8a4d8443a482ca7abb5f1f8d5d970cbd Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 2 Apr 2023 00:27:22 +0300 Subject: [PATCH 01/17] Remove support for PyQt5 and PySide2, deprecated in 9.2.0 --- .github/workflows/test-cygwin.yml | 2 +- Tests/test_deprecated_imageqt.py | 18 ------------------ Tests/test_imageqt.py | 11 ++--------- Tests/test_qt_image_qapplication.py | 14 +------------- Tests/test_qt_image_toqimage.py | 6 +----- docs/deprecations.rst | 26 +++++++++++++------------- docs/reference/ImageQt.rst | 14 +++----------- src/PIL/ImageQt.py | 13 ------------- 8 files changed, 21 insertions(+), 83 deletions(-) delete mode 100644 Tests/test_deprecated_imageqt.py diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 6c9ed66e3..397cfe0a1 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -67,7 +67,7 @@ jobs: python3${{ matrix.python-minor-version }}-numpy python3${{ matrix.python-minor-version }}-sip python3${{ matrix.python-minor-version }}-tkinter - qt5-devel-tools + qt6-devel-tools wget xorg-server-extra zlib-devel diff --git a/Tests/test_deprecated_imageqt.py b/Tests/test_deprecated_imageqt.py deleted file mode 100644 index 2528ff3f7..000000000 --- a/Tests/test_deprecated_imageqt.py +++ /dev/null @@ -1,18 +0,0 @@ -import warnings - -with warnings.catch_warnings(record=True) as w: - # Arrange: cause all warnings to always be triggered - warnings.simplefilter("always") - - # Act: trigger a warning with Qt5 - from PIL import ImageQt - - -def test_deprecated(): - # Assert - if ImageQt.qt_version in ("5", "side2"): - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert "deprecated" in str(w[0].message) - else: - assert len(w) == 0 diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index 2f2b07918..2c73a2094 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -2,13 +2,10 @@ import warnings import pytest +from PIL import ImageQt + from .helper import assert_image_similar, hopper -with warnings.catch_warnings() as w: - warnings.simplefilter("ignore", category=DeprecationWarning) - from PIL import ImageQt - - pytestmark = pytest.mark.skipif( not ImageQt.qt_is_installed, reason="Qt bindings are not installed" ) @@ -26,10 +23,6 @@ def test_rgb(): from PyQt6.QtGui import qRgb elif ImageQt.qt_version == "side6": from PySide6.QtGui import qRgb - elif ImageQt.qt_version == "5": - from PyQt5.QtGui import qRgb - elif ImageQt.qt_version == "side2": - from PySide2.QtGui import qRgb assert qRgb(0, 0, 0) == qRgba(0, 0, 0, 255) diff --git a/Tests/test_qt_image_qapplication.py b/Tests/test_qt_image_qapplication.py index 4929fa933..5d2e41212 100644 --- a/Tests/test_qt_image_qapplication.py +++ b/Tests/test_qt_image_qapplication.py @@ -1,10 +1,6 @@ -import warnings - import pytest -with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - from PIL import ImageQt +from PIL import ImageQt from .helper import assert_image_equal_tofile, assert_image_similar, hopper @@ -19,14 +15,6 @@ if ImageQt.qt_is_installed: from PySide6.QtCore import QPoint from PySide6.QtGui import QImage, QPainter, QRegion from PySide6.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget - elif ImageQt.qt_version == "5": - from PyQt5.QtCore import QPoint - from PyQt5.QtGui import QImage, QPainter, QRegion - from PyQt5.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget - elif ImageQt.qt_version == "side2": - from PySide2.QtCore import QPoint - from PySide2.QtGui import QImage, QPainter, QRegion - from PySide2.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget class Example(QWidget): def __init__(self): diff --git a/Tests/test_qt_image_toqimage.py b/Tests/test_qt_image_toqimage.py index c1983031a..399df670f 100644 --- a/Tests/test_qt_image_toqimage.py +++ b/Tests/test_qt_image_toqimage.py @@ -1,10 +1,6 @@ -import warnings - import pytest -with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - from PIL import ImageQt +from PIL import ImageQt from .helper import assert_image_equal, assert_image_equal_tofile, hopper diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 5669d2827..48e8b6d93 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -135,19 +135,6 @@ PhotoImage.paste box parameter The ``box`` parameter is unused. It will be removed in Pillow 10.0.0 (2023-07-01). -PyQt5 and PySide2 -~~~~~~~~~~~~~~~~~ - -.. deprecated:: 9.2.0 - -`Qt 5 reached end-of-life `_ on 2020-12-08 for -open-source users (and will reach EOL on 2023-12-08 for commercial licence holders). - -Support for PyQt5 and PySide2 has been deprecated from ``ImageQt`` and will be removed -in Pillow 10 (2023-07-01). Upgrade to -`PyQt6 `_ or -`PySide6 `_ instead. - Image.coerce_e ~~~~~~~~~~~~~~ @@ -223,6 +210,19 @@ Removed features Deprecated features are only removed in major releases after an appropriate period of deprecation has passed. +PyQt5 and PySide2 +~~~~~~~~~~~~~~~~~ + +.. deprecated:: 9.2.0 +.. versionremoved:: 10.0.0 + +`Qt 5 reached end-of-life `_ on 2020-12-08 for +open-source users (and will reach EOL on 2023-12-08 for commercial licence holders). + +Support for PyQt5 and PySide2 has been removed from ``ImageQt``. Upgrade to +`PyQt6 `_ or +`PySide6 `_ instead. + PILLOW_VERSION constant ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/reference/ImageQt.rst b/docs/reference/ImageQt.rst index 15d052d1c..7e67a44d3 100644 --- a/docs/reference/ImageQt.rst +++ b/docs/reference/ImageQt.rst @@ -4,16 +4,8 @@ :py:mod:`~PIL.ImageQt` Module ============================= -The :py:mod:`~PIL.ImageQt` module contains support for creating PyQt6, PySide6, PyQt5 -or PySide2 QImage objects from PIL images. - -`Qt 5 reached end-of-life `_ on 2020-12-08 for -open-source users (and will reach EOL on 2023-12-08 for commercial licence holders). - -Support for PyQt5 and PySide2 has been deprecated from ``ImageQt`` and will be removed -in Pillow 10 (2023-07-01). Upgrade to -`PyQt6 `_ or -`PySide6 `_ instead. +The :py:mod:`~PIL.ImageQt` module contains support for creating PyQt6 or PySide6 +QImage objects from PIL images. .. versionadded:: 1.1.6 @@ -22,7 +14,7 @@ in Pillow 10 (2023-07-01). Upgrade to Creates an :py:class:`~PIL.ImageQt.ImageQt` object from a PIL :py:class:`~PIL.Image.Image` object. This class is a subclass of QtGui.QImage, which means that you can pass the resulting objects directly - to PyQt6/PySide6/PyQt5/PySide2 API functions and methods. + to PyQt6/PySide6 API functions and methods. This operation is currently supported for mode 1, L, P, RGB, and RGBA images. To handle other modes, you need to convert the image first. diff --git a/src/PIL/ImageQt.py b/src/PIL/ImageQt.py index ad607a97b..9b7245454 100644 --- a/src/PIL/ImageQt.py +++ b/src/PIL/ImageQt.py @@ -20,14 +20,11 @@ import sys from io import BytesIO from . import Image -from ._deprecate import deprecate from ._util import is_path qt_versions = [ ["6", "PyQt6"], ["side6", "PySide6"], - ["5", "PyQt5"], - ["side2", "PySide2"], ] # If a version has already been imported, attempt it first @@ -40,16 +37,6 @@ for qt_version, qt_module in qt_versions: elif qt_module == "PySide6": from PySide6.QtCore import QBuffer, QIODevice from PySide6.QtGui import QImage, QPixmap, qRgba - elif qt_module == "PyQt5": - from PyQt5.QtCore import QBuffer, QIODevice - from PyQt5.QtGui import QImage, QPixmap, qRgba - - deprecate("Support for PyQt5", 10, "PyQt6 or PySide6") - elif qt_module == "PySide2": - from PySide2.QtCore import QBuffer, QIODevice - from PySide2.QtGui import QImage, QPixmap, qRgba - - deprecate("Support for PySide2", 10, "PyQt6 or PySide6") except (ImportError, RuntimeError): continue qt_is_installed = True From 070e7704695f92ea829c678b58bb1ce5b8cbc51a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 2 Apr 2023 19:32:26 +0300 Subject: [PATCH 02/17] Remove support for Tk/Tcl <= 8.4, deprecated in 8.2.0 --- docs/deprecations.rst | 16 +++++------ src/PIL/_tkinter_finder.py | 6 ---- src/Tk/_tkmini.h | 14 +-------- src/Tk/tkImaging.c | 58 +++++++++----------------------------- 4 files changed, 22 insertions(+), 72 deletions(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 48e8b6d93..47d1531ae 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,14 +12,6 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. -Tk/Tcl 8.4 -~~~~~~~~~~ - -.. deprecated:: 8.2.0 - -Support for Tk/Tcl 8.4 is deprecated and will be removed in Pillow 10.0.0 (2023-07-01), -when Tk/Tcl 8.5 will be the minimum supported. - Categories ~~~~~~~~~~ @@ -210,6 +202,14 @@ Removed features Deprecated features are only removed in major releases after an appropriate period of deprecation has passed. +Tk/Tcl 8.4 +~~~~~~~~~~ + +.. deprecated:: 8.2.0 +.. versionremoved:: 10.0.0 + +Support for Tk/Tcl 8.4 was removed in Pillow 10.0.0 (2023-07-01). + PyQt5 and PySide2 ~~~~~~~~~~~~~~~~~ diff --git a/src/PIL/_tkinter_finder.py b/src/PIL/_tkinter_finder.py index 5cd7e9b1f..597c21b5e 100644 --- a/src/PIL/_tkinter_finder.py +++ b/src/PIL/_tkinter_finder.py @@ -4,8 +4,6 @@ import sys import tkinter from tkinter import _tkinter as tk -from ._deprecate import deprecate - try: if hasattr(sys, "pypy_find_executable"): TKINTER_LIB = tk.tklib_cffi.__file__ @@ -17,7 +15,3 @@ except AttributeError: TKINTER_LIB = None tk_version = str(tkinter.TkVersion) -if tk_version == "8.4": - deprecate( - "Support for Tk/Tcl 8.4", 10, action="Please upgrade to Tk/Tcl 8.5 or newer" - ) diff --git a/src/Tk/_tkmini.h b/src/Tk/_tkmini.h index 9852fc9d6..68247bc47 100644 --- a/src/Tk/_tkmini.h +++ b/src/Tk/_tkmini.h @@ -119,17 +119,7 @@ typedef struct Tk_PhotoImageBlock { } Tk_PhotoImageBlock; /* Typedefs derived from function signatures in Tk header */ -/* Tk_PhotoPutBlock for Tk <= 8.4 */ -typedef void (*Tk_PhotoPutBlock_84_t)( - Tk_PhotoHandle handle, - Tk_PhotoImageBlock *blockPtr, - int x, - int y, - int width, - int height, - int compRule); -/* Tk_PhotoPutBlock for Tk >= 8.5 */ -typedef int (*Tk_PhotoPutBlock_85_t)( +typedef int (*Tk_PhotoPutBlock_t)( Tcl_Interp *interp, Tk_PhotoHandle handle, Tk_PhotoImageBlock *blockPtr, @@ -138,8 +128,6 @@ typedef int (*Tk_PhotoPutBlock_85_t)( int width, int height, int compRule); -/* Tk_PhotoSetSize for Tk <= 8.4 */ -typedef void (*Tk_PhotoSetSize_84_t)(Tk_PhotoHandle handle, int width, int height); /* Tk_FindPhoto */ typedef Tk_PhotoHandle (*Tk_FindPhoto_t)(Tcl_Interp *interp, const char *imageName); /* Tk_PhotoGetImage */ diff --git a/src/Tk/tkImaging.c b/src/Tk/tkImaging.c index ad503baec..bd3cafe95 100644 --- a/src/Tk/tkImaging.c +++ b/src/Tk/tkImaging.c @@ -48,14 +48,11 @@ * Global vars for Tcl / Tk functions. We load these symbols from the tkinter * extension module or loaded Tcl / Tk libraries at run-time. */ -static int TK_LT_85 = 0; static Tcl_CreateCommand_t TCL_CREATE_COMMAND; static Tcl_AppendResult_t TCL_APPEND_RESULT; static Tk_FindPhoto_t TK_FIND_PHOTO; static Tk_PhotoGetImage_t TK_PHOTO_GET_IMAGE; -static Tk_PhotoPutBlock_84_t TK_PHOTO_PUT_BLOCK_84; -static Tk_PhotoSetSize_84_t TK_PHOTO_SET_SIZE_84; -static Tk_PhotoPutBlock_85_t TK_PHOTO_PUT_BLOCK_85; +static Tk_PhotoPutBlock_t TK_PHOTO_PUT_BLOCK; static Imaging ImagingFind(const char *name) { @@ -130,26 +127,15 @@ PyImagingPhotoPut( block.pitch = im->linesize; block.pixelPtr = (unsigned char *)im->block; - if (TK_LT_85) { /* Tk 8.4 */ - TK_PHOTO_PUT_BLOCK_84( - photo, &block, 0, 0, block.width, block.height, TK_PHOTO_COMPOSITE_SET); - if (strcmp(im->mode, "RGBA") == 0) { - /* Tk workaround: we need apply ToggleComplexAlphaIfNeeded */ - /* (fixed in Tk 8.5a3) */ - TK_PHOTO_SET_SIZE_84(photo, block.width, block.height); - } - } else { - /* Tk >=8.5 */ - TK_PHOTO_PUT_BLOCK_85( - interp, - photo, - &block, - 0, - 0, - block.width, - block.height, - TK_PHOTO_COMPOSITE_SET); - } + TK_PHOTO_PUT_BLOCK( + interp, + photo, + &block, + 0, + 0, + block.width, + block.height, + TK_PHOTO_COMPOSITE_SET); return TCL_OK; } @@ -290,16 +276,7 @@ get_tk(HMODULE hMod) { if ((TK_FIND_PHOTO = (Tk_FindPhoto_t)_dfunc(hMod, "Tk_FindPhoto")) == NULL) { return -1; }; - TK_LT_85 = GetProcAddress(hMod, "Tk_PhotoPutBlock_Panic") == NULL; - /* Tk_PhotoPutBlock_Panic defined as of 8.5.0 */ - if (TK_LT_85) { - TK_PHOTO_PUT_BLOCK_84 = (Tk_PhotoPutBlock_84_t)func; - return ((TK_PHOTO_SET_SIZE_84 = - (Tk_PhotoSetSize_84_t)_dfunc(hMod, "Tk_PhotoSetSize")) == NULL) - ? -1 - : 1; - } - TK_PHOTO_PUT_BLOCK_85 = (Tk_PhotoPutBlock_85_t)func; + TK_PHOTO_PUT_BLOCK = (Tk_PhotoPutBlock_t)func; return 1; } @@ -422,18 +399,9 @@ _func_loader(void *lib) { if ((TK_FIND_PHOTO = (Tk_FindPhoto_t)_dfunc(lib, "Tk_FindPhoto")) == NULL) { return 1; } - /* Tk_PhotoPutBlock_Panic defined as of 8.5.0 */ - TK_LT_85 = (dlsym(lib, "Tk_PhotoPutBlock_Panic") == NULL); - if (TK_LT_85) { - return ( - ((TK_PHOTO_PUT_BLOCK_84 = - (Tk_PhotoPutBlock_84_t)_dfunc(lib, "Tk_PhotoPutBlock")) == NULL) || - ((TK_PHOTO_SET_SIZE_84 = - (Tk_PhotoSetSize_84_t)_dfunc(lib, "Tk_PhotoSetSize")) == NULL)); - } return ( - (TK_PHOTO_PUT_BLOCK_85 = - (Tk_PhotoPutBlock_85_t)_dfunc(lib, "Tk_PhotoPutBlock")) == NULL); + (TK_PHOTO_PUT_BLOCK = + (Tk_PhotoPutBlock_t)_dfunc(lib, "Tk_PhotoPutBlock")) == NULL); } int From ddc4e902352e1d4459592707e78081c48ba4803a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 2 Apr 2023 19:51:16 +0300 Subject: [PATCH 03/17] Remove im.category and related Image.NORMAL, Image.SEQUENCE, Image.CONTAINER, deprecated in 8.2.0 --- Tests/test_image.py | 11 ----------- docs/deprecations.rst | 24 ++++++++++++------------ src/PIL/Image.py | 10 ---------- 3 files changed, 12 insertions(+), 33 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 17f1edb00..cb31565e7 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -929,17 +929,6 @@ class TestImage: im.apply_transparency() assert im.palette.colors[(27, 35, 6, 214)] == 24 - def test_categories_deprecation(self): - with pytest.warns(DeprecationWarning): - assert hopper().category == 0 - - with pytest.warns(DeprecationWarning): - assert Image.NORMAL == 0 - with pytest.warns(DeprecationWarning): - assert Image.SEQUENCE == 1 - with pytest.warns(DeprecationWarning): - assert Image.CONTAINER == 2 - def test_constants(self): with pytest.warns(DeprecationWarning): assert Image.LINEAR == Image.Resampling.BILINEAR diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 47d1531ae..1e448ad31 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,18 +12,6 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. -Categories -~~~~~~~~~~ - -.. deprecated:: 8.2.0 - -``im.category`` is deprecated and will be removed in Pillow 10.0.0 (2023-07-01), -along with the related ``Image.NORMAL``, ``Image.SEQUENCE`` and -``Image.CONTAINER`` attributes. - -To determine if an image has multiple frames or not, -``getattr(im, "is_animated", False)`` can be used instead. - JpegImagePlugin.convert_dict_qtables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -210,6 +198,18 @@ Tk/Tcl 8.4 Support for Tk/Tcl 8.4 was removed in Pillow 10.0.0 (2023-07-01). +Categories +~~~~~~~~~~ + +.. deprecated:: 8.2.0 +.. versionremoved:: 10.0.0 + +``im.category`` was removed along with the related ``Image.NORMAL``, +``Image.SEQUENCE`` and ``Image.CONTAINER`` attributes. + +To determine if an image has multiple frames or not, +``getattr(im, "is_animated", False)`` can be used instead. + PyQt5 and PySide2 ~~~~~~~~~~~~~~~~~ diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 4a142a008..99a895fb0 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -61,10 +61,6 @@ from ._util import DeferredError, is_path def __getattr__(name): - categories = {"NORMAL": 0, "SEQUENCE": 1, "CONTAINER": 2} - if name in categories: - deprecate("Image categories", 10, "is_animated", plural=True) - return categories[name] old_resampling = { "LINEAR": "BILINEAR", "CUBIC": "BICUBIC", @@ -521,12 +517,6 @@ class Image: self.pyaccess = None self._exif = None - def __getattr__(self, name): - if name == "category": - deprecate("Image categories", 10, "is_animated", plural=True) - return self._category - raise AttributeError(name) - @property def width(self): return self.size[0] From 52f4fc59a23fb57159d0622226461bcf1be1058a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 2 Apr 2023 20:27:30 +0300 Subject: [PATCH 04/17] Remove JpegImagePlugin.convert_dict_qtables, deprecated in 8.3.0 --- Tests/test_file_jpeg.py | 6 ------ docs/deprecations.rst | 19 +++++++++---------- docs/releasenotes/8.3.0.rst | 2 +- src/PIL/JpegImagePlugin.py | 6 ------ 4 files changed, 10 insertions(+), 23 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 4981e15af..73a00386f 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -636,12 +636,6 @@ class TestFileJpeg: assert max(im2.quantization[0]) <= 255 assert max(im2.quantization[1]) <= 255 - def test_convert_dict_qtables_deprecation(self): - with pytest.warns(DeprecationWarning): - qtable = {0: [1, 2, 3, 4]} - qtable2 = JpegImagePlugin.convert_dict_qtables(qtable) - assert qtable == qtable2 - @pytest.mark.skipif(not djpeg_available(), reason="djpeg not available") def test_load_djpeg(self): with Image.open(TEST_FILE) as img: diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 1e448ad31..f70a91834 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,16 +12,6 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. -JpegImagePlugin.convert_dict_qtables -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. deprecated:: 8.3.0 - -JPEG ``quantization`` is now automatically converted, but still returned as a -dictionary. The :py:attr:`~PIL.JpegImagePlugin.convert_dict_qtables` method no longer -performs any operations on the data given to it, has been deprecated and will be -removed in Pillow 10.0.0 (2023-07-01). - ImagePalette size parameter ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -210,6 +200,15 @@ Categories To determine if an image has multiple frames or not, ``getattr(im, "is_animated", False)`` can be used instead. +JpegImagePlugin.convert_dict_qtables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 8.3.0 +.. versionremoved:: 10.0.0 + +Since deprecation in Pillow 8.3.0, the ``convert_dict_qtables`` method no longer +performed any operations on the data given to it, and has been removed. + PyQt5 and PySide2 ~~~~~~~~~~~~~~~~~ diff --git a/docs/releasenotes/8.3.0.rst b/docs/releasenotes/8.3.0.rst index b9642576f..e74880f6f 100644 --- a/docs/releasenotes/8.3.0.rst +++ b/docs/releasenotes/8.3.0.rst @@ -8,7 +8,7 @@ JpegImagePlugin.convert_dict_qtables ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ JPEG ``quantization`` is now automatically converted, but still returned as a -dictionary. The :py:attr:`~PIL.JpegImagePlugin.convert_dict_qtables` method no longer +dictionary. The ``convert_dict_qtables`` method no longer performs any operations on the data given to it, has been deprecated and will be removed in Pillow 10.0.0 (2023-07-01). diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 71ae84c04..5dd1a61af 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -46,7 +46,6 @@ from ._binary import i16be as i16 from ._binary import i32be as i32 from ._binary import o8 from ._binary import o16be as o16 -from ._deprecate import deprecate from .JpegPresets import presets # @@ -612,11 +611,6 @@ samplings = { # fmt: on -def convert_dict_qtables(qtables): - deprecate("convert_dict_qtables", 10, action="Conversion is no longer needed") - return qtables - - def get_sampling(im): # There's no subsampling when images have only 1 layer # (grayscale images) or when they are CMYK (4 layers), From 5dbef9e0a8483e1cc680779c49d4e61275920066 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 2 Apr 2023 20:45:51 +0300 Subject: [PATCH 05/17] Remove ImagePalette size parameter, deprecated in 8.4.0 --- Tests/test_imagepalette.py | 4 ---- docs/deprecations.rst | 21 ++++++++++----------- src/PIL/ImagePalette.py | 8 +------- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index ac99ef381..baa698bb4 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -9,10 +9,6 @@ def test_sanity(): palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) assert len(palette.colors) == 256 - with pytest.warns(DeprecationWarning): - with pytest.raises(ValueError): - ImagePalette.ImagePalette("RGB", list(range(256)) * 3, 10) - def test_reload(): with Image.open("Tests/images/hopper.gif") as im: diff --git a/docs/deprecations.rst b/docs/deprecations.rst index f70a91834..e740238b6 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,17 +12,6 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. -ImagePalette size parameter -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. deprecated:: 8.4.0 - -The ``size`` parameter will be removed in Pillow 10.0.0 (2023-07-01). - -Before Pillow 8.3.0, ``ImagePalette`` required palette data of particular lengths by -default, and the size parameter could be used to override that. Pillow 8.3.0 removed -the default required length, also removing the need for the size parameter. - ImageShow.Viewer.show_file file argument ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -209,6 +198,16 @@ JpegImagePlugin.convert_dict_qtables Since deprecation in Pillow 8.3.0, the ``convert_dict_qtables`` method no longer performed any operations on the data given to it, and has been removed. +ImagePalette size parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 8.4.0 +.. versionremoved:: 10.0.0 + +Before Pillow 8.3.0, ``ImagePalette`` required palette data of particular lengths by +default, and the ``size`` parameter could be used to override that. Pillow 8.3.0 +removed the default required length, also removing the need for the ``size`` parameter. + PyQt5 and PySide2 ~~~~~~~~~~~~~~~~~ diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index e455c0459..f0c094708 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -19,7 +19,6 @@ import array from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile -from ._deprecate import deprecate class ImagePalette: @@ -34,16 +33,11 @@ class ImagePalette: Defaults to an empty palette. """ - def __init__(self, mode="RGB", palette=None, size=0): + def __init__(self, mode="RGB", palette=None): self.mode = mode self.rawmode = None # if set, palette contains raw data self.palette = palette or bytearray() self.dirty = None - if size != 0: - deprecate("The size parameter", 10, None) - if size != len(self.palette): - msg = "wrong palette size" - raise ValueError(msg) @property def palette(self): From 8d83d5e66a7c73dd01d7dc526afd85f5696026c5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 2 Apr 2023 20:54:59 +0300 Subject: [PATCH 06/17] Remove ImageShow.Viewer.show_file file argument, deprecated in 9.1.0 --- Tests/test_imageshow.py | 17 -------- docs/deprecations.rst | 24 +++++------ src/PIL/ImageShow.py | 89 ++++------------------------------------- 3 files changed, 18 insertions(+), 112 deletions(-) diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index eda485cf6..e54372b60 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -89,20 +89,3 @@ def test_ipythonviewer(): im = hopper() assert test_viewer.show(im) == 1 - - -@pytest.mark.skipif( - not on_ci() or is_win32(), - reason="Only run on CIs; hangs on Windows CIs", -) -@pytest.mark.parametrize("viewer", ImageShow._viewers) -def test_file_deprecated(tmp_path, viewer): - f = str(tmp_path / "temp.jpg") - hopper().save(f) - with pytest.warns(DeprecationWarning): - try: - viewer.show_file(file=f) - except NotImplementedError: - pass - with pytest.raises(TypeError): - viewer.show_file() diff --git a/docs/deprecations.rst b/docs/deprecations.rst index e740238b6..e75d1f7fa 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,19 +12,6 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. -ImageShow.Viewer.show_file file argument -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. deprecated:: 9.1.0 - -The ``file`` argument in :py:meth:`~PIL.ImageShow.Viewer.show_file()` has been -deprecated and will be removed in Pillow 10.0.0 (2023-07-01). It has been replaced by -``path``. - -In effect, ``viewer.show_file("test.jpg")`` will continue to work unchanged. -``viewer.show_file(file="test.jpg")`` will raise a deprecation warning, and suggest -``viewer.show_file(path="test.jpg")`` instead. - Constants ~~~~~~~~~ @@ -208,6 +195,17 @@ Before Pillow 8.3.0, ``ImagePalette`` required palette data of particular length default, and the ``size`` parameter could be used to override that. Pillow 8.3.0 removed the default required length, also removing the need for the ``size`` parameter. +ImageShow.Viewer.show_file file argument +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 9.1.0 +.. versionremoved:: 10.0.0 + +The ``file`` argument in :py:meth:`~PIL.ImageShow.Viewer.show_file()` has been +removed and replaced by ``path``. + +In effect, ``viewer.show_file("test.jpg")`` will continue to work unchanged. + PyQt5 and PySide2 ~~~~~~~~~~~~~~~~~ diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index f0e73fb90..3f68a2696 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -19,8 +19,6 @@ from shlex import quote from PIL import Image -from ._deprecate import deprecate - _viewers = [] @@ -111,21 +109,10 @@ class Viewer: """Display the given image.""" return self.show_file(self.save_image(image), **options) - def show_file(self, path=None, **options): + def show_file(self, path, **options): """ Display given file. - - Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, - and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used - instead. """ - if path is None: - if "file" in options: - deprecate("The 'file' argument", 10, "'path'") - path = options.pop("file") - else: - msg = "Missing required argument: 'path'" - raise TypeError(msg) os.system(self.get_command(path, **options)) # nosec return 1 @@ -164,21 +151,10 @@ class MacViewer(Viewer): command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&" return command - def show_file(self, path=None, **options): + def show_file(self, path, **options): """ Display given file. - - Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, - and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used - instead. """ - if path is None: - if "file" in options: - deprecate("The 'file' argument", 10, "'path'") - path = options.pop("file") - else: - msg = "Missing required argument: 'path'" - raise TypeError(msg) subprocess.call(["open", "-a", "Preview.app", path]) executable = sys.executable or shutil.which("python3") if executable: @@ -215,21 +191,10 @@ class XDGViewer(UnixViewer): command = executable = "xdg-open" return command, executable - def show_file(self, path=None, **options): + def show_file(self, path, **options): """ Display given file. - - Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, - and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used - instead. """ - if path is None: - if "file" in options: - deprecate("The 'file' argument", 10, "'path'") - path = options.pop("file") - else: - msg = "Missing required argument: 'path'" - raise TypeError(msg) subprocess.Popen(["xdg-open", path]) return 1 @@ -246,20 +211,10 @@ class DisplayViewer(UnixViewer): command += f" -title {quote(title)}" return command, executable - def show_file(self, path=None, **options): + def show_file(self, path, **options): """ Display given file. - - Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, - and ``path`` should be used instead. """ - if path is None: - if "file" in options: - deprecate("The 'file' argument", 10, "'path'") - path = options.pop("file") - else: - msg = "Missing required argument: 'path'" - raise TypeError(msg) args = ["display"] title = options.get("title") if title: @@ -278,20 +233,10 @@ class GmDisplayViewer(UnixViewer): command = "gm display" return command, executable - def show_file(self, path=None, **options): + def show_file(self, path, **options): """ Display given file. - - Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, - and ``path`` should be used instead. """ - if path is None: - if "file" in options: - deprecate("The 'file' argument", 10, "'path'") - path = options.pop("file") - else: - msg = "Missing required argument: 'path'" - raise TypeError(msg) subprocess.Popen(["gm", "display", path]) return 1 @@ -304,20 +249,10 @@ class EogViewer(UnixViewer): command = "eog -n" return command, executable - def show_file(self, path=None, **options): + def show_file(self, path, **options): """ Display given file. - - Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, - and ``path`` should be used instead. """ - if path is None: - if "file" in options: - deprecate("The 'file' argument", 10, "'path'") - path = options.pop("file") - else: - msg = "Missing required argument: 'path'" - raise TypeError(msg) subprocess.Popen(["eog", "-n", path]) return 1 @@ -336,20 +271,10 @@ class XVViewer(UnixViewer): command += f" -name {quote(title)}" return command, executable - def show_file(self, path=None, **options): + def show_file(self, path, **options): """ Display given file. - - Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, - and ``path`` should be used instead. """ - if path is None: - if "file" in options: - deprecate("The 'file' argument", 10, "'path'") - path = options.pop("file") - else: - msg = "Missing required argument: 'path'" - raise TypeError(msg) args = ["xv"] title = options.get("title") if title: From c8ec15980b00261d8c6a105a3dbc2a2fc634940c Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 2 Apr 2023 21:19:11 +0300 Subject: [PATCH 07/17] Remove constants deprecated in 9.1.0 --- Tests/test_file_apng.py | 10 ----- Tests/test_file_blp.py | 13 +----- Tests/test_file_ftex.py | 9 ---- Tests/test_image.py | 7 --- Tests/test_imagecms.py | 10 ----- Tests/test_imagefont.py | 9 ---- docs/deprecations.rst | 91 +++++++++++++++++++------------------- src/PIL/BlpImagePlugin.py | 16 ------- src/PIL/FtexImagePlugin.py | 12 ----- src/PIL/Image.py | 16 ------- src/PIL/ImageCms.py | 13 ------ src/PIL/ImageFont.py | 11 ----- src/PIL/PngImagePlugin.py | 12 ----- 13 files changed, 47 insertions(+), 182 deletions(-) diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index feca72aa6..f78c086eb 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -655,13 +655,3 @@ def test_different_modes_in_later_frames(mode, tmp_path): im.save(test_file, save_all=True, append_images=[Image.new(mode, (1, 1))]) with Image.open(test_file) as reloaded: assert reloaded.mode == mode - - -def test_constants_deprecation(): - for enum, prefix in { - PngImagePlugin.Disposal: "APNG_DISPOSE_", - PngImagePlugin.Blend: "APNG_BLEND_", - }.items(): - for name in enum.__members__: - with pytest.warns(DeprecationWarning): - assert getattr(PngImagePlugin, prefix + name) == enum[name] diff --git a/Tests/test_file_blp.py b/Tests/test_file_blp.py index ba2781820..8b1355b62 100644 --- a/Tests/test_file_blp.py +++ b/Tests/test_file_blp.py @@ -1,6 +1,6 @@ import pytest -from PIL import BlpImagePlugin, Image +from PIL import Image from .helper import ( assert_image_equal, @@ -72,14 +72,3 @@ def test_crashes(test_file): with Image.open(f) as im: with pytest.raises(OSError): im.load() - - -def test_constants_deprecation(): - for enum, prefix in { - BlpImagePlugin.Format: "BLP_FORMAT_", - BlpImagePlugin.Encoding: "BLP_ENCODING_", - BlpImagePlugin.AlphaEncoding: "BLP_ALPHA_ENCODING_", - }.items(): - for name in enum.__members__: - with pytest.warns(DeprecationWarning): - assert getattr(BlpImagePlugin, prefix + name) == enum[name] diff --git a/Tests/test_file_ftex.py b/Tests/test_file_ftex.py index cae20fa46..ac6253db0 100644 --- a/Tests/test_file_ftex.py +++ b/Tests/test_file_ftex.py @@ -21,12 +21,3 @@ def test_invalid_file(): with pytest.raises(SyntaxError): FtexImagePlugin.FtexImageFile(invalid_file) - - -def test_constants_deprecation(): - for enum, prefix in { - FtexImagePlugin.Format: "FORMAT_", - }.items(): - for name in enum.__members__: - with pytest.warns(DeprecationWarning): - assert getattr(FtexImagePlugin, prefix + name) == enum[name] diff --git a/Tests/test_image.py b/Tests/test_image.py index cb31565e7..85f9f7d02 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -930,13 +930,6 @@ class TestImage: assert im.palette.colors[(27, 35, 6, 214)] == 24 def test_constants(self): - with pytest.warns(DeprecationWarning): - assert Image.LINEAR == Image.Resampling.BILINEAR - with pytest.warns(DeprecationWarning): - assert Image.CUBIC == Image.Resampling.BICUBIC - with pytest.warns(DeprecationWarning): - assert Image.ANTIALIAS == Image.Resampling.LANCZOS - for enum in ( Image.Transpose, Image.Transform, diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 66be02078..8efe063c1 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -617,16 +617,6 @@ def test_auxiliary_channels_isolated(): assert_image_equal(test_image.convert(dst_format[2]), reference_image) -def test_constants_deprecation(): - for enum, prefix in { - ImageCms.Intent: "INTENT_", - ImageCms.Direction: "DIRECTION_", - }.items(): - for name in enum.__members__: - with pytest.warns(DeprecationWarning): - assert getattr(ImageCms, prefix + name) == enum[name] - - @pytest.mark.parametrize("mode", ("RGB", "RGBA", "RGBX")) def test_rgb_lab(mode): im = Image.new(mode, (1, 1)) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index b115517ac..2d83b5a37 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -1130,12 +1130,3 @@ def test_raqm_missing_warning(monkeypatch): "Raqm layout was requested, but Raqm is not available. " "Falling back to basic layout." ) - - -def test_constants_deprecation(): - for enum, prefix in { - ImageFont.Layout: "LAYOUT_", - }.items(): - for name in enum.__members__: - with pytest.warns(DeprecationWarning): - assert getattr(ImageFont, prefix + name) == enum[name] diff --git a/docs/deprecations.rst b/docs/deprecations.rst index e75d1f7fa..8606ede4d 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,51 +12,6 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. -Constants -~~~~~~~~~ - -.. deprecated:: 9.1.0 - -A number of constants have been deprecated and will be removed in Pillow 10.0.0 -(2023-07-01). Instead, ``enum.IntEnum`` classes have been added. - -.. note:: - - Additional ``Image`` constants were deprecated in Pillow 9.1.0, but that - was reversed in Pillow 9.4.0 and those constants will now remain available. - See :ref:`restored-image-constants` - -===================================================== ============================================================ -Deprecated Use instead -===================================================== ============================================================ -``Image.LINEAR`` ``Image.BILINEAR`` or ``Image.Resampling.BILINEAR`` -``Image.CUBIC`` ``Image.BICUBIC`` or ``Image.Resampling.BICUBIC`` -``Image.ANTIALIAS`` ``Image.LANCZOS`` or ``Image.Resampling.LANCZOS`` -``ImageCms.INTENT_PERCEPTUAL`` ``ImageCms.Intent.PERCEPTUAL`` -``ImageCms.INTENT_RELATIVE_COLORMETRIC`` ``ImageCms.Intent.RELATIVE_COLORMETRIC`` -``ImageCms.INTENT_SATURATION`` ``ImageCms.Intent.SATURATION`` -``ImageCms.INTENT_ABSOLUTE_COLORIMETRIC`` ``ImageCms.Intent.ABSOLUTE_COLORIMETRIC`` -``ImageCms.DIRECTION_INPUT`` ``ImageCms.Direction.INPUT`` -``ImageCms.DIRECTION_OUTPUT`` ``ImageCms.Direction.OUTPUT`` -``ImageCms.DIRECTION_PROOF`` ``ImageCms.Direction.PROOF`` -``ImageFont.LAYOUT_BASIC`` ``ImageFont.Layout.BASIC`` -``ImageFont.LAYOUT_RAQM`` ``ImageFont.Layout.RAQM`` -``BlpImagePlugin.BLP_FORMAT_JPEG`` ``BlpImagePlugin.Format.JPEG`` -``BlpImagePlugin.BLP_ENCODING_UNCOMPRESSED`` ``BlpImagePlugin.Encoding.UNCOMPRESSED`` -``BlpImagePlugin.BLP_ENCODING_DXT`` ``BlpImagePlugin.Encoding.DXT`` -``BlpImagePlugin.BLP_ENCODING_UNCOMPRESSED_RAW_RGBA`` ``BlpImagePlugin.Encoding.UNCOMPRESSED_RAW_RGBA`` -``BlpImagePlugin.BLP_ALPHA_ENCODING_DXT1`` ``BlpImagePlugin.AlphaEncoding.DXT1`` -``BlpImagePlugin.BLP_ALPHA_ENCODING_DXT3`` ``BlpImagePlugin.AlphaEncoding.DXT3`` -``BlpImagePlugin.BLP_ALPHA_ENCODING_DXT5`` ``BlpImagePlugin.AlphaEncoding.DXT5`` -``FtexImagePlugin.FORMAT_DXT1`` ``FtexImagePlugin.Format.DXT1`` -``FtexImagePlugin.FORMAT_UNCOMPRESSED`` ``FtexImagePlugin.Format.UNCOMPRESSED`` -``PngImagePlugin.APNG_DISPOSE_OP_NONE`` ``PngImagePlugin.Disposal.OP_NONE`` -``PngImagePlugin.APNG_DISPOSE_OP_BACKGROUND`` ``PngImagePlugin.Disposal.OP_BACKGROUND`` -``PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS`` ``PngImagePlugin.Disposal.OP_PREVIOUS`` -``PngImagePlugin.APNG_BLEND_OP_SOURCE`` ``PngImagePlugin.Blend.OP_SOURCE`` -``PngImagePlugin.APNG_BLEND_OP_OVER`` ``PngImagePlugin.Blend.OP_OVER`` -===================================================== ============================================================ - FitsStubImagePlugin ~~~~~~~~~~~~~~~~~~~ @@ -206,6 +161,52 @@ removed and replaced by ``path``. In effect, ``viewer.show_file("test.jpg")`` will continue to work unchanged. +Constants +~~~~~~~~~ + +.. deprecated:: 9.1.0 +.. versionremoved:: 10.0.0 + +A number of constants have been removed. +Instead, ``enum.IntEnum`` classes have been added. + +.. note:: + + Additional ``Image`` constants were deprecated in Pillow 9.1.0, but that + was reversed in Pillow 9.4.0 and those constants will now remain available. + See :ref:`restored-image-constants` + +===================================================== ============================================================ +Removed Use instead +===================================================== ============================================================ +``Image.LINEAR`` ``Image.BILINEAR`` or ``Image.Resampling.BILINEAR`` +``Image.CUBIC`` ``Image.BICUBIC`` or ``Image.Resampling.BICUBIC`` +``Image.ANTIALIAS`` ``Image.LANCZOS`` or ``Image.Resampling.LANCZOS`` +``ImageCms.INTENT_PERCEPTUAL`` ``ImageCms.Intent.PERCEPTUAL`` +``ImageCms.INTENT_RELATIVE_COLORMETRIC`` ``ImageCms.Intent.RELATIVE_COLORMETRIC`` +``ImageCms.INTENT_SATURATION`` ``ImageCms.Intent.SATURATION`` +``ImageCms.INTENT_ABSOLUTE_COLORIMETRIC`` ``ImageCms.Intent.ABSOLUTE_COLORIMETRIC`` +``ImageCms.DIRECTION_INPUT`` ``ImageCms.Direction.INPUT`` +``ImageCms.DIRECTION_OUTPUT`` ``ImageCms.Direction.OUTPUT`` +``ImageCms.DIRECTION_PROOF`` ``ImageCms.Direction.PROOF`` +``ImageFont.LAYOUT_BASIC`` ``ImageFont.Layout.BASIC`` +``ImageFont.LAYOUT_RAQM`` ``ImageFont.Layout.RAQM`` +``BlpImagePlugin.BLP_FORMAT_JPEG`` ``BlpImagePlugin.Format.JPEG`` +``BlpImagePlugin.BLP_ENCODING_UNCOMPRESSED`` ``BlpImagePlugin.Encoding.UNCOMPRESSED`` +``BlpImagePlugin.BLP_ENCODING_DXT`` ``BlpImagePlugin.Encoding.DXT`` +``BlpImagePlugin.BLP_ENCODING_UNCOMPRESSED_RAW_RGBA`` ``BlpImagePlugin.Encoding.UNCOMPRESSED_RAW_RGBA`` +``BlpImagePlugin.BLP_ALPHA_ENCODING_DXT1`` ``BlpImagePlugin.AlphaEncoding.DXT1`` +``BlpImagePlugin.BLP_ALPHA_ENCODING_DXT3`` ``BlpImagePlugin.AlphaEncoding.DXT3`` +``BlpImagePlugin.BLP_ALPHA_ENCODING_DXT5`` ``BlpImagePlugin.AlphaEncoding.DXT5`` +``FtexImagePlugin.FORMAT_DXT1`` ``FtexImagePlugin.Format.DXT1`` +``FtexImagePlugin.FORMAT_UNCOMPRESSED`` ``FtexImagePlugin.Format.UNCOMPRESSED`` +``PngImagePlugin.APNG_DISPOSE_OP_NONE`` ``PngImagePlugin.Disposal.OP_NONE`` +``PngImagePlugin.APNG_DISPOSE_OP_BACKGROUND`` ``PngImagePlugin.Disposal.OP_BACKGROUND`` +``PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS`` ``PngImagePlugin.Disposal.OP_PREVIOUS`` +``PngImagePlugin.APNG_BLEND_OP_SOURCE`` ``PngImagePlugin.Blend.OP_SOURCE`` +``PngImagePlugin.APNG_BLEND_OP_OVER`` ``PngImagePlugin.Blend.OP_OVER`` +===================================================== ============================================================ + PyQt5 and PySide2 ~~~~~~~~~~~~~~~~~ diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 1cc0d4b3c..0ca60ff24 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -35,7 +35,6 @@ from enum import IntEnum from io import BytesIO from . import Image, ImageFile -from ._deprecate import deprecate class Format(IntEnum): @@ -54,21 +53,6 @@ class AlphaEncoding(IntEnum): DXT5 = 7 -def __getattr__(name): - for enum, prefix in { - Format: "BLP_FORMAT_", - Encoding: "BLP_ENCODING_", - AlphaEncoding: "BLP_ALPHA_ENCODING_", - }.items(): - if name.startswith(prefix): - name = name[len(prefix) :] - if name in enum.__members__: - deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}") - return enum[name] - msg = f"module '{__name__}' has no attribute '{name}'" - raise AttributeError(msg) - - def unpack_565(i): return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3 diff --git a/src/PIL/FtexImagePlugin.py b/src/PIL/FtexImagePlugin.py index c7c32252b..c46b2f28b 100644 --- a/src/PIL/FtexImagePlugin.py +++ b/src/PIL/FtexImagePlugin.py @@ -56,7 +56,6 @@ from enum import IntEnum from io import BytesIO from . import Image, ImageFile -from ._deprecate import deprecate MAGIC = b"FTEX" @@ -66,17 +65,6 @@ class Format(IntEnum): UNCOMPRESSED = 1 -def __getattr__(name): - for enum, prefix in {Format: "FORMAT_"}.items(): - if name.startswith(prefix): - name = name[len(prefix) :] - if name in enum.__members__: - deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}") - return enum[name] - msg = f"module '{__name__}' has no attribute '{name}'" - raise AttributeError(msg) - - class FtexImageFile(ImageFile.ImageFile): format = "FTEX" format_description = "Texture File Format (IW2:EOC)" diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 99a895fb0..bc846fde7 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -59,22 +59,6 @@ from ._binary import i32le, o32be, o32le from ._deprecate import deprecate from ._util import DeferredError, is_path - -def __getattr__(name): - old_resampling = { - "LINEAR": "BILINEAR", - "CUBIC": "BICUBIC", - "ANTIALIAS": "LANCZOS", - } - if name in old_resampling: - deprecate( - name, 10, f"{old_resampling[name]} or Resampling.{old_resampling[name]}" - ) - return Resampling[old_resampling[name]] - msg = f"module '{__name__}' has no attribute '{name}'" - raise AttributeError(msg) - - logger = logging.getLogger(__name__) diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index f87849680..31b0e5a5e 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -20,8 +20,6 @@ from enum import IntEnum from PIL import Image -from ._deprecate import deprecate - try: from PIL import _imagingcms except ImportError as ex: @@ -117,17 +115,6 @@ class Direction(IntEnum): PROOF = 2 -def __getattr__(name): - for enum, prefix in {Intent: "INTENT_", Direction: "DIRECTION_"}.items(): - if name.startswith(prefix): - name = name[len(prefix) :] - if name in enum.__members__: - deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}") - return enum[name] - msg = f"module '{__name__}' has no attribute '{name}'" - raise AttributeError(msg) - - # # flags diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 9cdad2961..34a04a6bf 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -43,17 +43,6 @@ class Layout(IntEnum): RAQM = 1 -def __getattr__(name): - for enum, prefix in {Layout: "LAYOUT_"}.items(): - if name.startswith(prefix): - name = name[len(prefix) :] - if name in enum.__members__: - deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}") - return enum[name] - msg = f"module '{__name__}' has no attribute '{name}'" - raise AttributeError(msg) - - try: from . import _imagingft as core except ImportError as ex: diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 15a3c8291..82a74b267 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -45,7 +45,6 @@ from ._binary import i32be as i32 from ._binary import o8 from ._binary import o16be as o16 from ._binary import o32be as o32 -from ._deprecate import deprecate logger = logging.getLogger(__name__) @@ -131,17 +130,6 @@ class Blend(IntEnum): """ -def __getattr__(name): - for enum, prefix in {Disposal: "APNG_DISPOSE_", Blend: "APNG_BLEND_"}.items(): - if name.startswith(prefix): - name = name[len(prefix) :] - if name in enum.__members__: - deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}") - return enum[name] - msg = f"module '{__name__}' has no attribute '{name}'" - raise AttributeError(msg) - - def _safe_zlib_decompress(s): dobj = zlib.decompressobj() plaintext = dobj.decompress(s, MAX_TEXT_CHUNK) From 575a038f9762ab142948d6d36a4ec3a37b8f7833 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 2 Apr 2023 21:32:18 +0300 Subject: [PATCH 08/17] Remove FitsStubImagePlugin, deprecated in 9.1.0 --- Tests/test_file_fits.py | 38 +---------------- docs/deprecations.rst | 18 ++++---- src/PIL/FitsStubImagePlugin.py | 76 ---------------------------------- src/PIL/__init__.py | 1 - 4 files changed, 10 insertions(+), 123 deletions(-) delete mode 100644 src/PIL/FitsStubImagePlugin.py diff --git a/Tests/test_file_fits.py b/Tests/test_file_fits.py index 6f988729f..68b3eb567 100644 --- a/Tests/test_file_fits.py +++ b/Tests/test_file_fits.py @@ -2,7 +2,7 @@ from io import BytesIO import pytest -from PIL import FitsImagePlugin, FitsStubImagePlugin, Image +from PIL import FitsImagePlugin, Image from .helper import assert_image_equal, hopper @@ -48,39 +48,3 @@ def test_comment(): image_data = b"SIMPLE = T / comment string" with pytest.raises(OSError): FitsImagePlugin.FitsImageFile(BytesIO(image_data)) - - -def test_stub_deprecated(): - class Handler: - opened = False - loaded = False - - def open(self, im): - self.opened = True - - def load(self, im): - self.loaded = True - im.fp.close() - return Image.new("RGB", (1, 1)) - - handler = Handler() - with pytest.warns(DeprecationWarning): - FitsStubImagePlugin.register_handler(handler) - - with Image.open(TEST_FILE) as im: - assert im.format == "FITS" - assert im.size == (128, 128) - assert im.mode == "L" - - assert handler.opened - assert not handler.loaded - - im.load() - assert handler.loaded - - FitsStubImagePlugin._handler = None - Image.register_open( - FitsImagePlugin.FitsImageFile.format, - FitsImagePlugin.FitsImageFile, - FitsImagePlugin._accept, - ) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 8606ede4d..577cd6c27 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,15 +12,6 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. -FitsStubImagePlugin -~~~~~~~~~~~~~~~~~~~ - -.. deprecated:: 9.1.0 - -The stub image plugin ``FitsStubImagePlugin`` has been deprecated and will be removed in -Pillow 10.0.0 (2023-07-01). FITS images can be read without a handler through -:mod:`~PIL.FitsImagePlugin` instead. - FreeTypeFont.getmask2 fill parameter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -207,6 +198,15 @@ Removed Use instead ``PngImagePlugin.APNG_BLEND_OP_OVER`` ``PngImagePlugin.Blend.OP_OVER`` ===================================================== ============================================================ +FitsStubImagePlugin +~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 9.1.0 +.. versionremoved:: 10.0.0 + +The stub image plugin ``FitsStubImagePlugin`` has been removed. +FITS images can be read without a handler through :mod:`~PIL.FitsImagePlugin` instead. + PyQt5 and PySide2 ~~~~~~~~~~~~~~~~~ diff --git a/src/PIL/FitsStubImagePlugin.py b/src/PIL/FitsStubImagePlugin.py deleted file mode 100644 index 50948ec42..000000000 --- a/src/PIL/FitsStubImagePlugin.py +++ /dev/null @@ -1,76 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# FITS stub adapter -# -# Copyright (c) 1998-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# - -from . import FitsImagePlugin, Image, ImageFile -from ._deprecate import deprecate - -_handler = None - - -def register_handler(handler): - """ - Install application-specific FITS image handler. - - :param handler: Handler object. - """ - global _handler - _handler = handler - - deprecate( - "FitsStubImagePlugin", - 10, - action="FITS images can now be read without " - "a handler through FitsImagePlugin instead", - ) - - # Override FitsImagePlugin with this handler - # for backwards compatibility - try: - Image.ID.remove(FITSStubImageFile.format) - except ValueError: - pass - - Image.register_open( - FITSStubImageFile.format, FITSStubImageFile, FitsImagePlugin._accept - ) - - -class FITSStubImageFile(ImageFile.StubImageFile): - format = FitsImagePlugin.FitsImageFile.format - format_description = FitsImagePlugin.FitsImageFile.format_description - - def _open(self): - offset = self.fp.tell() - - im = FitsImagePlugin.FitsImageFile(self.fp) - self._size = im.size - self.mode = im.mode - self.tile = [] - - self.fp.seek(offset) - - loader = self._load() - if loader: - loader.open(self) - - def _load(self): - return _handler - - -def _save(im, fp, filename): - msg = "FITS save handler not installed" - raise OSError(msg) - - -# -------------------------------------------------------------------- -# Registry - -Image.register_save(FITSStubImageFile.format, _save) diff --git a/src/PIL/__init__.py b/src/PIL/__init__.py index 32d2381f3..2bb8f6d7f 100644 --- a/src/PIL/__init__.py +++ b/src/PIL/__init__.py @@ -31,7 +31,6 @@ _plugins = [ "DdsImagePlugin", "EpsImagePlugin", "FitsImagePlugin", - "FitsStubImagePlugin", "FliImagePlugin", "FpxImagePlugin", "FtexImagePlugin", From c9f11565f1be05578f831e8f2050a2164683a70e Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 2 Apr 2023 21:40:39 +0300 Subject: [PATCH 09/17] Remove FreeTypeFont.getmask2 fill parameter, deprecated in 9.2.0 --- Tests/test_imagefont.py | 8 -------- docs/deprecations.rst | 17 +++++++++-------- src/PIL/ImageFont.py | 16 +--------------- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 2d83b5a37..ca76ca6b2 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -1083,14 +1083,6 @@ def test_woff2(layout_engine): assert_image_similar_tofile(im, "Tests/images/test_woff2.png", 5) -def test_fill_deprecation(font): - with pytest.warns(DeprecationWarning): - font.getmask2("Hello world", fill=Image.core.fill) - with pytest.warns(DeprecationWarning): - with pytest.raises(TypeError): - font.getmask2("Hello world", fill=None) - - def test_render_mono_size(): # issue 4177 diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 577cd6c27..eaef94907 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,14 +12,6 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. -FreeTypeFont.getmask2 fill parameter -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. deprecated:: 9.2.0 - -The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2` has been -deprecated and will be removed in Pillow 10 (2023-07-01). - PhotoImage.paste box parameter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -207,6 +199,15 @@ FitsStubImagePlugin The stub image plugin ``FitsStubImagePlugin`` has been removed. FITS images can be read without a handler through :mod:`~PIL.FitsImagePlugin` instead. +FreeTypeFont.getmask2 fill parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 9.2.0 +.. versionremoved:: 10.0.0 + +The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2` has been +removed. + PyQt5 and PySide2 ~~~~~~~~~~~~~~~~~ diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 34a04a6bf..e7d45636c 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -51,9 +51,6 @@ except ImportError as ex: core = DeferredError(ex) -_UNSPECIFIED = object() - - # FIXME: add support for pilfont2 format (see FontFile.py) # -------------------------------------------------------------------- @@ -654,7 +651,6 @@ class FreeTypeFont: self, text, mode="", - fill=_UNSPECIFIED, direction=None, features=None, language=None, @@ -680,12 +676,6 @@ class FreeTypeFont: .. versionadded:: 1.1.5 - :param fill: Optional fill function. By default, an internal Pillow function - will be used. - - Deprecated. This parameter will be removed in Pillow 10 - (2023-07-01). - :param direction: Direction of the text. It can be 'rtl' (right to left), 'ltr' (left to right) or 'ttb' (top to bottom). Requires libraqm. @@ -738,10 +728,6 @@ class FreeTypeFont: :py:mod:`PIL.Image.core` interface module, and the text offset, the gap between the starting coordinate and the first marking """ - if fill is _UNSPECIFIED: - fill = Image.core.fill - else: - deprecate("fill", 10) size, offset = self.font.getsize( text, mode, direction, features, language, anchor ) @@ -750,7 +736,7 @@ class FreeTypeFont: size = tuple(math.ceil(size[i] + stroke_width * 2 + start[i]) for i in range(2)) offset = offset[0] - stroke_width, offset[1] - stroke_width Image._decompression_bomb_check(size) - im = fill("RGBA" if mode == "RGBA" else "L", size, 0) + im = Image.core.fill("RGBA" if mode == "RGBA" else "L", size, 0) if min(size): self.font.render( text, From 584f8c39de3265440b920f3814a9a06ef420dcd7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 2 Apr 2023 21:54:51 +0300 Subject: [PATCH 10/17] Remove PhotoImage.paste() box parameter, deprecated in 9.2.0 --- Tests/test_imagetk.py | 7 ------- docs/deprecations.rst | 15 ++++++++------- src/PIL/ImageTk.py | 9 +-------- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index 995d0ee1f..a0c9574ba 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -89,13 +89,6 @@ def test_photoimage_blank(mode): assert_image_equal(reloaded.convert(mode), im) -def test_box_deprecation(): - im = hopper() - im_tk = ImageTk.PhotoImage(im) - with pytest.warns(DeprecationWarning): - im_tk.paste(im, (0, 0, 128, 128)) - - def test_bitmapimage(): im = hopper("1") diff --git a/docs/deprecations.rst b/docs/deprecations.rst index eaef94907..9b4186d01 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,13 +12,6 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. -PhotoImage.paste box parameter -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. deprecated:: 9.2.0 - -The ``box`` parameter is unused. It will be removed in Pillow 10.0.0 (2023-07-01). - Image.coerce_e ~~~~~~~~~~~~~~ @@ -208,6 +201,14 @@ FreeTypeFont.getmask2 fill parameter The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2` has been removed. +PhotoImage.paste box parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 9.2.0 +.. versionremoved:: 10.0.0 + +The ``box`` parameter was unused and has been removed. + PyQt5 and PySide2 ~~~~~~~~~~~~~~~~~ diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index ef569ed2e..bf98eb2c8 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -29,7 +29,6 @@ import tkinter from io import BytesIO from . import Image -from ._deprecate import deprecate # -------------------------------------------------------------------- # Check for Tkinter interface hooks @@ -162,7 +161,7 @@ class PhotoImage: """ return self.__size[1] - def paste(self, im, box=None): + def paste(self, im): """ Paste a PIL image into the photo image. Note that this can be very slow if the photo image is displayed. @@ -170,13 +169,7 @@ class PhotoImage: :param im: A PIL image. The size must match the target region. If the mode does not match, the image is converted to the mode of the bitmap image. - :param box: Deprecated. This parameter will be removed in Pillow 10 - (2023-07-01). """ - - if box is not None: - deprecate("The box parameter", 10, None) - # convert to blittable im.load() image = im.im From b25bf5161a088d17a2ee7ebb509b866683519dad Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 2 Apr 2023 22:16:13 +0300 Subject: [PATCH 11/17] Remove Image.coerce_e, deprecated in 9.2.0 --- Tests/test_image_point.py | 7 ------- docs/deprecations.rst | 16 ++++++++-------- src/PIL/Image.py | 25 ++++++++----------------- 3 files changed, 16 insertions(+), 32 deletions(-) diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index 157ecb120..c406cb8ec 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -1,7 +1,5 @@ import pytest -from PIL import Image - from .helper import assert_image_equal, hopper @@ -62,8 +60,3 @@ def test_f_mode(): im = hopper("F") with pytest.raises(ValueError): im.point(None) - - -def test_coerce_e_deprecation(): - with pytest.warns(DeprecationWarning): - assert Image.coerce_e(2).data == 2 diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 9b4186d01..0eb3d6b41 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,14 +12,6 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. -Image.coerce_e -~~~~~~~~~~~~~~ - -.. deprecated:: 9.2.0 - -This undocumented method has been deprecated and will be removed in Pillow 10 -(2023-07-01). - .. _Font size and offset methods: Font size and offset methods @@ -222,6 +214,14 @@ Support for PyQt5 and PySide2 has been removed from ``ImageQt``. Upgrade to `PyQt6 `_ or `PySide6 `_ instead. +Image.coerce_e +~~~~~~~~~~~~~~ + +.. deprecated:: 9.2.0 +.. versionremoved:: 10.0.0 + +This undocumented method has been removed. + PILLOW_VERSION constant ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/PIL/Image.py b/src/PIL/Image.py index bc846fde7..0d7a9bbfc 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -56,7 +56,6 @@ from . import ( _plugins, ) from ._binary import i32le, o32be, o32le -from ._deprecate import deprecate from ._util import DeferredError, is_path logger = logging.getLogger(__name__) @@ -421,26 +420,18 @@ def _getencoder(mode, encoder_name, args, extra=()): # Simple expression analyzer -def coerce_e(value): - deprecate("coerce_e", 10) - return value if isinstance(value, _E) else _E(1, value) - - -# _E(scale, offset) represents the affine transformation scale * x + offset. -# The "data" field is named for compatibility with the old implementation, -# and should be renamed once coerce_e is removed. class _E: - def __init__(self, scale, data): + def __init__(self, scale, offset): self.scale = scale - self.data = data + self.offset = offset def __neg__(self): - return _E(-self.scale, -self.data) + return _E(-self.scale, -self.offset) def __add__(self, other): if isinstance(other, _E): - return _E(self.scale + other.scale, self.data + other.data) - return _E(self.scale, self.data + other) + return _E(self.scale + other.scale, self.offset + other.offset) + return _E(self.scale, self.offset + other) __radd__ = __add__ @@ -453,19 +444,19 @@ class _E: def __mul__(self, other): if isinstance(other, _E): return NotImplemented - return _E(self.scale * other, self.data * other) + return _E(self.scale * other, self.offset * other) __rmul__ = __mul__ def __truediv__(self, other): if isinstance(other, _E): return NotImplemented - return _E(self.scale / other, self.data / other) + return _E(self.scale / other, self.offset / other) def _getscaleoffset(expr): a = expr(_E(1, 0)) - return (a.scale, a.data) if isinstance(a, _E) else (0, a) + return (a.scale, a.offset) if isinstance(a, _E) else (0, a) # -------------------------------------------------------------------- From f1f46a718d934fccd924483f147351c0d6a14fa7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 2 Apr 2023 23:41:16 +0300 Subject: [PATCH 12/17] Add removals to 10.0.0 release notes --- docs/releasenotes/10.0.0.rst | 149 +++++++++++++++++++++++++++++++++ docs/releasenotes/index.rst | 1 + docs/releasenotes/template.rst | 4 +- 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 docs/releasenotes/10.0.0.rst diff --git a/docs/releasenotes/10.0.0.rst b/docs/releasenotes/10.0.0.rst new file mode 100644 index 000000000..cd0296e3e --- /dev/null +++ b/docs/releasenotes/10.0.0.rst @@ -0,0 +1,149 @@ +10.0.0 +------ + +Backwards Incompatible Changes +============================== + +Categories +^^^^^^^^^^ + +``im.category`` has been removed, along with the related ``Image.NORMAL``, +``Image.SEQUENCE`` and ``Image.CONTAINER`` attributes. + +To determine if an image has multiple frames or not, +``getattr(im, "is_animated", False)`` can be used instead. + +Tk/Tcl 8.4 +^^^^^^^^^^ + +Support for Tk/Tcl 8.4 has been removed. + +JpegImagePlugin.convert_dict_qtables +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Since deprecation in Pillow 8.3.0, the ``convert_dict_qtables`` method no longer +performed any operations on the data given to it, and has been removed. + +ImagePalette size parameter +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Before Pillow 8.3.0, ``ImagePalette`` required palette data of particular lengths by +default, and the ``size`` parameter could be used to override that. Pillow 8.3.0 +removed the default required length, also removing the need for the ``size`` parameter. + +ImageShow.Viewer.show_file file argument +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``file`` argument in :py:meth:`~PIL.ImageShow.Viewer.show_file()` has been +removed and replaced by ``path``. + +In effect, ``viewer.show_file("test.jpg")`` will continue to work unchanged. + +Constants +^^^^^^^^^ + +A number of constants have been removed. +Instead, ``enum.IntEnum`` classes have been added. + +===================================================== ============================================================ +Removed Use instead +===================================================== ============================================================ +``Image.LINEAR`` ``Image.BILINEAR`` or ``Image.Resampling.BILINEAR`` +``Image.CUBIC`` ``Image.BICUBIC`` or ``Image.Resampling.BICUBIC`` +``Image.ANTIALIAS`` ``Image.LANCZOS`` or ``Image.Resampling.LANCZOS`` +``ImageCms.INTENT_PERCEPTUAL`` ``ImageCms.Intent.PERCEPTUAL`` +``ImageCms.INTENT_RELATIVE_COLORMETRIC`` ``ImageCms.Intent.RELATIVE_COLORMETRIC`` +``ImageCms.INTENT_SATURATION`` ``ImageCms.Intent.SATURATION`` +``ImageCms.INTENT_ABSOLUTE_COLORIMETRIC`` ``ImageCms.Intent.ABSOLUTE_COLORIMETRIC`` +``ImageCms.DIRECTION_INPUT`` ``ImageCms.Direction.INPUT`` +``ImageCms.DIRECTION_OUTPUT`` ``ImageCms.Direction.OUTPUT`` +``ImageCms.DIRECTION_PROOF`` ``ImageCms.Direction.PROOF`` +``ImageFont.LAYOUT_BASIC`` ``ImageFont.Layout.BASIC`` +``ImageFont.LAYOUT_RAQM`` ``ImageFont.Layout.RAQM`` +``BlpImagePlugin.BLP_FORMAT_JPEG`` ``BlpImagePlugin.Format.JPEG`` +``BlpImagePlugin.BLP_ENCODING_UNCOMPRESSED`` ``BlpImagePlugin.Encoding.UNCOMPRESSED`` +``BlpImagePlugin.BLP_ENCODING_DXT`` ``BlpImagePlugin.Encoding.DXT`` +``BlpImagePlugin.BLP_ENCODING_UNCOMPRESSED_RAW_RGBA`` ``BlpImagePlugin.Encoding.UNCOMPRESSED_RAW_RGBA`` +``BlpImagePlugin.BLP_ALPHA_ENCODING_DXT1`` ``BlpImagePlugin.AlphaEncoding.DXT1`` +``BlpImagePlugin.BLP_ALPHA_ENCODING_DXT3`` ``BlpImagePlugin.AlphaEncoding.DXT3`` +``BlpImagePlugin.BLP_ALPHA_ENCODING_DXT5`` ``BlpImagePlugin.AlphaEncoding.DXT5`` +``FtexImagePlugin.FORMAT_DXT1`` ``FtexImagePlugin.Format.DXT1`` +``FtexImagePlugin.FORMAT_UNCOMPRESSED`` ``FtexImagePlugin.Format.UNCOMPRESSED`` +``PngImagePlugin.APNG_DISPOSE_OP_NONE`` ``PngImagePlugin.Disposal.OP_NONE`` +``PngImagePlugin.APNG_DISPOSE_OP_BACKGROUND`` ``PngImagePlugin.Disposal.OP_BACKGROUND`` +``PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS`` ``PngImagePlugin.Disposal.OP_PREVIOUS`` +``PngImagePlugin.APNG_BLEND_OP_SOURCE`` ``PngImagePlugin.Blend.OP_SOURCE`` +``PngImagePlugin.APNG_BLEND_OP_OVER`` ``PngImagePlugin.Blend.OP_OVER`` +===================================================== ============================================================ + +FitsStubImagePlugin +^^^^^^^^^^^^^^^^^^^ + +The stub image plugin ``FitsStubImagePlugin`` has been removed. +FITS images can be read without a handler through :mod:`~PIL.FitsImagePlugin` instead. + +FreeTypeFont.getmask2 fill parameter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2` has been +removed. + +PhotoImage.paste box parameter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``box`` parameter was unused and has been removed. + +PyQt5 and PySide2 +^^^^^^^^^^^^^^^^^ + +`Qt 5 reached end-of-life `_ on 2020-12-08 for +open-source users (and will reach EOL on 2023-12-08 for commercial licence holders). + +Support for PyQt5 and PySide2 has been removed from ``ImageQt``. Upgrade to +`PyQt6 `_ or +`PySide6 `_ instead. + +Image.coerce_e +^^^^^^^^^^^^^^ + +This undocumented method has been removed. + +Deprecations +============ + +TODO +^^^^ + +TODO + +API Changes +=========== + +TODO +^^^^ + +TODO + +API Additions +============= + +TODO +^^^^ + +TODO + +Security +======== + +TODO +^^^^ + +TODO + +Other Changes +============= + +TODO +^^^^ + +TODO diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 177fb65dd..9bca98541 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 + 10.0.0 9.5.0 9.4.0 9.3.0 diff --git a/docs/releasenotes/template.rst b/docs/releasenotes/template.rst index f7271ae2b..440d04b1c 100644 --- a/docs/releasenotes/template.rst +++ b/docs/releasenotes/template.rst @@ -1,5 +1,5 @@ -x.y.z ------ +xx.y.z +------ Backwards Incompatible Changes ============================== From f707e8abd6cc820c62923056b17af9139f6d8b3d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 8 Apr 2023 11:40:31 +1000 Subject: [PATCH 13/17] 1 mode still fails for PyQt6 --- Tests/test_qt_image_toqimage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_qt_image_toqimage.py b/Tests/test_qt_image_toqimage.py index 399df670f..d071838f7 100644 --- a/Tests/test_qt_image_toqimage.py +++ b/Tests/test_qt_image_toqimage.py @@ -28,7 +28,7 @@ def test_sanity(mode, tmp_path): assert_image_equal(rt, src) if mode == "1": - # BW appears to not save correctly on QT5 + # BW appears to not save correctly on QT # kicks out errors on console: # libpng warning: Invalid color type/bit depth combination # in IHDR From 2cba9904db9df42a1c1502296e567a2d000df3b8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 8 Apr 2023 11:48:06 +1000 Subject: [PATCH 14/17] Removed _category --- src/PIL/Image.py | 2 -- src/PIL/MicImagePlugin.py | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 0d7a9bbfc..5a43f6c4a 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -487,7 +487,6 @@ class Image: self._size = (0, 0) self.palette = None self.info = {} - self._category = 0 self.readonly = 0 self.pyaccess = None self._exif = None @@ -604,7 +603,6 @@ class Image: and self.mode == other.mode and self.size == other.size and self.info == other.info - and self._category == other._category and self.getpalette() == other.getpalette() and self.tobytes() == other.tobytes() ) diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 58f7327bd..801318930 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -66,9 +66,6 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): self._n_frames = len(self.images) self.is_animated = self._n_frames > 1 - if len(self.images) > 1: - self._category = Image.CONTAINER - self.seek(0) def seek(self, frame): From c26b4621597e56c60f54707edde4ca4c88f4cfd7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 8 Apr 2023 11:52:38 +1000 Subject: [PATCH 15/17] Updated documentation --- docs/reference/Image.rst | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 0eba1141a..35a4c2110 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -412,18 +412,6 @@ See :ref:`concept-filters` for details. :undoc-members: :noindex: -Some deprecated filters are also available under the following names: - -.. data:: NONE - :noindex: - :value: Resampling.NEAREST -.. data:: LINEAR - :value: Resampling.BILINEAR -.. data:: CUBIC - :value: Resampling.BICUBIC -.. data:: ANTIALIAS - :value: Resampling.LANCZOS - Dither modes ^^^^^^^^^^^^ From 94aa76ebdeb33be85a6c23d907877b7d45032ba2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 8 Apr 2023 10:29:28 +0300 Subject: [PATCH 16/17] Fix typo --- Tests/test_qt_image_toqimage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_qt_image_toqimage.py b/Tests/test_qt_image_toqimage.py index d071838f7..95c13ba75 100644 --- a/Tests/test_qt_image_toqimage.py +++ b/Tests/test_qt_image_toqimage.py @@ -28,7 +28,7 @@ def test_sanity(mode, tmp_path): assert_image_equal(rt, src) if mode == "1": - # BW appears to not save correctly on QT + # BW appears to not save correctly on Qt # kicks out errors on console: # libpng warning: Invalid color type/bit depth combination # in IHDR From aa6c0dcc9e0c3d60ab77ced04f7c0e7e7d1f0fc0 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 8 Apr 2023 10:31:47 +0300 Subject: [PATCH 17/17] Cygwin doesn't provide any Qt6 packages Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- .github/workflows/test-cygwin.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 397cfe0a1..14a5f2c14 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -67,7 +67,6 @@ jobs: python3${{ matrix.python-minor-version }}-numpy python3${{ matrix.python-minor-version }}-sip python3${{ matrix.python-minor-version }}-tkinter - qt6-devel-tools wget xorg-server-extra zlib-devel