Merge branch 'main' into include-dir-type-confusion

This commit is contained in:
Andrew Murray 2023-04-10 22:49:32 +10:00
commit 6fce7d6ed4
46 changed files with 349 additions and 760 deletions

View File

@ -43,7 +43,7 @@ if [[ $(uname) != CYGWIN* ]]; then
# PyQt6 doesn't support PyPy3 # PyQt6 doesn't support PyPy3
if [[ $GHA_PYTHON_VERSION == 3.* ]]; then if [[ $GHA_PYTHON_VERSION == 3.* ]]; then
sudo apt-get -qq install libegl1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxkbcommon-x11-0 sudo apt-get -qq install libegl1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxkbcommon-x11-0
python3 -m pip install pyqt6 python3 -m pip install pyqt6 PyQt6-Qt6!=6.5.0
fi fi
# webp # webp

View File

@ -67,7 +67,6 @@ jobs:
python3${{ matrix.python-minor-version }}-numpy python3${{ matrix.python-minor-version }}-numpy
python3${{ matrix.python-minor-version }}-sip python3${{ matrix.python-minor-version }}-sip
python3${{ matrix.python-minor-version }}-tkinter python3${{ matrix.python-minor-version }}-tkinter
qt5-devel-tools
wget wget
xorg-server-extra xorg-server-extra
zlib-devel zlib-devel

View File

@ -5,6 +5,9 @@ Changelog (Pillow)
10.0.0 (unreleased) 10.0.0 (unreleased)
------------------- -------------------
- Remove deprecations for Pillow 10.0.0 #7059
[hugovk, radarhere]
- Drop support for soon-EOL Python 3.7 #7058 - Drop support for soon-EOL Python 3.7 #7058
[hugovk, radarhere] [hugovk, radarhere]

View File

@ -78,6 +78,7 @@ debug:
.PHONY: release-test .PHONY: release-test
release-test: release-test:
python3 Tests/check_release_notes.py
python3 -m pip install -e .[tests] python3 -m pip install -e .[tests]
python3 selftest.py python3 selftest.py
python3 -m pytest Tests python3 -m pytest Tests

View File

@ -0,0 +1,6 @@
import sys
from pathlib import Path
for rst in Path("docs/releasenotes").glob("[1-9]*.rst"):
if "TODO" in open(rst).read():
sys.exit(f"Error: remove TODO from {rst}")

View File

@ -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

View File

@ -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))]) im.save(test_file, save_all=True, append_images=[Image.new(mode, (1, 1))])
with Image.open(test_file) as reloaded: with Image.open(test_file) as reloaded:
assert reloaded.mode == mode 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]

View File

@ -1,6 +1,6 @@
import pytest import pytest
from PIL import BlpImagePlugin, Image from PIL import Image
from .helper import ( from .helper import (
assert_image_equal, assert_image_equal,
@ -72,14 +72,3 @@ def test_crashes(test_file):
with Image.open(f) as im: with Image.open(f) as im:
with pytest.raises(OSError): with pytest.raises(OSError):
im.load() 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]

View File

@ -2,7 +2,7 @@ from io import BytesIO
import pytest import pytest
from PIL import FitsImagePlugin, FitsStubImagePlugin, Image from PIL import FitsImagePlugin, Image
from .helper import assert_image_equal, hopper from .helper import assert_image_equal, hopper
@ -48,39 +48,3 @@ def test_comment():
image_data = b"SIMPLE = T / comment string" image_data = b"SIMPLE = T / comment string"
with pytest.raises(OSError): with pytest.raises(OSError):
FitsImagePlugin.FitsImageFile(BytesIO(image_data)) 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,
)

View File

@ -21,12 +21,3 @@ def test_invalid_file():
with pytest.raises(SyntaxError): with pytest.raises(SyntaxError):
FtexImagePlugin.FtexImageFile(invalid_file) 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]

View File

@ -636,12 +636,6 @@ class TestFileJpeg:
assert max(im2.quantization[0]) <= 255 assert max(im2.quantization[0]) <= 255
assert max(im2.quantization[1]) <= 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") @pytest.mark.skipif(not djpeg_available(), reason="djpeg not available")
def test_load_djpeg(self): def test_load_djpeg(self):
with Image.open(TEST_FILE) as img: with Image.open(TEST_FILE) as img:

View File

@ -929,25 +929,7 @@ class TestImage:
im.apply_transparency() im.apply_transparency()
assert im.palette.colors[(27, 35, 6, 214)] == 24 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): 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 ( for enum in (
Image.Transpose, Image.Transpose,
Image.Transform, Image.Transform,

View File

@ -1,7 +1,5 @@
import pytest import pytest
from PIL import Image
from .helper import assert_image_equal, hopper from .helper import assert_image_equal, hopper
@ -62,8 +60,3 @@ def test_f_mode():
im = hopper("F") im = hopper("F")
with pytest.raises(ValueError): with pytest.raises(ValueError):
im.point(None) im.point(None)
def test_coerce_e_deprecation():
with pytest.warns(DeprecationWarning):
assert Image.coerce_e(2).data == 2

View File

@ -617,16 +617,6 @@ def test_auxiliary_channels_isolated():
assert_image_equal(test_image.convert(dst_format[2]), reference_image) 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")) @pytest.mark.parametrize("mode", ("RGB", "RGBA", "RGBX"))
def test_rgb_lab(mode): def test_rgb_lab(mode):
im = Image.new(mode, (1, 1)) im = Image.new(mode, (1, 1))

View File

@ -1083,14 +1083,6 @@ def test_woff2(layout_engine):
assert_image_similar_tofile(im, "Tests/images/test_woff2.png", 5) 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(): def test_render_mono_size():
# issue 4177 # issue 4177
@ -1130,12 +1122,3 @@ def test_raqm_missing_warning(monkeypatch):
"Raqm layout was requested, but Raqm is not available. " "Raqm layout was requested, but Raqm is not available. "
"Falling back to basic layout." "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]

View File

@ -9,10 +9,6 @@ def test_sanity():
palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3)
assert len(palette.colors) == 256 assert len(palette.colors) == 256
with pytest.warns(DeprecationWarning):
with pytest.raises(ValueError):
ImagePalette.ImagePalette("RGB", list(range(256)) * 3, 10)
def test_reload(): def test_reload():
with Image.open("Tests/images/hopper.gif") as im: with Image.open("Tests/images/hopper.gif") as im:

View File

@ -2,12 +2,9 @@ import warnings
import pytest import pytest
from .helper import assert_image_similar, hopper
with warnings.catch_warnings() as w:
warnings.simplefilter("ignore", category=DeprecationWarning)
from PIL import ImageQt from PIL import ImageQt
from .helper import assert_image_similar, hopper
pytestmark = pytest.mark.skipif( pytestmark = pytest.mark.skipif(
not ImageQt.qt_is_installed, reason="Qt bindings are not installed" not ImageQt.qt_is_installed, reason="Qt bindings are not installed"
@ -26,10 +23,6 @@ def test_rgb():
from PyQt6.QtGui import qRgb from PyQt6.QtGui import qRgb
elif ImageQt.qt_version == "side6": elif ImageQt.qt_version == "side6":
from PySide6.QtGui import qRgb 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) assert qRgb(0, 0, 0) == qRgba(0, 0, 0, 255)

View File

@ -89,20 +89,3 @@ def test_ipythonviewer():
im = hopper() im = hopper()
assert test_viewer.show(im) == 1 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()

View File

@ -89,13 +89,6 @@ def test_photoimage_blank(mode):
assert_image_equal(reloaded.convert(mode), im) 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(): def test_bitmapimage():
im = hopper("1") im = hopper("1")

View File

@ -1,9 +1,5 @@
import warnings
import pytest 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 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.QtCore import QPoint
from PySide6.QtGui import QImage, QPainter, QRegion from PySide6.QtGui import QImage, QPainter, QRegion
from PySide6.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget 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): class Example(QWidget):
def __init__(self): def __init__(self):

View File

@ -1,9 +1,5 @@
import warnings
import pytest 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 from .helper import assert_image_equal, assert_image_equal_tofile, hopper
@ -32,7 +28,7 @@ def test_sanity(mode, tmp_path):
assert_image_equal(rt, src) assert_image_equal(rt, src)
if mode == "1": if mode == "1":
# BW appears to not save correctly on QT5 # BW appears to not save correctly on Qt
# kicks out errors on console: # kicks out errors on console:
# libpng warning: Invalid color type/bit depth combination # libpng warning: Invalid color type/bit depth combination
# in IHDR # in IHDR

View File

@ -12,150 +12,6 @@ Deprecated features
Below are features which are considered deprecated. Where appropriate, Below are features which are considered deprecated. Where appropriate,
a ``DeprecationWarning`` is issued. 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
~~~~~~~~~~
.. 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. 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
~~~~~~~~~
.. 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
~~~~~~~~~~~~~~~~~~~
.. 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 9.2.0
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 <https://www.qt.io/blog/qt-5.15-released>`_ 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 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
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:
Font size and offset methods Font size and offset methods
@ -223,6 +79,149 @@ Removed features
Deprecated features are only removed in major releases after an appropriate Deprecated features are only removed in major releases after an appropriate
period of deprecation has passed. 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).
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.
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.
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.
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.
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``
===================================================== ============================================================
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.
FreeTypeFont.getmask2 fill parameter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. deprecated:: 9.2.0
.. versionremoved:: 10.0.0
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
~~~~~~~~~~~~~~~~~
.. deprecated:: 9.2.0
.. versionremoved:: 10.0.0
`Qt 5 reached end-of-life <https://www.qt.io/blog/qt-5.15-released>`_ 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 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
Image.coerce_e
~~~~~~~~~~~~~~
.. deprecated:: 9.2.0
.. versionremoved:: 10.0.0
This undocumented method has been removed.
PILLOW_VERSION constant PILLOW_VERSION constant
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -412,18 +412,6 @@ See :ref:`concept-filters` for details.
:undoc-members: :undoc-members:
:noindex: :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 Dither modes
^^^^^^^^^^^^ ^^^^^^^^^^^^

View File

@ -4,16 +4,8 @@
:py:mod:`~PIL.ImageQt` Module :py:mod:`~PIL.ImageQt` Module
============================= =============================
The :py:mod:`~PIL.ImageQt` module contains support for creating PyQt6, PySide6, PyQt5 The :py:mod:`~PIL.ImageQt` module contains support for creating PyQt6 or PySide6
or PySide2 QImage objects from PIL images. QImage objects from PIL images.
`Qt 5 reached end-of-life <https://www.qt.io/blog/qt-5.15-released>`_ 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 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
.. versionadded:: 1.1.6 .. 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 Creates an :py:class:`~PIL.ImageQt.ImageQt` object from a PIL
:py:class:`~PIL.Image.Image` object. This class is a subclass of :py:class:`~PIL.Image.Image` object. This class is a subclass of
QtGui.QImage, which means that you can pass the resulting objects directly 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 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. images. To handle other modes, you need to convert the image first.

View File

@ -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 <https://www.qt.io/blog/qt-5.15-released>`_ 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 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
`PySide6 <https://doc.qt.io/qtforpython/>`_ 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

View File

@ -8,7 +8,7 @@ JpegImagePlugin.convert_dict_qtables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
JPEG ``quantization`` is now automatically converted, but still returned as a 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 performs any operations on the data given to it, has been deprecated and will be
removed in Pillow 10.0.0 (2023-07-01). removed in Pillow 10.0.0 (2023-07-01).

View File

@ -14,6 +14,7 @@ expected to be backported to earlier versions.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
10.0.0
9.5.0 9.5.0
9.4.0 9.4.0
9.3.0 9.3.0

View File

@ -1,5 +1,5 @@
x.y.z xx.y.z
----- ------
Backwards Incompatible Changes Backwards Incompatible Changes
============================== ==============================

View File

@ -685,10 +685,6 @@ class pil_build_ext(build_ext):
# Add the directory to the include path so we can include # Add the directory to the include path so we can include
# <openjpeg.h> rather than having to cope with the versioned # <openjpeg.h> rather than having to cope with the versioned
# include path # include path
# FIXME (melvyn-sopacua):
# At this point it's possible that best_path is already in
# self.compiler.include_dirs. Should investigate how that is
# possible.
_add_directory(self.compiler.include_dirs, best_path, 0) _add_directory(self.compiler.include_dirs, best_path, 0)
feature.jpeg2000 = "openjp2" feature.jpeg2000 = "openjp2"
feature.openjpeg_version = ".".join(str(x) for x in best_version) feature.openjpeg_version = ".".join(str(x) for x in best_version)

View File

@ -35,7 +35,6 @@ from enum import IntEnum
from io import BytesIO from io import BytesIO
from . import Image, ImageFile from . import Image, ImageFile
from ._deprecate import deprecate
class Format(IntEnum): class Format(IntEnum):
@ -54,21 +53,6 @@ class AlphaEncoding(IntEnum):
DXT5 = 7 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): def unpack_565(i):
return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3 return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3

View File

@ -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)

View File

@ -56,7 +56,6 @@ from enum import IntEnum
from io import BytesIO from io import BytesIO
from . import Image, ImageFile from . import Image, ImageFile
from ._deprecate import deprecate
MAGIC = b"FTEX" MAGIC = b"FTEX"
@ -66,17 +65,6 @@ class Format(IntEnum):
UNCOMPRESSED = 1 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): class FtexImageFile(ImageFile.ImageFile):
format = "FTEX" format = "FTEX"
format_description = "Texture File Format (IW2:EOC)" format_description = "Texture File Format (IW2:EOC)"

View File

@ -56,29 +56,8 @@ from . import (
_plugins, _plugins,
) )
from ._binary import i32le, o32be, o32le from ._binary import i32le, o32be, o32le
from ._deprecate import deprecate
from ._util import DeferredError, is_path 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",
"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__) logger = logging.getLogger(__name__)
@ -441,26 +420,18 @@ def _getencoder(mode, encoder_name, args, extra=()):
# Simple expression analyzer # 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: class _E:
def __init__(self, scale, data): def __init__(self, scale, offset):
self.scale = scale self.scale = scale
self.data = data self.offset = offset
def __neg__(self): def __neg__(self):
return _E(-self.scale, -self.data) return _E(-self.scale, -self.offset)
def __add__(self, other): def __add__(self, other):
if isinstance(other, _E): if isinstance(other, _E):
return _E(self.scale + other.scale, self.data + other.data) return _E(self.scale + other.scale, self.offset + other.offset)
return _E(self.scale, self.data + other) return _E(self.scale, self.offset + other)
__radd__ = __add__ __radd__ = __add__
@ -473,19 +444,19 @@ class _E:
def __mul__(self, other): def __mul__(self, other):
if isinstance(other, _E): if isinstance(other, _E):
return NotImplemented return NotImplemented
return _E(self.scale * other, self.data * other) return _E(self.scale * other, self.offset * other)
__rmul__ = __mul__ __rmul__ = __mul__
def __truediv__(self, other): def __truediv__(self, other):
if isinstance(other, _E): if isinstance(other, _E):
return NotImplemented return NotImplemented
return _E(self.scale / other, self.data / other) return _E(self.scale / other, self.offset / other)
def _getscaleoffset(expr): def _getscaleoffset(expr):
a = expr(_E(1, 0)) 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)
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@ -516,17 +487,10 @@ class Image:
self._size = (0, 0) self._size = (0, 0)
self.palette = None self.palette = None
self.info = {} self.info = {}
self._category = 0
self.readonly = 0 self.readonly = 0
self.pyaccess = None self.pyaccess = None
self._exif = 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 @property
def width(self): def width(self):
return self.size[0] return self.size[0]
@ -639,7 +603,6 @@ class Image:
and self.mode == other.mode and self.mode == other.mode
and self.size == other.size and self.size == other.size
and self.info == other.info and self.info == other.info
and self._category == other._category
and self.getpalette() == other.getpalette() and self.getpalette() == other.getpalette()
and self.tobytes() == other.tobytes() and self.tobytes() == other.tobytes()
) )

View File

@ -20,8 +20,6 @@ from enum import IntEnum
from PIL import Image from PIL import Image
from ._deprecate import deprecate
try: try:
from PIL import _imagingcms from PIL import _imagingcms
except ImportError as ex: except ImportError as ex:
@ -117,17 +115,6 @@ class Direction(IntEnum):
PROOF = 2 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 # flags

View File

@ -43,17 +43,6 @@ class Layout(IntEnum):
RAQM = 1 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: try:
from . import _imagingft as core from . import _imagingft as core
except ImportError as ex: except ImportError as ex:
@ -62,9 +51,6 @@ except ImportError as ex:
core = DeferredError(ex) core = DeferredError(ex)
_UNSPECIFIED = object()
# FIXME: add support for pilfont2 format (see FontFile.py) # FIXME: add support for pilfont2 format (see FontFile.py)
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@ -665,7 +651,6 @@ class FreeTypeFont:
self, self,
text, text,
mode="", mode="",
fill=_UNSPECIFIED,
direction=None, direction=None,
features=None, features=None,
language=None, language=None,
@ -691,12 +676,6 @@ class FreeTypeFont:
.. versionadded:: 1.1.5 .. 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 :param direction: Direction of the text. It can be 'rtl' (right to
left), 'ltr' (left to right) or 'ttb' (top to bottom). left), 'ltr' (left to right) or 'ttb' (top to bottom).
Requires libraqm. Requires libraqm.
@ -749,10 +728,6 @@ class FreeTypeFont:
:py:mod:`PIL.Image.core` interface module, and the text offset, the :py:mod:`PIL.Image.core` interface module, and the text offset, the
gap between the starting coordinate and the first marking 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( size, offset = self.font.getsize(
text, mode, direction, features, language, anchor text, mode, direction, features, language, anchor
) )
@ -761,7 +736,7 @@ class FreeTypeFont:
size = tuple(math.ceil(size[i] + stroke_width * 2 + start[i]) for i in range(2)) 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 offset = offset[0] - stroke_width, offset[1] - stroke_width
Image._decompression_bomb_check(size) 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): if min(size):
self.font.render( self.font.render(
text, text,

View File

@ -19,7 +19,6 @@
import array import array
from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile
from ._deprecate import deprecate
class ImagePalette: class ImagePalette:
@ -34,16 +33,11 @@ class ImagePalette:
Defaults to an empty palette. 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.mode = mode
self.rawmode = None # if set, palette contains raw data self.rawmode = None # if set, palette contains raw data
self.palette = palette or bytearray() self.palette = palette or bytearray()
self.dirty = None 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 @property
def palette(self): def palette(self):

View File

@ -20,14 +20,11 @@ import sys
from io import BytesIO from io import BytesIO
from . import Image from . import Image
from ._deprecate import deprecate
from ._util import is_path from ._util import is_path
qt_versions = [ qt_versions = [
["6", "PyQt6"], ["6", "PyQt6"],
["side6", "PySide6"], ["side6", "PySide6"],
["5", "PyQt5"],
["side2", "PySide2"],
] ]
# If a version has already been imported, attempt it first # 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": elif qt_module == "PySide6":
from PySide6.QtCore import QBuffer, QIODevice from PySide6.QtCore import QBuffer, QIODevice
from PySide6.QtGui import QImage, QPixmap, qRgba 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): except (ImportError, RuntimeError):
continue continue
qt_is_installed = True qt_is_installed = True

View File

@ -19,8 +19,6 @@ from shlex import quote
from PIL import Image from PIL import Image
from ._deprecate import deprecate
_viewers = [] _viewers = []
@ -111,21 +109,10 @@ class Viewer:
"""Display the given image.""" """Display the given image."""
return self.show_file(self.save_image(image), **options) 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. 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 os.system(self.get_command(path, **options)) # nosec
return 1 return 1
@ -164,21 +151,10 @@ class MacViewer(Viewer):
command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&" command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&"
return command return command
def show_file(self, path=None, **options): def show_file(self, path, **options):
""" """
Display given file. 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]) subprocess.call(["open", "-a", "Preview.app", path])
executable = sys.executable or shutil.which("python3") executable = sys.executable or shutil.which("python3")
if executable: if executable:
@ -215,21 +191,10 @@ class XDGViewer(UnixViewer):
command = executable = "xdg-open" command = executable = "xdg-open"
return command, executable return command, executable
def show_file(self, path=None, **options): def show_file(self, path, **options):
""" """
Display given file. 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]) subprocess.Popen(["xdg-open", path])
return 1 return 1
@ -246,20 +211,10 @@ class DisplayViewer(UnixViewer):
command += f" -title {quote(title)}" command += f" -title {quote(title)}"
return command, executable return command, executable
def show_file(self, path=None, **options): def show_file(self, path, **options):
""" """
Display given file. 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"] args = ["display"]
title = options.get("title") title = options.get("title")
if title: if title:
@ -278,20 +233,10 @@ class GmDisplayViewer(UnixViewer):
command = "gm display" command = "gm display"
return command, executable return command, executable
def show_file(self, path=None, **options): def show_file(self, path, **options):
""" """
Display given file. 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]) subprocess.Popen(["gm", "display", path])
return 1 return 1
@ -304,20 +249,10 @@ class EogViewer(UnixViewer):
command = "eog -n" command = "eog -n"
return command, executable return command, executable
def show_file(self, path=None, **options): def show_file(self, path, **options):
""" """
Display given file. 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]) subprocess.Popen(["eog", "-n", path])
return 1 return 1
@ -336,20 +271,10 @@ class XVViewer(UnixViewer):
command += f" -name {quote(title)}" command += f" -name {quote(title)}"
return command, executable return command, executable
def show_file(self, path=None, **options): def show_file(self, path, **options):
""" """
Display given file. 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"] args = ["xv"]
title = options.get("title") title = options.get("title")
if title: if title:

View File

@ -29,7 +29,6 @@ import tkinter
from io import BytesIO from io import BytesIO
from . import Image from . import Image
from ._deprecate import deprecate
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Check for Tkinter interface hooks # Check for Tkinter interface hooks
@ -162,7 +161,7 @@ class PhotoImage:
""" """
return self.__size[1] 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 Paste a PIL image into the photo image. Note that this can
be very slow if the photo image is displayed. 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 :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 mode does not match, the image is converted to the mode of
the bitmap image. 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 # convert to blittable
im.load() im.load()
image = im.im image = im.im

View File

@ -46,7 +46,6 @@ from ._binary import i16be as i16
from ._binary import i32be as i32 from ._binary import i32be as i32
from ._binary import o8 from ._binary import o8
from ._binary import o16be as o16 from ._binary import o16be as o16
from ._deprecate import deprecate
from .JpegPresets import presets from .JpegPresets import presets
# #
@ -612,11 +611,6 @@ samplings = {
# fmt: on # fmt: on
def convert_dict_qtables(qtables):
deprecate("convert_dict_qtables", 10, action="Conversion is no longer needed")
return qtables
def get_sampling(im): def get_sampling(im):
# There's no subsampling when images have only 1 layer # There's no subsampling when images have only 1 layer
# (grayscale images) or when they are CMYK (4 layers), # (grayscale images) or when they are CMYK (4 layers),

View File

@ -66,9 +66,6 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
self._n_frames = len(self.images) self._n_frames = len(self.images)
self.is_animated = self._n_frames > 1 self.is_animated = self._n_frames > 1
if len(self.images) > 1:
self._category = Image.CONTAINER
self.seek(0) self.seek(0)
def seek(self, frame): def seek(self, frame):

View File

@ -45,7 +45,6 @@ from ._binary import i32be as i32
from ._binary import o8 from ._binary import o8
from ._binary import o16be as o16 from ._binary import o16be as o16
from ._binary import o32be as o32 from ._binary import o32be as o32
from ._deprecate import deprecate
logger = logging.getLogger(__name__) 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): def _safe_zlib_decompress(s):
dobj = zlib.decompressobj() dobj = zlib.decompressobj()
plaintext = dobj.decompress(s, MAX_TEXT_CHUNK) plaintext = dobj.decompress(s, MAX_TEXT_CHUNK)

View File

@ -31,7 +31,6 @@ _plugins = [
"DdsImagePlugin", "DdsImagePlugin",
"EpsImagePlugin", "EpsImagePlugin",
"FitsImagePlugin", "FitsImagePlugin",
"FitsStubImagePlugin",
"FliImagePlugin", "FliImagePlugin",
"FpxImagePlugin", "FpxImagePlugin",
"FtexImagePlugin", "FtexImagePlugin",

View File

@ -4,8 +4,6 @@ import sys
import tkinter import tkinter
from tkinter import _tkinter as tk from tkinter import _tkinter as tk
from ._deprecate import deprecate
try: try:
if hasattr(sys, "pypy_find_executable"): if hasattr(sys, "pypy_find_executable"):
TKINTER_LIB = tk.tklib_cffi.__file__ TKINTER_LIB = tk.tklib_cffi.__file__
@ -17,7 +15,3 @@ except AttributeError:
TKINTER_LIB = None TKINTER_LIB = None
tk_version = str(tkinter.TkVersion) 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"
)

View File

@ -119,17 +119,7 @@ typedef struct Tk_PhotoImageBlock {
} Tk_PhotoImageBlock; } Tk_PhotoImageBlock;
/* Typedefs derived from function signatures in Tk header */ /* Typedefs derived from function signatures in Tk header */
/* Tk_PhotoPutBlock for Tk <= 8.4 */ typedef int (*Tk_PhotoPutBlock_t)(
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)(
Tcl_Interp *interp, Tcl_Interp *interp,
Tk_PhotoHandle handle, Tk_PhotoHandle handle,
Tk_PhotoImageBlock *blockPtr, Tk_PhotoImageBlock *blockPtr,
@ -138,8 +128,6 @@ typedef int (*Tk_PhotoPutBlock_85_t)(
int width, int width,
int height, int height,
int compRule); int compRule);
/* Tk_PhotoSetSize for Tk <= 8.4 */
typedef void (*Tk_PhotoSetSize_84_t)(Tk_PhotoHandle handle, int width, int height);
/* Tk_FindPhoto */ /* Tk_FindPhoto */
typedef Tk_PhotoHandle (*Tk_FindPhoto_t)(Tcl_Interp *interp, const char *imageName); typedef Tk_PhotoHandle (*Tk_FindPhoto_t)(Tcl_Interp *interp, const char *imageName);
/* Tk_PhotoGetImage */ /* Tk_PhotoGetImage */

View File

@ -48,14 +48,11 @@
* Global vars for Tcl / Tk functions. We load these symbols from the tkinter * Global vars for Tcl / Tk functions. We load these symbols from the tkinter
* extension module or loaded Tcl / Tk libraries at run-time. * 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_CreateCommand_t TCL_CREATE_COMMAND;
static Tcl_AppendResult_t TCL_APPEND_RESULT; static Tcl_AppendResult_t TCL_APPEND_RESULT;
static Tk_FindPhoto_t TK_FIND_PHOTO; static Tk_FindPhoto_t TK_FIND_PHOTO;
static Tk_PhotoGetImage_t TK_PHOTO_GET_IMAGE; static Tk_PhotoGetImage_t TK_PHOTO_GET_IMAGE;
static Tk_PhotoPutBlock_84_t TK_PHOTO_PUT_BLOCK_84; static Tk_PhotoPutBlock_t TK_PHOTO_PUT_BLOCK;
static Tk_PhotoSetSize_84_t TK_PHOTO_SET_SIZE_84;
static Tk_PhotoPutBlock_85_t TK_PHOTO_PUT_BLOCK_85;
static Imaging static Imaging
ImagingFind(const char *name) { ImagingFind(const char *name) {
@ -130,17 +127,7 @@ PyImagingPhotoPut(
block.pitch = im->linesize; block.pitch = im->linesize;
block.pixelPtr = (unsigned char *)im->block; block.pixelPtr = (unsigned char *)im->block;
if (TK_LT_85) { /* Tk 8.4 */ TK_PHOTO_PUT_BLOCK(
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, interp,
photo, photo,
&block, &block,
@ -149,7 +136,6 @@ PyImagingPhotoPut(
block.width, block.width,
block.height, block.height,
TK_PHOTO_COMPOSITE_SET); TK_PHOTO_COMPOSITE_SET);
}
return TCL_OK; return TCL_OK;
} }
@ -290,16 +276,7 @@ get_tk(HMODULE hMod) {
if ((TK_FIND_PHOTO = (Tk_FindPhoto_t)_dfunc(hMod, "Tk_FindPhoto")) == NULL) { if ((TK_FIND_PHOTO = (Tk_FindPhoto_t)_dfunc(hMod, "Tk_FindPhoto")) == NULL) {
return -1; return -1;
}; };
TK_LT_85 = GetProcAddress(hMod, "Tk_PhotoPutBlock_Panic") == NULL; TK_PHOTO_PUT_BLOCK = (Tk_PhotoPutBlock_t)func;
/* 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;
return 1; return 1;
} }
@ -422,18 +399,9 @@ _func_loader(void *lib) {
if ((TK_FIND_PHOTO = (Tk_FindPhoto_t)_dfunc(lib, "Tk_FindPhoto")) == NULL) { if ((TK_FIND_PHOTO = (Tk_FindPhoto_t)_dfunc(lib, "Tk_FindPhoto")) == NULL) {
return 1; 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 ( return (
((TK_PHOTO_PUT_BLOCK_84 = (TK_PHOTO_PUT_BLOCK =
(Tk_PhotoPutBlock_84_t)_dfunc(lib, "Tk_PhotoPutBlock")) == NULL) || (Tk_PhotoPutBlock_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);
} }
int int