Merge branch 'main' into cleanup-names

This commit is contained in:
Andrew Murray 2022-04-16 19:49:40 +10:00 committed by GitHub
commit 35fbf22250
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 207 additions and 183 deletions

91
Tests/test_deprecate.py Normal file
View File

@ -0,0 +1,91 @@
import pytest
from PIL import _deprecate
@pytest.mark.parametrize(
"version, expected",
[
(
10,
"Old thing is deprecated and will be removed in Pillow 10 "
r"\(2023-07-01\)\. Use new thing instead\.",
),
(
None,
r"Old thing is deprecated and will be removed in a future version\. "
r"Use new thing instead\.",
),
],
)
def test_version(version, expected):
with pytest.warns(DeprecationWarning, match=expected):
_deprecate.deprecate("Old thing", version, "new thing")
def test_unknown_version():
expected = r"Unknown removal version, update PIL\._deprecate\?"
with pytest.raises(ValueError, match=expected):
_deprecate.deprecate("Old thing", 12345, "new thing")
@pytest.mark.parametrize(
"deprecated, plural, expected",
[
(
"Old thing",
False,
r"Old thing is deprecated and should be removed\.",
),
(
"Old things",
True,
r"Old things are deprecated and should be removed\.",
),
],
)
def test_old_version(deprecated, plural, expected):
expected = r""
with pytest.raises(RuntimeError, match=expected):
_deprecate.deprecate(deprecated, 1, plural=plural)
def test_plural():
expected = (
r"Old things are deprecated and will be removed in Pillow 10 \(2023-07-01\)\. "
r"Use new thing instead\."
)
with pytest.warns(DeprecationWarning, match=expected):
_deprecate.deprecate("Old things", 10, "new thing", plural=True)
def test_replacement_and_action():
expected = "Use only one of 'replacement' and 'action'"
with pytest.raises(ValueError, match=expected):
_deprecate.deprecate(
"Old thing", 10, replacement="new thing", action="Upgrade to new thing"
)
@pytest.mark.parametrize(
"action",
[
"Upgrade to new thing",
"Upgrade to new thing.",
],
)
def test_action(action):
expected = (
r"Old thing is deprecated and will be removed in Pillow 10 \(2023-07-01\)\. "
r"Upgrade to new thing\."
)
with pytest.warns(DeprecationWarning, match=expected):
_deprecate.deprecate("Old thing", 10, action=action)
def test_no_replacement_or_action():
expected = (
r"Old thing is deprecated and will be removed in Pillow 10 \(2023-07-01\)"
)
with pytest.warns(DeprecationWarning, match=expected):
_deprecate.deprecate("Old thing", 10)

View File

@ -9,6 +9,14 @@ Internal Modules
:undoc-members:
:show-inheritance:
:mod:`~PIL._deprecate` Module
-----------------------------
.. automodule:: PIL._deprecate
:members:
:undoc-members:
:show-inheritance:
:mod:`~PIL._tkinter_finder` Module
----------------------------------

View File

@ -31,11 +31,11 @@ BLP files come in many different flavours:
import os
import struct
import warnings
from enum import IntEnum
from io import BytesIO
from . import Image, ImageFile
from ._deprecate import deprecate
class Format(IntEnum):
@ -55,7 +55,6 @@ class AlphaEncoding(IntEnum):
def __getattr__(name):
deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). "
for enum, prefix in {
Format: "BLP_FORMAT_",
Encoding: "BLP_ENCODING_",
@ -64,19 +63,7 @@ def __getattr__(name):
if name.startswith(prefix):
name = name[len(prefix) :]
if name in enum.__members__:
warnings.warn(
prefix
+ name
+ " is "
+ deprecated
+ "Use "
+ enum.__name__
+ "."
+ name
+ " instead.",
DeprecationWarning,
stacklevel=2,
)
deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}")
return enum[name]
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")

View File

@ -9,9 +9,8 @@
# See the README file for information on usage and redistribution.
#
import warnings
from . import FitsImagePlugin, Image, ImageFile
from ._deprecate import deprecate
_handler = None
@ -25,11 +24,11 @@ def register_handler(handler):
global _handler
_handler = handler
warnings.warn(
"FitsStubImagePlugin is deprecated and will be removed in Pillow "
"10 (2023-07-01). FITS images can now be read without a handler through "
"FitsImagePlugin instead.",
DeprecationWarning,
deprecate(
"FitsStubImagePlugin",
10,
action="FITS images can now be read without "
"a handler through FitsImagePlugin instead",
)
# Override FitsImagePlugin with this handler

View File

@ -52,11 +52,11 @@ Note: All data is stored in little-Endian (Intel) byte order.
"""
import struct
import warnings
from enum import IntEnum
from io import BytesIO
from . import Image, ImageFile
from ._deprecate import deprecate
MAGIC = b"FTEX"
@ -67,24 +67,11 @@ class Format(IntEnum):
def __getattr__(name):
deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). "
for enum, prefix in {Format: "FORMAT_"}.items():
if name.startswith(prefix):
name = name[len(prefix) :]
if name in enum.__members__:
warnings.warn(
prefix
+ name
+ " is "
+ deprecated
+ "Use "
+ enum.__name__
+ "."
+ name
+ " instead.",
DeprecationWarning,
stacklevel=2,
)
deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}")
return enum[name]
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")

View File

@ -50,28 +50,17 @@ except ImportError:
# Use __version__ instead.
from . import ImageMode, TiffTags, UnidentifiedImageError, __version__, _plugins
from ._binary import i32le, o32be, o32le
from ._deprecate import deprecate
from ._util import DeferredError, is_path
def __getattr__(name):
deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). "
categories = {"NORMAL": 0, "SEQUENCE": 1, "CONTAINER": 2}
if name in categories:
warnings.warn(
"Image categories are " + deprecated + "Use is_animated instead.",
DeprecationWarning,
stacklevel=2,
)
deprecate("Image categories", 10, "is_animated", plural=True)
return categories[name]
elif name in ("NEAREST", "NONE"):
warnings.warn(
name
+ " is "
+ deprecated
+ "Use Resampling.NEAREST or Dither.NONE instead.",
DeprecationWarning,
stacklevel=2,
)
deprecate(name, 10, "Resampling.NEAREST or Dither.NONE")
return 0
old_resampling = {
"LINEAR": "BILINEAR",
@ -79,31 +68,11 @@ def __getattr__(name):
"ANTIALIAS": "LANCZOS",
}
if name in old_resampling:
warnings.warn(
name
+ " is "
+ deprecated
+ "Use Resampling."
+ old_resampling[name]
+ " instead.",
DeprecationWarning,
stacklevel=2,
)
deprecate(name, 10, f"Resampling.{old_resampling[name]}")
return Resampling[old_resampling[name]]
for enum in (Transpose, Transform, Resampling, Dither, Palette, Quantize):
if name in enum.__members__:
warnings.warn(
name
+ " is "
+ deprecated
+ "Use "
+ enum.__name__
+ "."
+ name
+ " instead.",
DeprecationWarning,
stacklevel=2,
)
deprecate(name, 10, f"{enum.__name__}.{name}")
return enum[name]
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
@ -538,12 +507,7 @@ class Image:
def __getattr__(self, name):
if name == "category":
warnings.warn(
"Image categories are deprecated and will be removed in Pillow 10 "
"(2023-07-01). Use is_animated instead.",
DeprecationWarning,
stacklevel=2,
)
deprecate("Image categories", 10, "is_animated", plural=True)
return self._category
raise AttributeError(name)

View File

@ -16,11 +16,12 @@
# below for the original description.
import sys
import warnings
from enum import IntEnum
from PIL import Image
from ._deprecate import deprecate
try:
from PIL import _imagingcms
except ImportError as ex:
@ -117,24 +118,11 @@ class Direction(IntEnum):
def __getattr__(name):
deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). "
for enum, prefix in {Intent: "INTENT_", Direction: "DIRECTION_"}.items():
if name.startswith(prefix):
name = name[len(prefix) :]
if name in enum.__members__:
warnings.warn(
prefix
+ name
+ " is "
+ deprecated
+ "Use "
+ enum.__name__
+ "."
+ name
+ " instead.",
DeprecationWarning,
stacklevel=2,
)
deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}")
return enum[name]
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")

View File

@ -33,6 +33,7 @@ from enum import IntEnum
from io import BytesIO
from . import Image
from ._deprecate import deprecate
from ._util import is_directory, is_path
@ -42,24 +43,11 @@ class Layout(IntEnum):
def __getattr__(name):
deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). "
for enum, prefix in {Layout: "LAYOUT_"}.items():
if name.startswith(prefix):
name = name[len(prefix) :]
if name in enum.__members__:
warnings.warn(
prefix
+ name
+ " is "
+ deprecated
+ "Use "
+ enum.__name__
+ "."
+ name
+ " instead.",
DeprecationWarning,
stacklevel=2,
)
deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}")
return enum[name]
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
@ -196,8 +184,6 @@ class FreeTypeFont:
if core.HAVE_RAQM:
layout_engine = Layout.RAQM
elif layout_engine == Layout.RAQM and not core.HAVE_RAQM:
import warnings
warnings.warn(
"Raqm layout was requested, but Raqm is not available. "
"Falling back to basic layout."

View File

@ -17,9 +17,9 @@
#
import array
import warnings
from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile
from ._deprecate import deprecate
class ImagePalette:
@ -40,11 +40,7 @@ class ImagePalette:
self.palette = palette or bytearray()
self.dirty = None
if size != 0:
warnings.warn(
"The size parameter is deprecated and will be removed in Pillow 10 "
"(2023-07-01).",
DeprecationWarning,
)
deprecate("The size parameter", 10, None)
if size != len(self.palette):
raise ValueError("wrong palette size")

View File

@ -15,11 +15,12 @@ import os
import shutil
import subprocess
import sys
import warnings
from shlex import quote
from PIL import Image
from ._deprecate import deprecate
_viewers = []
@ -120,11 +121,7 @@ class Viewer:
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")
@ -176,11 +173,7 @@ class MacViewer(Viewer):
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")
@ -228,11 +221,7 @@ class XDGViewer(UnixViewer):
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")
@ -261,11 +250,7 @@ class DisplayViewer(UnixViewer):
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")
@ -296,11 +281,7 @@ class GmDisplayViewer(UnixViewer):
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")
@ -325,11 +306,7 @@ class EogViewer(UnixViewer):
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")
@ -360,11 +337,7 @@ class XVViewer(UnixViewer):
"""
if path is None:
if "file" in options:
warnings.warn(
"The 'file' argument is deprecated and will be removed in Pillow "
"10 (2023-07-01). Use 'path' instead.",
DeprecationWarning,
)
deprecate("The 'file' argument", 10, "'path'")
path = options.pop("file")
else:
raise TypeError("Missing required argument: 'path'")

View File

@ -26,10 +26,10 @@
#
import tkinter
import warnings
from io import BytesIO
from . import Image
from ._deprecate import deprecate
# --------------------------------------------------------------------
# Check for Tkinter interface hooks
@ -187,11 +187,7 @@ class PhotoImage:
"""
if box is not None:
warnings.warn(
"The box parameter is deprecated and will be removed in Pillow 10 "
"(2023-07-01).",
DeprecationWarning,
)
deprecate("The box parameter", 10, None)
# convert to blittable
im.load()

View File

@ -45,6 +45,7 @@ from . import Image, ImageFile, TiffImagePlugin
from ._binary import i16be as i16
from ._binary import i32be as i32
from ._binary import o8
from ._deprecate import deprecate
from .JpegPresets import presets
#
@ -603,11 +604,7 @@ samplings = {
def convert_dict_qtables(qtables):
warnings.warn(
"convert_dict_qtables is deprecated and will be removed in Pillow 10"
"(2023-07-01). Conversion is no longer needed.",
DeprecationWarning,
)
deprecate("convert_dict_qtables", 10, action="Conversion is no longer needed")
return qtables

View File

@ -45,6 +45,7 @@ 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,24 +132,11 @@ class Blend(IntEnum):
def __getattr__(name):
deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). "
for enum, prefix in {Disposal: "APNG_DISPOSE_", Blend: "APNG_BLEND_"}.items():
if name.startswith(prefix):
name = name[len(prefix) :]
if name in enum.__members__:
warnings.warn(
prefix
+ name
+ " is "
+ deprecated
+ "Use "
+ enum.__name__
+ "."
+ name
+ " instead.",
DeprecationWarning,
stacklevel=2,
)
deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}")
return enum[name]
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")

66
src/PIL/_deprecate.py Normal file
View File

@ -0,0 +1,66 @@
from __future__ import annotations
import warnings
from . import __version__
def deprecate(
deprecated: str,
when: int | None,
replacement: str | None = None,
*,
action: str | None = None,
plural: bool = False,
) -> None:
"""
Deprecations helper.
:param deprecated: Name of thing to be deprecated.
:param when: Pillow major version to be removed in.
:param replacement: Name of replacement.
:param action: Instead of "replacement", give a custom call to action
e.g. "Upgrade to new thing".
:param plural: if the deprecated thing is plural, needing "are" instead of "is".
Usually of the form:
"[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd).
Use [replacement] instead."
You can leave out the replacement sentence:
"[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd)"
Or with another call to action:
"[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd).
[action]."
"""
is_ = "are" if plural else "is"
if when is None:
removed = "a future version"
elif when <= int(__version__.split(".")[0]):
raise RuntimeError(f"{deprecated} {is_} deprecated and should be removed.")
elif when == 10:
removed = "Pillow 10 (2023-07-01)"
else:
raise ValueError(f"Unknown removal version, update {__name__}?")
if replacement and action:
raise ValueError("Use only one of 'replacement' and 'action'")
if replacement:
action = f". Use {replacement} instead."
elif action:
action = f". {action.rstrip('.')}."
else:
action = ""
warnings.warn(
f"{deprecated} {is_} deprecated and will be removed in {removed}{action}",
DeprecationWarning,
stacklevel=3,
)

View File

@ -2,9 +2,10 @@
"""
import sys
import tkinter
import warnings
from tkinter import _tkinter as tk
from ._deprecate import deprecate
try:
if hasattr(sys, "pypy_find_executable"):
TKINTER_LIB = tk.tklib_cffi.__file__
@ -17,9 +18,6 @@ except AttributeError:
tk_version = str(tkinter.TkVersion)
if tk_version == "8.4":
warnings.warn(
"Support for Tk/Tcl 8.4 is deprecated and will be removed"
" in Pillow 10 (2023-07-01). Please upgrade to Tk/Tcl 8.5 "
"or newer.",
DeprecationWarning,
deprecate(
"Support for Tk/Tcl 8.4", 10, action="Please upgrade to Tk/Tcl 8.5 or newer"
)